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