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