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