removed copying file/game version between native RND and MM levels
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "tools.h"
22 #include "tape.h"
23 #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->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
3980   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
3981
3982   level_mm->time = level->time;
3983   level_mm->kettles_needed = level->gems_needed;
3984   level_mm->auto_count_kettles = level->auto_count_gems;
3985
3986   level_mm->laser_red = level->mm_laser_red;
3987   level_mm->laser_green = level->mm_laser_green;
3988   level_mm->laser_blue = level->mm_laser_blue;
3989
3990   strcpy(level_mm->name, level->name);
3991   strcpy(level_mm->author, level->author);
3992
3993   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
3994   level_mm->score[SC_KEY]        = level->score[SC_KEY];
3995   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
3996
3997   level_mm->amoeba_speed = level->amoeba_speed;
3998   level_mm->time_fuse = level->mm_time_fuse;
3999
4000   for (x = 0; x < level->fieldx; x++)
4001     for (y = 0; y < level->fieldy; y++)
4002       Ur[x][y] =
4003         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4004 }
4005
4006 void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4007 {
4008   struct LevelInfo_MM *level_mm = level->native_mm_level;
4009   int x, y;
4010
4011   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4012   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4013
4014   level->time = level_mm->time;
4015   level->gems_needed = level_mm->kettles_needed;
4016   level->auto_count_gems = level_mm->auto_count_kettles;
4017
4018   level->mm_laser_red = level_mm->laser_red;
4019   level->mm_laser_green = level_mm->laser_green;
4020   level->mm_laser_blue = level_mm->laser_blue;
4021
4022   strcpy(level->name, level_mm->name);
4023
4024   /* only overwrite author from 'levelinfo.conf' if author defined in level */
4025   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4026     strcpy(level->author, level_mm->author);
4027
4028   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
4029   level->score[SC_KEY]        = level_mm->score[SC_KEY];
4030   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4031
4032   level->amoeba_speed = level_mm->amoeba_speed;
4033   level->mm_time_fuse = level_mm->time_fuse;
4034
4035   for (x = 0; x < level->fieldx; x++)
4036     for (y = 0; y < level->fieldy; y++)
4037       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4038 }
4039
4040
4041 /* ------------------------------------------------------------------------- */
4042 /* functions for loading DC level                                            */
4043 /* ------------------------------------------------------------------------- */
4044
4045 #define DC_LEVEL_HEADER_SIZE            344
4046
4047 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
4048 {
4049   static int last_data_encoded;
4050   static int offset1;
4051   static int offset2;
4052   int diff;
4053   int diff_hi, diff_lo;
4054   int data_hi, data_lo;
4055   unsigned short data_decoded;
4056
4057   if (init)
4058   {
4059     last_data_encoded = 0;
4060     offset1 = -1;
4061     offset2 = 0;
4062
4063     return 0;
4064   }
4065
4066   diff = data_encoded - last_data_encoded;
4067   diff_hi = diff & ~0xff;
4068   diff_lo = diff &  0xff;
4069
4070   offset2 += diff_lo;
4071
4072   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4073   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4074   data_hi = data_hi & 0xff00;
4075
4076   data_decoded = data_hi | data_lo;
4077
4078   last_data_encoded = data_encoded;
4079
4080   offset1 = (offset1 + 1) % 31;
4081   offset2 = offset2 & 0xff;
4082
4083   return data_decoded;
4084 }
4085
4086 int getMappedElement_DC(int element)
4087 {
4088   switch (element)
4089   {
4090     case 0x0000:
4091       element = EL_ROCK;
4092       break;
4093
4094       /* 0x0117 - 0x036e: (?) */
4095       /* EL_DIAMOND */
4096
4097       /* 0x042d - 0x0684: (?) */
4098       /* EL_EMERALD */
4099
4100     case 0x06f1:
4101       element = EL_NUT;
4102       break;
4103
4104     case 0x074c:
4105       element = EL_BOMB;
4106       break;
4107
4108     case 0x07a4:
4109       element = EL_PEARL;
4110       break;
4111
4112     case 0x0823:
4113       element = EL_CRYSTAL;
4114       break;
4115
4116     case 0x0e77:        /* quicksand (boulder) */
4117       element = EL_QUICKSAND_FAST_FULL;
4118       break;
4119
4120     case 0x0e99:        /* slow quicksand (boulder) */
4121       element = EL_QUICKSAND_FULL;
4122       break;
4123
4124     case 0x0ed2:
4125       element = EL_EM_EXIT_OPEN;
4126       break;
4127
4128     case 0x0ee3:
4129       element = EL_EM_EXIT_CLOSED;
4130       break;
4131
4132     case 0x0eeb:
4133       element = EL_EM_STEEL_EXIT_OPEN;
4134       break;
4135
4136     case 0x0efc:
4137       element = EL_EM_STEEL_EXIT_CLOSED;
4138       break;
4139
4140     case 0x0f4f:        /* dynamite (lit 1) */
4141       element = EL_EM_DYNAMITE_ACTIVE;
4142       break;
4143
4144     case 0x0f57:        /* dynamite (lit 2) */
4145       element = EL_EM_DYNAMITE_ACTIVE;
4146       break;
4147
4148     case 0x0f5f:        /* dynamite (lit 3) */
4149       element = EL_EM_DYNAMITE_ACTIVE;
4150       break;
4151
4152     case 0x0f67:        /* dynamite (lit 4) */
4153       element = EL_EM_DYNAMITE_ACTIVE;
4154       break;
4155
4156     case 0x0f81:
4157     case 0x0f82:
4158     case 0x0f83:
4159     case 0x0f84:
4160       element = EL_AMOEBA_WET;
4161       break;
4162
4163     case 0x0f85:
4164       element = EL_AMOEBA_DROP;
4165       break;
4166
4167     case 0x0fb9:
4168       element = EL_DC_MAGIC_WALL;
4169       break;
4170
4171     case 0x0fd0:
4172       element = EL_SPACESHIP_UP;
4173       break;
4174
4175     case 0x0fd9:
4176       element = EL_SPACESHIP_DOWN;
4177       break;
4178
4179     case 0x0ff1:
4180       element = EL_SPACESHIP_LEFT;
4181       break;
4182
4183     case 0x0ff9:
4184       element = EL_SPACESHIP_RIGHT;
4185       break;
4186
4187     case 0x1057:
4188       element = EL_BUG_UP;
4189       break;
4190
4191     case 0x1060:
4192       element = EL_BUG_DOWN;
4193       break;
4194
4195     case 0x1078:
4196       element = EL_BUG_LEFT;
4197       break;
4198
4199     case 0x1080:
4200       element = EL_BUG_RIGHT;
4201       break;
4202
4203     case 0x10de:
4204       element = EL_MOLE_UP;
4205       break;
4206
4207     case 0x10e7:
4208       element = EL_MOLE_DOWN;
4209       break;
4210
4211     case 0x10ff:
4212       element = EL_MOLE_LEFT;
4213       break;
4214
4215     case 0x1107:
4216       element = EL_MOLE_RIGHT;
4217       break;
4218
4219     case 0x11c0:
4220       element = EL_ROBOT;
4221       break;
4222
4223     case 0x13f5:
4224       element = EL_YAMYAM;
4225       break;
4226
4227     case 0x1425:
4228       element = EL_SWITCHGATE_OPEN;
4229       break;
4230
4231     case 0x1426:
4232       element = EL_SWITCHGATE_CLOSED;
4233       break;
4234
4235     case 0x1437:
4236       element = EL_DC_SWITCHGATE_SWITCH_UP;
4237       break;
4238
4239     case 0x143a:
4240       element = EL_TIMEGATE_CLOSED;
4241       break;
4242
4243     case 0x144c:        /* conveyor belt switch (green) */
4244       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4245       break;
4246
4247     case 0x144f:        /* conveyor belt switch (red) */
4248       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4249       break;
4250
4251     case 0x1452:        /* conveyor belt switch (blue) */
4252       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4253       break;
4254
4255     case 0x145b:
4256       element = EL_CONVEYOR_BELT_3_MIDDLE;
4257       break;
4258
4259     case 0x1463:
4260       element = EL_CONVEYOR_BELT_3_LEFT;
4261       break;
4262
4263     case 0x146b:
4264       element = EL_CONVEYOR_BELT_3_RIGHT;
4265       break;
4266
4267     case 0x1473:
4268       element = EL_CONVEYOR_BELT_1_MIDDLE;
4269       break;
4270
4271     case 0x147b:
4272       element = EL_CONVEYOR_BELT_1_LEFT;
4273       break;
4274
4275     case 0x1483:
4276       element = EL_CONVEYOR_BELT_1_RIGHT;
4277       break;
4278
4279     case 0x148b:
4280       element = EL_CONVEYOR_BELT_4_MIDDLE;
4281       break;
4282
4283     case 0x1493:
4284       element = EL_CONVEYOR_BELT_4_LEFT;
4285       break;
4286
4287     case 0x149b:
4288       element = EL_CONVEYOR_BELT_4_RIGHT;
4289       break;
4290
4291     case 0x14ac:
4292       element = EL_EXPANDABLE_WALL_HORIZONTAL;
4293       break;
4294
4295     case 0x14bd:
4296       element = EL_EXPANDABLE_WALL_VERTICAL;
4297       break;
4298
4299     case 0x14c6:
4300       element = EL_EXPANDABLE_WALL_ANY;
4301       break;
4302
4303     case 0x14ce:        /* growing steel wall (left/right) */
4304       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4305       break;
4306
4307     case 0x14df:        /* growing steel wall (up/down) */
4308       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4309       break;
4310
4311     case 0x14e8:        /* growing steel wall (up/down/left/right) */
4312       element = EL_EXPANDABLE_STEELWALL_ANY;
4313       break;
4314
4315     case 0x14e9:
4316       element = EL_SHIELD_DEADLY;
4317       break;
4318
4319     case 0x1501:
4320       element = EL_EXTRA_TIME;
4321       break;
4322
4323     case 0x154f:
4324       element = EL_ACID;
4325       break;
4326
4327     case 0x1577:
4328       element = EL_EMPTY_SPACE;
4329       break;
4330
4331     case 0x1578:        /* quicksand (empty) */
4332       element = EL_QUICKSAND_FAST_EMPTY;
4333       break;
4334
4335     case 0x1579:        /* slow quicksand (empty) */
4336       element = EL_QUICKSAND_EMPTY;
4337       break;
4338
4339       /* 0x157c - 0x158b: */
4340       /* EL_SAND */
4341
4342       /* 0x1590 - 0x159f: */
4343       /* EL_DC_LANDMINE */
4344
4345     case 0x15a0:
4346       element = EL_EM_DYNAMITE;
4347       break;
4348
4349     case 0x15a1:        /* key (red) */
4350       element = EL_EM_KEY_1;
4351       break;
4352
4353     case 0x15a2:        /* key (yellow) */
4354       element = EL_EM_KEY_2;
4355       break;
4356
4357     case 0x15a3:        /* key (blue) */
4358       element = EL_EM_KEY_4;
4359       break;
4360
4361     case 0x15a4:        /* key (green) */
4362       element = EL_EM_KEY_3;
4363       break;
4364
4365     case 0x15a5:        /* key (white) */
4366       element = EL_DC_KEY_WHITE;
4367       break;
4368
4369     case 0x15a6:
4370       element = EL_WALL_SLIPPERY;
4371       break;
4372
4373     case 0x15a7:
4374       element = EL_WALL;
4375       break;
4376
4377     case 0x15a8:        /* wall (not round) */
4378       element = EL_WALL;
4379       break;
4380
4381     case 0x15a9:        /* (blue) */
4382       element = EL_CHAR_A;
4383       break;
4384
4385     case 0x15aa:        /* (blue) */
4386       element = EL_CHAR_B;
4387       break;
4388
4389     case 0x15ab:        /* (blue) */
4390       element = EL_CHAR_C;
4391       break;
4392
4393     case 0x15ac:        /* (blue) */
4394       element = EL_CHAR_D;
4395       break;
4396
4397     case 0x15ad:        /* (blue) */
4398       element = EL_CHAR_E;
4399       break;
4400
4401     case 0x15ae:        /* (blue) */
4402       element = EL_CHAR_F;
4403       break;
4404
4405     case 0x15af:        /* (blue) */
4406       element = EL_CHAR_G;
4407       break;
4408
4409     case 0x15b0:        /* (blue) */
4410       element = EL_CHAR_H;
4411       break;
4412
4413     case 0x15b1:        /* (blue) */
4414       element = EL_CHAR_I;
4415       break;
4416
4417     case 0x15b2:        /* (blue) */
4418       element = EL_CHAR_J;
4419       break;
4420
4421     case 0x15b3:        /* (blue) */
4422       element = EL_CHAR_K;
4423       break;
4424
4425     case 0x15b4:        /* (blue) */
4426       element = EL_CHAR_L;
4427       break;
4428
4429     case 0x15b5:        /* (blue) */
4430       element = EL_CHAR_M;
4431       break;
4432
4433     case 0x15b6:        /* (blue) */
4434       element = EL_CHAR_N;
4435       break;
4436
4437     case 0x15b7:        /* (blue) */
4438       element = EL_CHAR_O;
4439       break;
4440
4441     case 0x15b8:        /* (blue) */
4442       element = EL_CHAR_P;
4443       break;
4444
4445     case 0x15b9:        /* (blue) */
4446       element = EL_CHAR_Q;
4447       break;
4448
4449     case 0x15ba:        /* (blue) */
4450       element = EL_CHAR_R;
4451       break;
4452
4453     case 0x15bb:        /* (blue) */
4454       element = EL_CHAR_S;
4455       break;
4456
4457     case 0x15bc:        /* (blue) */
4458       element = EL_CHAR_T;
4459       break;
4460
4461     case 0x15bd:        /* (blue) */
4462       element = EL_CHAR_U;
4463       break;
4464
4465     case 0x15be:        /* (blue) */
4466       element = EL_CHAR_V;
4467       break;
4468
4469     case 0x15bf:        /* (blue) */
4470       element = EL_CHAR_W;
4471       break;
4472
4473     case 0x15c0:        /* (blue) */
4474       element = EL_CHAR_X;
4475       break;
4476
4477     case 0x15c1:        /* (blue) */
4478       element = EL_CHAR_Y;
4479       break;
4480
4481     case 0x15c2:        /* (blue) */
4482       element = EL_CHAR_Z;
4483       break;
4484
4485     case 0x15c3:        /* (blue) */
4486       element = EL_CHAR_AUMLAUT;
4487       break;
4488
4489     case 0x15c4:        /* (blue) */
4490       element = EL_CHAR_OUMLAUT;
4491       break;
4492
4493     case 0x15c5:        /* (blue) */
4494       element = EL_CHAR_UUMLAUT;
4495       break;
4496
4497     case 0x15c6:        /* (blue) */
4498       element = EL_CHAR_0;
4499       break;
4500
4501     case 0x15c7:        /* (blue) */
4502       element = EL_CHAR_1;
4503       break;
4504
4505     case 0x15c8:        /* (blue) */
4506       element = EL_CHAR_2;
4507       break;
4508
4509     case 0x15c9:        /* (blue) */
4510       element = EL_CHAR_3;
4511       break;
4512
4513     case 0x15ca:        /* (blue) */
4514       element = EL_CHAR_4;
4515       break;
4516
4517     case 0x15cb:        /* (blue) */
4518       element = EL_CHAR_5;
4519       break;
4520
4521     case 0x15cc:        /* (blue) */
4522       element = EL_CHAR_6;
4523       break;
4524
4525     case 0x15cd:        /* (blue) */
4526       element = EL_CHAR_7;
4527       break;
4528
4529     case 0x15ce:        /* (blue) */
4530       element = EL_CHAR_8;
4531       break;
4532
4533     case 0x15cf:        /* (blue) */
4534       element = EL_CHAR_9;
4535       break;
4536
4537     case 0x15d0:        /* (blue) */
4538       element = EL_CHAR_PERIOD;
4539       break;
4540
4541     case 0x15d1:        /* (blue) */
4542       element = EL_CHAR_EXCLAM;
4543       break;
4544
4545     case 0x15d2:        /* (blue) */
4546       element = EL_CHAR_COLON;
4547       break;
4548
4549     case 0x15d3:        /* (blue) */
4550       element = EL_CHAR_LESS;
4551       break;
4552
4553     case 0x15d4:        /* (blue) */
4554       element = EL_CHAR_GREATER;
4555       break;
4556
4557     case 0x15d5:        /* (blue) */
4558       element = EL_CHAR_QUESTION;
4559       break;
4560
4561     case 0x15d6:        /* (blue) */
4562       element = EL_CHAR_COPYRIGHT;
4563       break;
4564
4565     case 0x15d7:        /* (blue) */
4566       element = EL_CHAR_UP;
4567       break;
4568
4569     case 0x15d8:        /* (blue) */
4570       element = EL_CHAR_DOWN;
4571       break;
4572
4573     case 0x15d9:        /* (blue) */
4574       element = EL_CHAR_BUTTON;
4575       break;
4576
4577     case 0x15da:        /* (blue) */
4578       element = EL_CHAR_PLUS;
4579       break;
4580
4581     case 0x15db:        /* (blue) */
4582       element = EL_CHAR_MINUS;
4583       break;
4584
4585     case 0x15dc:        /* (blue) */
4586       element = EL_CHAR_APOSTROPHE;
4587       break;
4588
4589     case 0x15dd:        /* (blue) */
4590       element = EL_CHAR_PARENLEFT;
4591       break;
4592
4593     case 0x15de:        /* (blue) */
4594       element = EL_CHAR_PARENRIGHT;
4595       break;
4596
4597     case 0x15df:        /* (green) */
4598       element = EL_CHAR_A;
4599       break;
4600
4601     case 0x15e0:        /* (green) */
4602       element = EL_CHAR_B;
4603       break;
4604
4605     case 0x15e1:        /* (green) */
4606       element = EL_CHAR_C;
4607       break;
4608
4609     case 0x15e2:        /* (green) */
4610       element = EL_CHAR_D;
4611       break;
4612
4613     case 0x15e3:        /* (green) */
4614       element = EL_CHAR_E;
4615       break;
4616
4617     case 0x15e4:        /* (green) */
4618       element = EL_CHAR_F;
4619       break;
4620
4621     case 0x15e5:        /* (green) */
4622       element = EL_CHAR_G;
4623       break;
4624
4625     case 0x15e6:        /* (green) */
4626       element = EL_CHAR_H;
4627       break;
4628
4629     case 0x15e7:        /* (green) */
4630       element = EL_CHAR_I;
4631       break;
4632
4633     case 0x15e8:        /* (green) */
4634       element = EL_CHAR_J;
4635       break;
4636
4637     case 0x15e9:        /* (green) */
4638       element = EL_CHAR_K;
4639       break;
4640
4641     case 0x15ea:        /* (green) */
4642       element = EL_CHAR_L;
4643       break;
4644
4645     case 0x15eb:        /* (green) */
4646       element = EL_CHAR_M;
4647       break;
4648
4649     case 0x15ec:        /* (green) */
4650       element = EL_CHAR_N;
4651       break;
4652
4653     case 0x15ed:        /* (green) */
4654       element = EL_CHAR_O;
4655       break;
4656
4657     case 0x15ee:        /* (green) */
4658       element = EL_CHAR_P;
4659       break;
4660
4661     case 0x15ef:        /* (green) */
4662       element = EL_CHAR_Q;
4663       break;
4664
4665     case 0x15f0:        /* (green) */
4666       element = EL_CHAR_R;
4667       break;
4668
4669     case 0x15f1:        /* (green) */
4670       element = EL_CHAR_S;
4671       break;
4672
4673     case 0x15f2:        /* (green) */
4674       element = EL_CHAR_T;
4675       break;
4676
4677     case 0x15f3:        /* (green) */
4678       element = EL_CHAR_U;
4679       break;
4680
4681     case 0x15f4:        /* (green) */
4682       element = EL_CHAR_V;
4683       break;
4684
4685     case 0x15f5:        /* (green) */
4686       element = EL_CHAR_W;
4687       break;
4688
4689     case 0x15f6:        /* (green) */
4690       element = EL_CHAR_X;
4691       break;
4692
4693     case 0x15f7:        /* (green) */
4694       element = EL_CHAR_Y;
4695       break;
4696
4697     case 0x15f8:        /* (green) */
4698       element = EL_CHAR_Z;
4699       break;
4700
4701     case 0x15f9:        /* (green) */
4702       element = EL_CHAR_AUMLAUT;
4703       break;
4704
4705     case 0x15fa:        /* (green) */
4706       element = EL_CHAR_OUMLAUT;
4707       break;
4708
4709     case 0x15fb:        /* (green) */
4710       element = EL_CHAR_UUMLAUT;
4711       break;
4712
4713     case 0x15fc:        /* (green) */
4714       element = EL_CHAR_0;
4715       break;
4716
4717     case 0x15fd:        /* (green) */
4718       element = EL_CHAR_1;
4719       break;
4720
4721     case 0x15fe:        /* (green) */
4722       element = EL_CHAR_2;
4723       break;
4724
4725     case 0x15ff:        /* (green) */
4726       element = EL_CHAR_3;
4727       break;
4728
4729     case 0x1600:        /* (green) */
4730       element = EL_CHAR_4;
4731       break;
4732
4733     case 0x1601:        /* (green) */
4734       element = EL_CHAR_5;
4735       break;
4736
4737     case 0x1602:        /* (green) */
4738       element = EL_CHAR_6;
4739       break;
4740
4741     case 0x1603:        /* (green) */
4742       element = EL_CHAR_7;
4743       break;
4744
4745     case 0x1604:        /* (green) */
4746       element = EL_CHAR_8;
4747       break;
4748
4749     case 0x1605:        /* (green) */
4750       element = EL_CHAR_9;
4751       break;
4752
4753     case 0x1606:        /* (green) */
4754       element = EL_CHAR_PERIOD;
4755       break;
4756
4757     case 0x1607:        /* (green) */
4758       element = EL_CHAR_EXCLAM;
4759       break;
4760
4761     case 0x1608:        /* (green) */
4762       element = EL_CHAR_COLON;
4763       break;
4764
4765     case 0x1609:        /* (green) */
4766       element = EL_CHAR_LESS;
4767       break;
4768
4769     case 0x160a:        /* (green) */
4770       element = EL_CHAR_GREATER;
4771       break;
4772
4773     case 0x160b:        /* (green) */
4774       element = EL_CHAR_QUESTION;
4775       break;
4776
4777     case 0x160c:        /* (green) */
4778       element = EL_CHAR_COPYRIGHT;
4779       break;
4780
4781     case 0x160d:        /* (green) */
4782       element = EL_CHAR_UP;
4783       break;
4784
4785     case 0x160e:        /* (green) */
4786       element = EL_CHAR_DOWN;
4787       break;
4788
4789     case 0x160f:        /* (green) */
4790       element = EL_CHAR_BUTTON;
4791       break;
4792
4793     case 0x1610:        /* (green) */
4794       element = EL_CHAR_PLUS;
4795       break;
4796
4797     case 0x1611:        /* (green) */
4798       element = EL_CHAR_MINUS;
4799       break;
4800
4801     case 0x1612:        /* (green) */
4802       element = EL_CHAR_APOSTROPHE;
4803       break;
4804
4805     case 0x1613:        /* (green) */
4806       element = EL_CHAR_PARENLEFT;
4807       break;
4808
4809     case 0x1614:        /* (green) */
4810       element = EL_CHAR_PARENRIGHT;
4811       break;
4812
4813     case 0x1615:        /* (blue steel) */
4814       element = EL_STEEL_CHAR_A;
4815       break;
4816
4817     case 0x1616:        /* (blue steel) */
4818       element = EL_STEEL_CHAR_B;
4819       break;
4820
4821     case 0x1617:        /* (blue steel) */
4822       element = EL_STEEL_CHAR_C;
4823       break;
4824
4825     case 0x1618:        /* (blue steel) */
4826       element = EL_STEEL_CHAR_D;
4827       break;
4828
4829     case 0x1619:        /* (blue steel) */
4830       element = EL_STEEL_CHAR_E;
4831       break;
4832
4833     case 0x161a:        /* (blue steel) */
4834       element = EL_STEEL_CHAR_F;
4835       break;
4836
4837     case 0x161b:        /* (blue steel) */
4838       element = EL_STEEL_CHAR_G;
4839       break;
4840
4841     case 0x161c:        /* (blue steel) */
4842       element = EL_STEEL_CHAR_H;
4843       break;
4844
4845     case 0x161d:        /* (blue steel) */
4846       element = EL_STEEL_CHAR_I;
4847       break;
4848
4849     case 0x161e:        /* (blue steel) */
4850       element = EL_STEEL_CHAR_J;
4851       break;
4852
4853     case 0x161f:        /* (blue steel) */
4854       element = EL_STEEL_CHAR_K;
4855       break;
4856
4857     case 0x1620:        /* (blue steel) */
4858       element = EL_STEEL_CHAR_L;
4859       break;
4860
4861     case 0x1621:        /* (blue steel) */
4862       element = EL_STEEL_CHAR_M;
4863       break;
4864
4865     case 0x1622:        /* (blue steel) */
4866       element = EL_STEEL_CHAR_N;
4867       break;
4868
4869     case 0x1623:        /* (blue steel) */
4870       element = EL_STEEL_CHAR_O;
4871       break;
4872
4873     case 0x1624:        /* (blue steel) */
4874       element = EL_STEEL_CHAR_P;
4875       break;
4876
4877     case 0x1625:        /* (blue steel) */
4878       element = EL_STEEL_CHAR_Q;
4879       break;
4880
4881     case 0x1626:        /* (blue steel) */
4882       element = EL_STEEL_CHAR_R;
4883       break;
4884
4885     case 0x1627:        /* (blue steel) */
4886       element = EL_STEEL_CHAR_S;
4887       break;
4888
4889     case 0x1628:        /* (blue steel) */
4890       element = EL_STEEL_CHAR_T;
4891       break;
4892
4893     case 0x1629:        /* (blue steel) */
4894       element = EL_STEEL_CHAR_U;
4895       break;
4896
4897     case 0x162a:        /* (blue steel) */
4898       element = EL_STEEL_CHAR_V;
4899       break;
4900
4901     case 0x162b:        /* (blue steel) */
4902       element = EL_STEEL_CHAR_W;
4903       break;
4904
4905     case 0x162c:        /* (blue steel) */
4906       element = EL_STEEL_CHAR_X;
4907       break;
4908
4909     case 0x162d:        /* (blue steel) */
4910       element = EL_STEEL_CHAR_Y;
4911       break;
4912
4913     case 0x162e:        /* (blue steel) */
4914       element = EL_STEEL_CHAR_Z;
4915       break;
4916
4917     case 0x162f:        /* (blue steel) */
4918       element = EL_STEEL_CHAR_AUMLAUT;
4919       break;
4920
4921     case 0x1630:        /* (blue steel) */
4922       element = EL_STEEL_CHAR_OUMLAUT;
4923       break;
4924
4925     case 0x1631:        /* (blue steel) */
4926       element = EL_STEEL_CHAR_UUMLAUT;
4927       break;
4928
4929     case 0x1632:        /* (blue steel) */
4930       element = EL_STEEL_CHAR_0;
4931       break;
4932
4933     case 0x1633:        /* (blue steel) */
4934       element = EL_STEEL_CHAR_1;
4935       break;
4936
4937     case 0x1634:        /* (blue steel) */
4938       element = EL_STEEL_CHAR_2;
4939       break;
4940
4941     case 0x1635:        /* (blue steel) */
4942       element = EL_STEEL_CHAR_3;
4943       break;
4944
4945     case 0x1636:        /* (blue steel) */
4946       element = EL_STEEL_CHAR_4;
4947       break;
4948
4949     case 0x1637:        /* (blue steel) */
4950       element = EL_STEEL_CHAR_5;
4951       break;
4952
4953     case 0x1638:        /* (blue steel) */
4954       element = EL_STEEL_CHAR_6;
4955       break;
4956
4957     case 0x1639:        /* (blue steel) */
4958       element = EL_STEEL_CHAR_7;
4959       break;
4960
4961     case 0x163a:        /* (blue steel) */
4962       element = EL_STEEL_CHAR_8;
4963       break;
4964
4965     case 0x163b:        /* (blue steel) */
4966       element = EL_STEEL_CHAR_9;
4967       break;
4968
4969     case 0x163c:        /* (blue steel) */
4970       element = EL_STEEL_CHAR_PERIOD;
4971       break;
4972
4973     case 0x163d:        /* (blue steel) */
4974       element = EL_STEEL_CHAR_EXCLAM;
4975       break;
4976
4977     case 0x163e:        /* (blue steel) */
4978       element = EL_STEEL_CHAR_COLON;
4979       break;
4980
4981     case 0x163f:        /* (blue steel) */
4982       element = EL_STEEL_CHAR_LESS;
4983       break;
4984
4985     case 0x1640:        /* (blue steel) */
4986       element = EL_STEEL_CHAR_GREATER;
4987       break;
4988
4989     case 0x1641:        /* (blue steel) */
4990       element = EL_STEEL_CHAR_QUESTION;
4991       break;
4992
4993     case 0x1642:        /* (blue steel) */
4994       element = EL_STEEL_CHAR_COPYRIGHT;
4995       break;
4996
4997     case 0x1643:        /* (blue steel) */
4998       element = EL_STEEL_CHAR_UP;
4999       break;
5000
5001     case 0x1644:        /* (blue steel) */
5002       element = EL_STEEL_CHAR_DOWN;
5003       break;
5004
5005     case 0x1645:        /* (blue steel) */
5006       element = EL_STEEL_CHAR_BUTTON;
5007       break;
5008
5009     case 0x1646:        /* (blue steel) */
5010       element = EL_STEEL_CHAR_PLUS;
5011       break;
5012
5013     case 0x1647:        /* (blue steel) */
5014       element = EL_STEEL_CHAR_MINUS;
5015       break;
5016
5017     case 0x1648:        /* (blue steel) */
5018       element = EL_STEEL_CHAR_APOSTROPHE;
5019       break;
5020
5021     case 0x1649:        /* (blue steel) */
5022       element = EL_STEEL_CHAR_PARENLEFT;
5023       break;
5024
5025     case 0x164a:        /* (blue steel) */
5026       element = EL_STEEL_CHAR_PARENRIGHT;
5027       break;
5028
5029     case 0x164b:        /* (green steel) */
5030       element = EL_STEEL_CHAR_A;
5031       break;
5032
5033     case 0x164c:        /* (green steel) */
5034       element = EL_STEEL_CHAR_B;
5035       break;
5036
5037     case 0x164d:        /* (green steel) */
5038       element = EL_STEEL_CHAR_C;
5039       break;
5040
5041     case 0x164e:        /* (green steel) */
5042       element = EL_STEEL_CHAR_D;
5043       break;
5044
5045     case 0x164f:        /* (green steel) */
5046       element = EL_STEEL_CHAR_E;
5047       break;
5048
5049     case 0x1650:        /* (green steel) */
5050       element = EL_STEEL_CHAR_F;
5051       break;
5052
5053     case 0x1651:        /* (green steel) */
5054       element = EL_STEEL_CHAR_G;
5055       break;
5056
5057     case 0x1652:        /* (green steel) */
5058       element = EL_STEEL_CHAR_H;
5059       break;
5060
5061     case 0x1653:        /* (green steel) */
5062       element = EL_STEEL_CHAR_I;
5063       break;
5064
5065     case 0x1654:        /* (green steel) */
5066       element = EL_STEEL_CHAR_J;
5067       break;
5068
5069     case 0x1655:        /* (green steel) */
5070       element = EL_STEEL_CHAR_K;
5071       break;
5072
5073     case 0x1656:        /* (green steel) */
5074       element = EL_STEEL_CHAR_L;
5075       break;
5076
5077     case 0x1657:        /* (green steel) */
5078       element = EL_STEEL_CHAR_M;
5079       break;
5080
5081     case 0x1658:        /* (green steel) */
5082       element = EL_STEEL_CHAR_N;
5083       break;
5084
5085     case 0x1659:        /* (green steel) */
5086       element = EL_STEEL_CHAR_O;
5087       break;
5088
5089     case 0x165a:        /* (green steel) */
5090       element = EL_STEEL_CHAR_P;
5091       break;
5092
5093     case 0x165b:        /* (green steel) */
5094       element = EL_STEEL_CHAR_Q;
5095       break;
5096
5097     case 0x165c:        /* (green steel) */
5098       element = EL_STEEL_CHAR_R;
5099       break;
5100
5101     case 0x165d:        /* (green steel) */
5102       element = EL_STEEL_CHAR_S;
5103       break;
5104
5105     case 0x165e:        /* (green steel) */
5106       element = EL_STEEL_CHAR_T;
5107       break;
5108
5109     case 0x165f:        /* (green steel) */
5110       element = EL_STEEL_CHAR_U;
5111       break;
5112
5113     case 0x1660:        /* (green steel) */
5114       element = EL_STEEL_CHAR_V;
5115       break;
5116
5117     case 0x1661:        /* (green steel) */
5118       element = EL_STEEL_CHAR_W;
5119       break;
5120
5121     case 0x1662:        /* (green steel) */
5122       element = EL_STEEL_CHAR_X;
5123       break;
5124
5125     case 0x1663:        /* (green steel) */
5126       element = EL_STEEL_CHAR_Y;
5127       break;
5128
5129     case 0x1664:        /* (green steel) */
5130       element = EL_STEEL_CHAR_Z;
5131       break;
5132
5133     case 0x1665:        /* (green steel) */
5134       element = EL_STEEL_CHAR_AUMLAUT;
5135       break;
5136
5137     case 0x1666:        /* (green steel) */
5138       element = EL_STEEL_CHAR_OUMLAUT;
5139       break;
5140
5141     case 0x1667:        /* (green steel) */
5142       element = EL_STEEL_CHAR_UUMLAUT;
5143       break;
5144
5145     case 0x1668:        /* (green steel) */
5146       element = EL_STEEL_CHAR_0;
5147       break;
5148
5149     case 0x1669:        /* (green steel) */
5150       element = EL_STEEL_CHAR_1;
5151       break;
5152
5153     case 0x166a:        /* (green steel) */
5154       element = EL_STEEL_CHAR_2;
5155       break;
5156
5157     case 0x166b:        /* (green steel) */
5158       element = EL_STEEL_CHAR_3;
5159       break;
5160
5161     case 0x166c:        /* (green steel) */
5162       element = EL_STEEL_CHAR_4;
5163       break;
5164
5165     case 0x166d:        /* (green steel) */
5166       element = EL_STEEL_CHAR_5;
5167       break;
5168
5169     case 0x166e:        /* (green steel) */
5170       element = EL_STEEL_CHAR_6;
5171       break;
5172
5173     case 0x166f:        /* (green steel) */
5174       element = EL_STEEL_CHAR_7;
5175       break;
5176
5177     case 0x1670:        /* (green steel) */
5178       element = EL_STEEL_CHAR_8;
5179       break;
5180
5181     case 0x1671:        /* (green steel) */
5182       element = EL_STEEL_CHAR_9;
5183       break;
5184
5185     case 0x1672:        /* (green steel) */
5186       element = EL_STEEL_CHAR_PERIOD;
5187       break;
5188
5189     case 0x1673:        /* (green steel) */
5190       element = EL_STEEL_CHAR_EXCLAM;
5191       break;
5192
5193     case 0x1674:        /* (green steel) */
5194       element = EL_STEEL_CHAR_COLON;
5195       break;
5196
5197     case 0x1675:        /* (green steel) */
5198       element = EL_STEEL_CHAR_LESS;
5199       break;
5200
5201     case 0x1676:        /* (green steel) */
5202       element = EL_STEEL_CHAR_GREATER;
5203       break;
5204
5205     case 0x1677:        /* (green steel) */
5206       element = EL_STEEL_CHAR_QUESTION;
5207       break;
5208
5209     case 0x1678:        /* (green steel) */
5210       element = EL_STEEL_CHAR_COPYRIGHT;
5211       break;
5212
5213     case 0x1679:        /* (green steel) */
5214       element = EL_STEEL_CHAR_UP;
5215       break;
5216
5217     case 0x167a:        /* (green steel) */
5218       element = EL_STEEL_CHAR_DOWN;
5219       break;
5220
5221     case 0x167b:        /* (green steel) */
5222       element = EL_STEEL_CHAR_BUTTON;
5223       break;
5224
5225     case 0x167c:        /* (green steel) */
5226       element = EL_STEEL_CHAR_PLUS;
5227       break;
5228
5229     case 0x167d:        /* (green steel) */
5230       element = EL_STEEL_CHAR_MINUS;
5231       break;
5232
5233     case 0x167e:        /* (green steel) */
5234       element = EL_STEEL_CHAR_APOSTROPHE;
5235       break;
5236
5237     case 0x167f:        /* (green steel) */
5238       element = EL_STEEL_CHAR_PARENLEFT;
5239       break;
5240
5241     case 0x1680:        /* (green steel) */
5242       element = EL_STEEL_CHAR_PARENRIGHT;
5243       break;
5244
5245     case 0x1681:        /* gate (red) */
5246       element = EL_EM_GATE_1;
5247       break;
5248
5249     case 0x1682:        /* secret gate (red) */
5250       element = EL_GATE_1_GRAY;
5251       break;
5252
5253     case 0x1683:        /* gate (yellow) */
5254       element = EL_EM_GATE_2;
5255       break;
5256
5257     case 0x1684:        /* secret gate (yellow) */
5258       element = EL_GATE_2_GRAY;
5259       break;
5260
5261     case 0x1685:        /* gate (blue) */
5262       element = EL_EM_GATE_4;
5263       break;
5264
5265     case 0x1686:        /* secret gate (blue) */
5266       element = EL_GATE_4_GRAY;
5267       break;
5268
5269     case 0x1687:        /* gate (green) */
5270       element = EL_EM_GATE_3;
5271       break;
5272
5273     case 0x1688:        /* secret gate (green) */
5274       element = EL_GATE_3_GRAY;
5275       break;
5276
5277     case 0x1689:        /* gate (white) */
5278       element = EL_DC_GATE_WHITE;
5279       break;
5280
5281     case 0x168a:        /* secret gate (white) */
5282       element = EL_DC_GATE_WHITE_GRAY;
5283       break;
5284
5285     case 0x168b:        /* secret gate (no key) */
5286       element = EL_DC_GATE_FAKE_GRAY;
5287       break;
5288
5289     case 0x168c:
5290       element = EL_ROBOT_WHEEL;
5291       break;
5292
5293     case 0x168d:
5294       element = EL_DC_TIMEGATE_SWITCH;
5295       break;
5296
5297     case 0x168e:
5298       element = EL_ACID_POOL_BOTTOM;
5299       break;
5300
5301     case 0x168f:
5302       element = EL_ACID_POOL_TOPLEFT;
5303       break;
5304
5305     case 0x1690:
5306       element = EL_ACID_POOL_TOPRIGHT;
5307       break;
5308
5309     case 0x1691:
5310       element = EL_ACID_POOL_BOTTOMLEFT;
5311       break;
5312
5313     case 0x1692:
5314       element = EL_ACID_POOL_BOTTOMRIGHT;
5315       break;
5316
5317     case 0x1693:
5318       element = EL_STEELWALL;
5319       break;
5320
5321     case 0x1694:
5322       element = EL_STEELWALL_SLIPPERY;
5323       break;
5324
5325     case 0x1695:        /* steel wall (not round) */
5326       element = EL_STEELWALL;
5327       break;
5328
5329     case 0x1696:        /* steel wall (left) */
5330       element = EL_DC_STEELWALL_1_LEFT;
5331       break;
5332
5333     case 0x1697:        /* steel wall (bottom) */
5334       element = EL_DC_STEELWALL_1_BOTTOM;
5335       break;
5336
5337     case 0x1698:        /* steel wall (right) */
5338       element = EL_DC_STEELWALL_1_RIGHT;
5339       break;
5340
5341     case 0x1699:        /* steel wall (top) */
5342       element = EL_DC_STEELWALL_1_TOP;
5343       break;
5344
5345     case 0x169a:        /* steel wall (left/bottom) */
5346       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5347       break;
5348
5349     case 0x169b:        /* steel wall (right/bottom) */
5350       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5351       break;
5352
5353     case 0x169c:        /* steel wall (right/top) */
5354       element = EL_DC_STEELWALL_1_TOPRIGHT;
5355       break;
5356
5357     case 0x169d:        /* steel wall (left/top) */
5358       element = EL_DC_STEELWALL_1_TOPLEFT;
5359       break;
5360
5361     case 0x169e:        /* steel wall (right/bottom small) */
5362       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5363       break;
5364
5365     case 0x169f:        /* steel wall (left/bottom small) */
5366       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5367       break;
5368
5369     case 0x16a0:        /* steel wall (right/top small) */
5370       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5371       break;
5372
5373     case 0x16a1:        /* steel wall (left/top small) */
5374       element = EL_DC_STEELWALL_1_TOPLEFT_2;
5375       break;
5376
5377     case 0x16a2:        /* steel wall (left/right) */
5378       element = EL_DC_STEELWALL_1_VERTICAL;
5379       break;
5380
5381     case 0x16a3:        /* steel wall (top/bottom) */
5382       element = EL_DC_STEELWALL_1_HORIZONTAL;
5383       break;
5384
5385     case 0x16a4:        /* steel wall 2 (left end) */
5386       element = EL_DC_STEELWALL_2_LEFT;
5387       break;
5388
5389     case 0x16a5:        /* steel wall 2 (right end) */
5390       element = EL_DC_STEELWALL_2_RIGHT;
5391       break;
5392
5393     case 0x16a6:        /* steel wall 2 (top end) */
5394       element = EL_DC_STEELWALL_2_TOP;
5395       break;
5396
5397     case 0x16a7:        /* steel wall 2 (bottom end) */
5398       element = EL_DC_STEELWALL_2_BOTTOM;
5399       break;
5400
5401     case 0x16a8:        /* steel wall 2 (left/right) */
5402       element = EL_DC_STEELWALL_2_HORIZONTAL;
5403       break;
5404
5405     case 0x16a9:        /* steel wall 2 (up/down) */
5406       element = EL_DC_STEELWALL_2_VERTICAL;
5407       break;
5408
5409     case 0x16aa:        /* steel wall 2 (mid) */
5410       element = EL_DC_STEELWALL_2_MIDDLE;
5411       break;
5412
5413     case 0x16ab:
5414       element = EL_SIGN_EXCLAMATION;
5415       break;
5416
5417     case 0x16ac:
5418       element = EL_SIGN_RADIOACTIVITY;
5419       break;
5420
5421     case 0x16ad:
5422       element = EL_SIGN_STOP;
5423       break;
5424
5425     case 0x16ae:
5426       element = EL_SIGN_WHEELCHAIR;
5427       break;
5428
5429     case 0x16af:
5430       element = EL_SIGN_PARKING;
5431       break;
5432
5433     case 0x16b0:
5434       element = EL_SIGN_NO_ENTRY;
5435       break;
5436
5437     case 0x16b1:
5438       element = EL_SIGN_HEART;
5439       break;
5440
5441     case 0x16b2:
5442       element = EL_SIGN_GIVE_WAY;
5443       break;
5444
5445     case 0x16b3:
5446       element = EL_SIGN_ENTRY_FORBIDDEN;
5447       break;
5448
5449     case 0x16b4:
5450       element = EL_SIGN_EMERGENCY_EXIT;
5451       break;
5452
5453     case 0x16b5:
5454       element = EL_SIGN_YIN_YANG;
5455       break;
5456
5457     case 0x16b6:
5458       element = EL_WALL_EMERALD;
5459       break;
5460
5461     case 0x16b7:
5462       element = EL_WALL_DIAMOND;
5463       break;
5464
5465     case 0x16b8:
5466       element = EL_WALL_PEARL;
5467       break;
5468
5469     case 0x16b9:
5470       element = EL_WALL_CRYSTAL;
5471       break;
5472
5473     case 0x16ba:
5474       element = EL_INVISIBLE_WALL;
5475       break;
5476
5477     case 0x16bb:
5478       element = EL_INVISIBLE_STEELWALL;
5479       break;
5480
5481       /* 0x16bc - 0x16cb: */
5482       /* EL_INVISIBLE_SAND */
5483
5484     case 0x16cc:
5485       element = EL_LIGHT_SWITCH;
5486       break;
5487
5488     case 0x16cd:
5489       element = EL_ENVELOPE_1;
5490       break;
5491
5492     default:
5493       if (element >= 0x0117 && element <= 0x036e)       /* (?) */
5494         element = EL_DIAMOND;
5495       else if (element >= 0x042d && element <= 0x0684)  /* (?) */
5496         element = EL_EMERALD;
5497       else if (element >= 0x157c && element <= 0x158b)
5498         element = EL_SAND;
5499       else if (element >= 0x1590 && element <= 0x159f)
5500         element = EL_DC_LANDMINE;
5501       else if (element >= 0x16bc && element <= 0x16cb)
5502         element = EL_INVISIBLE_SAND;
5503       else
5504       {
5505         Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5506         element = EL_UNKNOWN;
5507       }
5508       break;
5509   }
5510
5511   return getMappedElement(element);
5512 }
5513
5514 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5515                                        int nr)
5516 {
5517   byte header[DC_LEVEL_HEADER_SIZE];
5518   int envelope_size;
5519   int envelope_header_pos = 62;
5520   int envelope_content_pos = 94;
5521   int level_name_pos = 251;
5522   int level_author_pos = 292;
5523   int envelope_header_len;
5524   int envelope_content_len;
5525   int level_name_len;
5526   int level_author_len;
5527   int fieldx, fieldy;
5528   int num_yamyam_contents;
5529   int i, x, y;
5530
5531   getDecodedWord_DC(0, TRUE);           /* initialize DC2 decoding engine */
5532
5533   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5534   {
5535     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5536
5537     header[i * 2 + 0] = header_word >> 8;
5538     header[i * 2 + 1] = header_word & 0xff;
5539   }
5540
5541   /* read some values from level header to check level decoding integrity */
5542   fieldx = header[6] | (header[7] << 8);
5543   fieldy = header[8] | (header[9] << 8);
5544   num_yamyam_contents = header[60] | (header[61] << 8);
5545
5546   /* do some simple sanity checks to ensure that level was correctly decoded */
5547   if (fieldx < 1 || fieldx > 256 ||
5548       fieldy < 1 || fieldy > 256 ||
5549       num_yamyam_contents < 1 || num_yamyam_contents > 8)
5550   {
5551     level->no_valid_file = TRUE;
5552
5553     Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5554
5555     return;
5556   }
5557
5558   /* maximum envelope header size is 31 bytes */
5559   envelope_header_len   = header[envelope_header_pos];
5560   /* maximum envelope content size is 110 (156?) bytes */
5561   envelope_content_len  = header[envelope_content_pos];
5562
5563   /* maximum level title size is 40 bytes */
5564   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
5565   /* maximum level author size is 30 (51?) bytes */
5566   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5567
5568   envelope_size = 0;
5569
5570   for (i = 0; i < envelope_header_len; i++)
5571     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5572       level->envelope[0].text[envelope_size++] =
5573         header[envelope_header_pos + 1 + i];
5574
5575   if (envelope_header_len > 0 && envelope_content_len > 0)
5576   {
5577     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5578       level->envelope[0].text[envelope_size++] = '\n';
5579     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5580       level->envelope[0].text[envelope_size++] = '\n';
5581   }
5582
5583   for (i = 0; i < envelope_content_len; i++)
5584     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5585       level->envelope[0].text[envelope_size++] =
5586         header[envelope_content_pos + 1 + i];
5587
5588   level->envelope[0].text[envelope_size] = '\0';
5589
5590   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5591   level->envelope[0].ysize = 10;
5592   level->envelope[0].autowrap = TRUE;
5593   level->envelope[0].centered = TRUE;
5594
5595   for (i = 0; i < level_name_len; i++)
5596     level->name[i] = header[level_name_pos + 1 + i];
5597   level->name[level_name_len] = '\0';
5598
5599   for (i = 0; i < level_author_len; i++)
5600     level->author[i] = header[level_author_pos + 1 + i];
5601   level->author[level_author_len] = '\0';
5602
5603   num_yamyam_contents = header[60] | (header[61] << 8);
5604   level->num_yamyam_contents =
5605     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5606
5607   for (i = 0; i < num_yamyam_contents; i++)
5608   {
5609     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5610     {
5611       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5612       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5613
5614       if (i < MAX_ELEMENT_CONTENTS)
5615         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5616     }
5617   }
5618
5619   fieldx = header[6] | (header[7] << 8);
5620   fieldy = header[8] | (header[9] << 8);
5621   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5622   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5623
5624   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5625   {
5626     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5627     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5628
5629     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5630       level->field[x][y] = getMappedElement_DC(element_dc);
5631   }
5632
5633   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5634   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5635   level->field[x][y] = EL_PLAYER_1;
5636
5637   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5638   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5639   level->field[x][y] = EL_PLAYER_2;
5640
5641   level->gems_needed            = header[18] | (header[19] << 8);
5642
5643   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
5644   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
5645   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
5646   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
5647   level->score[SC_NUT]          = header[28] | (header[29] << 8);
5648   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
5649   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
5650   level->score[SC_BUG]          = header[34] | (header[35] << 8);
5651   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
5652   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
5653   level->score[SC_KEY]          = header[40] | (header[41] << 8);
5654   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
5655
5656   level->time                   = header[44] | (header[45] << 8);
5657
5658   level->amoeba_speed           = header[46] | (header[47] << 8);
5659   level->time_light             = header[48] | (header[49] << 8);
5660   level->time_timegate          = header[50] | (header[51] << 8);
5661   level->time_wheel             = header[52] | (header[53] << 8);
5662   level->time_magic_wall        = header[54] | (header[55] << 8);
5663   level->extra_time             = header[56] | (header[57] << 8);
5664   level->shield_normal_time     = header[58] | (header[59] << 8);
5665
5666   /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5667      can slip down from flat walls, like normal walls and steel walls */
5668   level->em_slippery_gems = TRUE;
5669 }
5670
5671 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5672                                      struct LevelFileInfo *level_file_info,
5673                                      boolean level_info_only)
5674 {
5675   char *filename = level_file_info->filename;
5676   File *file;
5677   int num_magic_bytes = 8;
5678   char magic_bytes[num_magic_bytes + 1];
5679   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5680
5681   if (!(file = openFile(filename, MODE_READ)))
5682   {
5683     level->no_valid_file = TRUE;
5684
5685     if (!level_info_only)
5686       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5687
5688     return;
5689   }
5690
5691   // fseek(file, 0x0000, SEEK_SET);
5692
5693   if (level_file_info->packed)
5694   {
5695     /* read "magic bytes" from start of file */
5696     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5697       magic_bytes[0] = '\0';
5698
5699     /* check "magic bytes" for correct file format */
5700     if (!strPrefix(magic_bytes, "DC2"))
5701     {
5702       level->no_valid_file = TRUE;
5703
5704       Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5705             filename);
5706
5707       return;
5708     }
5709
5710     if (strPrefix(magic_bytes, "DC2Win95") ||
5711         strPrefix(magic_bytes, "DC2Win98"))
5712     {
5713       int position_first_level = 0x00fa;
5714       int extra_bytes = 4;
5715       int skip_bytes;
5716
5717       /* advance file stream to first level inside the level package */
5718       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5719
5720       /* each block of level data is followed by block of non-level data */
5721       num_levels_to_skip *= 2;
5722
5723       /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5724       while (num_levels_to_skip >= 0)
5725       {
5726         /* advance file stream to next level inside the level package */
5727         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5728         {
5729           level->no_valid_file = TRUE;
5730
5731           Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5732                 filename);
5733
5734           return;
5735         }
5736
5737         /* skip apparently unused extra bytes following each level */
5738         ReadUnusedBytesFromFile(file, extra_bytes);
5739
5740         /* read size of next level in level package */
5741         skip_bytes = getFile32BitLE(file);
5742
5743         num_levels_to_skip--;
5744       }
5745     }
5746     else
5747     {
5748       level->no_valid_file = TRUE;
5749
5750       Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5751             filename);
5752
5753       return;
5754     }
5755   }
5756
5757   LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5758
5759   closeFile(file);
5760 }
5761
5762
5763 /* ------------------------------------------------------------------------- */
5764 /* functions for loading SB level                                            */
5765 /* ------------------------------------------------------------------------- */
5766
5767 int getMappedElement_SB(int element_ascii, boolean use_ces)
5768 {
5769   static struct
5770   {
5771     int ascii;
5772     int sb;
5773     int ce;
5774   }
5775   sb_element_mapping[] =
5776   {
5777     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  /* floor (space) */
5778     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  /* wall */
5779     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  /* player */
5780     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  /* box */
5781     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  /* goal square */
5782     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  /* box on goal square */
5783     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  /* player on goal square */
5784     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  /* floor beyond border */
5785
5786     { 0,   -1,                      -1          },
5787   };
5788
5789   int i;
5790
5791   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5792     if (element_ascii == sb_element_mapping[i].ascii)
5793       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5794
5795   return EL_UNDEFINED;
5796 }
5797
5798 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5799                                      struct LevelFileInfo *level_file_info,
5800                                      boolean level_info_only)
5801 {
5802   char *filename = level_file_info->filename;
5803   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5804   char last_comment[MAX_LINE_LEN];
5805   char level_name[MAX_LINE_LEN];
5806   char *line_ptr;
5807   File *file;
5808   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5809   boolean read_continued_line = FALSE;
5810   boolean reading_playfield = FALSE;
5811   boolean got_valid_playfield_line = FALSE;
5812   boolean invalid_playfield_char = FALSE;
5813   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5814   int file_level_nr = 0;
5815   int line_nr = 0;
5816   int x = 0, y = 0;             /* initialized to make compilers happy */
5817
5818   last_comment[0] = '\0';
5819   level_name[0] = '\0';
5820
5821   if (!(file = openFile(filename, MODE_READ)))
5822   {
5823     level->no_valid_file = TRUE;
5824
5825     if (!level_info_only)
5826       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5827
5828     return;
5829   }
5830
5831   while (!checkEndOfFile(file))
5832   {
5833     /* level successfully read, but next level may follow here */
5834     if (!got_valid_playfield_line && reading_playfield)
5835     {
5836       /* read playfield from single level file -- skip remaining file */
5837       if (!level_file_info->packed)
5838         break;
5839
5840       if (file_level_nr >= num_levels_to_skip)
5841         break;
5842
5843       file_level_nr++;
5844
5845       last_comment[0] = '\0';
5846       level_name[0] = '\0';
5847
5848       reading_playfield = FALSE;
5849     }
5850
5851     got_valid_playfield_line = FALSE;
5852
5853     /* read next line of input file */
5854     if (!getStringFromFile(file, line, MAX_LINE_LEN))
5855       break;
5856
5857     /* check if line was completely read and is terminated by line break */
5858     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5859       line_nr++;
5860
5861     /* cut trailing line break (this can be newline and/or carriage return) */
5862     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5863       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5864         *line_ptr = '\0';
5865
5866     /* copy raw input line for later use (mainly debugging output) */
5867     strcpy(line_raw, line);
5868
5869     if (read_continued_line)
5870     {
5871       /* append new line to existing line, if there is enough space */
5872       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5873         strcat(previous_line, line_ptr);
5874
5875       strcpy(line, previous_line);      /* copy storage buffer to line */
5876
5877       read_continued_line = FALSE;
5878     }
5879
5880     /* if the last character is '\', continue at next line */
5881     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5882     {
5883       line[strlen(line) - 1] = '\0';    /* cut off trailing backslash */
5884       strcpy(previous_line, line);      /* copy line to storage buffer */
5885
5886       read_continued_line = TRUE;
5887
5888       continue;
5889     }
5890
5891     /* skip empty lines */
5892     if (line[0] == '\0')
5893       continue;
5894
5895     /* extract comment text from comment line */
5896     if (line[0] == ';')
5897     {
5898       for (line_ptr = line; *line_ptr; line_ptr++)
5899         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5900           break;
5901
5902       strcpy(last_comment, line_ptr);
5903
5904       continue;
5905     }
5906
5907     /* extract level title text from line containing level title */
5908     if (line[0] == '\'')
5909     {
5910       strcpy(level_name, &line[1]);
5911
5912       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5913         level_name[strlen(level_name) - 1] = '\0';
5914
5915       continue;
5916     }
5917
5918     /* skip lines containing only spaces (or empty lines) */
5919     for (line_ptr = line; *line_ptr; line_ptr++)
5920       if (*line_ptr != ' ')
5921         break;
5922     if (*line_ptr == '\0')
5923       continue;
5924
5925     /* at this point, we have found a line containing part of a playfield */
5926
5927     got_valid_playfield_line = TRUE;
5928
5929     if (!reading_playfield)
5930     {
5931       reading_playfield = TRUE;
5932       invalid_playfield_char = FALSE;
5933
5934       for (x = 0; x < MAX_LEV_FIELDX; x++)
5935         for (y = 0; y < MAX_LEV_FIELDY; y++)
5936           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5937
5938       level->fieldx = 0;
5939       level->fieldy = 0;
5940
5941       /* start with topmost tile row */
5942       y = 0;
5943     }
5944
5945     /* skip playfield line if larger row than allowed */
5946     if (y >= MAX_LEV_FIELDY)
5947       continue;
5948
5949     /* start with leftmost tile column */
5950     x = 0;
5951
5952     /* read playfield elements from line */
5953     for (line_ptr = line; *line_ptr; line_ptr++)
5954     {
5955       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5956
5957       /* stop parsing playfield line if larger column than allowed */
5958       if (x >= MAX_LEV_FIELDX)
5959         break;
5960
5961       if (mapped_sb_element == EL_UNDEFINED)
5962       {
5963         invalid_playfield_char = TRUE;
5964
5965         break;
5966       }
5967
5968       level->field[x][y] = mapped_sb_element;
5969
5970       /* continue with next tile column */
5971       x++;
5972
5973       level->fieldx = MAX(x, level->fieldx);
5974     }
5975
5976     if (invalid_playfield_char)
5977     {
5978       /* if first playfield line, treat invalid lines as comment lines */
5979       if (y == 0)
5980         reading_playfield = FALSE;
5981
5982       continue;
5983     }
5984
5985     /* continue with next tile row */
5986     y++;
5987   }
5988
5989   closeFile(file);
5990
5991   level->fieldy = y;
5992
5993   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
5994   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
5995
5996   if (!reading_playfield)
5997   {
5998     level->no_valid_file = TRUE;
5999
6000     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6001
6002     return;
6003   }
6004
6005   if (*level_name != '\0')
6006   {
6007     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6008     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6009   }
6010   else if (*last_comment != '\0')
6011   {
6012     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6013     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6014   }
6015   else
6016   {
6017     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6018   }
6019
6020   /* set all empty fields beyond the border walls to invisible steel wall */
6021   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6022   {
6023     if ((x == 0 || x == level->fieldx - 1 ||
6024          y == 0 || y == level->fieldy - 1) &&
6025         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6026       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6027                      level->field, level->fieldx, level->fieldy);
6028   }
6029
6030   /* set special level settings for Sokoban levels */
6031
6032   level->time = 0;
6033   level->use_step_counter = TRUE;
6034
6035   if (load_xsb_to_ces)
6036   {
6037     /* special global settings can now be set in level template */
6038
6039     level->use_custom_template = TRUE;
6040   }
6041 }
6042
6043
6044 /* ------------------------------------------------------------------------- */
6045 /* functions for handling native levels                                      */
6046 /* ------------------------------------------------------------------------- */
6047
6048 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6049                                      struct LevelFileInfo *level_file_info,
6050                                      boolean level_info_only)
6051 {
6052   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6053     level->no_valid_file = TRUE;
6054 }
6055
6056 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6057                                      struct LevelFileInfo *level_file_info,
6058                                      boolean level_info_only)
6059 {
6060   int pos = 0;
6061
6062   /* determine position of requested level inside level package */
6063   if (level_file_info->packed)
6064     pos = level_file_info->nr - leveldir_current->first_level;
6065
6066   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6067     level->no_valid_file = TRUE;
6068 }
6069
6070 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6071                                      struct LevelFileInfo *level_file_info,
6072                                      boolean level_info_only)
6073 {
6074   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6075     level->no_valid_file = TRUE;
6076 }
6077
6078 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6079 {
6080   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6081     CopyNativeLevel_RND_to_EM(level);
6082   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6083     CopyNativeLevel_RND_to_SP(level);
6084   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6085     CopyNativeLevel_RND_to_MM(level);
6086 }
6087
6088 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6089 {
6090   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6091     CopyNativeLevel_EM_to_RND(level);
6092   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6093     CopyNativeLevel_SP_to_RND(level);
6094   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6095     CopyNativeLevel_MM_to_RND(level);
6096 }
6097
6098 void SaveNativeLevel(struct LevelInfo *level)
6099 {
6100   if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6101   {
6102     char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6103     char *filename = getLevelFilenameFromBasename(basename);
6104
6105     CopyNativeLevel_RND_to_SP(level);
6106     CopyNativeTape_RND_to_SP(level);
6107
6108     SaveNativeLevel_SP(filename);
6109   }
6110 }
6111
6112
6113 /* ------------------------------------------------------------------------- */
6114 /* functions for loading generic level                                       */
6115 /* ------------------------------------------------------------------------- */
6116
6117 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6118                                   struct LevelFileInfo *level_file_info,
6119                                   boolean level_info_only)
6120 {
6121   /* always start with reliable default values */
6122   setLevelInfoToDefaults(level, level_info_only, TRUE);
6123
6124   switch (level_file_info->type)
6125   {
6126     case LEVEL_FILE_TYPE_RND:
6127       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6128       break;
6129
6130     case LEVEL_FILE_TYPE_EM:
6131       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6132       level->game_engine_type = GAME_ENGINE_TYPE_EM;
6133       break;
6134
6135     case LEVEL_FILE_TYPE_SP:
6136       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6137       level->game_engine_type = GAME_ENGINE_TYPE_SP;
6138       break;
6139
6140     case LEVEL_FILE_TYPE_MM:
6141       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6142       level->game_engine_type = GAME_ENGINE_TYPE_MM;
6143       break;
6144
6145     case LEVEL_FILE_TYPE_DC:
6146       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6147       break;
6148
6149     case LEVEL_FILE_TYPE_SB:
6150       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6151       break;
6152
6153     default:
6154       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6155       break;
6156   }
6157
6158   /* if level file is invalid, restore level structure to default values */
6159   if (level->no_valid_file)
6160     setLevelInfoToDefaults(level, level_info_only, FALSE);
6161
6162   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6163     level->game_engine_type = GAME_ENGINE_TYPE_RND;
6164
6165   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6166     CopyNativeLevel_Native_to_RND(level);
6167 }
6168
6169 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6170 {
6171   static struct LevelFileInfo level_file_info;
6172
6173   /* always start with reliable default values */
6174   setFileInfoToDefaults(&level_file_info);
6175
6176   level_file_info.nr = 0;                       /* unknown level number */
6177   level_file_info.type = LEVEL_FILE_TYPE_RND;   /* no others supported yet */
6178   level_file_info.filename = filename;
6179
6180   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6181 }
6182
6183 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
6184 {
6185   int i, j;
6186
6187   if (leveldir_current == NULL)         /* only when dumping level */
6188     return;
6189
6190   /* all engine modifications also valid for levels which use latest engine */
6191   if (level->game_version < VERSION_IDENT(3,2,0,5))
6192   {
6193     /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6194     level->score[SC_TIME_BONUS] /= 10;
6195   }
6196
6197   if (leveldir_current->latest_engine)
6198   {
6199     /* ---------- use latest game engine ----------------------------------- */
6200
6201     /* For all levels which are forced to use the latest game engine version
6202        (normally all but user contributed, private and undefined levels), set
6203        the game engine version to the actual version; this allows for actual
6204        corrections in the game engine to take effect for existing, converted
6205        levels (from "classic" or other existing games) to make the emulation
6206        of the corresponding game more accurate, while (hopefully) not breaking
6207        existing levels created from other players. */
6208
6209     level->game_version = GAME_VERSION_ACTUAL;
6210
6211     /* Set special EM style gems behaviour: EM style gems slip down from
6212        normal, steel and growing wall. As this is a more fundamental change,
6213        it seems better to set the default behaviour to "off" (as it is more
6214        natural) and make it configurable in the level editor (as a property
6215        of gem style elements). Already existing converted levels (neither
6216        private nor contributed levels) are changed to the new behaviour. */
6217
6218     if (level->file_version < FILE_VERSION_2_0)
6219       level->em_slippery_gems = TRUE;
6220
6221     return;
6222   }
6223
6224   /* ---------- use game engine the level was created with ----------------- */
6225
6226   /* For all levels which are not forced to use the latest game engine
6227      version (normally user contributed, private and undefined levels),
6228      use the version of the game engine the levels were created for.
6229
6230      Since 2.0.1, the game engine version is now directly stored
6231      in the level file (chunk "VERS"), so there is no need anymore
6232      to set the game version from the file version (except for old,
6233      pre-2.0 levels, where the game version is still taken from the
6234      file format version used to store the level -- see above). */
6235
6236   /* player was faster than enemies in 1.0.0 and before */
6237   if (level->file_version == FILE_VERSION_1_0)
6238     for (i = 0; i < MAX_PLAYERS; i++)
6239       level->initial_player_stepsize[i] = STEPSIZE_FAST;
6240
6241   /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6242   if (level->game_version == VERSION_IDENT(2,0,1,0))
6243     level->em_slippery_gems = TRUE;
6244
6245   /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6246   if (level->game_version < VERSION_IDENT(2,2,0,0))
6247     level->use_spring_bug = TRUE;
6248
6249   if (level->game_version < VERSION_IDENT(3,2,0,5))
6250   {
6251     /* time orb caused limited time in endless time levels before 3.2.0-5 */
6252     level->use_time_orb_bug = TRUE;
6253
6254     /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6255     level->block_snap_field = FALSE;
6256
6257     /* extra time score was same value as time left score before 3.2.0-5 */
6258     level->extra_time_score = level->score[SC_TIME_BONUS];
6259   }
6260
6261   if (level->game_version < VERSION_IDENT(3,2,0,7))
6262   {
6263     /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6264     level->continuous_snapping = FALSE;
6265   }
6266
6267   /* only few elements were able to actively move into acid before 3.1.0 */
6268   /* trigger settings did not exist before 3.1.0; set to default "any" */
6269   if (level->game_version < VERSION_IDENT(3,1,0,0))
6270   {
6271     /* correct "can move into acid" settings (all zero in old levels) */
6272
6273     level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6274     level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6275
6276     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
6277     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6278     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
6279     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
6280
6281     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6282       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6283
6284     /* correct trigger settings (stored as zero == "none" in old levels) */
6285
6286     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6287     {
6288       int element = EL_CUSTOM_START + i;
6289       struct ElementInfo *ei = &element_info[element];
6290
6291       for (j = 0; j < ei->num_change_pages; j++)
6292       {
6293         struct ElementChangeInfo *change = &ei->change_page[j];
6294
6295         change->trigger_player = CH_PLAYER_ANY;
6296         change->trigger_page = CH_PAGE_ANY;
6297       }
6298     }
6299   }
6300
6301   /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6302   {
6303     int element = EL_CUSTOM_256;
6304     struct ElementInfo *ei = &element_info[element];
6305     struct ElementChangeInfo *change = &ei->change_page[0];
6306
6307     /* This is needed to fix a problem that was caused by a bugfix in function
6308        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6309        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6310        not replace walkable elements, but instead just placed the player on it,
6311        without placing the Sokoban field under the player). Unfortunately, this
6312        breaks "Snake Bite" style levels when the snake is halfway through a door
6313        that just closes (the snake head is still alive and can be moved in this
6314        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6315        player (without Sokoban element) which then gets killed as designed). */
6316
6317     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6318          strncmp(ei->description, "pause b4 death", 14) == 0) &&
6319         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6320       change->target_element = EL_PLAYER_1;
6321   }
6322
6323   /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6324   if (level->game_version < VERSION_IDENT(3,2,5,0))
6325   {
6326     /* This is needed to fix a problem that was caused by a bugfix in function
6327        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6328        corrects the behaviour when a custom element changes to another custom
6329        element with a higher element number that has change actions defined.
6330        Normally, only one change per frame is allowed for custom elements.
6331        Therefore, it is checked if a custom element already changed in the
6332        current frame; if it did, subsequent changes are suppressed.
6333        Unfortunately, this is only checked for element changes, but not for
6334        change actions, which are still executed. As the function above loops
6335        through all custom elements from lower to higher, an element change
6336        resulting in a lower CE number won't be checked again, while a target
6337        element with a higher number will also be checked, and potential change
6338        actions will get executed for this CE, too (which is wrong), while
6339        further changes are ignored (which is correct). As this bugfix breaks
6340        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6341        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6342        behaviour for existing levels and tapes that make use of this bug */
6343
6344     level->use_action_after_change_bug = TRUE;
6345   }
6346
6347   /* not centering level after relocating player was default only in 3.2.3 */
6348   if (level->game_version == VERSION_IDENT(3,2,3,0))    /* (no pre-releases) */
6349     level->shifted_relocation = TRUE;
6350
6351   /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6352   if (level->game_version < VERSION_IDENT(3,2,6,0))
6353     level->em_explodes_by_fire = TRUE;
6354 }
6355
6356 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6357 {
6358   int i, x, y;
6359
6360   /* map elements that have changed in newer versions */
6361   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6362                                                     level->game_version);
6363   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6364     for (x = 0; x < 3; x++)
6365       for (y = 0; y < 3; y++)
6366         level->yamyam_content[i].e[x][y] =
6367           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6368                                     level->game_version);
6369
6370 }
6371
6372 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6373 {
6374   int i, j;
6375
6376   /* map custom element change events that have changed in newer versions
6377      (these following values were accidentally changed in version 3.0.1)
6378      (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6379   if (level->game_version <= VERSION_IDENT(3,0,0,0))
6380   {
6381     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6382     {
6383       int element = EL_CUSTOM_START + i;
6384
6385       /* order of checking and copying events to be mapped is important */
6386       /* (do not change the start and end value -- they are constant) */
6387       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6388       {
6389         if (HAS_CHANGE_EVENT(element, j - 2))
6390         {
6391           SET_CHANGE_EVENT(element, j - 2, FALSE);
6392           SET_CHANGE_EVENT(element, j, TRUE);
6393         }
6394       }
6395
6396       /* order of checking and copying events to be mapped is important */
6397       /* (do not change the start and end value -- they are constant) */
6398       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6399       {
6400         if (HAS_CHANGE_EVENT(element, j - 1))
6401         {
6402           SET_CHANGE_EVENT(element, j - 1, FALSE);
6403           SET_CHANGE_EVENT(element, j, TRUE);
6404         }
6405       }
6406     }
6407   }
6408
6409   /* initialize "can_change" field for old levels with only one change page */
6410   if (level->game_version <= VERSION_IDENT(3,0,2,0))
6411   {
6412     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6413     {
6414       int element = EL_CUSTOM_START + i;
6415
6416       if (CAN_CHANGE(element))
6417         element_info[element].change->can_change = TRUE;
6418     }
6419   }
6420
6421   /* correct custom element values (for old levels without these options) */
6422   if (level->game_version < VERSION_IDENT(3,1,1,0))
6423   {
6424     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6425     {
6426       int element = EL_CUSTOM_START + i;
6427       struct ElementInfo *ei = &element_info[element];
6428
6429       if (ei->access_direction == MV_NO_DIRECTION)
6430         ei->access_direction = MV_ALL_DIRECTIONS;
6431     }
6432   }
6433
6434   /* correct custom element values (fix invalid values for all versions) */
6435   if (1)
6436   {
6437     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6438     {
6439       int element = EL_CUSTOM_START + i;
6440       struct ElementInfo *ei = &element_info[element];
6441
6442       for (j = 0; j < ei->num_change_pages; j++)
6443       {
6444         struct ElementChangeInfo *change = &ei->change_page[j];
6445
6446         if (change->trigger_player == CH_PLAYER_NONE)
6447           change->trigger_player = CH_PLAYER_ANY;
6448
6449         if (change->trigger_side == CH_SIDE_NONE)
6450           change->trigger_side = CH_SIDE_ANY;
6451       }
6452     }
6453   }
6454
6455   /* initialize "can_explode" field for old levels which did not store this */
6456   /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6457   if (level->game_version <= VERSION_IDENT(3,1,0,0))
6458   {
6459     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6460     {
6461       int element = EL_CUSTOM_START + i;
6462
6463       if (EXPLODES_1X1_OLD(element))
6464         element_info[element].explosion_type = EXPLODES_1X1;
6465
6466       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6467                                              EXPLODES_SMASHED(element) ||
6468                                              EXPLODES_IMPACT(element)));
6469     }
6470   }
6471
6472   /* correct previously hard-coded move delay values for maze runner style */
6473   if (level->game_version < VERSION_IDENT(3,1,1,0))
6474   {
6475     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6476     {
6477       int element = EL_CUSTOM_START + i;
6478
6479       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6480       {
6481         /* previously hard-coded and therefore ignored */
6482         element_info[element].move_delay_fixed = 9;
6483         element_info[element].move_delay_random = 0;
6484       }
6485     }
6486   }
6487
6488   /* set some other uninitialized values of custom elements in older levels */
6489   if (level->game_version < VERSION_IDENT(3,1,0,0))
6490   {
6491     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6492     {
6493       int element = EL_CUSTOM_START + i;
6494
6495       element_info[element].access_direction = MV_ALL_DIRECTIONS;
6496
6497       element_info[element].explosion_delay = 17;
6498       element_info[element].ignition_delay = 8;
6499     }
6500   }
6501 }
6502
6503 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6504 {
6505   LoadLevel_InitStandardElements(level);
6506
6507   if (level->file_has_custom_elements)
6508     LoadLevel_InitCustomElements(level);
6509
6510   /* initialize element properties for level editor etc. */
6511   InitElementPropertiesEngine(level->game_version);
6512   InitElementPropertiesGfxElement();
6513 }
6514
6515 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6516 {
6517   int x, y;
6518
6519   /* map elements that have changed in newer versions */
6520   for (y = 0; y < level->fieldy; y++)
6521     for (x = 0; x < level->fieldx; x++)
6522       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6523                                                      level->game_version);
6524
6525   /* clear unused playfield data (nicer if level gets resized in editor) */
6526   for (x = 0; x < MAX_LEV_FIELDX; x++)
6527     for (y = 0; y < MAX_LEV_FIELDY; y++)
6528       if (x >= level->fieldx || y >= level->fieldy)
6529         level->field[x][y] = EL_EMPTY;
6530
6531   /* copy elements to runtime playfield array */
6532   for (x = 0; x < MAX_LEV_FIELDX; x++)
6533     for (y = 0; y < MAX_LEV_FIELDY; y++)
6534       Feld[x][y] = level->field[x][y];
6535
6536   /* initialize level size variables for faster access */
6537   lev_fieldx = level->fieldx;
6538   lev_fieldy = level->fieldy;
6539
6540   /* determine border element for this level */
6541   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6542     BorderElement = EL_EMPTY;   /* (in editor, SetBorderElement() is used) */
6543   else
6544     SetBorderElement();
6545 }
6546
6547 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6548 {
6549   struct LevelFileInfo *level_file_info = &level->file_info;
6550
6551   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6552     CopyNativeLevel_RND_to_Native(level);
6553 }
6554
6555 void LoadLevelTemplate(int nr)
6556 {
6557   char *filename;
6558
6559   setLevelFileInfo(&level_template.file_info, nr);
6560   filename = level_template.file_info.filename;
6561
6562   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6563
6564   LoadLevel_InitVersion(&level_template, filename);
6565   LoadLevel_InitElements(&level_template, filename);
6566
6567   ActivateLevelTemplate();
6568 }
6569
6570 void LoadLevel(int nr)
6571 {
6572   char *filename;
6573
6574   setLevelFileInfo(&level.file_info, nr);
6575   filename = level.file_info.filename;
6576
6577   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6578
6579   if (level.use_custom_template)
6580     LoadLevelTemplate(-1);
6581
6582   LoadLevel_InitVersion(&level, filename);
6583   LoadLevel_InitElements(&level, filename);
6584   LoadLevel_InitPlayfield(&level, filename);
6585
6586   LoadLevel_InitNativeEngines(&level, filename);
6587 }
6588
6589 void LoadLevelInfoOnly(int nr)
6590 {
6591   setLevelFileInfo(&level.file_info, nr);
6592
6593   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6594 }
6595
6596 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6597 {
6598   int chunk_size = 0;
6599
6600   chunk_size += putFileVersion(file, level->file_version);
6601   chunk_size += putFileVersion(file, level->game_version);
6602
6603   return chunk_size;
6604 }
6605
6606 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6607 {
6608   int chunk_size = 0;
6609
6610   chunk_size += putFile16BitBE(file, level->creation_date.year);
6611   chunk_size += putFile8Bit(file,    level->creation_date.month);
6612   chunk_size += putFile8Bit(file,    level->creation_date.day);
6613
6614   return chunk_size;
6615 }
6616
6617 #if ENABLE_HISTORIC_CHUNKS
6618 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6619 {
6620   int i, x, y;
6621
6622   putFile8Bit(file, level->fieldx);
6623   putFile8Bit(file, level->fieldy);
6624
6625   putFile16BitBE(file, level->time);
6626   putFile16BitBE(file, level->gems_needed);
6627
6628   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6629     putFile8Bit(file, level->name[i]);
6630
6631   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6632     putFile8Bit(file, level->score[i]);
6633
6634   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6635     for (y = 0; y < 3; y++)
6636       for (x = 0; x < 3; x++)
6637         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6638                            level->yamyam_content[i].e[x][y]));
6639   putFile8Bit(file, level->amoeba_speed);
6640   putFile8Bit(file, level->time_magic_wall);
6641   putFile8Bit(file, level->time_wheel);
6642   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6643                      level->amoeba_content));
6644   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6645   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6646   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6647   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6648
6649   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6650
6651   putFile8Bit(file, (level->block_last_field ? 1 : 0));
6652   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6653   putFile32BitBE(file, level->can_move_into_acid_bits);
6654   putFile8Bit(file, level->dont_collide_with_bits);
6655
6656   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6657   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6658
6659   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6660   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6661   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6662
6663   putFile8Bit(file, level->game_engine_type);
6664
6665   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6666 }
6667 #endif
6668
6669 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6670 {
6671   int chunk_size = 0;
6672   int i;
6673
6674   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6675     chunk_size += putFile8Bit(file, level->name[i]);
6676
6677   return chunk_size;
6678 }
6679
6680 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6681 {
6682   int chunk_size = 0;
6683   int i;
6684
6685   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6686     chunk_size += putFile8Bit(file, level->author[i]);
6687
6688   return chunk_size;
6689 }
6690
6691 #if ENABLE_HISTORIC_CHUNKS
6692 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6693 {
6694   int chunk_size = 0;
6695   int x, y;
6696
6697   for (y = 0; y < level->fieldy; y++) 
6698     for (x = 0; x < level->fieldx; x++) 
6699       if (level->encoding_16bit_field)
6700         chunk_size += putFile16BitBE(file, level->field[x][y]);
6701       else
6702         chunk_size += putFile8Bit(file, level->field[x][y]);
6703
6704   return chunk_size;
6705 }
6706 #endif
6707
6708 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6709 {
6710   int chunk_size = 0;
6711   int x, y;
6712
6713   for (y = 0; y < level->fieldy; y++) 
6714     for (x = 0; x < level->fieldx; x++) 
6715       chunk_size += putFile16BitBE(file, level->field[x][y]);
6716
6717   return chunk_size;
6718 }
6719
6720 #if ENABLE_HISTORIC_CHUNKS
6721 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6722 {
6723   int i, x, y;
6724
6725   putFile8Bit(file, EL_YAMYAM);
6726   putFile8Bit(file, level->num_yamyam_contents);
6727   putFile8Bit(file, 0);
6728   putFile8Bit(file, 0);
6729
6730   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6731     for (y = 0; y < 3; y++)
6732       for (x = 0; x < 3; x++)
6733         if (level->encoding_16bit_field)
6734           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6735         else
6736           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6737 }
6738 #endif
6739
6740 #if ENABLE_HISTORIC_CHUNKS
6741 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6742 {
6743   int i, x, y;
6744   int num_contents, content_xsize, content_ysize;
6745   int content_array[MAX_ELEMENT_CONTENTS][3][3];
6746
6747   if (element == EL_YAMYAM)
6748   {
6749     num_contents = level->num_yamyam_contents;
6750     content_xsize = 3;
6751     content_ysize = 3;
6752
6753     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6754       for (y = 0; y < 3; y++)
6755         for (x = 0; x < 3; x++)
6756           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6757   }
6758   else if (element == EL_BD_AMOEBA)
6759   {
6760     num_contents = 1;
6761     content_xsize = 1;
6762     content_ysize = 1;
6763
6764     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6765       for (y = 0; y < 3; y++)
6766         for (x = 0; x < 3; x++)
6767           content_array[i][x][y] = EL_EMPTY;
6768     content_array[0][0][0] = level->amoeba_content;
6769   }
6770   else
6771   {
6772     /* chunk header already written -- write empty chunk data */
6773     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6774
6775     Error(ERR_WARN, "cannot save content for element '%d'", element);
6776     return;
6777   }
6778
6779   putFile16BitBE(file, element);
6780   putFile8Bit(file, num_contents);
6781   putFile8Bit(file, content_xsize);
6782   putFile8Bit(file, content_ysize);
6783
6784   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6785
6786   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6787     for (y = 0; y < 3; y++)
6788       for (x = 0; x < 3; x++)
6789         putFile16BitBE(file, content_array[i][x][y]);
6790 }
6791 #endif
6792
6793 #if ENABLE_HISTORIC_CHUNKS
6794 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6795 {
6796   int envelope_nr = element - EL_ENVELOPE_1;
6797   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6798   int chunk_size = 0;
6799   int i;
6800
6801   chunk_size += putFile16BitBE(file, element);
6802   chunk_size += putFile16BitBE(file, envelope_len);
6803   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6804   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6805
6806   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6807   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6808
6809   for (i = 0; i < envelope_len; i++)
6810     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6811
6812   return chunk_size;
6813 }
6814 #endif
6815
6816 #if ENABLE_HISTORIC_CHUNKS
6817 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6818                            int num_changed_custom_elements)
6819 {
6820   int i, check = 0;
6821
6822   putFile16BitBE(file, num_changed_custom_elements);
6823
6824   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6825   {
6826     int element = EL_CUSTOM_START + i;
6827
6828     struct ElementInfo *ei = &element_info[element];
6829
6830     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6831     {
6832       if (check < num_changed_custom_elements)
6833       {
6834         putFile16BitBE(file, element);
6835         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6836       }
6837
6838       check++;
6839     }
6840   }
6841
6842   if (check != num_changed_custom_elements)     /* should not happen */
6843     Error(ERR_WARN, "inconsistent number of custom element properties");
6844 }
6845 #endif
6846
6847 #if ENABLE_HISTORIC_CHUNKS
6848 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6849                            int num_changed_custom_elements)
6850 {
6851   int i, check = 0;
6852
6853   putFile16BitBE(file, num_changed_custom_elements);
6854
6855   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6856   {
6857     int element = EL_CUSTOM_START + i;
6858
6859     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6860     {
6861       if (check < num_changed_custom_elements)
6862       {
6863         putFile16BitBE(file, element);
6864         putFile16BitBE(file, element_info[element].change->target_element);
6865       }
6866
6867       check++;
6868     }
6869   }
6870
6871   if (check != num_changed_custom_elements)     /* should not happen */
6872     Error(ERR_WARN, "inconsistent number of custom target elements");
6873 }
6874 #endif
6875
6876 #if ENABLE_HISTORIC_CHUNKS
6877 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6878                            int num_changed_custom_elements)
6879 {
6880   int i, j, x, y, check = 0;
6881
6882   putFile16BitBE(file, num_changed_custom_elements);
6883
6884   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6885   {
6886     int element = EL_CUSTOM_START + i;
6887     struct ElementInfo *ei = &element_info[element];
6888
6889     if (ei->modified_settings)
6890     {
6891       if (check < num_changed_custom_elements)
6892       {
6893         putFile16BitBE(file, element);
6894
6895         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6896           putFile8Bit(file, ei->description[j]);
6897
6898         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6899
6900         /* some free bytes for future properties and padding */
6901         WriteUnusedBytesToFile(file, 7);
6902
6903         putFile8Bit(file, ei->use_gfx_element);
6904         putFile16BitBE(file, ei->gfx_element_initial);
6905
6906         putFile8Bit(file, ei->collect_score_initial);
6907         putFile8Bit(file, ei->collect_count_initial);
6908
6909         putFile16BitBE(file, ei->push_delay_fixed);
6910         putFile16BitBE(file, ei->push_delay_random);
6911         putFile16BitBE(file, ei->move_delay_fixed);
6912         putFile16BitBE(file, ei->move_delay_random);
6913
6914         putFile16BitBE(file, ei->move_pattern);
6915         putFile8Bit(file, ei->move_direction_initial);
6916         putFile8Bit(file, ei->move_stepsize);
6917
6918         for (y = 0; y < 3; y++)
6919           for (x = 0; x < 3; x++)
6920             putFile16BitBE(file, ei->content.e[x][y]);
6921
6922         putFile32BitBE(file, ei->change->events);
6923
6924         putFile16BitBE(file, ei->change->target_element);
6925
6926         putFile16BitBE(file, ei->change->delay_fixed);
6927         putFile16BitBE(file, ei->change->delay_random);
6928         putFile16BitBE(file, ei->change->delay_frames);
6929
6930         putFile16BitBE(file, ei->change->initial_trigger_element);
6931
6932         putFile8Bit(file, ei->change->explode);
6933         putFile8Bit(file, ei->change->use_target_content);
6934         putFile8Bit(file, ei->change->only_if_complete);
6935         putFile8Bit(file, ei->change->use_random_replace);
6936
6937         putFile8Bit(file, ei->change->random_percentage);
6938         putFile8Bit(file, ei->change->replace_when);
6939
6940         for (y = 0; y < 3; y++)
6941           for (x = 0; x < 3; x++)
6942             putFile16BitBE(file, ei->change->content.e[x][y]);
6943
6944         putFile8Bit(file, ei->slippery_type);
6945
6946         /* some free bytes for future properties and padding */
6947         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6948       }
6949
6950       check++;
6951     }
6952   }
6953
6954   if (check != num_changed_custom_elements)     /* should not happen */
6955     Error(ERR_WARN, "inconsistent number of custom element properties");
6956 }
6957 #endif
6958
6959 #if ENABLE_HISTORIC_CHUNKS
6960 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6961 {
6962   struct ElementInfo *ei = &element_info[element];
6963   int i, j, x, y;
6964
6965   /* ---------- custom element base property values (96 bytes) ------------- */
6966
6967   putFile16BitBE(file, element);
6968
6969   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6970     putFile8Bit(file, ei->description[i]);
6971
6972   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6973
6974   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
6975
6976   putFile8Bit(file, ei->num_change_pages);
6977
6978   putFile16BitBE(file, ei->ce_value_fixed_initial);
6979   putFile16BitBE(file, ei->ce_value_random_initial);
6980   putFile8Bit(file, ei->use_last_ce_value);
6981
6982   putFile8Bit(file, ei->use_gfx_element);
6983   putFile16BitBE(file, ei->gfx_element_initial);
6984
6985   putFile8Bit(file, ei->collect_score_initial);
6986   putFile8Bit(file, ei->collect_count_initial);
6987
6988   putFile8Bit(file, ei->drop_delay_fixed);
6989   putFile8Bit(file, ei->push_delay_fixed);
6990   putFile8Bit(file, ei->drop_delay_random);
6991   putFile8Bit(file, ei->push_delay_random);
6992   putFile16BitBE(file, ei->move_delay_fixed);
6993   putFile16BitBE(file, ei->move_delay_random);
6994
6995   /* bits 0 - 15 of "move_pattern" ... */
6996   putFile16BitBE(file, ei->move_pattern & 0xffff);
6997   putFile8Bit(file, ei->move_direction_initial);
6998   putFile8Bit(file, ei->move_stepsize);
6999
7000   putFile8Bit(file, ei->slippery_type);
7001
7002   for (y = 0; y < 3; y++)
7003     for (x = 0; x < 3; x++)
7004       putFile16BitBE(file, ei->content.e[x][y]);
7005
7006   putFile16BitBE(file, ei->move_enter_element);
7007   putFile16BitBE(file, ei->move_leave_element);
7008   putFile8Bit(file, ei->move_leave_type);
7009
7010   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
7011   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7012
7013   putFile8Bit(file, ei->access_direction);
7014
7015   putFile8Bit(file, ei->explosion_delay);
7016   putFile8Bit(file, ei->ignition_delay);
7017   putFile8Bit(file, ei->explosion_type);
7018
7019   /* some free bytes for future custom property values and padding */
7020   WriteUnusedBytesToFile(file, 1);
7021
7022   /* ---------- change page property values (48 bytes) --------------------- */
7023
7024   for (i = 0; i < ei->num_change_pages; i++)
7025   {
7026     struct ElementChangeInfo *change = &ei->change_page[i];
7027     unsigned int event_bits;
7028
7029     /* bits 0 - 31 of "has_event[]" ... */
7030     event_bits = 0;
7031     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7032       if (change->has_event[j])
7033         event_bits |= (1 << j);
7034     putFile32BitBE(file, event_bits);
7035
7036     putFile16BitBE(file, change->target_element);
7037
7038     putFile16BitBE(file, change->delay_fixed);
7039     putFile16BitBE(file, change->delay_random);
7040     putFile16BitBE(file, change->delay_frames);
7041
7042     putFile16BitBE(file, change->initial_trigger_element);
7043
7044     putFile8Bit(file, change->explode);
7045     putFile8Bit(file, change->use_target_content);
7046     putFile8Bit(file, change->only_if_complete);
7047     putFile8Bit(file, change->use_random_replace);
7048
7049     putFile8Bit(file, change->random_percentage);
7050     putFile8Bit(file, change->replace_when);
7051
7052     for (y = 0; y < 3; y++)
7053       for (x = 0; x < 3; x++)
7054         putFile16BitBE(file, change->target_content.e[x][y]);
7055
7056     putFile8Bit(file, change->can_change);
7057
7058     putFile8Bit(file, change->trigger_side);
7059
7060     putFile8Bit(file, change->trigger_player);
7061     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7062                        log_2(change->trigger_page)));
7063
7064     putFile8Bit(file, change->has_action);
7065     putFile8Bit(file, change->action_type);
7066     putFile8Bit(file, change->action_mode);
7067     putFile16BitBE(file, change->action_arg);
7068
7069     /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7070     event_bits = 0;
7071     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7072       if (change->has_event[j])
7073         event_bits |= (1 << (j - 32));
7074     putFile8Bit(file, event_bits);
7075   }
7076 }
7077 #endif
7078
7079 #if ENABLE_HISTORIC_CHUNKS
7080 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7081 {
7082   struct ElementInfo *ei = &element_info[element];
7083   struct ElementGroupInfo *group = ei->group;
7084   int i;
7085
7086   putFile16BitBE(file, element);
7087
7088   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7089     putFile8Bit(file, ei->description[i]);
7090
7091   putFile8Bit(file, group->num_elements);
7092
7093   putFile8Bit(file, ei->use_gfx_element);
7094   putFile16BitBE(file, ei->gfx_element_initial);
7095
7096   putFile8Bit(file, group->choice_mode);
7097
7098   /* some free bytes for future values and padding */
7099   WriteUnusedBytesToFile(file, 3);
7100
7101   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7102     putFile16BitBE(file, group->element[i]);
7103 }
7104 #endif
7105
7106 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7107                                 boolean write_element)
7108 {
7109   int save_type = entry->save_type;
7110   int data_type = entry->data_type;
7111   int conf_type = entry->conf_type;
7112   int byte_mask = conf_type & CONF_MASK_BYTES;
7113   int element = entry->element;
7114   int default_value = entry->default_value;
7115   int num_bytes = 0;
7116   boolean modified = FALSE;
7117
7118   if (byte_mask != CONF_MASK_MULTI_BYTES)
7119   {
7120     void *value_ptr = entry->value;
7121     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7122                  *(int *)value_ptr);
7123
7124     /* check if any settings have been modified before saving them */
7125     if (value != default_value)
7126       modified = TRUE;
7127
7128     /* do not save if explicitly told or if unmodified default settings */
7129     if ((save_type == SAVE_CONF_NEVER) ||
7130         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7131       return 0;
7132
7133     if (write_element)
7134       num_bytes += putFile16BitBE(file, element);
7135
7136     num_bytes += putFile8Bit(file, conf_type);
7137     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
7138                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7139                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7140                   0);
7141   }
7142   else if (data_type == TYPE_STRING)
7143   {
7144     char *default_string = entry->default_string;
7145     char *string = (char *)(entry->value);
7146     int string_length = strlen(string);
7147     int i;
7148
7149     /* check if any settings have been modified before saving them */
7150     if (!strEqual(string, default_string))
7151       modified = TRUE;
7152
7153     /* do not save if explicitly told or if unmodified default settings */
7154     if ((save_type == SAVE_CONF_NEVER) ||
7155         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7156       return 0;
7157
7158     if (write_element)
7159       num_bytes += putFile16BitBE(file, element);
7160
7161     num_bytes += putFile8Bit(file, conf_type);
7162     num_bytes += putFile16BitBE(file, string_length);
7163
7164     for (i = 0; i < string_length; i++)
7165       num_bytes += putFile8Bit(file, string[i]);
7166   }
7167   else if (data_type == TYPE_ELEMENT_LIST)
7168   {
7169     int *element_array = (int *)(entry->value);
7170     int num_elements = *(int *)(entry->num_entities);
7171     int i;
7172
7173     /* check if any settings have been modified before saving them */
7174     for (i = 0; i < num_elements; i++)
7175       if (element_array[i] != default_value)
7176         modified = TRUE;
7177
7178     /* do not save if explicitly told or if unmodified default settings */
7179     if ((save_type == SAVE_CONF_NEVER) ||
7180         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7181       return 0;
7182
7183     if (write_element)
7184       num_bytes += putFile16BitBE(file, element);
7185
7186     num_bytes += putFile8Bit(file, conf_type);
7187     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7188
7189     for (i = 0; i < num_elements; i++)
7190       num_bytes += putFile16BitBE(file, element_array[i]);
7191   }
7192   else if (data_type == TYPE_CONTENT_LIST)
7193   {
7194     struct Content *content = (struct Content *)(entry->value);
7195     int num_contents = *(int *)(entry->num_entities);
7196     int i, x, y;
7197
7198     /* check if any settings have been modified before saving them */
7199     for (i = 0; i < num_contents; i++)
7200       for (y = 0; y < 3; y++)
7201         for (x = 0; x < 3; x++)
7202           if (content[i].e[x][y] != default_value)
7203             modified = TRUE;
7204
7205     /* do not save if explicitly told or if unmodified default settings */
7206     if ((save_type == SAVE_CONF_NEVER) ||
7207         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7208       return 0;
7209
7210     if (write_element)
7211       num_bytes += putFile16BitBE(file, element);
7212
7213     num_bytes += putFile8Bit(file, conf_type);
7214     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7215
7216     for (i = 0; i < num_contents; i++)
7217       for (y = 0; y < 3; y++)
7218         for (x = 0; x < 3; x++)
7219           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7220   }
7221
7222   return num_bytes;
7223 }
7224
7225 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7226 {
7227   int chunk_size = 0;
7228   int i;
7229
7230   li = *level;          /* copy level data into temporary buffer */
7231
7232   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7233     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7234
7235   return chunk_size;
7236 }
7237
7238 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7239 {
7240   int chunk_size = 0;
7241   int i;
7242
7243   li = *level;          /* copy level data into temporary buffer */
7244
7245   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7246     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7247
7248   return chunk_size;
7249 }
7250
7251 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7252 {
7253   int envelope_nr = element - EL_ENVELOPE_1;
7254   int chunk_size = 0;
7255   int i;
7256
7257   chunk_size += putFile16BitBE(file, element);
7258
7259   /* copy envelope data into temporary buffer */
7260   xx_envelope = level->envelope[envelope_nr];
7261
7262   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7263     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7264
7265   return chunk_size;
7266 }
7267
7268 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7269 {
7270   struct ElementInfo *ei = &element_info[element];
7271   int chunk_size = 0;
7272   int i, j;
7273
7274   chunk_size += putFile16BitBE(file, element);
7275
7276   xx_ei = *ei;          /* copy element data into temporary buffer */
7277
7278   /* set default description string for this specific element */
7279   strcpy(xx_default_description, getDefaultElementDescription(ei));
7280
7281   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7282     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7283
7284   for (i = 0; i < ei->num_change_pages; i++)
7285   {
7286     struct ElementChangeInfo *change = &ei->change_page[i];
7287
7288     xx_current_change_page = i;
7289
7290     xx_change = *change;        /* copy change data into temporary buffer */
7291
7292     resetEventBits();
7293     setEventBitsFromEventFlags(change);
7294
7295     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7296       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7297                                          FALSE);
7298   }
7299
7300   return chunk_size;
7301 }
7302
7303 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7304 {
7305   struct ElementInfo *ei = &element_info[element];
7306   struct ElementGroupInfo *group = ei->group;
7307   int chunk_size = 0;
7308   int i;
7309
7310   chunk_size += putFile16BitBE(file, element);
7311
7312   xx_ei = *ei;          /* copy element data into temporary buffer */
7313   xx_group = *group;    /* copy group data into temporary buffer */
7314
7315   /* set default description string for this specific element */
7316   strcpy(xx_default_description, getDefaultElementDescription(ei));
7317
7318   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7319     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7320
7321   return chunk_size;
7322 }
7323
7324 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7325                                   boolean save_as_template)
7326 {
7327   int chunk_size;
7328   int i;
7329   FILE *file;
7330
7331   if (!(file = fopen(filename, MODE_WRITE)))
7332   {
7333     Error(ERR_WARN, "cannot save level file '%s'", filename);
7334     return;
7335   }
7336
7337   level->file_version = FILE_VERSION_ACTUAL;
7338   level->game_version = GAME_VERSION_ACTUAL;
7339
7340   level->creation_date = getCurrentDate();
7341
7342   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7343   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7344
7345   chunk_size = SaveLevel_VERS(NULL, level);
7346   putFileChunkBE(file, "VERS", chunk_size);
7347   SaveLevel_VERS(file, level);
7348
7349   chunk_size = SaveLevel_DATE(NULL, level);
7350   putFileChunkBE(file, "DATE", chunk_size);
7351   SaveLevel_DATE(file, level);
7352
7353   chunk_size = SaveLevel_NAME(NULL, level);
7354   putFileChunkBE(file, "NAME", chunk_size);
7355   SaveLevel_NAME(file, level);
7356
7357   chunk_size = SaveLevel_AUTH(NULL, level);
7358   putFileChunkBE(file, "AUTH", chunk_size);
7359   SaveLevel_AUTH(file, level);
7360
7361   chunk_size = SaveLevel_INFO(NULL, level);
7362   putFileChunkBE(file, "INFO", chunk_size);
7363   SaveLevel_INFO(file, level);
7364
7365   chunk_size = SaveLevel_BODY(NULL, level);
7366   putFileChunkBE(file, "BODY", chunk_size);
7367   SaveLevel_BODY(file, level);
7368
7369   chunk_size = SaveLevel_ELEM(NULL, level);
7370   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          /* save if changed */
7371   {
7372     putFileChunkBE(file, "ELEM", chunk_size);
7373     SaveLevel_ELEM(file, level);
7374   }
7375
7376   for (i = 0; i < NUM_ENVELOPES; i++)
7377   {
7378     int element = EL_ENVELOPE_1 + i;
7379
7380     chunk_size = SaveLevel_NOTE(NULL, level, element);
7381     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        /* save if changed */
7382     {
7383       putFileChunkBE(file, "NOTE", chunk_size);
7384       SaveLevel_NOTE(file, level, element);
7385     }
7386   }
7387
7388   /* if not using template level, check for non-default custom/group elements */
7389   if (!level->use_custom_template || save_as_template)
7390   {
7391     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7392     {
7393       int element = EL_CUSTOM_START + i;
7394
7395       chunk_size = SaveLevel_CUSX(NULL, level, element);
7396       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      /* save if changed */
7397       {
7398         putFileChunkBE(file, "CUSX", chunk_size);
7399         SaveLevel_CUSX(file, level, element);
7400       }
7401     }
7402
7403     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7404     {
7405       int element = EL_GROUP_START + i;
7406
7407       chunk_size = SaveLevel_GRPX(NULL, level, element);
7408       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      /* save if changed */
7409       {
7410         putFileChunkBE(file, "GRPX", chunk_size);
7411         SaveLevel_GRPX(file, level, element);
7412       }
7413     }
7414   }
7415
7416   fclose(file);
7417
7418   SetFilePermissions(filename, PERMS_PRIVATE);
7419 }
7420
7421 void SaveLevel(int nr)
7422 {
7423   char *filename = getDefaultLevelFilename(nr);
7424
7425   SaveLevelFromFilename(&level, filename, FALSE);
7426 }
7427
7428 void SaveLevelTemplate()
7429 {
7430   char *filename = getLocalLevelTemplateFilename();
7431
7432   SaveLevelFromFilename(&level, filename, TRUE);
7433 }
7434
7435 boolean SaveLevelChecked(int nr)
7436 {
7437   char *filename = getDefaultLevelFilename(nr);
7438   boolean new_level = !fileExists(filename);
7439   boolean level_saved = FALSE;
7440
7441   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7442   {
7443     SaveLevel(nr);
7444
7445     if (new_level)
7446       Request("Level saved!", REQ_CONFIRM);
7447
7448     level_saved = TRUE;
7449   }
7450
7451   return level_saved;
7452 }
7453
7454 void DumpLevel(struct LevelInfo *level)
7455 {
7456   if (level->no_level_file || level->no_valid_file)
7457   {
7458     Error(ERR_WARN, "cannot dump -- no valid level file found");
7459
7460     return;
7461   }
7462
7463   PrintLine("-", 79);
7464   Print("Level xxx (file version %08d, game version %08d)\n",
7465         level->file_version, level->game_version);
7466   PrintLine("-", 79);
7467
7468   Print("Level author: '%s'\n", level->author);
7469   Print("Level title:  '%s'\n", level->name);
7470   Print("\n");
7471   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7472   Print("\n");
7473   Print("Level time:  %d seconds\n", level->time);
7474   Print("Gems needed: %d\n", level->gems_needed);
7475   Print("\n");
7476   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7477   Print("Time for wheel:      %d seconds\n", level->time_wheel);
7478   Print("Time for light:      %d seconds\n", level->time_light);
7479   Print("Time for timegate:   %d seconds\n", level->time_timegate);
7480   Print("\n");
7481   Print("Amoeba speed: %d\n", level->amoeba_speed);
7482   Print("\n");
7483
7484   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
7485   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
7486   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7487   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7488   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7489
7490   PrintLine("-", 79);
7491 }
7492
7493
7494 /* ========================================================================= */
7495 /* tape file functions                                                       */
7496 /* ========================================================================= */
7497
7498 static void setTapeInfoToDefaults()
7499 {
7500   int i;
7501
7502   /* always start with reliable default values (empty tape) */
7503   TapeErase();
7504
7505   /* default values (also for pre-1.2 tapes) with only the first player */
7506   tape.player_participates[0] = TRUE;
7507   for (i = 1; i < MAX_PLAYERS; i++)
7508     tape.player_participates[i] = FALSE;
7509
7510   /* at least one (default: the first) player participates in every tape */
7511   tape.num_participating_players = 1;
7512
7513   tape.level_nr = level_nr;
7514   tape.counter = 0;
7515   tape.changed = FALSE;
7516
7517   tape.recording = FALSE;
7518   tape.playing = FALSE;
7519   tape.pausing = FALSE;
7520
7521   tape.no_valid_file = FALSE;
7522 }
7523
7524 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7525 {
7526   tape->file_version = getFileVersion(file);
7527   tape->game_version = getFileVersion(file);
7528
7529   return chunk_size;
7530 }
7531
7532 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7533 {
7534   int i;
7535
7536   tape->random_seed = getFile32BitBE(file);
7537   tape->date        = getFile32BitBE(file);
7538   tape->length      = getFile32BitBE(file);
7539
7540   /* read header fields that are new since version 1.2 */
7541   if (tape->file_version >= FILE_VERSION_1_2)
7542   {
7543     byte store_participating_players = getFile8Bit(file);
7544     int engine_version;
7545
7546     /* since version 1.2, tapes store which players participate in the tape */
7547     tape->num_participating_players = 0;
7548     for (i = 0; i < MAX_PLAYERS; i++)
7549     {
7550       tape->player_participates[i] = FALSE;
7551
7552       if (store_participating_players & (1 << i))
7553       {
7554         tape->player_participates[i] = TRUE;
7555         tape->num_participating_players++;
7556       }
7557     }
7558
7559     tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7560
7561     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7562
7563     engine_version = getFileVersion(file);
7564     if (engine_version > 0)
7565       tape->engine_version = engine_version;
7566     else
7567       tape->engine_version = tape->game_version;
7568   }
7569
7570   return chunk_size;
7571 }
7572
7573 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7574 {
7575   int level_identifier_size;
7576   int i;
7577
7578   level_identifier_size = getFile16BitBE(file);
7579
7580   tape->level_identifier =
7581     checked_realloc(tape->level_identifier, level_identifier_size);
7582
7583   for (i = 0; i < level_identifier_size; i++)
7584     tape->level_identifier[i] = getFile8Bit(file);
7585
7586   tape->level_nr = getFile16BitBE(file);
7587
7588   chunk_size = 2 + level_identifier_size + 2;
7589
7590   return chunk_size;
7591 }
7592
7593 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7594 {
7595   int i, j;
7596   int tape_pos_size =
7597     (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7598   int chunk_size_expected = tape_pos_size * tape->length;
7599
7600   if (chunk_size_expected != chunk_size)
7601   {
7602     ReadUnusedBytesFromFile(file, chunk_size);
7603     return chunk_size_expected;
7604   }
7605
7606   for (i = 0; i < tape->length; i++)
7607   {
7608     if (i >= MAX_TAPE_LEN)
7609     {
7610       Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7611             MAX_TAPE_LEN);
7612
7613       // tape too large; read and ignore remaining tape data from this chunk
7614       for (;i < tape->length; i++)
7615         ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7616
7617       break;
7618     }
7619
7620     if (tape->use_mouse)
7621     {
7622       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
7623       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
7624       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7625
7626       tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7627     }
7628     else
7629     {
7630       for (j = 0; j < MAX_PLAYERS; j++)
7631       {
7632         tape->pos[i].action[j] = MV_NONE;
7633
7634         if (tape->player_participates[j])
7635           tape->pos[i].action[j] = getFile8Bit(file);
7636       }
7637     }
7638
7639     tape->pos[i].delay = getFile8Bit(file);
7640
7641     if (tape->file_version == FILE_VERSION_1_0)
7642     {
7643       /* eliminate possible diagonal moves in old tapes */
7644       /* this is only for backward compatibility */
7645
7646       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7647       byte action = tape->pos[i].action[0];
7648       int k, num_moves = 0;
7649
7650       for (k = 0; k<4; k++)
7651       {
7652         if (action & joy_dir[k])
7653         {
7654           tape->pos[i + num_moves].action[0] = joy_dir[k];
7655           if (num_moves > 0)
7656             tape->pos[i + num_moves].delay = 0;
7657           num_moves++;
7658         }
7659       }
7660
7661       if (num_moves > 1)
7662       {
7663         num_moves--;
7664         i += num_moves;
7665         tape->length += num_moves;
7666       }
7667     }
7668     else if (tape->file_version < FILE_VERSION_2_0)
7669     {
7670       /* convert pre-2.0 tapes to new tape format */
7671
7672       if (tape->pos[i].delay > 1)
7673       {
7674         /* action part */
7675         tape->pos[i + 1] = tape->pos[i];
7676         tape->pos[i + 1].delay = 1;
7677
7678         /* delay part */
7679         for (j = 0; j < MAX_PLAYERS; j++)
7680           tape->pos[i].action[j] = MV_NONE;
7681         tape->pos[i].delay--;
7682
7683         i++;
7684         tape->length++;
7685       }
7686     }
7687
7688     if (checkEndOfFile(file))
7689       break;
7690   }
7691
7692   if (i != tape->length)
7693     chunk_size = tape_pos_size * i;
7694
7695   return chunk_size;
7696 }
7697
7698 void LoadTape_SokobanSolution(char *filename)
7699 {
7700   File *file;
7701   int move_delay = TILESIZE / level.initial_player_stepsize[0];
7702
7703   if (!(file = openFile(filename, MODE_READ)))
7704   {
7705     tape.no_valid_file = TRUE;
7706
7707     return;
7708   }
7709
7710   while (!checkEndOfFile(file))
7711   {
7712     unsigned char c = getByteFromFile(file);
7713
7714     if (checkEndOfFile(file))
7715       break;
7716
7717     switch (c)
7718     {
7719       case 'u':
7720       case 'U':
7721         tape.pos[tape.length].action[0] = MV_UP;
7722         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7723         tape.length++;
7724         break;
7725
7726       case 'd':
7727       case 'D':
7728         tape.pos[tape.length].action[0] = MV_DOWN;
7729         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7730         tape.length++;
7731         break;
7732
7733       case 'l':
7734       case 'L':
7735         tape.pos[tape.length].action[0] = MV_LEFT;
7736         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7737         tape.length++;
7738         break;
7739
7740       case 'r':
7741       case 'R':
7742         tape.pos[tape.length].action[0] = MV_RIGHT;
7743         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7744         tape.length++;
7745         break;
7746
7747       case '\n':
7748       case '\r':
7749       case '\t':
7750       case ' ':
7751         /* ignore white-space characters */
7752         break;
7753
7754       default:
7755         tape.no_valid_file = TRUE;
7756
7757         Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7758
7759         break;
7760     }
7761   }
7762
7763   closeFile(file);
7764
7765   if (tape.no_valid_file)
7766     return;
7767
7768   tape.length_frames  = GetTapeLengthFrames();
7769   tape.length_seconds = GetTapeLengthSeconds();
7770 }
7771
7772 void LoadTapeFromFilename(char *filename)
7773 {
7774   char cookie[MAX_LINE_LEN];
7775   char chunk_name[CHUNK_ID_LEN + 1];
7776   File *file;
7777   int chunk_size;
7778
7779   /* always start with reliable default values */
7780   setTapeInfoToDefaults();
7781
7782   if (strSuffix(filename, ".sln"))
7783   {
7784     LoadTape_SokobanSolution(filename);
7785
7786     return;
7787   }
7788
7789   if (!(file = openFile(filename, MODE_READ)))
7790   {
7791     tape.no_valid_file = TRUE;
7792
7793     return;
7794   }
7795
7796   getFileChunkBE(file, chunk_name, NULL);
7797   if (strEqual(chunk_name, "RND1"))
7798   {
7799     getFile32BitBE(file);               /* not used */
7800
7801     getFileChunkBE(file, chunk_name, NULL);
7802     if (!strEqual(chunk_name, "TAPE"))
7803     {
7804       tape.no_valid_file = TRUE;
7805
7806       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7807
7808       closeFile(file);
7809
7810       return;
7811     }
7812   }
7813   else  /* check for pre-2.0 file format with cookie string */
7814   {
7815     strcpy(cookie, chunk_name);
7816     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7817       cookie[4] = '\0';
7818     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7819       cookie[strlen(cookie) - 1] = '\0';
7820
7821     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7822     {
7823       tape.no_valid_file = TRUE;
7824
7825       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7826
7827       closeFile(file);
7828
7829       return;
7830     }
7831
7832     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7833     {
7834       tape.no_valid_file = TRUE;
7835
7836       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7837
7838       closeFile(file);
7839
7840       return;
7841     }
7842
7843     /* pre-2.0 tape files have no game version, so use file version here */
7844     tape.game_version = tape.file_version;
7845   }
7846
7847   if (tape.file_version < FILE_VERSION_1_2)
7848   {
7849     /* tape files from versions before 1.2.0 without chunk structure */
7850     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7851     LoadTape_BODY(file, 2 * tape.length,      &tape);
7852   }
7853   else
7854   {
7855     static struct
7856     {
7857       char *name;
7858       int size;
7859       int (*loader)(File *, int, struct TapeInfo *);
7860     }
7861     chunk_info[] =
7862     {
7863       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
7864       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
7865       { "INFO", -1,                     LoadTape_INFO },
7866       { "BODY", -1,                     LoadTape_BODY },
7867       {  NULL,  0,                      NULL }
7868     };
7869
7870     while (getFileChunkBE(file, chunk_name, &chunk_size))
7871     {
7872       int i = 0;
7873
7874       while (chunk_info[i].name != NULL &&
7875              !strEqual(chunk_name, chunk_info[i].name))
7876         i++;
7877
7878       if (chunk_info[i].name == NULL)
7879       {
7880         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7881               chunk_name, filename);
7882         ReadUnusedBytesFromFile(file, chunk_size);
7883       }
7884       else if (chunk_info[i].size != -1 &&
7885                chunk_info[i].size != chunk_size)
7886       {
7887         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7888               chunk_size, chunk_name, filename);
7889         ReadUnusedBytesFromFile(file, chunk_size);
7890       }
7891       else
7892       {
7893         /* call function to load this tape chunk */
7894         int chunk_size_expected =
7895           (chunk_info[i].loader)(file, chunk_size, &tape);
7896
7897         /* the size of some chunks cannot be checked before reading other
7898            chunks first (like "HEAD" and "BODY") that contain some header
7899            information, so check them here */
7900         if (chunk_size_expected != chunk_size)
7901         {
7902           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7903                 chunk_size, chunk_name, filename);
7904         }
7905       }
7906     }
7907   }
7908
7909   closeFile(file);
7910
7911   tape.length_frames  = GetTapeLengthFrames();
7912   tape.length_seconds = GetTapeLengthSeconds();
7913
7914 #if 0
7915   printf("::: tape file version: %d\n",   tape.file_version);
7916   printf("::: tape game version: %d\n",   tape.game_version);
7917   printf("::: tape engine version: %d\n", tape.engine_version);
7918 #endif
7919 }
7920
7921 void LoadTape(int nr)
7922 {
7923   char *filename = getTapeFilename(nr);
7924
7925   LoadTapeFromFilename(filename);
7926 }
7927
7928 void LoadSolutionTape(int nr)
7929 {
7930   char *filename = getSolutionTapeFilename(nr);
7931
7932   LoadTapeFromFilename(filename);
7933
7934   if (TAPE_IS_EMPTY(tape) &&
7935       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7936       level.native_sp_level->demo.is_available)
7937     CopyNativeTape_SP_to_RND(&level);
7938 }
7939
7940 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7941 {
7942   putFileVersion(file, tape->file_version);
7943   putFileVersion(file, tape->game_version);
7944 }
7945
7946 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7947 {
7948   int i;
7949   byte store_participating_players = 0;
7950
7951   /* set bits for participating players for compact storage */
7952   for (i = 0; i < MAX_PLAYERS; i++)
7953     if (tape->player_participates[i])
7954       store_participating_players |= (1 << i);
7955
7956   putFile32BitBE(file, tape->random_seed);
7957   putFile32BitBE(file, tape->date);
7958   putFile32BitBE(file, tape->length);
7959
7960   putFile8Bit(file, store_participating_players);
7961
7962   putFile8Bit(file, (tape->use_mouse ? 1 : 0));
7963
7964   /* unused bytes not at the end here for 4-byte alignment of engine_version */
7965   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7966
7967   putFileVersion(file, tape->engine_version);
7968 }
7969
7970 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7971 {
7972   int level_identifier_size = strlen(tape->level_identifier) + 1;
7973   int i;
7974
7975   putFile16BitBE(file, level_identifier_size);
7976
7977   for (i = 0; i < level_identifier_size; i++)
7978     putFile8Bit(file, tape->level_identifier[i]);
7979
7980   putFile16BitBE(file, tape->level_nr);
7981 }
7982
7983 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7984 {
7985   int i, j;
7986
7987   for (i = 0; i < tape->length; i++)
7988   {
7989     if (tape->use_mouse)
7990     {
7991       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
7992       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
7993       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
7994     }
7995     else
7996     {
7997       for (j = 0; j < MAX_PLAYERS; j++)
7998         if (tape->player_participates[j])
7999           putFile8Bit(file, tape->pos[i].action[j]);
8000     }
8001
8002     putFile8Bit(file, tape->pos[i].delay);
8003   }
8004 }
8005
8006 void SaveTape(int nr)
8007 {
8008   char *filename = getTapeFilename(nr);
8009   FILE *file;
8010   int num_participating_players = 0;
8011   int tape_pos_size;
8012   int info_chunk_size;
8013   int body_chunk_size;
8014   int i;
8015
8016   InitTapeDirectory(leveldir_current->subdir);
8017
8018   if (!(file = fopen(filename, MODE_WRITE)))
8019   {
8020     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8021     return;
8022   }
8023
8024   tape.file_version = FILE_VERSION_ACTUAL;
8025   tape.game_version = GAME_VERSION_ACTUAL;
8026
8027   /* count number of participating players  */
8028   for (i = 0; i < MAX_PLAYERS; i++)
8029     if (tape.player_participates[i])
8030       num_participating_players++;
8031
8032   tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
8033
8034   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8035   body_chunk_size = tape_pos_size * tape.length;
8036
8037   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8038   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8039
8040   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8041   SaveTape_VERS(file, &tape);
8042
8043   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8044   SaveTape_HEAD(file, &tape);
8045
8046   putFileChunkBE(file, "INFO", info_chunk_size);
8047   SaveTape_INFO(file, &tape);
8048
8049   putFileChunkBE(file, "BODY", body_chunk_size);
8050   SaveTape_BODY(file, &tape);
8051
8052   fclose(file);
8053
8054   SetFilePermissions(filename, PERMS_PRIVATE);
8055
8056   tape.changed = FALSE;
8057 }
8058
8059 boolean SaveTapeChecked(int nr)
8060 {
8061   char *filename = getTapeFilename(nr);
8062   boolean new_tape = !fileExists(filename);
8063   boolean tape_saved = FALSE;
8064
8065   if (new_tape || Request("Replace old tape?", REQ_ASK))
8066   {
8067     SaveTape(nr);
8068
8069     if (new_tape)
8070       Request("Tape saved!", REQ_CONFIRM);
8071
8072     tape_saved = TRUE;
8073   }
8074
8075   return tape_saved;
8076 }
8077
8078 void DumpTape(struct TapeInfo *tape)
8079 {
8080   int tape_frame_counter;
8081   int i, j;
8082
8083   if (tape->no_valid_file)
8084   {
8085     Error(ERR_WARN, "cannot dump -- no valid tape file found");
8086
8087     return;
8088   }
8089
8090   PrintLine("-", 79);
8091   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8092         tape->level_nr, tape->file_version, tape->game_version);
8093   Print("                  (effective engine version %08d)\n",
8094         tape->engine_version);
8095   Print("Level series identifier: '%s'\n", tape->level_identifier);
8096   PrintLine("-", 79);
8097
8098   tape_frame_counter = 0;
8099
8100   for (i = 0; i < tape->length; i++)
8101   {
8102     if (i >= MAX_TAPE_LEN)
8103       break;
8104
8105     Print("%04d: ", i);
8106
8107     for (j = 0; j < MAX_PLAYERS; j++)
8108     {
8109       if (tape->player_participates[j])
8110       {
8111         int action = tape->pos[i].action[j];
8112
8113         Print("%d:%02x ", j, action);
8114         Print("[%c%c%c%c|%c%c] - ",
8115               (action & JOY_LEFT ? '<' : ' '),
8116               (action & JOY_RIGHT ? '>' : ' '),
8117               (action & JOY_UP ? '^' : ' '),
8118               (action & JOY_DOWN ? 'v' : ' '),
8119               (action & JOY_BUTTON_1 ? '1' : ' '),
8120               (action & JOY_BUTTON_2 ? '2' : ' '));
8121       }
8122     }
8123
8124     Print("(%03d) ", tape->pos[i].delay);
8125     Print("[%05d]\n", tape_frame_counter);
8126
8127     tape_frame_counter += tape->pos[i].delay;
8128   }
8129
8130   PrintLine("-", 79);
8131 }
8132
8133
8134 /* ========================================================================= */
8135 /* score file functions                                                      */
8136 /* ========================================================================= */
8137
8138 void LoadScore(int nr)
8139 {
8140   int i;
8141   char *filename = getScoreFilename(nr);
8142   char cookie[MAX_LINE_LEN];
8143   char line[MAX_LINE_LEN];
8144   char *line_ptr;
8145   FILE *file;
8146
8147   /* always start with reliable default values */
8148   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8149   {
8150     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8151     highscore[i].Score = 0;
8152   }
8153
8154   if (!(file = fopen(filename, MODE_READ)))
8155     return;
8156
8157   /* check file identifier */
8158   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8159     cookie[0] = '\0';
8160   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8161     cookie[strlen(cookie) - 1] = '\0';
8162
8163   if (!checkCookieString(cookie, SCORE_COOKIE))
8164   {
8165     Error(ERR_WARN, "unknown format of score file '%s'", filename);
8166     fclose(file);
8167     return;
8168   }
8169
8170   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8171   {
8172     if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8173       Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8174     if (fgets(line, MAX_LINE_LEN, file) == NULL)
8175       line[0] = '\0';
8176
8177     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8178       line[strlen(line) - 1] = '\0';
8179
8180     for (line_ptr = line; *line_ptr; line_ptr++)
8181     {
8182       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8183       {
8184         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8185         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8186         break;
8187       }
8188     }
8189   }
8190
8191   fclose(file);
8192 }
8193
8194 void SaveScore(int nr)
8195 {
8196   int i;
8197   int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8198   char *filename = getScoreFilename(nr);
8199   FILE *file;
8200
8201   InitScoreDirectory(leveldir_current->subdir);
8202
8203   if (!(file = fopen(filename, MODE_WRITE)))
8204   {
8205     Error(ERR_WARN, "cannot save score for level %d", nr);
8206     return;
8207   }
8208
8209   fprintf(file, "%s\n\n", SCORE_COOKIE);
8210
8211   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8212     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8213
8214   fclose(file);
8215
8216   SetFilePermissions(filename, permissions);
8217 }
8218
8219
8220 /* ========================================================================= */
8221 /* setup file functions                                                      */
8222 /* ========================================================================= */
8223
8224 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
8225
8226 /* global setup */
8227 #define SETUP_TOKEN_PLAYER_NAME                 0
8228 #define SETUP_TOKEN_SOUND                       1
8229 #define SETUP_TOKEN_SOUND_LOOPS                 2
8230 #define SETUP_TOKEN_SOUND_MUSIC                 3
8231 #define SETUP_TOKEN_SOUND_SIMPLE                4
8232 #define SETUP_TOKEN_TOONS                       5
8233 #define SETUP_TOKEN_SCROLL_DELAY                6
8234 #define SETUP_TOKEN_SCROLL_DELAY_VALUE          7
8235 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE        8
8236 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY      9
8237 #define SETUP_TOKEN_FADE_SCREENS                10
8238 #define SETUP_TOKEN_AUTORECORD                  11
8239 #define SETUP_TOKEN_SHOW_TITLESCREEN            12
8240 #define SETUP_TOKEN_QUICK_DOORS                 13
8241 #define SETUP_TOKEN_TEAM_MODE                   14
8242 #define SETUP_TOKEN_HANDICAP                    15
8243 #define SETUP_TOKEN_SKIP_LEVELS                 16
8244 #define SETUP_TOKEN_INCREMENT_LEVELS            17
8245 #define SETUP_TOKEN_TIME_LIMIT                  18
8246 #define SETUP_TOKEN_FULLSCREEN                  19
8247 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT      20
8248 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY      21
8249 #define SETUP_TOKEN_SCREEN_RENDERING_MODE       22
8250 #define SETUP_TOKEN_ASK_ON_ESCAPE               23
8251 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR        24
8252 #define SETUP_TOKEN_QUICK_SWITCH                25
8253 #define SETUP_TOKEN_INPUT_ON_FOCUS              26
8254 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS         27
8255 #define SETUP_TOKEN_GAME_FRAME_DELAY            28
8256 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS     29
8257 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS         30
8258 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS       31
8259 #define SETUP_TOKEN_GRAPHICS_SET                32
8260 #define SETUP_TOKEN_SOUNDS_SET                  33
8261 #define SETUP_TOKEN_MUSIC_SET                   34
8262 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     35
8263 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       36
8264 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        37
8265 #define SETUP_TOKEN_VOLUME_SIMPLE               38
8266 #define SETUP_TOKEN_VOLUME_LOOPS                39
8267 #define SETUP_TOKEN_VOLUME_MUSIC                40
8268 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE          41
8269 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE         42
8270 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE         43
8271
8272 #define NUM_GLOBAL_SETUP_TOKENS                 44
8273
8274 /* auto setup */
8275 #define SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE   0
8276
8277 #define NUM_AUTO_SETUP_TOKENS                   1
8278
8279 /* editor setup */
8280 #define SETUP_TOKEN_EDITOR_EL_CLASSIC           0
8281 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            1
8282 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      2
8283 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC           3
8284 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         4
8285 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN   5
8286
8287 #define NUM_EDITOR_SETUP_TOKENS                 6
8288
8289 /* editor cascade setup */
8290 #define SETUP_TOKEN_EDITOR_CASCADE_BD           0
8291 #define SETUP_TOKEN_EDITOR_CASCADE_EM           1
8292 #define SETUP_TOKEN_EDITOR_CASCADE_EMC          2
8293 #define SETUP_TOKEN_EDITOR_CASCADE_RND          3
8294 #define SETUP_TOKEN_EDITOR_CASCADE_SB           4
8295 #define SETUP_TOKEN_EDITOR_CASCADE_SP           5
8296 #define SETUP_TOKEN_EDITOR_CASCADE_DC           6
8297 #define SETUP_TOKEN_EDITOR_CASCADE_DX           7
8298 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT         8
8299 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT    9
8300 #define SETUP_TOKEN_EDITOR_CASCADE_CE           10
8301 #define SETUP_TOKEN_EDITOR_CASCADE_GE           11
8302 #define SETUP_TOKEN_EDITOR_CASCADE_REF          12
8303 #define SETUP_TOKEN_EDITOR_CASCADE_USER         13
8304 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC      14
8305
8306 #define NUM_EDITOR_CASCADE_SETUP_TOKENS         15
8307
8308 /* shortcut setup */
8309 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
8310 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
8311 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
8312 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1     3
8313 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2     4
8314 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3     5
8315 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4     6
8316 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL   7
8317 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT         8
8318 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA         9
8319 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP          10
8320 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE         11
8321 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD        12
8322 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY          13
8323 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE       14
8324 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS        15
8325 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC        16
8326 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT          17
8327 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT         18
8328 #define SETUP_TOKEN_SHORTCUT_SNAP_UP            19
8329 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN          20
8330
8331 #define NUM_SHORTCUT_SETUP_TOKENS               21
8332
8333 /* player setup */
8334 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
8335 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
8336 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
8337 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
8338 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
8339 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
8340 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
8341 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
8342 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
8343 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
8344 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
8345 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
8346 #define SETUP_TOKEN_PLAYER_KEY_UP               12
8347 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
8348 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
8349 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
8350
8351 #define NUM_PLAYER_SETUP_TOKENS                 16
8352
8353 /* system setup */
8354 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER      0
8355 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      1
8356 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  2
8357
8358 #define NUM_SYSTEM_SETUP_TOKENS                 3
8359
8360 /* internal setup */
8361 #define SETUP_TOKEN_INT_PROGRAM_TITLE           0
8362 #define SETUP_TOKEN_INT_PROGRAM_VERSION         1
8363 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR          2
8364 #define SETUP_TOKEN_INT_PROGRAM_EMAIL           3
8365 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE         4
8366 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT       5
8367 #define SETUP_TOKEN_INT_PROGRAM_COMPANY         6
8368 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE       7
8369 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET    8
8370 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET      9
8371 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET       10
8372 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE  11
8373 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE    12
8374 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE     13
8375 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES    14
8376 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
8377 #define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE   16
8378 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH    17
8379 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT   18
8380
8381 #define NUM_INTERNAL_SETUP_TOKENS               19
8382
8383 /* debug setup */
8384 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0         0
8385 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1         1
8386 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2         2
8387 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3         3
8388 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4         4
8389 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5         5
8390 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6         6
8391 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7         7
8392 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8         8
8393 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9         9
8394 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0     10
8395 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1     11
8396 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2     12
8397 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3     13
8398 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4     14
8399 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5     15
8400 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6     16
8401 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7     17
8402 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8     18
8403 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9     19
8404 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8405 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8406 #define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
8407
8408 #define NUM_DEBUG_SETUP_TOKENS                  23
8409
8410 /* options setup */
8411 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
8412
8413 #define NUM_OPTIONS_SETUP_TOKENS                1
8414
8415
8416 static struct SetupInfo si;
8417 static struct SetupAutoSetupInfo sasi;
8418 static struct SetupEditorInfo sei;
8419 static struct SetupEditorCascadeInfo seci;
8420 static struct SetupShortcutInfo ssi;
8421 static struct SetupInputInfo sii;
8422 static struct SetupSystemInfo syi;
8423 static struct SetupInternalInfo sxi;
8424 static struct SetupDebugInfo sdi;
8425 static struct OptionInfo soi;
8426
8427 static struct TokenInfo global_setup_tokens[] =
8428 {
8429   { TYPE_STRING, &si.player_name,             "player_name"             },
8430   { TYPE_SWITCH, &si.sound,                   "sound"                   },
8431   { TYPE_SWITCH, &si.sound_loops,             "repeating_sound_loops"   },
8432   { TYPE_SWITCH, &si.sound_music,             "background_music"        },
8433   { TYPE_SWITCH, &si.sound_simple,            "simple_sound_effects"    },
8434   { TYPE_SWITCH, &si.toons,                   "toons"                   },
8435   { TYPE_SWITCH, &si.scroll_delay,            "scroll_delay"            },
8436   { TYPE_INTEGER,&si.scroll_delay_value,      "scroll_delay_value"      },
8437   { TYPE_STRING, &si.engine_snapshot_mode,    "engine_snapshot_mode"    },
8438   { TYPE_INTEGER,&si.engine_snapshot_memory,  "engine_snapshot_memory"  },
8439   { TYPE_SWITCH, &si.fade_screens,            "fade_screens"            },
8440   { TYPE_SWITCH, &si.autorecord,              "automatic_tape_recording"},
8441   { TYPE_SWITCH, &si.show_titlescreen,        "show_titlescreen"        },
8442   { TYPE_SWITCH, &si.quick_doors,             "quick_doors"             },
8443   { TYPE_SWITCH, &si.team_mode,               "team_mode"               },
8444   { TYPE_SWITCH, &si.handicap,                "handicap"                },
8445   { TYPE_SWITCH, &si.skip_levels,             "skip_levels"             },
8446   { TYPE_SWITCH, &si.increment_levels,        "increment_levels"        },
8447   { TYPE_SWITCH, &si.time_limit,              "time_limit"              },
8448   { TYPE_SWITCH, &si.fullscreen,              "fullscreen"              },
8449   { TYPE_INTEGER,&si.window_scaling_percent,  "window_scaling_percent"  },
8450   { TYPE_STRING, &si.window_scaling_quality,  "window_scaling_quality"  },
8451   { TYPE_STRING, &si.screen_rendering_mode,   "screen_rendering_mode"   },
8452   { TYPE_SWITCH, &si.ask_on_escape,           "ask_on_escape"           },
8453   { TYPE_SWITCH, &si.ask_on_escape_editor,    "ask_on_escape_editor"    },
8454   { TYPE_SWITCH, &si.quick_switch,            "quick_player_switch"     },
8455   { TYPE_SWITCH, &si.input_on_focus,          "input_on_focus"          },
8456   { TYPE_SWITCH, &si.prefer_aga_graphics,     "prefer_aga_graphics"     },
8457   { TYPE_INTEGER,&si.game_frame_delay,        "game_frame_delay"        },
8458   { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8459   { TYPE_SWITCH, &si.small_game_graphics,     "small_game_graphics"     },
8460   { TYPE_SWITCH, &si.show_snapshot_buttons,   "show_snapshot_buttons"   },
8461   { TYPE_STRING, &si.graphics_set,            "graphics_set"            },
8462   { TYPE_STRING, &si.sounds_set,              "sounds_set"              },
8463   { TYPE_STRING, &si.music_set,               "music_set"               },
8464   { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8465   { TYPE_SWITCH3,&si.override_level_sounds,   "override_level_sounds"   },
8466   { TYPE_SWITCH3,&si.override_level_music,    "override_level_music"    },
8467   { TYPE_INTEGER,&si.volume_simple,           "volume_simple"           },
8468   { TYPE_INTEGER,&si.volume_loops,            "volume_loops"            },
8469   { TYPE_INTEGER,&si.volume_music,            "volume_music"            },
8470   { TYPE_STRING, &si.touch.control_type,      "touch.control_type"      },
8471   { TYPE_INTEGER,&si.touch.move_distance,     "touch.move_distance"     },
8472   { TYPE_INTEGER,&si.touch.drop_distance,     "touch.drop_distance"     },
8473 };
8474
8475 static struct TokenInfo auto_setup_tokens[] =
8476 {
8477   { TYPE_INTEGER,&sasi.editor_zoom_tilesize,    "editor.zoom_tilesize"  },
8478 };
8479
8480 static struct TokenInfo editor_setup_tokens[] =
8481 {
8482   { TYPE_SWITCH, &sei.el_classic,       "editor.el_classic"             },
8483   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
8484   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
8485   { TYPE_SWITCH, &sei.el_dynamic,       "editor.el_dynamic"             },
8486   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
8487   { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token"    },
8488 };
8489
8490 static struct TokenInfo editor_cascade_setup_tokens[] =
8491 {
8492   { TYPE_SWITCH, &seci.el_bd,           "editor.cascade.el_bd"          },
8493   { TYPE_SWITCH, &seci.el_em,           "editor.cascade.el_em"          },
8494   { TYPE_SWITCH, &seci.el_emc,          "editor.cascade.el_emc"         },
8495   { TYPE_SWITCH, &seci.el_rnd,          "editor.cascade.el_rnd"         },
8496   { TYPE_SWITCH, &seci.el_sb,           "editor.cascade.el_sb"          },
8497   { TYPE_SWITCH, &seci.el_sp,           "editor.cascade.el_sp"          },
8498   { TYPE_SWITCH, &seci.el_dc,           "editor.cascade.el_dc"          },
8499   { TYPE_SWITCH, &seci.el_dx,           "editor.cascade.el_dx"          },
8500   { TYPE_SWITCH, &seci.el_mm,           "editor.cascade.el_mm"          },
8501   { TYPE_SWITCH, &seci.el_df,           "editor.cascade.el_df"          },
8502   { TYPE_SWITCH, &seci.el_chars,        "editor.cascade.el_chars"       },
8503   { TYPE_SWITCH, &seci.el_steel_chars,  "editor.cascade.el_steel_chars" },
8504   { TYPE_SWITCH, &seci.el_ce,           "editor.cascade.el_ce"          },
8505   { TYPE_SWITCH, &seci.el_ge,           "editor.cascade.el_ge"          },
8506   { TYPE_SWITCH, &seci.el_ref,          "editor.cascade.el_ref"         },
8507   { TYPE_SWITCH, &seci.el_user,         "editor.cascade.el_user"        },
8508   { TYPE_SWITCH, &seci.el_dynamic,      "editor.cascade.el_dynamic"     },
8509 };
8510
8511 static struct TokenInfo shortcut_setup_tokens[] =
8512 {
8513   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
8514   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
8515   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         },
8516   { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1"       },
8517   { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2"       },
8518   { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3"       },
8519   { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4"       },
8520   { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all"     },
8521   { TYPE_KEY_X11, &ssi.tape_eject,      "shortcut.tape_eject"           },
8522   { TYPE_KEY_X11, &ssi.tape_extra,      "shortcut.tape_extra"           },
8523   { TYPE_KEY_X11, &ssi.tape_stop,       "shortcut.tape_stop"            },
8524   { TYPE_KEY_X11, &ssi.tape_pause,      "shortcut.tape_pause"           },
8525   { TYPE_KEY_X11, &ssi.tape_record,     "shortcut.tape_record"          },
8526   { TYPE_KEY_X11, &ssi.tape_play,       "shortcut.tape_play"            },
8527   { TYPE_KEY_X11, &ssi.sound_simple,    "shortcut.sound_simple"         },
8528   { TYPE_KEY_X11, &ssi.sound_loops,     "shortcut.sound_loops"          },
8529   { TYPE_KEY_X11, &ssi.sound_music,     "shortcut.sound_music"          },
8530   { TYPE_KEY_X11, &ssi.snap_left,       "shortcut.snap_left"            },
8531   { TYPE_KEY_X11, &ssi.snap_right,      "shortcut.snap_right"           },
8532   { TYPE_KEY_X11, &ssi.snap_up,         "shortcut.snap_up"              },
8533   { TYPE_KEY_X11, &ssi.snap_down,       "shortcut.snap_down"            },
8534 };
8535
8536 static struct TokenInfo player_setup_tokens[] =
8537 {
8538   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
8539   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
8540   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
8541   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
8542   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
8543   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
8544   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
8545   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
8546   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
8547   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
8548   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
8549   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
8550   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
8551   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
8552   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
8553   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               },
8554 };
8555
8556 static struct TokenInfo system_setup_tokens[] =
8557 {
8558   { TYPE_STRING,  &syi.sdl_videodriver,    "system.sdl_videodriver"     },
8559   { TYPE_STRING,  &syi.sdl_audiodriver,    "system.sdl_audiodriver"     },
8560   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8561 };
8562
8563 static struct TokenInfo internal_setup_tokens[] =
8564 {
8565   { TYPE_STRING, &sxi.program_title,            "program_title"         },
8566   { TYPE_STRING, &sxi.program_version,          "program_version"       },
8567   { TYPE_STRING, &sxi.program_author,           "program_author"        },
8568   { TYPE_STRING, &sxi.program_email,            "program_email"         },
8569   { TYPE_STRING, &sxi.program_website,          "program_website"       },
8570   { TYPE_STRING, &sxi.program_copyright,        "program_copyright"     },
8571   { TYPE_STRING, &sxi.program_company,          "program_company"       },
8572   { TYPE_STRING, &sxi.program_icon_file,        "program_icon_file"     },
8573   { TYPE_STRING, &sxi.default_graphics_set,     "default_graphics_set"  },
8574   { TYPE_STRING, &sxi.default_sounds_set,       "default_sounds_set"    },
8575   { TYPE_STRING, &sxi.default_music_set,        "default_music_set"     },
8576   { TYPE_STRING, &sxi.fallback_graphics_file,   "fallback_graphics_file"},
8577   { TYPE_STRING, &sxi.fallback_sounds_file,     "fallback_sounds_file"  },
8578   { TYPE_STRING, &sxi.fallback_music_file,      "fallback_music_file"   },
8579   { TYPE_STRING, &sxi.default_level_series,     "default_level_series"  },
8580   { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8581   { TYPE_BOOLEAN,&sxi.show_scaling_in_title,    "show_scaling_in_title" },
8582   { TYPE_INTEGER,&sxi.default_window_width,     "default_window_width"  },
8583   { TYPE_INTEGER,&sxi.default_window_height,    "default_window_height" },
8584 };
8585
8586 static struct TokenInfo debug_setup_tokens[] =
8587 {
8588   { TYPE_INTEGER, &sdi.frame_delay[0],          "debug.frame_delay_0"   },
8589   { TYPE_INTEGER, &sdi.frame_delay[1],          "debug.frame_delay_1"   },
8590   { TYPE_INTEGER, &sdi.frame_delay[2],          "debug.frame_delay_2"   },
8591   { TYPE_INTEGER, &sdi.frame_delay[3],          "debug.frame_delay_3"   },
8592   { TYPE_INTEGER, &sdi.frame_delay[4],          "debug.frame_delay_4"   },
8593   { TYPE_INTEGER, &sdi.frame_delay[5],          "debug.frame_delay_5"   },
8594   { TYPE_INTEGER, &sdi.frame_delay[6],          "debug.frame_delay_6"   },
8595   { TYPE_INTEGER, &sdi.frame_delay[7],          "debug.frame_delay_7"   },
8596   { TYPE_INTEGER, &sdi.frame_delay[8],          "debug.frame_delay_8"   },
8597   { TYPE_INTEGER, &sdi.frame_delay[9],          "debug.frame_delay_9"   },
8598   { TYPE_KEY_X11, &sdi.frame_delay_key[0],      "debug.key.frame_delay_0" },
8599   { TYPE_KEY_X11, &sdi.frame_delay_key[1],      "debug.key.frame_delay_1" },
8600   { TYPE_KEY_X11, &sdi.frame_delay_key[2],      "debug.key.frame_delay_2" },
8601   { TYPE_KEY_X11, &sdi.frame_delay_key[3],      "debug.key.frame_delay_3" },
8602   { TYPE_KEY_X11, &sdi.frame_delay_key[4],      "debug.key.frame_delay_4" },
8603   { TYPE_KEY_X11, &sdi.frame_delay_key[5],      "debug.key.frame_delay_5" },
8604   { TYPE_KEY_X11, &sdi.frame_delay_key[6],      "debug.key.frame_delay_6" },
8605   { TYPE_KEY_X11, &sdi.frame_delay_key[7],      "debug.key.frame_delay_7" },
8606   { TYPE_KEY_X11, &sdi.frame_delay_key[8],      "debug.key.frame_delay_8" },
8607   { TYPE_KEY_X11, &sdi.frame_delay_key[9],      "debug.key.frame_delay_9" },
8608   { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8609   { TYPE_BOOLEAN, &sdi.frame_delay_game_only,  "debug.frame_delay.game_only" },
8610   { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8611 };
8612
8613 static struct TokenInfo options_setup_tokens[] =
8614 {
8615   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               },
8616 };
8617
8618 static char *get_corrected_login_name(char *login_name)
8619 {
8620   /* needed because player name must be a fixed length string */
8621   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8622
8623   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8624   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8625
8626   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
8627     if (strchr(login_name_new, ' '))
8628       *strchr(login_name_new, ' ') = '\0';
8629
8630   return login_name_new;
8631 }
8632
8633 static void setSetupInfoToDefaults(struct SetupInfo *si)
8634 {
8635   int i;
8636
8637   si->player_name = get_corrected_login_name(getLoginName());
8638
8639   si->sound = TRUE;
8640   si->sound_loops = TRUE;
8641   si->sound_music = TRUE;
8642   si->sound_simple = TRUE;
8643   si->toons = TRUE;
8644   si->scroll_delay = TRUE;
8645   si->scroll_delay_value = STD_SCROLL_DELAY;
8646   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8647   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8648   si->fade_screens = TRUE;
8649   si->autorecord = TRUE;
8650   si->show_titlescreen = TRUE;
8651   si->quick_doors = FALSE;
8652   si->team_mode = FALSE;
8653   si->handicap = TRUE;
8654   si->skip_levels = TRUE;
8655   si->increment_levels = TRUE;
8656   si->time_limit = TRUE;
8657   si->fullscreen = FALSE;
8658   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8659   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8660   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8661   si->ask_on_escape = TRUE;
8662   si->ask_on_escape_editor = TRUE;
8663   si->quick_switch = FALSE;
8664   si->input_on_focus = FALSE;
8665   si->prefer_aga_graphics = TRUE;
8666   si->game_frame_delay = GAME_FRAME_DELAY;
8667   si->sp_show_border_elements = FALSE;
8668   si->small_game_graphics = FALSE;
8669   si->show_snapshot_buttons = FALSE;
8670
8671   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8672   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8673   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8674
8675   si->override_level_graphics = FALSE;
8676   si->override_level_sounds = FALSE;
8677   si->override_level_music = FALSE;
8678
8679   si->volume_simple = 100;              /* percent */
8680   si->volume_loops = 100;               /* percent */
8681   si->volume_music = 100;               /* percent */
8682
8683   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8684   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        /* percent */
8685   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        /* percent */
8686
8687   si->editor.el_boulderdash             = TRUE;
8688   si->editor.el_emerald_mine            = TRUE;
8689   si->editor.el_emerald_mine_club       = TRUE;
8690   si->editor.el_more                    = TRUE;
8691   si->editor.el_sokoban                 = TRUE;
8692   si->editor.el_supaplex                = TRUE;
8693   si->editor.el_diamond_caves           = TRUE;
8694   si->editor.el_dx_boulderdash          = TRUE;
8695
8696   si->editor.el_mirror_magic            = TRUE;
8697   si->editor.el_deflektor               = TRUE;
8698
8699   si->editor.el_chars                   = TRUE;
8700   si->editor.el_steel_chars             = TRUE;
8701
8702   si->editor.el_classic                 = TRUE;
8703   si->editor.el_custom                  = TRUE;
8704
8705   si->editor.el_user_defined            = FALSE;
8706   si->editor.el_dynamic                 = TRUE;
8707
8708   si->editor.el_headlines               = TRUE;
8709
8710   si->editor.show_element_token         = FALSE;
8711
8712   si->editor.use_template_for_new_levels = TRUE;
8713
8714   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
8715   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
8716   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
8717
8718   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
8719   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
8720   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
8721   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
8722   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8723
8724   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
8725   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
8726   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
8727   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
8728   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
8729   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
8730
8731   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
8732   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
8733   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
8734
8735   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
8736   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
8737   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
8738   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
8739
8740   for (i = 0; i < MAX_PLAYERS; i++)
8741   {
8742     si->input[i].use_joystick = FALSE;
8743     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8744     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
8745     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8746     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
8747     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
8748     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8749     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
8750     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
8751     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
8752     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
8753     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8754     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
8755     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
8756     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
8757     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
8758   }
8759
8760   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8761   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8762   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8763
8764   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
8765   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
8766   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
8767   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
8768   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
8769   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8770   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
8771
8772   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8773
8774   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8775   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8776   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8777
8778   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8779   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
8780   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
8781
8782   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8783   si->internal.choose_from_top_leveldir = FALSE;
8784   si->internal.show_scaling_in_title = TRUE;
8785
8786   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
8787   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8788
8789   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8790   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8791   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8792   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8793   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8794   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8795   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8796   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8797   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8798   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8799
8800   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8801   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8802   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8803   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8804   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8805   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8806   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8807   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8808   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8809   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8810
8811   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8812   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
8813
8814   si->debug.show_frames_per_second = FALSE;
8815
8816   si->options.verbose = FALSE;
8817
8818 #if defined(PLATFORM_ANDROID)
8819   si->fullscreen = TRUE;
8820 #endif
8821 }
8822
8823 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
8824 {
8825   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
8826 }
8827
8828 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8829 {
8830   si->editor_cascade.el_bd              = TRUE;
8831   si->editor_cascade.el_em              = TRUE;
8832   si->editor_cascade.el_emc             = TRUE;
8833   si->editor_cascade.el_rnd             = TRUE;
8834   si->editor_cascade.el_sb              = TRUE;
8835   si->editor_cascade.el_sp              = TRUE;
8836   si->editor_cascade.el_dc              = TRUE;
8837   si->editor_cascade.el_dx              = TRUE;
8838
8839   si->editor_cascade.el_mm              = TRUE;
8840   si->editor_cascade.el_df              = TRUE;
8841
8842   si->editor_cascade.el_chars           = FALSE;
8843   si->editor_cascade.el_steel_chars     = FALSE;
8844   si->editor_cascade.el_ce              = FALSE;
8845   si->editor_cascade.el_ge              = FALSE;
8846   si->editor_cascade.el_ref             = FALSE;
8847   si->editor_cascade.el_user            = FALSE;
8848   si->editor_cascade.el_dynamic         = FALSE;
8849 }
8850
8851 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
8852
8853 static char *getHideSetupToken(void *setup_value)
8854 {
8855   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
8856
8857   if (setup_value != NULL)
8858     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
8859
8860   return hide_setup_token;
8861 }
8862
8863 static void setHideSetupEntry(void *setup_value_raw)
8864 {
8865   /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
8866   void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
8867
8868   char *hide_setup_token = getHideSetupToken(setup_value);
8869
8870   if (setup_value != NULL)
8871     setHashEntry(hide_setup_hash, hide_setup_token, "");
8872 }
8873
8874 boolean hideSetupEntry(void *setup_value)
8875 {
8876   char *hide_setup_token = getHideSetupToken(setup_value);
8877
8878   return (setup_value != NULL &&
8879           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
8880 }
8881
8882 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
8883                                       struct TokenInfo *token_info,
8884                                       int token_nr, char *token_text)
8885 {
8886   char *token_hide_text = getStringCat2(token_text, ".hide");
8887   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
8888
8889   /* set the value of this setup option in the setup option structure */
8890   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
8891
8892   /* check if this setup option should be hidden in the setup menu */
8893   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
8894     setHideSetupEntry(token_info[token_nr].value);
8895 }
8896
8897 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
8898                                       struct TokenInfo *token_info,
8899                                       int token_nr)
8900 {
8901   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
8902                             token_info[token_nr].text);
8903 }
8904
8905 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8906 {
8907   int i, pnr;
8908
8909   if (!setup_file_hash)
8910     return;
8911
8912   if (hide_setup_hash == NULL)
8913     hide_setup_hash = newSetupFileHash();
8914
8915   /* global setup */
8916   si = setup;
8917   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8918     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
8919   setup = si;
8920
8921   /* editor setup */
8922   sei = setup.editor;
8923   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8924     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
8925   setup.editor = sei;
8926
8927   /* shortcut setup */
8928   ssi = setup.shortcut;
8929   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8930     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
8931   setup.shortcut = ssi;
8932
8933   /* player setup */
8934   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8935   {
8936     char prefix[30];
8937
8938     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8939
8940     sii = setup.input[pnr];
8941     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8942     {
8943       char full_token[100];
8944
8945       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8946       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
8947                                 full_token);
8948     }
8949     setup.input[pnr] = sii;
8950   }
8951
8952   /* system setup */
8953   syi = setup.system;
8954   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8955     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
8956   setup.system = syi;
8957
8958   /* internal setup */
8959   sxi = setup.internal;
8960   for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8961     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
8962   setup.internal = sxi;
8963
8964   /* debug setup */
8965   sdi = setup.debug;
8966   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8967     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
8968   setup.debug = sdi;
8969
8970   /* options setup */
8971   soi = setup.options;
8972   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8973     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
8974   setup.options = soi;
8975 }
8976
8977 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
8978 {
8979   int i;
8980
8981   if (!setup_file_hash)
8982     return;
8983
8984   /* auto setup */
8985   sasi = setup.auto_setup;
8986   for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
8987     setSetupInfo(auto_setup_tokens, i,
8988                  getHashEntry(setup_file_hash,
8989                               auto_setup_tokens[i].text));
8990   setup.auto_setup = sasi;
8991 }
8992
8993 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8994 {
8995   int i;
8996
8997   if (!setup_file_hash)
8998     return;
8999
9000   /* editor cascade setup */
9001   seci = setup.editor_cascade;
9002   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9003     setSetupInfo(editor_cascade_setup_tokens, i,
9004                  getHashEntry(setup_file_hash,
9005                               editor_cascade_setup_tokens[i].text));
9006   setup.editor_cascade = seci;
9007 }
9008
9009 void LoadSetupFromFilename(char *filename)
9010 {
9011   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9012
9013   if (setup_file_hash)
9014   {
9015     decodeSetupFileHash(setup_file_hash);
9016
9017     freeSetupFileHash(setup_file_hash);
9018   }
9019   else
9020   {
9021     Error(ERR_DEBUG, "using default setup values");
9022   }
9023 }
9024
9025 static void LoadSetup_SpecialPostProcessing()
9026 {
9027   char *player_name_new;
9028
9029   /* needed to work around problems with fixed length strings */
9030   player_name_new = get_corrected_login_name(setup.player_name);
9031   free(setup.player_name);
9032   setup.player_name = player_name_new;
9033
9034   /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9035   if (setup.scroll_delay == FALSE)
9036   {
9037     setup.scroll_delay_value = MIN_SCROLL_DELAY;
9038     setup.scroll_delay = TRUE;                  /* now always "on" */
9039   }
9040
9041   /* make sure that scroll delay value stays inside valid range */
9042   setup.scroll_delay_value =
9043     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9044 }
9045
9046 void LoadSetup()
9047 {
9048   char *filename;
9049
9050   /* always start with reliable default values */
9051   setSetupInfoToDefaults(&setup);
9052
9053   /* try to load setup values from default setup file */
9054   filename = getDefaultSetupFilename();
9055
9056   if (fileExists(filename))
9057     LoadSetupFromFilename(filename);
9058
9059   /* try to load setup values from user setup file */
9060   filename = getSetupFilename();
9061
9062   LoadSetupFromFilename(filename);
9063
9064   LoadSetup_SpecialPostProcessing();
9065 }
9066
9067 void LoadSetup_AutoSetup()
9068 {
9069   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9070   SetupFileHash *setup_file_hash = NULL;
9071
9072   /* always start with reliable default values */
9073   setSetupInfoToDefaults_AutoSetup(&setup);
9074
9075   setup_file_hash = loadSetupFileHash(filename);
9076
9077   if (setup_file_hash)
9078   {
9079     decodeSetupFileHash_AutoSetup(setup_file_hash);
9080
9081     freeSetupFileHash(setup_file_hash);
9082   }
9083
9084   free(filename);
9085 }
9086
9087 void LoadSetup_EditorCascade()
9088 {
9089   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9090   SetupFileHash *setup_file_hash = NULL;
9091
9092   /* always start with reliable default values */
9093   setSetupInfoToDefaults_EditorCascade(&setup);
9094
9095   setup_file_hash = loadSetupFileHash(filename);
9096
9097   if (setup_file_hash)
9098   {
9099     decodeSetupFileHash_EditorCascade(setup_file_hash);
9100
9101     freeSetupFileHash(setup_file_hash);
9102   }
9103
9104   free(filename);
9105 }
9106
9107 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9108                                            char *mapping_line)
9109 {
9110   char mapping_guid[MAX_LINE_LEN];
9111   char *mapping_start, *mapping_end;
9112
9113   // get GUID from game controller mapping line: copy complete line
9114   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9115   mapping_guid[MAX_LINE_LEN - 1] = '\0';
9116
9117   // get GUID from game controller mapping line: cut after GUID part
9118   mapping_start = strchr(mapping_guid, ',');
9119   if (mapping_start != NULL)
9120     *mapping_start = '\0';
9121
9122   // cut newline from game controller mapping line
9123   mapping_end = strchr(mapping_line, '\n');
9124   if (mapping_end != NULL)
9125     *mapping_end = '\0';
9126
9127   // add mapping entry to game controller mappings hash
9128   setHashEntry(mappings_hash, mapping_guid, mapping_line);
9129 }
9130
9131 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9132                                                  char *filename)
9133 {
9134   FILE *file;
9135
9136   if (!(file = fopen(filename, MODE_READ)))
9137   {
9138     Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9139
9140     return;
9141   }
9142
9143   while (!feof(file))
9144   {
9145     char line[MAX_LINE_LEN];
9146
9147     if (!fgets(line, MAX_LINE_LEN, file))
9148       break;
9149
9150     addGameControllerMappingToHash(mappings_hash, line);
9151   }
9152
9153   fclose(file);
9154 }
9155
9156 void SaveSetup()
9157 {
9158   char *filename = getSetupFilename();
9159   FILE *file;
9160   int i, pnr;
9161
9162   InitUserDataDirectory();
9163
9164   if (!(file = fopen(filename, MODE_WRITE)))
9165   {
9166     Error(ERR_WARN, "cannot write setup file '%s'", filename);
9167     return;
9168   }
9169
9170   fprintFileHeader(file, SETUP_FILENAME);
9171
9172   /* global setup */
9173   si = setup;
9174   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9175   {
9176     /* just to make things nicer :) */
9177     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9178         i == SETUP_TOKEN_GRAPHICS_SET ||
9179         i == SETUP_TOKEN_VOLUME_SIMPLE ||
9180         i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
9181       fprintf(file, "\n");
9182
9183     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9184   }
9185
9186   /* editor setup */
9187   sei = setup.editor;
9188   fprintf(file, "\n");
9189   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9190     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9191
9192   /* shortcut setup */
9193   ssi = setup.shortcut;
9194   fprintf(file, "\n");
9195   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9196     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9197
9198   /* player setup */
9199   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9200   {
9201     char prefix[30];
9202
9203     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9204     fprintf(file, "\n");
9205
9206     sii = setup.input[pnr];
9207     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9208       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9209   }
9210
9211   /* system setup */
9212   syi = setup.system;
9213   fprintf(file, "\n");
9214   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9215     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9216
9217   /* internal setup */
9218   /* (internal setup values not saved to user setup file) */
9219
9220   /* debug setup */
9221   sdi = setup.debug;
9222   fprintf(file, "\n");
9223   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9224     fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9225
9226   /* options setup */
9227   soi = setup.options;
9228   fprintf(file, "\n");
9229   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9230     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9231
9232   fclose(file);
9233
9234   SetFilePermissions(filename, PERMS_PRIVATE);
9235 }
9236
9237 void SaveSetup_AutoSetup()
9238 {
9239   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9240   FILE *file;
9241   int i;
9242
9243   InitUserDataDirectory();
9244
9245   if (!(file = fopen(filename, MODE_WRITE)))
9246   {
9247     Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9248     free(filename);
9249     return;
9250   }
9251
9252   fprintFileHeader(file, AUTOSETUP_FILENAME);
9253
9254   sasi = setup.auto_setup;
9255   for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9256     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9257
9258   fclose(file);
9259
9260   SetFilePermissions(filename, PERMS_PRIVATE);
9261
9262   free(filename);
9263 }
9264
9265 void SaveSetup_EditorCascade()
9266 {
9267   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9268   FILE *file;
9269   int i;
9270
9271   InitUserDataDirectory();
9272
9273   if (!(file = fopen(filename, MODE_WRITE)))
9274   {
9275     Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9276     free(filename);
9277     return;
9278   }
9279
9280   fprintFileHeader(file, EDITORCASCADE_FILENAME);
9281
9282   seci = setup.editor_cascade;
9283   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9284     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9285
9286   fclose(file);
9287
9288   SetFilePermissions(filename, PERMS_PRIVATE);
9289
9290   free(filename);
9291 }
9292
9293 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9294                                                   char *filename)
9295 {
9296   FILE *file;
9297
9298   if (!(file = fopen(filename, MODE_WRITE)))
9299   {
9300     Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9301
9302     return;
9303   }
9304
9305   BEGIN_HASH_ITERATION(mappings_hash, itr)
9306   {
9307     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9308   }
9309   END_HASH_ITERATION(mappings_hash, itr)
9310
9311   fclose(file);
9312 }
9313
9314 void SaveSetup_AddGameControllerMapping(char *mapping)
9315 {
9316   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9317   SetupFileHash *mappings_hash = newSetupFileHash();
9318
9319   InitUserDataDirectory();
9320
9321   // load existing personal game controller mappings
9322   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9323
9324   // add new mapping to personal game controller mappings
9325   addGameControllerMappingToHash(mappings_hash, mapping);
9326
9327   // save updated personal game controller mappings
9328   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9329
9330   freeSetupFileHash(mappings_hash);
9331   free(filename);
9332 }
9333
9334 void LoadCustomElementDescriptions()
9335 {
9336   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9337   SetupFileHash *setup_file_hash;
9338   int i;
9339
9340   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9341   {
9342     if (element_info[i].custom_description != NULL)
9343     {
9344       free(element_info[i].custom_description);
9345       element_info[i].custom_description = NULL;
9346     }
9347   }
9348
9349   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9350     return;
9351
9352   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9353   {
9354     char *token = getStringCat2(element_info[i].token_name, ".name");
9355     char *value = getHashEntry(setup_file_hash, token);
9356
9357     if (value != NULL)
9358       element_info[i].custom_description = getStringCopy(value);
9359
9360     free(token);
9361   }
9362
9363   freeSetupFileHash(setup_file_hash);
9364 }
9365
9366 static int getElementFromToken(char *token)
9367 {
9368   char *value = getHashEntry(element_token_hash, token);
9369
9370   if (value != NULL)
9371     return atoi(value);
9372
9373   Error(ERR_WARN, "unknown element token '%s'", token);
9374
9375   return EL_UNDEFINED;
9376 }
9377
9378 static int get_token_parameter_value(char *token, char *value_raw)
9379 {
9380   char *suffix;
9381
9382   if (token == NULL || value_raw == NULL)
9383     return ARG_UNDEFINED_VALUE;
9384
9385   suffix = strrchr(token, '.');
9386   if (suffix == NULL)
9387     suffix = token;
9388
9389   if (strEqual(suffix, ".element"))
9390     return getElementFromToken(value_raw);
9391
9392   /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9393   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9394 }
9395
9396 void InitMenuDesignSettings_Static()
9397 {
9398   int i;
9399
9400   /* always start with reliable default values from static default config */
9401   for (i = 0; image_config_vars[i].token != NULL; i++)
9402   {
9403     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9404
9405     if (value != NULL)
9406       *image_config_vars[i].value =
9407         get_token_parameter_value(image_config_vars[i].token, value);
9408   }
9409 }
9410
9411 static void InitMenuDesignSettings_SpecialPreProcessing()
9412 {
9413   int i;
9414
9415   /* the following initializes hierarchical values from static configuration */
9416
9417   /* special case: initialize "ARG_DEFAULT" values in static default config */
9418   /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9419   titlescreen_initial_first_default.fade_mode  =
9420     title_initial_first_default.fade_mode;
9421   titlescreen_initial_first_default.fade_delay =
9422     title_initial_first_default.fade_delay;
9423   titlescreen_initial_first_default.post_delay =
9424     title_initial_first_default.post_delay;
9425   titlescreen_initial_first_default.auto_delay =
9426     title_initial_first_default.auto_delay;
9427   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
9428   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9429   titlescreen_first_default.post_delay = title_first_default.post_delay;
9430   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9431   titlemessage_initial_first_default.fade_mode  =
9432     title_initial_first_default.fade_mode;
9433   titlemessage_initial_first_default.fade_delay =
9434     title_initial_first_default.fade_delay;
9435   titlemessage_initial_first_default.post_delay =
9436     title_initial_first_default.post_delay;
9437   titlemessage_initial_first_default.auto_delay =
9438     title_initial_first_default.auto_delay;
9439   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
9440   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9441   titlemessage_first_default.post_delay = title_first_default.post_delay;
9442   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9443
9444   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
9445   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9446   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9447   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9448   titlescreen_default.fade_mode  = title_default.fade_mode;
9449   titlescreen_default.fade_delay = title_default.fade_delay;
9450   titlescreen_default.post_delay = title_default.post_delay;
9451   titlescreen_default.auto_delay = title_default.auto_delay;
9452   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
9453   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9454   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9455   titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9456   titlemessage_default.fade_mode  = title_default.fade_mode;
9457   titlemessage_default.fade_delay = title_default.fade_delay;
9458   titlemessage_default.post_delay = title_default.post_delay;
9459   titlemessage_default.auto_delay = title_default.auto_delay;
9460
9461   /* special case: initialize "ARG_DEFAULT" values in static default config */
9462   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9463   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9464   {
9465     titlescreen_initial_first[i] = titlescreen_initial_first_default;
9466     titlescreen_first[i] = titlescreen_first_default;
9467     titlemessage_initial_first[i] = titlemessage_initial_first_default;
9468     titlemessage_first[i] = titlemessage_first_default;
9469
9470     titlescreen_initial[i] = titlescreen_initial_default;
9471     titlescreen[i] = titlescreen_default;
9472     titlemessage_initial[i] = titlemessage_initial_default;
9473     titlemessage[i] = titlemessage_default;
9474   }
9475
9476   /* special case: initialize "ARG_DEFAULT" values in static default config */
9477   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9478   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9479   {
9480     if (i == GFX_SPECIAL_ARG_TITLE)     /* title values already initialized */
9481       continue;
9482
9483     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9484     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9485     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9486   }
9487
9488   /* special case: initialize "ARG_DEFAULT" values in static default config */
9489   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9490   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9491   {
9492     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9493     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9494     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9495
9496     if (i == GFX_SPECIAL_ARG_EDITOR)    /* editor values already initialized */
9497       continue;
9498
9499     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9500   }
9501 }
9502
9503 static void InitMenuDesignSettings_SpecialPostProcessing()
9504 {
9505   static struct
9506   {
9507     struct XY *dst, *src;
9508   }
9509   game_buttons_xy[] =
9510   {
9511     { &game.button.save,        &game.button.stop       },
9512     { &game.button.pause2,      &game.button.pause      },
9513     { &game.button.load,        &game.button.play       },
9514     { &game.button.undo,        &game.button.stop       },
9515     { &game.button.redo,        &game.button.play       },
9516
9517     { NULL,                     NULL                    }
9518   };
9519   int i;
9520
9521   /* special case: initialize later added SETUP list size from LEVELS value */
9522   if (menu.list_size[GAME_MODE_SETUP] == -1)
9523     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9524
9525   /* set default position for snapshot buttons to stop/pause/play buttons */
9526   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9527     if ((*game_buttons_xy[i].dst).x == -1 &&
9528         (*game_buttons_xy[i].dst).y == -1)
9529       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9530 }
9531
9532 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9533 {
9534   static struct
9535   {
9536     struct XYTileSize *dst, *src;
9537     int graphic;
9538   }
9539   editor_buttons_xy[] =
9540   {
9541     {
9542       &editor.button.element_left,      &editor.palette.element_left,
9543       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9544     },
9545     {
9546       &editor.button.element_middle,    &editor.palette.element_middle,
9547       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9548     },
9549     {
9550       &editor.button.element_right,     &editor.palette.element_right,
9551       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9552     },
9553
9554     { NULL,                     NULL                    }
9555   };
9556   int i;
9557
9558   /* set default position for element buttons to element graphics */
9559   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9560   {
9561     if ((*editor_buttons_xy[i].dst).x == -1 &&
9562         (*editor_buttons_xy[i].dst).y == -1)
9563     {
9564       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9565
9566       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9567
9568       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9569     }
9570   }
9571 }
9572
9573 static void LoadMenuDesignSettingsFromFilename(char *filename)
9574 {
9575   static struct TitleFadingInfo tfi;
9576   static struct TitleMessageInfo tmi;
9577   static struct TokenInfo title_tokens[] =
9578   {
9579     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
9580     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
9581     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
9582     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
9583
9584     { -1,               NULL,                   NULL                    }
9585   };
9586   static struct TokenInfo titlemessage_tokens[] =
9587   {
9588     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
9589     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
9590     { TYPE_INTEGER,     &tmi.width,             ".width"                },
9591     { TYPE_INTEGER,     &tmi.height,            ".height"               },
9592     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
9593     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
9594     { TYPE_INTEGER,     &tmi.align,             ".align"                },
9595     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
9596     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
9597     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
9598     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
9599     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
9600     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
9601     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
9602     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
9603     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
9604     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
9605
9606     { -1,               NULL,                   NULL                    }
9607   };
9608   static struct
9609   {
9610     struct TitleFadingInfo *info;
9611     char *text;
9612   }
9613   title_info[] =
9614   {
9615     /* initialize first titles from "enter screen" definitions, if defined */
9616     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
9617     { &title_first_default,             "menu.enter_screen.TITLE"       },
9618
9619     /* initialize title screens from "next screen" definitions, if defined */
9620     { &title_initial_default,           "menu.next_screen.TITLE"        },
9621     { &title_default,                   "menu.next_screen.TITLE"        },
9622
9623     { NULL,                             NULL                            }
9624   };
9625   static struct
9626   {
9627     struct TitleMessageInfo *array;
9628     char *text;
9629   }
9630   titlemessage_arrays[] =
9631   {
9632     /* initialize first titles from "enter screen" definitions, if defined */
9633     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
9634     { titlescreen_first,                "menu.enter_screen.TITLE"       },
9635     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
9636     { titlemessage_first,               "menu.enter_screen.TITLE"       },
9637
9638     /* initialize titles from "next screen" definitions, if defined */
9639     { titlescreen_initial,              "menu.next_screen.TITLE"        },
9640     { titlescreen,                      "menu.next_screen.TITLE"        },
9641     { titlemessage_initial,             "menu.next_screen.TITLE"        },
9642     { titlemessage,                     "menu.next_screen.TITLE"        },
9643
9644     /* overwrite titles with title definitions, if defined */
9645     { titlescreen_initial_first,        "[title_initial]"               },
9646     { titlescreen_first,                "[title]"                       },
9647     { titlemessage_initial_first,       "[title_initial]"               },
9648     { titlemessage_first,               "[title]"                       },
9649
9650     { titlescreen_initial,              "[title_initial]"               },
9651     { titlescreen,                      "[title]"                       },
9652     { titlemessage_initial,             "[title_initial]"               },
9653     { titlemessage,                     "[title]"                       },
9654
9655     /* overwrite titles with title screen/message definitions, if defined */
9656     { titlescreen_initial_first,        "[titlescreen_initial]"         },
9657     { titlescreen_first,                "[titlescreen]"                 },
9658     { titlemessage_initial_first,       "[titlemessage_initial]"        },
9659     { titlemessage_first,               "[titlemessage]"                },
9660
9661     { titlescreen_initial,              "[titlescreen_initial]"         },
9662     { titlescreen,                      "[titlescreen]"                 },
9663     { titlemessage_initial,             "[titlemessage_initial]"        },
9664     { titlemessage,                     "[titlemessage]"                },
9665
9666     { NULL,                             NULL                            }
9667   };
9668   SetupFileHash *setup_file_hash;
9669   int i, j, k;
9670
9671   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9672     return;
9673
9674   /* the following initializes hierarchical values from dynamic configuration */
9675
9676   /* special case: initialize with default values that may be overwritten */
9677   /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9678   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9679   {
9680     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9681     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9682     char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9683
9684     if (value_1 != NULL)
9685       menu.draw_xoffset[i] = get_integer_from_string(value_1);
9686     if (value_2 != NULL)
9687       menu.draw_yoffset[i] = get_integer_from_string(value_2);
9688     if (value_3 != NULL)
9689       menu.list_size[i] = get_integer_from_string(value_3);
9690   }
9691
9692   /* special case: initialize with default values that may be overwritten */
9693   /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9694   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9695   {
9696     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9697     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9698
9699     if (value_1 != NULL)
9700       menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9701     if (value_2 != NULL)
9702       menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9703
9704     if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9705     {
9706       char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9707
9708       if (value_1 != NULL)
9709         menu.list_size_info[i] = get_integer_from_string(value_1);
9710     }
9711   }
9712
9713   /* special case: initialize with default values that may be overwritten */
9714   /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9715   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9716   {
9717     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9718     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9719
9720     if (value_1 != NULL)
9721       menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9722     if (value_2 != NULL)
9723       menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9724   }
9725
9726   /* special case: initialize with default values that may be overwritten */
9727   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9728   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9729   {
9730     char *token_1 = "menu.enter_screen.fade_mode";
9731     char *token_2 = "menu.enter_screen.fade_delay";
9732     char *token_3 = "menu.enter_screen.post_delay";
9733     char *token_4 = "menu.leave_screen.fade_mode";
9734     char *token_5 = "menu.leave_screen.fade_delay";
9735     char *token_6 = "menu.leave_screen.post_delay";
9736     char *token_7 = "menu.next_screen.fade_mode";
9737     char *token_8 = "menu.next_screen.fade_delay";
9738     char *token_9 = "menu.next_screen.post_delay";
9739     char *value_1 = getHashEntry(setup_file_hash, token_1);
9740     char *value_2 = getHashEntry(setup_file_hash, token_2);
9741     char *value_3 = getHashEntry(setup_file_hash, token_3);
9742     char *value_4 = getHashEntry(setup_file_hash, token_4);
9743     char *value_5 = getHashEntry(setup_file_hash, token_5);
9744     char *value_6 = getHashEntry(setup_file_hash, token_6);
9745     char *value_7 = getHashEntry(setup_file_hash, token_7);
9746     char *value_8 = getHashEntry(setup_file_hash, token_8);
9747     char *value_9 = getHashEntry(setup_file_hash, token_9);
9748
9749     if (value_1 != NULL)
9750       menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9751                                                                  value_1);
9752     if (value_2 != NULL)
9753       menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9754                                                                   value_2);
9755     if (value_3 != NULL)
9756       menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9757                                                                   value_3);
9758     if (value_4 != NULL)
9759       menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9760                                                                  value_4);
9761     if (value_5 != NULL)
9762       menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9763                                                                   value_5);
9764     if (value_6 != NULL)
9765       menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9766                                                                   value_6);
9767     if (value_7 != NULL)
9768       menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9769                                                                 value_7);
9770     if (value_8 != NULL)
9771       menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9772                                                                  value_8);
9773     if (value_9 != NULL)
9774       menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9775                                                                  value_9);
9776   }
9777
9778   /* special case: initialize with default values that may be overwritten */
9779   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9780   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9781   {
9782     char *token_w1 = "viewport.window.width";
9783     char *token_w2 = "viewport.window.height";
9784     char *token_01 = "viewport.playfield.x";
9785     char *token_02 = "viewport.playfield.y";
9786     char *token_03 = "viewport.playfield.width";
9787     char *token_04 = "viewport.playfield.height";
9788     char *token_05 = "viewport.playfield.border_size";
9789     char *token_06 = "viewport.door_1.x";
9790     char *token_07 = "viewport.door_1.y";
9791     char *token_08 = "viewport.door_1.width";
9792     char *token_09 = "viewport.door_1.height";
9793     char *token_10 = "viewport.door_1.border_size";
9794     char *token_11 = "viewport.door_2.x";
9795     char *token_12 = "viewport.door_2.y";
9796     char *token_13 = "viewport.door_2.width";
9797     char *token_14 = "viewport.door_2.height";
9798     char *token_15 = "viewport.door_2.border_size";
9799     char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9800     char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9801     char *value_01 = getHashEntry(setup_file_hash, token_01);
9802     char *value_02 = getHashEntry(setup_file_hash, token_02);
9803     char *value_03 = getHashEntry(setup_file_hash, token_03);
9804     char *value_04 = getHashEntry(setup_file_hash, token_04);
9805     char *value_05 = getHashEntry(setup_file_hash, token_05);
9806     char *value_06 = getHashEntry(setup_file_hash, token_06);
9807     char *value_07 = getHashEntry(setup_file_hash, token_07);
9808     char *value_08 = getHashEntry(setup_file_hash, token_08);
9809     char *value_09 = getHashEntry(setup_file_hash, token_09);
9810     char *value_10 = getHashEntry(setup_file_hash, token_10);
9811     char *value_11 = getHashEntry(setup_file_hash, token_11);
9812     char *value_12 = getHashEntry(setup_file_hash, token_12);
9813     char *value_13 = getHashEntry(setup_file_hash, token_13);
9814     char *value_14 = getHashEntry(setup_file_hash, token_14);
9815     char *value_15 = getHashEntry(setup_file_hash, token_15);
9816
9817     if (value_w1 != NULL)
9818       viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9819     if (value_w2 != NULL)
9820       viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9821     if (value_01 != NULL)
9822       viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9823     if (value_02 != NULL)
9824       viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9825     if (value_03 != NULL)
9826       viewport.playfield[i].width = get_token_parameter_value(token_03,
9827                                                               value_03);
9828     if (value_04 != NULL)
9829       viewport.playfield[i].height = get_token_parameter_value(token_04,
9830                                                                value_04);
9831     if (value_05 != NULL)
9832       viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9833                                                                     value_05);
9834     if (value_06 != NULL)
9835       viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9836     if (value_07 != NULL)
9837       viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9838     if (value_08 != NULL)
9839       viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9840     if (value_09 != NULL)
9841       viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9842     if (value_10 != NULL)
9843       viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9844                                                                  value_10);
9845     if (value_11 != NULL)
9846       viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9847     if (value_12 != NULL)
9848       viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9849     if (value_13 != NULL)
9850       viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9851     if (value_14 != NULL)
9852       viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9853     if (value_15 != NULL)
9854       viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9855                                                                  value_15);
9856   }
9857
9858   /* special case: initialize with default values that may be overwritten */
9859   /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9860   for (i = 0; title_info[i].info != NULL; i++)
9861   {
9862     struct TitleFadingInfo *info = title_info[i].info;
9863     char *base_token = title_info[i].text;
9864
9865     for (j = 0; title_tokens[j].type != -1; j++)
9866     {
9867       char *token = getStringCat2(base_token, title_tokens[j].text);
9868       char *value = getHashEntry(setup_file_hash, token);
9869
9870       if (value != NULL)
9871       {
9872         int parameter_value = get_token_parameter_value(token, value);
9873
9874         tfi = *info;
9875
9876         *(int *)title_tokens[j].value = (int)parameter_value;
9877
9878         *info = tfi;
9879       }
9880
9881       free(token);
9882     }
9883   }
9884
9885   /* special case: initialize with default values that may be overwritten */
9886   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9887   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9888   {
9889     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9890     char *base_token = titlemessage_arrays[i].text;
9891
9892     for (j = 0; titlemessage_tokens[j].type != -1; j++)
9893     {
9894       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9895       char *value = getHashEntry(setup_file_hash, token);
9896
9897       if (value != NULL)
9898       {
9899         int parameter_value = get_token_parameter_value(token, value);
9900
9901         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9902         {
9903           tmi = array[k];
9904
9905           if (titlemessage_tokens[j].type == TYPE_INTEGER)
9906             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
9907           else
9908             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9909
9910           array[k] = tmi;
9911         }
9912       }
9913
9914       free(token);
9915     }
9916   }
9917
9918   /* read (and overwrite with) values that may be specified in config file */
9919   for (i = 0; image_config_vars[i].token != NULL; i++)
9920   {
9921     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9922
9923     /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9924     if (value != NULL && !strEqual(value, ARG_DEFAULT))
9925       *image_config_vars[i].value =
9926         get_token_parameter_value(image_config_vars[i].token, value);
9927   }
9928
9929   freeSetupFileHash(setup_file_hash);
9930 }
9931
9932 void LoadMenuDesignSettings()
9933 {
9934   char *filename_base = UNDEFINED_FILENAME, *filename_local;
9935
9936   InitMenuDesignSettings_Static();
9937   InitMenuDesignSettings_SpecialPreProcessing();
9938
9939   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9940   {
9941     /* first look for special settings configured in level series config */
9942     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9943
9944     if (fileExists(filename_base))
9945       LoadMenuDesignSettingsFromFilename(filename_base);
9946   }
9947
9948   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9949
9950   if (filename_local != NULL && !strEqual(filename_base, filename_local))
9951     LoadMenuDesignSettingsFromFilename(filename_local);
9952
9953   InitMenuDesignSettings_SpecialPostProcessing();
9954 }
9955
9956 void LoadMenuDesignSettings_AfterGraphics()
9957 {
9958   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9959 }
9960
9961 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9962 {
9963   char *filename = getEditorSetupFilename();
9964   SetupFileList *setup_file_list, *list;
9965   SetupFileHash *element_hash;
9966   int num_unknown_tokens = 0;
9967   int i;
9968
9969   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9970     return;
9971
9972   element_hash = newSetupFileHash();
9973
9974   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9975     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9976
9977   /* determined size may be larger than needed (due to unknown elements) */
9978   *num_elements = 0;
9979   for (list = setup_file_list; list != NULL; list = list->next)
9980     (*num_elements)++;
9981
9982   /* add space for up to 3 more elements for padding that may be needed */
9983   *num_elements += 3;
9984
9985   /* free memory for old list of elements, if needed */
9986   checked_free(*elements);
9987
9988   /* allocate memory for new list of elements */
9989   *elements = checked_malloc(*num_elements * sizeof(int));
9990
9991   *num_elements = 0;
9992   for (list = setup_file_list; list != NULL; list = list->next)
9993   {
9994     char *value = getHashEntry(element_hash, list->token);
9995
9996     if (value == NULL)          /* try to find obsolete token mapping */
9997     {
9998       char *mapped_token = get_mapped_token(list->token);
9999
10000       if (mapped_token != NULL)
10001       {
10002         value = getHashEntry(element_hash, mapped_token);
10003
10004         free(mapped_token);
10005       }
10006     }
10007
10008     if (value != NULL)
10009     {
10010       (*elements)[(*num_elements)++] = atoi(value);
10011     }
10012     else
10013     {
10014       if (num_unknown_tokens == 0)
10015       {
10016         Error(ERR_INFO_LINE, "-");
10017         Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10018         Error(ERR_INFO, "- config file: '%s'", filename);
10019
10020         num_unknown_tokens++;
10021       }
10022
10023       Error(ERR_INFO, "- token: '%s'", list->token);
10024     }
10025   }
10026
10027   if (num_unknown_tokens > 0)
10028     Error(ERR_INFO_LINE, "-");
10029
10030   while (*num_elements % 4)     /* pad with empty elements, if needed */
10031     (*elements)[(*num_elements)++] = EL_EMPTY;
10032
10033   freeSetupFileList(setup_file_list);
10034   freeSetupFileHash(element_hash);
10035
10036 #if 0
10037   for (i = 0; i < *num_elements; i++)
10038     printf("editor: element '%s' [%d]\n",
10039            element_info[(*elements)[i]].token_name, (*elements)[i]);
10040 #endif
10041 }
10042
10043 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10044                                                      boolean is_sound)
10045 {
10046   SetupFileHash *setup_file_hash = NULL;
10047   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10048   char *filename_music, *filename_prefix, *filename_info;
10049   struct
10050   {
10051     char *token;
10052     char **value_ptr;
10053   }
10054   token_to_value_ptr[] =
10055   {
10056     { "title_header",   &tmp_music_file_info.title_header       },
10057     { "artist_header",  &tmp_music_file_info.artist_header      },
10058     { "album_header",   &tmp_music_file_info.album_header       },
10059     { "year_header",    &tmp_music_file_info.year_header        },
10060
10061     { "title",          &tmp_music_file_info.title              },
10062     { "artist",         &tmp_music_file_info.artist             },
10063     { "album",          &tmp_music_file_info.album              },
10064     { "year",           &tmp_music_file_info.year               },
10065
10066     { NULL,             NULL                                    },
10067   };
10068   int i;
10069
10070   filename_music = (is_sound ? getCustomSoundFilename(basename) :
10071                     getCustomMusicFilename(basename));
10072
10073   if (filename_music == NULL)
10074     return NULL;
10075
10076   /* ---------- try to replace file extension ---------- */
10077
10078   filename_prefix = getStringCopy(filename_music);
10079   if (strrchr(filename_prefix, '.') != NULL)
10080     *strrchr(filename_prefix, '.') = '\0';
10081   filename_info = getStringCat2(filename_prefix, ".txt");
10082
10083   if (fileExists(filename_info))
10084     setup_file_hash = loadSetupFileHash(filename_info);
10085
10086   free(filename_prefix);
10087   free(filename_info);
10088
10089   if (setup_file_hash == NULL)
10090   {
10091     /* ---------- try to add file extension ---------- */
10092
10093     filename_prefix = getStringCopy(filename_music);
10094     filename_info = getStringCat2(filename_prefix, ".txt");
10095
10096     if (fileExists(filename_info))
10097       setup_file_hash = loadSetupFileHash(filename_info);
10098
10099     free(filename_prefix);
10100     free(filename_info);
10101   }
10102
10103   if (setup_file_hash == NULL)
10104     return NULL;
10105
10106   /* ---------- music file info found ---------- */
10107
10108   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10109
10110   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10111   {
10112     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10113
10114     *token_to_value_ptr[i].value_ptr =
10115       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10116   }
10117
10118   tmp_music_file_info.basename = getStringCopy(basename);
10119   tmp_music_file_info.music = music;
10120   tmp_music_file_info.is_sound = is_sound;
10121
10122   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10123   *new_music_file_info = tmp_music_file_info;
10124
10125   return new_music_file_info;
10126 }
10127
10128 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10129 {
10130   return get_music_file_info_ext(basename, music, FALSE);
10131 }
10132
10133 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10134 {
10135   return get_music_file_info_ext(basename, sound, TRUE);
10136 }
10137
10138 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10139                                      char *basename, boolean is_sound)
10140 {
10141   for (; list != NULL; list = list->next)
10142     if (list->is_sound == is_sound && strEqual(list->basename, basename))
10143       return TRUE;
10144
10145   return FALSE;
10146 }
10147
10148 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10149 {
10150   return music_info_listed_ext(list, basename, FALSE);
10151 }
10152
10153 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10154 {
10155   return music_info_listed_ext(list, basename, TRUE);
10156 }
10157
10158 void LoadMusicInfo()
10159 {
10160   char *music_directory = getCustomMusicDirectory();
10161   int num_music = getMusicListSize();
10162   int num_music_noconf = 0;
10163   int num_sounds = getSoundListSize();
10164   Directory *dir;
10165   DirectoryEntry *dir_entry;
10166   struct FileInfo *music, *sound;
10167   struct MusicFileInfo *next, **new;
10168   int i;
10169
10170   while (music_file_info != NULL)
10171   {
10172     next = music_file_info->next;
10173
10174     checked_free(music_file_info->basename);
10175
10176     checked_free(music_file_info->title_header);
10177     checked_free(music_file_info->artist_header);
10178     checked_free(music_file_info->album_header);
10179     checked_free(music_file_info->year_header);
10180
10181     checked_free(music_file_info->title);
10182     checked_free(music_file_info->artist);
10183     checked_free(music_file_info->album);
10184     checked_free(music_file_info->year);
10185
10186     free(music_file_info);
10187
10188     music_file_info = next;
10189   }
10190
10191   new = &music_file_info;
10192
10193   for (i = 0; i < num_music; i++)
10194   {
10195     music = getMusicListEntry(i);
10196
10197     if (music->filename == NULL)
10198       continue;
10199
10200     if (strEqual(music->filename, UNDEFINED_FILENAME))
10201       continue;
10202
10203     /* a configured file may be not recognized as music */
10204     if (!FileIsMusic(music->filename))
10205       continue;
10206
10207     if (!music_info_listed(music_file_info, music->filename))
10208     {
10209       *new = get_music_file_info(music->filename, i);
10210
10211       if (*new != NULL)
10212         new = &(*new)->next;
10213     }
10214   }
10215
10216   if ((dir = openDirectory(music_directory)) == NULL)
10217   {
10218     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10219     return;
10220   }
10221
10222   while ((dir_entry = readDirectory(dir)) != NULL)      /* loop all entries */
10223   {
10224     char *basename = dir_entry->basename;
10225     boolean music_already_used = FALSE;
10226     int i;
10227
10228     /* skip all music files that are configured in music config file */
10229     for (i = 0; i < num_music; i++)
10230     {
10231       music = getMusicListEntry(i);
10232
10233       if (music->filename == NULL)
10234         continue;
10235
10236       if (strEqual(basename, music->filename))
10237       {
10238         music_already_used = TRUE;
10239         break;
10240       }
10241     }
10242
10243     if (music_already_used)
10244       continue;
10245
10246     if (!FileIsMusic(dir_entry->filename))
10247       continue;
10248
10249     if (!music_info_listed(music_file_info, basename))
10250     {
10251       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10252
10253       if (*new != NULL)
10254         new = &(*new)->next;
10255     }
10256
10257     num_music_noconf++;
10258   }
10259
10260   closeDirectory(dir);
10261
10262   for (i = 0; i < num_sounds; i++)
10263   {
10264     sound = getSoundListEntry(i);
10265
10266     if (sound->filename == NULL)
10267       continue;
10268
10269     if (strEqual(sound->filename, UNDEFINED_FILENAME))
10270       continue;
10271
10272     /* a configured file may be not recognized as sound */
10273     if (!FileIsSound(sound->filename))
10274       continue;
10275
10276     if (!sound_info_listed(music_file_info, sound->filename))
10277     {
10278       *new = get_sound_file_info(sound->filename, i);
10279       if (*new != NULL)
10280         new = &(*new)->next;
10281     }
10282   }
10283 }
10284
10285 void add_helpanim_entry(int element, int action, int direction, int delay,
10286                         int *num_list_entries)
10287 {
10288   struct HelpAnimInfo *new_list_entry;
10289   (*num_list_entries)++;
10290
10291   helpanim_info =
10292     checked_realloc(helpanim_info,
10293                     *num_list_entries * sizeof(struct HelpAnimInfo));
10294   new_list_entry = &helpanim_info[*num_list_entries - 1];
10295
10296   new_list_entry->element = element;
10297   new_list_entry->action = action;
10298   new_list_entry->direction = direction;
10299   new_list_entry->delay = delay;
10300 }
10301
10302 void print_unknown_token(char *filename, char *token, int token_nr)
10303 {
10304   if (token_nr == 0)
10305   {
10306     Error(ERR_INFO_LINE, "-");
10307     Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10308     Error(ERR_INFO, "- config file: '%s'", filename);
10309   }
10310
10311   Error(ERR_INFO, "- token: '%s'", token);
10312 }
10313
10314 void print_unknown_token_end(int token_nr)
10315 {
10316   if (token_nr > 0)
10317     Error(ERR_INFO_LINE, "-");
10318 }
10319
10320 void LoadHelpAnimInfo()
10321 {
10322   char *filename = getHelpAnimFilename();
10323   SetupFileList *setup_file_list = NULL, *list;
10324   SetupFileHash *element_hash, *action_hash, *direction_hash;
10325   int num_list_entries = 0;
10326   int num_unknown_tokens = 0;
10327   int i;
10328
10329   if (fileExists(filename))
10330     setup_file_list = loadSetupFileList(filename);
10331
10332   if (setup_file_list == NULL)
10333   {
10334     /* use reliable default values from static configuration */
10335     SetupFileList *insert_ptr;
10336
10337     insert_ptr = setup_file_list =
10338       newSetupFileList(helpanim_config[0].token,
10339                        helpanim_config[0].value);
10340
10341     for (i = 1; helpanim_config[i].token; i++)
10342       insert_ptr = addListEntry(insert_ptr,
10343                                 helpanim_config[i].token,
10344                                 helpanim_config[i].value);
10345   }
10346
10347   element_hash   = newSetupFileHash();
10348   action_hash    = newSetupFileHash();
10349   direction_hash = newSetupFileHash();
10350
10351   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10352     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10353
10354   for (i = 0; i < NUM_ACTIONS; i++)
10355     setHashEntry(action_hash, element_action_info[i].suffix,
10356                  i_to_a(element_action_info[i].value));
10357
10358   /* do not store direction index (bit) here, but direction value! */
10359   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10360     setHashEntry(direction_hash, element_direction_info[i].suffix,
10361                  i_to_a(1 << element_direction_info[i].value));
10362
10363   for (list = setup_file_list; list != NULL; list = list->next)
10364   {
10365     char *element_token, *action_token, *direction_token;
10366     char *element_value, *action_value, *direction_value;
10367     int delay = atoi(list->value);
10368
10369     if (strEqual(list->token, "end"))
10370     {
10371       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10372
10373       continue;
10374     }
10375
10376     /* first try to break element into element/action/direction parts;
10377        if this does not work, also accept combined "element[.act][.dir]"
10378        elements (like "dynamite.active"), which are unique elements */
10379
10380     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
10381     {
10382       element_value = getHashEntry(element_hash, list->token);
10383       if (element_value != NULL)        /* element found */
10384         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10385                            &num_list_entries);
10386       else
10387       {
10388         /* no further suffixes found -- this is not an element */
10389         print_unknown_token(filename, list->token, num_unknown_tokens++);
10390       }
10391
10392       continue;
10393     }
10394
10395     /* token has format "<prefix>.<something>" */
10396
10397     action_token = strchr(list->token, '.');    /* suffix may be action ... */
10398     direction_token = action_token;             /* ... or direction */
10399
10400     element_token = getStringCopy(list->token);
10401     *strchr(element_token, '.') = '\0';
10402
10403     element_value = getHashEntry(element_hash, element_token);
10404
10405     if (element_value == NULL)          /* this is no element */
10406     {
10407       element_value = getHashEntry(element_hash, list->token);
10408       if (element_value != NULL)        /* combined element found */
10409         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10410                            &num_list_entries);
10411       else
10412         print_unknown_token(filename, list->token, num_unknown_tokens++);
10413
10414       free(element_token);
10415
10416       continue;
10417     }
10418
10419     action_value = getHashEntry(action_hash, action_token);
10420
10421     if (action_value != NULL)           /* action found */
10422     {
10423       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10424                     &num_list_entries);
10425
10426       free(element_token);
10427
10428       continue;
10429     }
10430
10431     direction_value = getHashEntry(direction_hash, direction_token);
10432
10433     if (direction_value != NULL)        /* direction found */
10434     {
10435       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10436                          &num_list_entries);
10437
10438       free(element_token);
10439
10440       continue;
10441     }
10442
10443     if (strchr(action_token + 1, '.') == NULL)
10444     {
10445       /* no further suffixes found -- this is not an action nor direction */
10446
10447       element_value = getHashEntry(element_hash, list->token);
10448       if (element_value != NULL)        /* combined element found */
10449         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10450                            &num_list_entries);
10451       else
10452         print_unknown_token(filename, list->token, num_unknown_tokens++);
10453
10454       free(element_token);
10455
10456       continue;
10457     }
10458
10459     /* token has format "<prefix>.<suffix>.<something>" */
10460
10461     direction_token = strchr(action_token + 1, '.');
10462
10463     action_token = getStringCopy(action_token);
10464     *strchr(action_token + 1, '.') = '\0';
10465
10466     action_value = getHashEntry(action_hash, action_token);
10467
10468     if (action_value == NULL)           /* this is no action */
10469     {
10470       element_value = getHashEntry(element_hash, list->token);
10471       if (element_value != NULL)        /* combined element found */
10472         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10473                            &num_list_entries);
10474       else
10475         print_unknown_token(filename, list->token, num_unknown_tokens++);
10476
10477       free(element_token);
10478       free(action_token);
10479
10480       continue;
10481     }
10482
10483     direction_value = getHashEntry(direction_hash, direction_token);
10484
10485     if (direction_value != NULL)        /* direction found */
10486     {
10487       add_helpanim_entry(atoi(element_value), atoi(action_value),
10488                          atoi(direction_value), delay, &num_list_entries);
10489
10490       free(element_token);
10491       free(action_token);
10492
10493       continue;
10494     }
10495
10496     /* this is no direction */
10497
10498     element_value = getHashEntry(element_hash, list->token);
10499     if (element_value != NULL)          /* combined element found */
10500       add_helpanim_entry(atoi(element_value), -1, -1, delay,
10501                          &num_list_entries);
10502     else
10503       print_unknown_token(filename, list->token, num_unknown_tokens++);
10504
10505     free(element_token);
10506     free(action_token);
10507   }
10508
10509   print_unknown_token_end(num_unknown_tokens);
10510
10511   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10512   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
10513
10514   freeSetupFileList(setup_file_list);
10515   freeSetupFileHash(element_hash);
10516   freeSetupFileHash(action_hash);
10517   freeSetupFileHash(direction_hash);
10518
10519 #if 0
10520   for (i = 0; i < num_list_entries; i++)
10521     printf("::: '%s': %d, %d, %d => %d\n",
10522            EL_NAME(helpanim_info[i].element),
10523            helpanim_info[i].element,
10524            helpanim_info[i].action,
10525            helpanim_info[i].direction,
10526            helpanim_info[i].delay);
10527 #endif
10528 }
10529
10530 void LoadHelpTextInfo()
10531 {
10532   char *filename = getHelpTextFilename();
10533   int i;
10534
10535   if (helptext_info != NULL)
10536   {
10537     freeSetupFileHash(helptext_info);
10538     helptext_info = NULL;
10539   }
10540
10541   if (fileExists(filename))
10542     helptext_info = loadSetupFileHash(filename);
10543
10544   if (helptext_info == NULL)
10545   {
10546     /* use reliable default values from static configuration */
10547     helptext_info = newSetupFileHash();
10548
10549     for (i = 0; helptext_config[i].token; i++)
10550       setHashEntry(helptext_info,
10551                    helptext_config[i].token,
10552                    helptext_config[i].value);
10553   }
10554
10555 #if 0
10556   BEGIN_HASH_ITERATION(helptext_info, itr)
10557   {
10558     printf("::: '%s' => '%s'\n",
10559            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10560   }
10561   END_HASH_ITERATION(hash, itr)
10562 #endif
10563 }
10564
10565
10566 /* ------------------------------------------------------------------------- */
10567 /* convert levels                                                            */
10568 /* ------------------------------------------------------------------------- */
10569
10570 #define MAX_NUM_CONVERT_LEVELS          1000
10571
10572 void ConvertLevels()
10573 {
10574   static LevelDirTree *convert_leveldir = NULL;
10575   static int convert_level_nr = -1;
10576   static int num_levels_handled = 0;
10577   static int num_levels_converted = 0;
10578   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10579   int i;
10580
10581   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10582                                                global.convert_leveldir);
10583
10584   if (convert_leveldir == NULL)
10585     Error(ERR_EXIT, "no such level identifier: '%s'",
10586           global.convert_leveldir);
10587
10588   leveldir_current = convert_leveldir;
10589
10590   if (global.convert_level_nr != -1)
10591   {
10592     convert_leveldir->first_level = global.convert_level_nr;
10593     convert_leveldir->last_level  = global.convert_level_nr;
10594   }
10595
10596   convert_level_nr = convert_leveldir->first_level;
10597
10598   PrintLine("=", 79);
10599   Print("Converting levels\n");
10600   PrintLine("-", 79);
10601   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10602   Print("Level series name:       '%s'\n", convert_leveldir->name);
10603   Print("Level series author:     '%s'\n", convert_leveldir->author);
10604   Print("Number of levels:        %d\n",   convert_leveldir->levels);
10605   PrintLine("=", 79);
10606   Print("\n");
10607
10608   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10609     levels_failed[i] = FALSE;
10610
10611   while (convert_level_nr <= convert_leveldir->last_level)
10612   {
10613     char *level_filename;
10614     boolean new_level;
10615
10616     level_nr = convert_level_nr++;
10617
10618     Print("Level %03d: ", level_nr);
10619
10620     LoadLevel(level_nr);
10621     if (level.no_level_file || level.no_valid_file)
10622     {
10623       Print("(no level)\n");
10624       continue;
10625     }
10626
10627     Print("converting level ... ");
10628
10629     level_filename = getDefaultLevelFilename(level_nr);
10630     new_level = !fileExists(level_filename);
10631
10632     if (new_level)
10633     {
10634       SaveLevel(level_nr);
10635
10636       num_levels_converted++;
10637
10638       Print("converted.\n");
10639     }
10640     else
10641     {
10642       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10643         levels_failed[level_nr] = TRUE;
10644
10645       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10646     }
10647
10648     num_levels_handled++;
10649   }
10650
10651   Print("\n");
10652   PrintLine("=", 79);
10653   Print("Number of levels handled: %d\n", num_levels_handled);
10654   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10655          (num_levels_handled ?
10656           num_levels_converted * 100 / num_levels_handled : 0));
10657   PrintLine("-", 79);
10658   Print("Summary (for automatic parsing by scripts):\n");
10659   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10660          convert_leveldir->identifier, num_levels_converted,
10661          num_levels_handled,
10662          (num_levels_handled ?
10663           num_levels_converted * 100 / num_levels_handled : 0));
10664
10665   if (num_levels_handled != num_levels_converted)
10666   {
10667     Print(", FAILED:");
10668     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10669       if (levels_failed[i])
10670         Print(" %03d", i);
10671   }
10672
10673   Print("\n");
10674   PrintLine("=", 79);
10675
10676   CloseAllAndExit(0);
10677 }
10678
10679
10680 /* ------------------------------------------------------------------------- */
10681 /* create and save images for use in level sketches (raw BMP format)         */
10682 /* ------------------------------------------------------------------------- */
10683
10684 void CreateLevelSketchImages()
10685 {
10686 #if defined(TARGET_SDL)
10687   Bitmap *bitmap1;
10688   Bitmap *bitmap2;
10689   int i;
10690
10691   InitElementPropertiesGfxElement();
10692
10693   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10694   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10695
10696   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10697   {
10698     Bitmap *src_bitmap;
10699     int src_x, src_y;
10700     int element = getMappedElement(i);
10701     int graphic = el2edimg(element);
10702     char basename1[16];
10703     char basename2[16];
10704     char *filename1;
10705     char *filename2;
10706
10707     sprintf(basename1, "%03d.bmp", i);
10708     sprintf(basename2, "%03ds.bmp", i);
10709
10710     filename1 = getPath2(global.create_images_dir, basename1);
10711     filename2 = getPath2(global.create_images_dir, basename2);
10712
10713     getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10714     BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10715                0, 0);
10716
10717     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10718       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10719
10720     getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10721     BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10722
10723     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10724       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10725
10726     free(filename1);
10727     free(filename2);
10728
10729     if (options.debug)
10730       printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10731   }
10732
10733   FreeBitmap(bitmap1);
10734   FreeBitmap(bitmap2);
10735
10736   if (options.debug)
10737     printf("\n");
10738
10739   Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10740
10741   CloseAllAndExit(0);
10742 #endif
10743 }
10744
10745
10746 /* ------------------------------------------------------------------------- */
10747 /* create and save images for custom and group elements (raw BMP format)     */
10748 /* ------------------------------------------------------------------------- */
10749
10750 void CreateCustomElementImages(char *directory)
10751 {
10752 #if defined(TARGET_SDL)
10753   char *src_basename = "RocksCE-template.ilbm";
10754   char *dst_basename = "RocksCE.bmp";
10755   char *src_filename = getPath2(directory, src_basename);
10756   char *dst_filename = getPath2(directory, dst_basename);
10757   Bitmap *src_bitmap;
10758   Bitmap *bitmap;
10759   int yoffset_ce = 0;
10760   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10761   int i;
10762
10763   SDLInitVideoDisplay();
10764
10765   ReCreateBitmap(&backbuffer, video.width, video.height);
10766
10767   src_bitmap = LoadImage(src_filename);
10768
10769   bitmap = CreateBitmap(TILEX * 16 * 2,
10770                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10771                         DEFAULT_DEPTH);
10772
10773   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10774   {
10775     int x = i % 16;
10776     int y = i / 16;
10777     int ii = i + 1;
10778     int j;
10779
10780     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10781                TILEX * x, TILEY * y + yoffset_ce);
10782
10783     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10784                TILEX, TILEY,
10785                TILEX * x + TILEX * 16,
10786                TILEY * y + yoffset_ce);
10787
10788     for (j = 2; j >= 0; j--)
10789     {
10790       int c = ii % 10;
10791
10792       BlitBitmap(src_bitmap, bitmap,
10793                  TILEX + c * 7, 0, 6, 10,
10794                  TILEX * x + 6 + j * 7,
10795                  TILEY * y + 11 + yoffset_ce);
10796
10797       BlitBitmap(src_bitmap, bitmap,
10798                  TILEX + c * 8, TILEY, 6, 10,
10799                  TILEX * 16 + TILEX * x + 6 + j * 8,
10800                  TILEY * y + 10 + yoffset_ce);
10801
10802       ii /= 10;
10803     }
10804   }
10805
10806   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10807   {
10808     int x = i % 16;
10809     int y = i / 16;
10810     int ii = i + 1;
10811     int j;
10812
10813     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10814                TILEX * x, TILEY * y + yoffset_ge);
10815
10816     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10817                TILEX, TILEY,
10818                TILEX * x + TILEX * 16,
10819                TILEY * y + yoffset_ge);
10820
10821     for (j = 1; j >= 0; j--)
10822     {
10823       int c = ii % 10;
10824
10825       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10826                  TILEX * x + 6 + j * 10,
10827                  TILEY * y + 11 + yoffset_ge);
10828
10829       BlitBitmap(src_bitmap, bitmap,
10830                  TILEX + c * 8, TILEY + 12, 6, 10,
10831                  TILEX * 16 + TILEX * x + 10 + j * 8,
10832                  TILEY * y + 10 + yoffset_ge);
10833
10834       ii /= 10;
10835     }
10836   }
10837
10838   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10839     Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10840
10841   FreeBitmap(bitmap);
10842
10843   CloseAllAndExit(0);
10844 #endif
10845 }