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