151c8079966f31384daa580d6a82119bb04267b6
[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   setLevelFileInfo(&level_template.file_info, nr);
6586   filename = level_template.file_info.filename;
6587
6588   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6589
6590   LoadLevel_InitVersion(&level_template, filename);
6591   LoadLevel_InitElements(&level_template, filename);
6592
6593   ActivateLevelTemplate();
6594 }
6595
6596 void LoadLevel(int nr)
6597 {
6598   char *filename;
6599
6600   setLevelFileInfo(&level.file_info, nr);
6601   filename = level.file_info.filename;
6602
6603   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6604
6605   if (level.use_custom_template)
6606     LoadLevelTemplate(-1);
6607
6608   LoadLevel_InitVersion(&level, filename);
6609   LoadLevel_InitElements(&level, filename);
6610   LoadLevel_InitPlayfield(&level, filename);
6611
6612   LoadLevel_InitNativeEngines(&level, filename);
6613 }
6614
6615 void LoadLevelInfoOnly(int nr)
6616 {
6617   setLevelFileInfo(&level.file_info, nr);
6618
6619   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6620 }
6621
6622 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6623 {
6624   int chunk_size = 0;
6625
6626   chunk_size += putFileVersion(file, level->file_version);
6627   chunk_size += putFileVersion(file, level->game_version);
6628
6629   return chunk_size;
6630 }
6631
6632 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6633 {
6634   int chunk_size = 0;
6635
6636   chunk_size += putFile16BitBE(file, level->creation_date.year);
6637   chunk_size += putFile8Bit(file,    level->creation_date.month);
6638   chunk_size += putFile8Bit(file,    level->creation_date.day);
6639
6640   return chunk_size;
6641 }
6642
6643 #if ENABLE_HISTORIC_CHUNKS
6644 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6645 {
6646   int i, x, y;
6647
6648   putFile8Bit(file, level->fieldx);
6649   putFile8Bit(file, level->fieldy);
6650
6651   putFile16BitBE(file, level->time);
6652   putFile16BitBE(file, level->gems_needed);
6653
6654   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6655     putFile8Bit(file, level->name[i]);
6656
6657   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6658     putFile8Bit(file, level->score[i]);
6659
6660   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6661     for (y = 0; y < 3; y++)
6662       for (x = 0; x < 3; x++)
6663         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6664                            level->yamyam_content[i].e[x][y]));
6665   putFile8Bit(file, level->amoeba_speed);
6666   putFile8Bit(file, level->time_magic_wall);
6667   putFile8Bit(file, level->time_wheel);
6668   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6669                      level->amoeba_content));
6670   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6671   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6672   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6673   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6674
6675   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6676
6677   putFile8Bit(file, (level->block_last_field ? 1 : 0));
6678   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6679   putFile32BitBE(file, level->can_move_into_acid_bits);
6680   putFile8Bit(file, level->dont_collide_with_bits);
6681
6682   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6683   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6684
6685   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6686   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6687   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6688
6689   putFile8Bit(file, level->game_engine_type);
6690
6691   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6692 }
6693 #endif
6694
6695 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6696 {
6697   int chunk_size = 0;
6698   int i;
6699
6700   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6701     chunk_size += putFile8Bit(file, level->name[i]);
6702
6703   return chunk_size;
6704 }
6705
6706 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6707 {
6708   int chunk_size = 0;
6709   int i;
6710
6711   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6712     chunk_size += putFile8Bit(file, level->author[i]);
6713
6714   return chunk_size;
6715 }
6716
6717 #if ENABLE_HISTORIC_CHUNKS
6718 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6719 {
6720   int chunk_size = 0;
6721   int x, y;
6722
6723   for (y = 0; y < level->fieldy; y++) 
6724     for (x = 0; x < level->fieldx; x++) 
6725       if (level->encoding_16bit_field)
6726         chunk_size += putFile16BitBE(file, level->field[x][y]);
6727       else
6728         chunk_size += putFile8Bit(file, level->field[x][y]);
6729
6730   return chunk_size;
6731 }
6732 #endif
6733
6734 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6735 {
6736   int chunk_size = 0;
6737   int x, y;
6738
6739   for (y = 0; y < level->fieldy; y++) 
6740     for (x = 0; x < level->fieldx; x++) 
6741       chunk_size += putFile16BitBE(file, level->field[x][y]);
6742
6743   return chunk_size;
6744 }
6745
6746 #if ENABLE_HISTORIC_CHUNKS
6747 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6748 {
6749   int i, x, y;
6750
6751   putFile8Bit(file, EL_YAMYAM);
6752   putFile8Bit(file, level->num_yamyam_contents);
6753   putFile8Bit(file, 0);
6754   putFile8Bit(file, 0);
6755
6756   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6757     for (y = 0; y < 3; y++)
6758       for (x = 0; x < 3; x++)
6759         if (level->encoding_16bit_field)
6760           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6761         else
6762           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6763 }
6764 #endif
6765
6766 #if ENABLE_HISTORIC_CHUNKS
6767 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6768 {
6769   int i, x, y;
6770   int num_contents, content_xsize, content_ysize;
6771   int content_array[MAX_ELEMENT_CONTENTS][3][3];
6772
6773   if (element == EL_YAMYAM)
6774   {
6775     num_contents = level->num_yamyam_contents;
6776     content_xsize = 3;
6777     content_ysize = 3;
6778
6779     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6780       for (y = 0; y < 3; y++)
6781         for (x = 0; x < 3; x++)
6782           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6783   }
6784   else if (element == EL_BD_AMOEBA)
6785   {
6786     num_contents = 1;
6787     content_xsize = 1;
6788     content_ysize = 1;
6789
6790     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6791       for (y = 0; y < 3; y++)
6792         for (x = 0; x < 3; x++)
6793           content_array[i][x][y] = EL_EMPTY;
6794     content_array[0][0][0] = level->amoeba_content;
6795   }
6796   else
6797   {
6798     /* chunk header already written -- write empty chunk data */
6799     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6800
6801     Error(ERR_WARN, "cannot save content for element '%d'", element);
6802     return;
6803   }
6804
6805   putFile16BitBE(file, element);
6806   putFile8Bit(file, num_contents);
6807   putFile8Bit(file, content_xsize);
6808   putFile8Bit(file, content_ysize);
6809
6810   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6811
6812   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6813     for (y = 0; y < 3; y++)
6814       for (x = 0; x < 3; x++)
6815         putFile16BitBE(file, content_array[i][x][y]);
6816 }
6817 #endif
6818
6819 #if ENABLE_HISTORIC_CHUNKS
6820 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6821 {
6822   int envelope_nr = element - EL_ENVELOPE_1;
6823   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6824   int chunk_size = 0;
6825   int i;
6826
6827   chunk_size += putFile16BitBE(file, element);
6828   chunk_size += putFile16BitBE(file, envelope_len);
6829   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6830   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6831
6832   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6833   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6834
6835   for (i = 0; i < envelope_len; i++)
6836     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6837
6838   return chunk_size;
6839 }
6840 #endif
6841
6842 #if ENABLE_HISTORIC_CHUNKS
6843 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6844                            int num_changed_custom_elements)
6845 {
6846   int i, check = 0;
6847
6848   putFile16BitBE(file, num_changed_custom_elements);
6849
6850   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6851   {
6852     int element = EL_CUSTOM_START + i;
6853
6854     struct ElementInfo *ei = &element_info[element];
6855
6856     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6857     {
6858       if (check < num_changed_custom_elements)
6859       {
6860         putFile16BitBE(file, element);
6861         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6862       }
6863
6864       check++;
6865     }
6866   }
6867
6868   if (check != num_changed_custom_elements)     /* should not happen */
6869     Error(ERR_WARN, "inconsistent number of custom element properties");
6870 }
6871 #endif
6872
6873 #if ENABLE_HISTORIC_CHUNKS
6874 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6875                            int num_changed_custom_elements)
6876 {
6877   int i, check = 0;
6878
6879   putFile16BitBE(file, num_changed_custom_elements);
6880
6881   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6882   {
6883     int element = EL_CUSTOM_START + i;
6884
6885     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6886     {
6887       if (check < num_changed_custom_elements)
6888       {
6889         putFile16BitBE(file, element);
6890         putFile16BitBE(file, element_info[element].change->target_element);
6891       }
6892
6893       check++;
6894     }
6895   }
6896
6897   if (check != num_changed_custom_elements)     /* should not happen */
6898     Error(ERR_WARN, "inconsistent number of custom target elements");
6899 }
6900 #endif
6901
6902 #if ENABLE_HISTORIC_CHUNKS
6903 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6904                            int num_changed_custom_elements)
6905 {
6906   int i, j, x, y, check = 0;
6907
6908   putFile16BitBE(file, num_changed_custom_elements);
6909
6910   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6911   {
6912     int element = EL_CUSTOM_START + i;
6913     struct ElementInfo *ei = &element_info[element];
6914
6915     if (ei->modified_settings)
6916     {
6917       if (check < num_changed_custom_elements)
6918       {
6919         putFile16BitBE(file, element);
6920
6921         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6922           putFile8Bit(file, ei->description[j]);
6923
6924         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6925
6926         /* some free bytes for future properties and padding */
6927         WriteUnusedBytesToFile(file, 7);
6928
6929         putFile8Bit(file, ei->use_gfx_element);
6930         putFile16BitBE(file, ei->gfx_element_initial);
6931
6932         putFile8Bit(file, ei->collect_score_initial);
6933         putFile8Bit(file, ei->collect_count_initial);
6934
6935         putFile16BitBE(file, ei->push_delay_fixed);
6936         putFile16BitBE(file, ei->push_delay_random);
6937         putFile16BitBE(file, ei->move_delay_fixed);
6938         putFile16BitBE(file, ei->move_delay_random);
6939
6940         putFile16BitBE(file, ei->move_pattern);
6941         putFile8Bit(file, ei->move_direction_initial);
6942         putFile8Bit(file, ei->move_stepsize);
6943
6944         for (y = 0; y < 3; y++)
6945           for (x = 0; x < 3; x++)
6946             putFile16BitBE(file, ei->content.e[x][y]);
6947
6948         putFile32BitBE(file, ei->change->events);
6949
6950         putFile16BitBE(file, ei->change->target_element);
6951
6952         putFile16BitBE(file, ei->change->delay_fixed);
6953         putFile16BitBE(file, ei->change->delay_random);
6954         putFile16BitBE(file, ei->change->delay_frames);
6955
6956         putFile16BitBE(file, ei->change->initial_trigger_element);
6957
6958         putFile8Bit(file, ei->change->explode);
6959         putFile8Bit(file, ei->change->use_target_content);
6960         putFile8Bit(file, ei->change->only_if_complete);
6961         putFile8Bit(file, ei->change->use_random_replace);
6962
6963         putFile8Bit(file, ei->change->random_percentage);
6964         putFile8Bit(file, ei->change->replace_when);
6965
6966         for (y = 0; y < 3; y++)
6967           for (x = 0; x < 3; x++)
6968             putFile16BitBE(file, ei->change->content.e[x][y]);
6969
6970         putFile8Bit(file, ei->slippery_type);
6971
6972         /* some free bytes for future properties and padding */
6973         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6974       }
6975
6976       check++;
6977     }
6978   }
6979
6980   if (check != num_changed_custom_elements)     /* should not happen */
6981     Error(ERR_WARN, "inconsistent number of custom element properties");
6982 }
6983 #endif
6984
6985 #if ENABLE_HISTORIC_CHUNKS
6986 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6987 {
6988   struct ElementInfo *ei = &element_info[element];
6989   int i, j, x, y;
6990
6991   /* ---------- custom element base property values (96 bytes) ------------- */
6992
6993   putFile16BitBE(file, element);
6994
6995   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6996     putFile8Bit(file, ei->description[i]);
6997
6998   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6999
7000   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
7001
7002   putFile8Bit(file, ei->num_change_pages);
7003
7004   putFile16BitBE(file, ei->ce_value_fixed_initial);
7005   putFile16BitBE(file, ei->ce_value_random_initial);
7006   putFile8Bit(file, ei->use_last_ce_value);
7007
7008   putFile8Bit(file, ei->use_gfx_element);
7009   putFile16BitBE(file, ei->gfx_element_initial);
7010
7011   putFile8Bit(file, ei->collect_score_initial);
7012   putFile8Bit(file, ei->collect_count_initial);
7013
7014   putFile8Bit(file, ei->drop_delay_fixed);
7015   putFile8Bit(file, ei->push_delay_fixed);
7016   putFile8Bit(file, ei->drop_delay_random);
7017   putFile8Bit(file, ei->push_delay_random);
7018   putFile16BitBE(file, ei->move_delay_fixed);
7019   putFile16BitBE(file, ei->move_delay_random);
7020
7021   /* bits 0 - 15 of "move_pattern" ... */
7022   putFile16BitBE(file, ei->move_pattern & 0xffff);
7023   putFile8Bit(file, ei->move_direction_initial);
7024   putFile8Bit(file, ei->move_stepsize);
7025
7026   putFile8Bit(file, ei->slippery_type);
7027
7028   for (y = 0; y < 3; y++)
7029     for (x = 0; x < 3; x++)
7030       putFile16BitBE(file, ei->content.e[x][y]);
7031
7032   putFile16BitBE(file, ei->move_enter_element);
7033   putFile16BitBE(file, ei->move_leave_element);
7034   putFile8Bit(file, ei->move_leave_type);
7035
7036   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
7037   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7038
7039   putFile8Bit(file, ei->access_direction);
7040
7041   putFile8Bit(file, ei->explosion_delay);
7042   putFile8Bit(file, ei->ignition_delay);
7043   putFile8Bit(file, ei->explosion_type);
7044
7045   /* some free bytes for future custom property values and padding */
7046   WriteUnusedBytesToFile(file, 1);
7047
7048   /* ---------- change page property values (48 bytes) --------------------- */
7049
7050   for (i = 0; i < ei->num_change_pages; i++)
7051   {
7052     struct ElementChangeInfo *change = &ei->change_page[i];
7053     unsigned int event_bits;
7054
7055     /* bits 0 - 31 of "has_event[]" ... */
7056     event_bits = 0;
7057     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7058       if (change->has_event[j])
7059         event_bits |= (1 << j);
7060     putFile32BitBE(file, event_bits);
7061
7062     putFile16BitBE(file, change->target_element);
7063
7064     putFile16BitBE(file, change->delay_fixed);
7065     putFile16BitBE(file, change->delay_random);
7066     putFile16BitBE(file, change->delay_frames);
7067
7068     putFile16BitBE(file, change->initial_trigger_element);
7069
7070     putFile8Bit(file, change->explode);
7071     putFile8Bit(file, change->use_target_content);
7072     putFile8Bit(file, change->only_if_complete);
7073     putFile8Bit(file, change->use_random_replace);
7074
7075     putFile8Bit(file, change->random_percentage);
7076     putFile8Bit(file, change->replace_when);
7077
7078     for (y = 0; y < 3; y++)
7079       for (x = 0; x < 3; x++)
7080         putFile16BitBE(file, change->target_content.e[x][y]);
7081
7082     putFile8Bit(file, change->can_change);
7083
7084     putFile8Bit(file, change->trigger_side);
7085
7086     putFile8Bit(file, change->trigger_player);
7087     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7088                        log_2(change->trigger_page)));
7089
7090     putFile8Bit(file, change->has_action);
7091     putFile8Bit(file, change->action_type);
7092     putFile8Bit(file, change->action_mode);
7093     putFile16BitBE(file, change->action_arg);
7094
7095     /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7096     event_bits = 0;
7097     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7098       if (change->has_event[j])
7099         event_bits |= (1 << (j - 32));
7100     putFile8Bit(file, event_bits);
7101   }
7102 }
7103 #endif
7104
7105 #if ENABLE_HISTORIC_CHUNKS
7106 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7107 {
7108   struct ElementInfo *ei = &element_info[element];
7109   struct ElementGroupInfo *group = ei->group;
7110   int i;
7111
7112   putFile16BitBE(file, element);
7113
7114   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7115     putFile8Bit(file, ei->description[i]);
7116
7117   putFile8Bit(file, group->num_elements);
7118
7119   putFile8Bit(file, ei->use_gfx_element);
7120   putFile16BitBE(file, ei->gfx_element_initial);
7121
7122   putFile8Bit(file, group->choice_mode);
7123
7124   /* some free bytes for future values and padding */
7125   WriteUnusedBytesToFile(file, 3);
7126
7127   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7128     putFile16BitBE(file, group->element[i]);
7129 }
7130 #endif
7131
7132 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7133                                 boolean write_element)
7134 {
7135   int save_type = entry->save_type;
7136   int data_type = entry->data_type;
7137   int conf_type = entry->conf_type;
7138   int byte_mask = conf_type & CONF_MASK_BYTES;
7139   int element = entry->element;
7140   int default_value = entry->default_value;
7141   int num_bytes = 0;
7142   boolean modified = FALSE;
7143
7144   if (byte_mask != CONF_MASK_MULTI_BYTES)
7145   {
7146     void *value_ptr = entry->value;
7147     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7148                  *(int *)value_ptr);
7149
7150     /* check if any settings have been modified before saving them */
7151     if (value != default_value)
7152       modified = TRUE;
7153
7154     /* do not save if explicitly told or if unmodified default settings */
7155     if ((save_type == SAVE_CONF_NEVER) ||
7156         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7157       return 0;
7158
7159     if (write_element)
7160       num_bytes += putFile16BitBE(file, element);
7161
7162     num_bytes += putFile8Bit(file, conf_type);
7163     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
7164                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7165                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7166                   0);
7167   }
7168   else if (data_type == TYPE_STRING)
7169   {
7170     char *default_string = entry->default_string;
7171     char *string = (char *)(entry->value);
7172     int string_length = strlen(string);
7173     int i;
7174
7175     /* check if any settings have been modified before saving them */
7176     if (!strEqual(string, default_string))
7177       modified = TRUE;
7178
7179     /* do not save if explicitly told or if unmodified default settings */
7180     if ((save_type == SAVE_CONF_NEVER) ||
7181         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7182       return 0;
7183
7184     if (write_element)
7185       num_bytes += putFile16BitBE(file, element);
7186
7187     num_bytes += putFile8Bit(file, conf_type);
7188     num_bytes += putFile16BitBE(file, string_length);
7189
7190     for (i = 0; i < string_length; i++)
7191       num_bytes += putFile8Bit(file, string[i]);
7192   }
7193   else if (data_type == TYPE_ELEMENT_LIST)
7194   {
7195     int *element_array = (int *)(entry->value);
7196     int num_elements = *(int *)(entry->num_entities);
7197     int i;
7198
7199     /* check if any settings have been modified before saving them */
7200     for (i = 0; i < num_elements; i++)
7201       if (element_array[i] != default_value)
7202         modified = TRUE;
7203
7204     /* do not save if explicitly told or if unmodified default settings */
7205     if ((save_type == SAVE_CONF_NEVER) ||
7206         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7207       return 0;
7208
7209     if (write_element)
7210       num_bytes += putFile16BitBE(file, element);
7211
7212     num_bytes += putFile8Bit(file, conf_type);
7213     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7214
7215     for (i = 0; i < num_elements; i++)
7216       num_bytes += putFile16BitBE(file, element_array[i]);
7217   }
7218   else if (data_type == TYPE_CONTENT_LIST)
7219   {
7220     struct Content *content = (struct Content *)(entry->value);
7221     int num_contents = *(int *)(entry->num_entities);
7222     int i, x, y;
7223
7224     /* check if any settings have been modified before saving them */
7225     for (i = 0; i < num_contents; i++)
7226       for (y = 0; y < 3; y++)
7227         for (x = 0; x < 3; x++)
7228           if (content[i].e[x][y] != default_value)
7229             modified = TRUE;
7230
7231     /* do not save if explicitly told or if unmodified default settings */
7232     if ((save_type == SAVE_CONF_NEVER) ||
7233         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7234       return 0;
7235
7236     if (write_element)
7237       num_bytes += putFile16BitBE(file, element);
7238
7239     num_bytes += putFile8Bit(file, conf_type);
7240     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7241
7242     for (i = 0; i < num_contents; i++)
7243       for (y = 0; y < 3; y++)
7244         for (x = 0; x < 3; x++)
7245           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7246   }
7247
7248   return num_bytes;
7249 }
7250
7251 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7252 {
7253   int chunk_size = 0;
7254   int i;
7255
7256   li = *level;          /* copy level data into temporary buffer */
7257
7258   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7259     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7260
7261   return chunk_size;
7262 }
7263
7264 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7265 {
7266   int chunk_size = 0;
7267   int i;
7268
7269   li = *level;          /* copy level data into temporary buffer */
7270
7271   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7272     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7273
7274   return chunk_size;
7275 }
7276
7277 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7278 {
7279   int envelope_nr = element - EL_ENVELOPE_1;
7280   int chunk_size = 0;
7281   int i;
7282
7283   chunk_size += putFile16BitBE(file, element);
7284
7285   /* copy envelope data into temporary buffer */
7286   xx_envelope = level->envelope[envelope_nr];
7287
7288   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7289     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7290
7291   return chunk_size;
7292 }
7293
7294 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7295 {
7296   struct ElementInfo *ei = &element_info[element];
7297   int chunk_size = 0;
7298   int i, j;
7299
7300   chunk_size += putFile16BitBE(file, element);
7301
7302   xx_ei = *ei;          /* copy element data into temporary buffer */
7303
7304   /* set default description string for this specific element */
7305   strcpy(xx_default_description, getDefaultElementDescription(ei));
7306
7307   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7308     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7309
7310   for (i = 0; i < ei->num_change_pages; i++)
7311   {
7312     struct ElementChangeInfo *change = &ei->change_page[i];
7313
7314     xx_current_change_page = i;
7315
7316     xx_change = *change;        /* copy change data into temporary buffer */
7317
7318     resetEventBits();
7319     setEventBitsFromEventFlags(change);
7320
7321     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7322       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7323                                          FALSE);
7324   }
7325
7326   return chunk_size;
7327 }
7328
7329 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7330 {
7331   struct ElementInfo *ei = &element_info[element];
7332   struct ElementGroupInfo *group = ei->group;
7333   int chunk_size = 0;
7334   int i;
7335
7336   chunk_size += putFile16BitBE(file, element);
7337
7338   xx_ei = *ei;          /* copy element data into temporary buffer */
7339   xx_group = *group;    /* copy group data into temporary buffer */
7340
7341   /* set default description string for this specific element */
7342   strcpy(xx_default_description, getDefaultElementDescription(ei));
7343
7344   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7345     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7346
7347   return chunk_size;
7348 }
7349
7350 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7351                                   boolean save_as_template)
7352 {
7353   int chunk_size;
7354   int i;
7355   FILE *file;
7356
7357   if (!(file = fopen(filename, MODE_WRITE)))
7358   {
7359     Error(ERR_WARN, "cannot save level file '%s'", filename);
7360     return;
7361   }
7362
7363   level->file_version = FILE_VERSION_ACTUAL;
7364   level->game_version = GAME_VERSION_ACTUAL;
7365
7366   level->creation_date = getCurrentDate();
7367
7368   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7369   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7370
7371   chunk_size = SaveLevel_VERS(NULL, level);
7372   putFileChunkBE(file, "VERS", chunk_size);
7373   SaveLevel_VERS(file, level);
7374
7375   chunk_size = SaveLevel_DATE(NULL, level);
7376   putFileChunkBE(file, "DATE", chunk_size);
7377   SaveLevel_DATE(file, level);
7378
7379   chunk_size = SaveLevel_NAME(NULL, level);
7380   putFileChunkBE(file, "NAME", chunk_size);
7381   SaveLevel_NAME(file, level);
7382
7383   chunk_size = SaveLevel_AUTH(NULL, level);
7384   putFileChunkBE(file, "AUTH", chunk_size);
7385   SaveLevel_AUTH(file, level);
7386
7387   chunk_size = SaveLevel_INFO(NULL, level);
7388   putFileChunkBE(file, "INFO", chunk_size);
7389   SaveLevel_INFO(file, level);
7390
7391   chunk_size = SaveLevel_BODY(NULL, level);
7392   putFileChunkBE(file, "BODY", chunk_size);
7393   SaveLevel_BODY(file, level);
7394
7395   chunk_size = SaveLevel_ELEM(NULL, level);
7396   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          /* save if changed */
7397   {
7398     putFileChunkBE(file, "ELEM", chunk_size);
7399     SaveLevel_ELEM(file, level);
7400   }
7401
7402   for (i = 0; i < NUM_ENVELOPES; i++)
7403   {
7404     int element = EL_ENVELOPE_1 + i;
7405
7406     chunk_size = SaveLevel_NOTE(NULL, level, element);
7407     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        /* save if changed */
7408     {
7409       putFileChunkBE(file, "NOTE", chunk_size);
7410       SaveLevel_NOTE(file, level, element);
7411     }
7412   }
7413
7414   /* if not using template level, check for non-default custom/group elements */
7415   if (!level->use_custom_template || save_as_template)
7416   {
7417     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7418     {
7419       int element = EL_CUSTOM_START + i;
7420
7421       chunk_size = SaveLevel_CUSX(NULL, level, element);
7422       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      /* save if changed */
7423       {
7424         putFileChunkBE(file, "CUSX", chunk_size);
7425         SaveLevel_CUSX(file, level, element);
7426       }
7427     }
7428
7429     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7430     {
7431       int element = EL_GROUP_START + i;
7432
7433       chunk_size = SaveLevel_GRPX(NULL, level, element);
7434       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      /* save if changed */
7435       {
7436         putFileChunkBE(file, "GRPX", chunk_size);
7437         SaveLevel_GRPX(file, level, element);
7438       }
7439     }
7440   }
7441
7442   fclose(file);
7443
7444   SetFilePermissions(filename, PERMS_PRIVATE);
7445 }
7446
7447 void SaveLevel(int nr)
7448 {
7449   char *filename = getDefaultLevelFilename(nr);
7450
7451   SaveLevelFromFilename(&level, filename, FALSE);
7452 }
7453
7454 void SaveLevelTemplate()
7455 {
7456   char *filename = getLocalLevelTemplateFilename();
7457
7458   SaveLevelFromFilename(&level, filename, TRUE);
7459 }
7460
7461 boolean SaveLevelChecked(int nr)
7462 {
7463   char *filename = getDefaultLevelFilename(nr);
7464   boolean new_level = !fileExists(filename);
7465   boolean level_saved = FALSE;
7466
7467   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7468   {
7469     SaveLevel(nr);
7470
7471     if (new_level)
7472       Request("Level saved!", REQ_CONFIRM);
7473
7474     level_saved = TRUE;
7475   }
7476
7477   return level_saved;
7478 }
7479
7480 void DumpLevel(struct LevelInfo *level)
7481 {
7482   if (level->no_level_file || level->no_valid_file)
7483   {
7484     Error(ERR_WARN, "cannot dump -- no valid level file found");
7485
7486     return;
7487   }
7488
7489   PrintLine("-", 79);
7490   Print("Level xxx (file version %08d, game version %08d)\n",
7491         level->file_version, level->game_version);
7492   PrintLine("-", 79);
7493
7494   Print("Level author: '%s'\n", level->author);
7495   Print("Level title:  '%s'\n", level->name);
7496   Print("\n");
7497   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7498   Print("\n");
7499   Print("Level time:  %d seconds\n", level->time);
7500   Print("Gems needed: %d\n", level->gems_needed);
7501   Print("\n");
7502   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7503   Print("Time for wheel:      %d seconds\n", level->time_wheel);
7504   Print("Time for light:      %d seconds\n", level->time_light);
7505   Print("Time for timegate:   %d seconds\n", level->time_timegate);
7506   Print("\n");
7507   Print("Amoeba speed: %d\n", level->amoeba_speed);
7508   Print("\n");
7509
7510   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
7511   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
7512   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7513   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7514   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7515
7516   PrintLine("-", 79);
7517 }
7518
7519
7520 /* ========================================================================= */
7521 /* tape file functions                                                       */
7522 /* ========================================================================= */
7523
7524 static void setTapeInfoToDefaults()
7525 {
7526   int i;
7527
7528   /* always start with reliable default values (empty tape) */
7529   TapeErase();
7530
7531   /* default values (also for pre-1.2 tapes) with only the first player */
7532   tape.player_participates[0] = TRUE;
7533   for (i = 1; i < MAX_PLAYERS; i++)
7534     tape.player_participates[i] = FALSE;
7535
7536   /* at least one (default: the first) player participates in every tape */
7537   tape.num_participating_players = 1;
7538
7539   tape.level_nr = level_nr;
7540   tape.counter = 0;
7541   tape.changed = FALSE;
7542
7543   tape.recording = FALSE;
7544   tape.playing = FALSE;
7545   tape.pausing = FALSE;
7546
7547   tape.no_valid_file = FALSE;
7548 }
7549
7550 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7551 {
7552   tape->file_version = getFileVersion(file);
7553   tape->game_version = getFileVersion(file);
7554
7555   return chunk_size;
7556 }
7557
7558 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7559 {
7560   int i;
7561
7562   tape->random_seed = getFile32BitBE(file);
7563   tape->date        = getFile32BitBE(file);
7564   tape->length      = getFile32BitBE(file);
7565
7566   /* read header fields that are new since version 1.2 */
7567   if (tape->file_version >= FILE_VERSION_1_2)
7568   {
7569     byte store_participating_players = getFile8Bit(file);
7570     int engine_version;
7571
7572     /* since version 1.2, tapes store which players participate in the tape */
7573     tape->num_participating_players = 0;
7574     for (i = 0; i < MAX_PLAYERS; i++)
7575     {
7576       tape->player_participates[i] = FALSE;
7577
7578       if (store_participating_players & (1 << i))
7579       {
7580         tape->player_participates[i] = TRUE;
7581         tape->num_participating_players++;
7582       }
7583     }
7584
7585     tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7586
7587     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7588
7589     engine_version = getFileVersion(file);
7590     if (engine_version > 0)
7591       tape->engine_version = engine_version;
7592     else
7593       tape->engine_version = tape->game_version;
7594   }
7595
7596   return chunk_size;
7597 }
7598
7599 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7600 {
7601   int level_identifier_size;
7602   int i;
7603
7604   level_identifier_size = getFile16BitBE(file);
7605
7606   tape->level_identifier =
7607     checked_realloc(tape->level_identifier, level_identifier_size);
7608
7609   for (i = 0; i < level_identifier_size; i++)
7610     tape->level_identifier[i] = getFile8Bit(file);
7611
7612   tape->level_nr = getFile16BitBE(file);
7613
7614   chunk_size = 2 + level_identifier_size + 2;
7615
7616   return chunk_size;
7617 }
7618
7619 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7620 {
7621   int i, j;
7622   int tape_pos_size =
7623     (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7624   int chunk_size_expected = tape_pos_size * tape->length;
7625
7626   if (chunk_size_expected != chunk_size)
7627   {
7628     ReadUnusedBytesFromFile(file, chunk_size);
7629     return chunk_size_expected;
7630   }
7631
7632   for (i = 0; i < tape->length; i++)
7633   {
7634     if (i >= MAX_TAPE_LEN)
7635     {
7636       Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7637             MAX_TAPE_LEN);
7638
7639       // tape too large; read and ignore remaining tape data from this chunk
7640       for (;i < tape->length; i++)
7641         ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7642
7643       break;
7644     }
7645
7646     if (tape->use_mouse)
7647     {
7648       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
7649       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
7650       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7651
7652       tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7653     }
7654     else
7655     {
7656       for (j = 0; j < MAX_PLAYERS; j++)
7657       {
7658         tape->pos[i].action[j] = MV_NONE;
7659
7660         if (tape->player_participates[j])
7661           tape->pos[i].action[j] = getFile8Bit(file);
7662       }
7663     }
7664
7665     tape->pos[i].delay = getFile8Bit(file);
7666
7667     if (tape->file_version == FILE_VERSION_1_0)
7668     {
7669       /* eliminate possible diagonal moves in old tapes */
7670       /* this is only for backward compatibility */
7671
7672       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7673       byte action = tape->pos[i].action[0];
7674       int k, num_moves = 0;
7675
7676       for (k = 0; k<4; k++)
7677       {
7678         if (action & joy_dir[k])
7679         {
7680           tape->pos[i + num_moves].action[0] = joy_dir[k];
7681           if (num_moves > 0)
7682             tape->pos[i + num_moves].delay = 0;
7683           num_moves++;
7684         }
7685       }
7686
7687       if (num_moves > 1)
7688       {
7689         num_moves--;
7690         i += num_moves;
7691         tape->length += num_moves;
7692       }
7693     }
7694     else if (tape->file_version < FILE_VERSION_2_0)
7695     {
7696       /* convert pre-2.0 tapes to new tape format */
7697
7698       if (tape->pos[i].delay > 1)
7699       {
7700         /* action part */
7701         tape->pos[i + 1] = tape->pos[i];
7702         tape->pos[i + 1].delay = 1;
7703
7704         /* delay part */
7705         for (j = 0; j < MAX_PLAYERS; j++)
7706           tape->pos[i].action[j] = MV_NONE;
7707         tape->pos[i].delay--;
7708
7709         i++;
7710         tape->length++;
7711       }
7712     }
7713
7714     if (checkEndOfFile(file))
7715       break;
7716   }
7717
7718   if (i != tape->length)
7719     chunk_size = tape_pos_size * i;
7720
7721   return chunk_size;
7722 }
7723
7724 void LoadTape_SokobanSolution(char *filename)
7725 {
7726   File *file;
7727   int move_delay = TILESIZE / level.initial_player_stepsize[0];
7728
7729   if (!(file = openFile(filename, MODE_READ)))
7730   {
7731     tape.no_valid_file = TRUE;
7732
7733     return;
7734   }
7735
7736   while (!checkEndOfFile(file))
7737   {
7738     unsigned char c = getByteFromFile(file);
7739
7740     if (checkEndOfFile(file))
7741       break;
7742
7743     switch (c)
7744     {
7745       case 'u':
7746       case 'U':
7747         tape.pos[tape.length].action[0] = MV_UP;
7748         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7749         tape.length++;
7750         break;
7751
7752       case 'd':
7753       case 'D':
7754         tape.pos[tape.length].action[0] = MV_DOWN;
7755         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7756         tape.length++;
7757         break;
7758
7759       case 'l':
7760       case 'L':
7761         tape.pos[tape.length].action[0] = MV_LEFT;
7762         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7763         tape.length++;
7764         break;
7765
7766       case 'r':
7767       case 'R':
7768         tape.pos[tape.length].action[0] = MV_RIGHT;
7769         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7770         tape.length++;
7771         break;
7772
7773       case '\n':
7774       case '\r':
7775       case '\t':
7776       case ' ':
7777         /* ignore white-space characters */
7778         break;
7779
7780       default:
7781         tape.no_valid_file = TRUE;
7782
7783         Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7784
7785         break;
7786     }
7787   }
7788
7789   closeFile(file);
7790
7791   if (tape.no_valid_file)
7792     return;
7793
7794   tape.length_frames  = GetTapeLengthFrames();
7795   tape.length_seconds = GetTapeLengthSeconds();
7796 }
7797
7798 void LoadTapeFromFilename(char *filename)
7799 {
7800   char cookie[MAX_LINE_LEN];
7801   char chunk_name[CHUNK_ID_LEN + 1];
7802   File *file;
7803   int chunk_size;
7804
7805   /* always start with reliable default values */
7806   setTapeInfoToDefaults();
7807
7808   if (strSuffix(filename, ".sln"))
7809   {
7810     LoadTape_SokobanSolution(filename);
7811
7812     return;
7813   }
7814
7815   if (!(file = openFile(filename, MODE_READ)))
7816   {
7817     tape.no_valid_file = TRUE;
7818
7819     return;
7820   }
7821
7822   getFileChunkBE(file, chunk_name, NULL);
7823   if (strEqual(chunk_name, "RND1"))
7824   {
7825     getFile32BitBE(file);               /* not used */
7826
7827     getFileChunkBE(file, chunk_name, NULL);
7828     if (!strEqual(chunk_name, "TAPE"))
7829     {
7830       tape.no_valid_file = TRUE;
7831
7832       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7833
7834       closeFile(file);
7835
7836       return;
7837     }
7838   }
7839   else  /* check for pre-2.0 file format with cookie string */
7840   {
7841     strcpy(cookie, chunk_name);
7842     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7843       cookie[4] = '\0';
7844     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7845       cookie[strlen(cookie) - 1] = '\0';
7846
7847     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7848     {
7849       tape.no_valid_file = TRUE;
7850
7851       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7852
7853       closeFile(file);
7854
7855       return;
7856     }
7857
7858     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7859     {
7860       tape.no_valid_file = TRUE;
7861
7862       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7863
7864       closeFile(file);
7865
7866       return;
7867     }
7868
7869     /* pre-2.0 tape files have no game version, so use file version here */
7870     tape.game_version = tape.file_version;
7871   }
7872
7873   if (tape.file_version < FILE_VERSION_1_2)
7874   {
7875     /* tape files from versions before 1.2.0 without chunk structure */
7876     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7877     LoadTape_BODY(file, 2 * tape.length,      &tape);
7878   }
7879   else
7880   {
7881     static struct
7882     {
7883       char *name;
7884       int size;
7885       int (*loader)(File *, int, struct TapeInfo *);
7886     }
7887     chunk_info[] =
7888     {
7889       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
7890       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
7891       { "INFO", -1,                     LoadTape_INFO },
7892       { "BODY", -1,                     LoadTape_BODY },
7893       {  NULL,  0,                      NULL }
7894     };
7895
7896     while (getFileChunkBE(file, chunk_name, &chunk_size))
7897     {
7898       int i = 0;
7899
7900       while (chunk_info[i].name != NULL &&
7901              !strEqual(chunk_name, chunk_info[i].name))
7902         i++;
7903
7904       if (chunk_info[i].name == NULL)
7905       {
7906         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7907               chunk_name, filename);
7908         ReadUnusedBytesFromFile(file, chunk_size);
7909       }
7910       else if (chunk_info[i].size != -1 &&
7911                chunk_info[i].size != chunk_size)
7912       {
7913         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7914               chunk_size, chunk_name, filename);
7915         ReadUnusedBytesFromFile(file, chunk_size);
7916       }
7917       else
7918       {
7919         /* call function to load this tape chunk */
7920         int chunk_size_expected =
7921           (chunk_info[i].loader)(file, chunk_size, &tape);
7922
7923         /* the size of some chunks cannot be checked before reading other
7924            chunks first (like "HEAD" and "BODY") that contain some header
7925            information, so check them here */
7926         if (chunk_size_expected != chunk_size)
7927         {
7928           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7929                 chunk_size, chunk_name, filename);
7930         }
7931       }
7932     }
7933   }
7934
7935   closeFile(file);
7936
7937   tape.length_frames  = GetTapeLengthFrames();
7938   tape.length_seconds = GetTapeLengthSeconds();
7939
7940 #if 0
7941   printf("::: tape file version: %d\n",   tape.file_version);
7942   printf("::: tape game version: %d\n",   tape.game_version);
7943   printf("::: tape engine version: %d\n", tape.engine_version);
7944 #endif
7945 }
7946
7947 void LoadTape(int nr)
7948 {
7949   char *filename = getTapeFilename(nr);
7950
7951   LoadTapeFromFilename(filename);
7952 }
7953
7954 void LoadSolutionTape(int nr)
7955 {
7956   char *filename = getSolutionTapeFilename(nr);
7957
7958   LoadTapeFromFilename(filename);
7959
7960   if (TAPE_IS_EMPTY(tape) &&
7961       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7962       level.native_sp_level->demo.is_available)
7963     CopyNativeTape_SP_to_RND(&level);
7964 }
7965
7966 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7967 {
7968   putFileVersion(file, tape->file_version);
7969   putFileVersion(file, tape->game_version);
7970 }
7971
7972 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7973 {
7974   int i;
7975   byte store_participating_players = 0;
7976
7977   /* set bits for participating players for compact storage */
7978   for (i = 0; i < MAX_PLAYERS; i++)
7979     if (tape->player_participates[i])
7980       store_participating_players |= (1 << i);
7981
7982   putFile32BitBE(file, tape->random_seed);
7983   putFile32BitBE(file, tape->date);
7984   putFile32BitBE(file, tape->length);
7985
7986   putFile8Bit(file, store_participating_players);
7987
7988   putFile8Bit(file, (tape->use_mouse ? 1 : 0));
7989
7990   /* unused bytes not at the end here for 4-byte alignment of engine_version */
7991   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7992
7993   putFileVersion(file, tape->engine_version);
7994 }
7995
7996 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7997 {
7998   int level_identifier_size = strlen(tape->level_identifier) + 1;
7999   int i;
8000
8001   putFile16BitBE(file, level_identifier_size);
8002
8003   for (i = 0; i < level_identifier_size; i++)
8004     putFile8Bit(file, tape->level_identifier[i]);
8005
8006   putFile16BitBE(file, tape->level_nr);
8007 }
8008
8009 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8010 {
8011   int i, j;
8012
8013   for (i = 0; i < tape->length; i++)
8014   {
8015     if (tape->use_mouse)
8016     {
8017       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8018       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8019       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8020     }
8021     else
8022     {
8023       for (j = 0; j < MAX_PLAYERS; j++)
8024         if (tape->player_participates[j])
8025           putFile8Bit(file, tape->pos[i].action[j]);
8026     }
8027
8028     putFile8Bit(file, tape->pos[i].delay);
8029   }
8030 }
8031
8032 void SaveTape(int nr)
8033 {
8034   char *filename = getTapeFilename(nr);
8035   FILE *file;
8036   int num_participating_players = 0;
8037   int tape_pos_size;
8038   int info_chunk_size;
8039   int body_chunk_size;
8040   int i;
8041
8042   InitTapeDirectory(leveldir_current->subdir);
8043
8044   if (!(file = fopen(filename, MODE_WRITE)))
8045   {
8046     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8047     return;
8048   }
8049
8050   tape.file_version = FILE_VERSION_ACTUAL;
8051   tape.game_version = GAME_VERSION_ACTUAL;
8052
8053   /* count number of participating players  */
8054   for (i = 0; i < MAX_PLAYERS; i++)
8055     if (tape.player_participates[i])
8056       num_participating_players++;
8057
8058   tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
8059
8060   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8061   body_chunk_size = tape_pos_size * tape.length;
8062
8063   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8064   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8065
8066   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8067   SaveTape_VERS(file, &tape);
8068
8069   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8070   SaveTape_HEAD(file, &tape);
8071
8072   putFileChunkBE(file, "INFO", info_chunk_size);
8073   SaveTape_INFO(file, &tape);
8074
8075   putFileChunkBE(file, "BODY", body_chunk_size);
8076   SaveTape_BODY(file, &tape);
8077
8078   fclose(file);
8079
8080   SetFilePermissions(filename, PERMS_PRIVATE);
8081
8082   tape.changed = FALSE;
8083 }
8084
8085 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved)
8086 {
8087   char *filename = getTapeFilename(nr);
8088   boolean new_tape = !fileExists(filename);
8089   boolean tape_saved = FALSE;
8090
8091   if (new_tape || Request(msg_replace, REQ_ASK))
8092   {
8093     SaveTape(nr);
8094
8095     if (new_tape)
8096       Request(msg_saved, REQ_CONFIRM);
8097
8098     tape_saved = TRUE;
8099   }
8100
8101   return tape_saved;
8102 }
8103
8104 boolean SaveTapeChecked(int nr)
8105 {
8106   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!");
8107 }
8108
8109 boolean SaveTapeChecked_LevelSolved(int nr)
8110 {
8111   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8112                                 "Level solved! Tape saved!");
8113 }
8114
8115 void DumpTape(struct TapeInfo *tape)
8116 {
8117   int tape_frame_counter;
8118   int i, j;
8119
8120   if (tape->no_valid_file)
8121   {
8122     Error(ERR_WARN, "cannot dump -- no valid tape file found");
8123
8124     return;
8125   }
8126
8127   PrintLine("-", 79);
8128   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8129         tape->level_nr, tape->file_version, tape->game_version);
8130   Print("                  (effective engine version %08d)\n",
8131         tape->engine_version);
8132   Print("Level series identifier: '%s'\n", tape->level_identifier);
8133   PrintLine("-", 79);
8134
8135   tape_frame_counter = 0;
8136
8137   for (i = 0; i < tape->length; i++)
8138   {
8139     if (i >= MAX_TAPE_LEN)
8140       break;
8141
8142     Print("%04d: ", i);
8143
8144     for (j = 0; j < MAX_PLAYERS; j++)
8145     {
8146       if (tape->player_participates[j])
8147       {
8148         int action = tape->pos[i].action[j];
8149
8150         Print("%d:%02x ", j, action);
8151         Print("[%c%c%c%c|%c%c] - ",
8152               (action & JOY_LEFT ? '<' : ' '),
8153               (action & JOY_RIGHT ? '>' : ' '),
8154               (action & JOY_UP ? '^' : ' '),
8155               (action & JOY_DOWN ? 'v' : ' '),
8156               (action & JOY_BUTTON_1 ? '1' : ' '),
8157               (action & JOY_BUTTON_2 ? '2' : ' '));
8158       }
8159     }
8160
8161     Print("(%03d) ", tape->pos[i].delay);
8162     Print("[%05d]\n", tape_frame_counter);
8163
8164     tape_frame_counter += tape->pos[i].delay;
8165   }
8166
8167   PrintLine("-", 79);
8168 }
8169
8170
8171 /* ========================================================================= */
8172 /* score file functions                                                      */
8173 /* ========================================================================= */
8174
8175 void LoadScore(int nr)
8176 {
8177   int i;
8178   char *filename = getScoreFilename(nr);
8179   char cookie[MAX_LINE_LEN];
8180   char line[MAX_LINE_LEN];
8181   char *line_ptr;
8182   FILE *file;
8183
8184   /* always start with reliable default values */
8185   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8186   {
8187     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8188     highscore[i].Score = 0;
8189   }
8190
8191   if (!(file = fopen(filename, MODE_READ)))
8192     return;
8193
8194   /* check file identifier */
8195   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8196     cookie[0] = '\0';
8197   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8198     cookie[strlen(cookie) - 1] = '\0';
8199
8200   if (!checkCookieString(cookie, SCORE_COOKIE))
8201   {
8202     Error(ERR_WARN, "unknown format of score file '%s'", filename);
8203     fclose(file);
8204     return;
8205   }
8206
8207   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8208   {
8209     if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8210       Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8211     if (fgets(line, MAX_LINE_LEN, file) == NULL)
8212       line[0] = '\0';
8213
8214     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8215       line[strlen(line) - 1] = '\0';
8216
8217     for (line_ptr = line; *line_ptr; line_ptr++)
8218     {
8219       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8220       {
8221         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8222         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8223         break;
8224       }
8225     }
8226   }
8227
8228   fclose(file);
8229 }
8230
8231 void SaveScore(int nr)
8232 {
8233   int i;
8234   int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8235   char *filename = getScoreFilename(nr);
8236   FILE *file;
8237
8238   InitScoreDirectory(leveldir_current->subdir);
8239
8240   if (!(file = fopen(filename, MODE_WRITE)))
8241   {
8242     Error(ERR_WARN, "cannot save score for level %d", nr);
8243     return;
8244   }
8245
8246   fprintf(file, "%s\n\n", SCORE_COOKIE);
8247
8248   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8249     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8250
8251   fclose(file);
8252
8253   SetFilePermissions(filename, permissions);
8254 }
8255
8256
8257 /* ========================================================================= */
8258 /* setup file functions                                                      */
8259 /* ========================================================================= */
8260
8261 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
8262
8263 /* global setup */
8264 enum
8265 {
8266   SETUP_TOKEN_PLAYER_NAME = 0,
8267   SETUP_TOKEN_SOUND,
8268   SETUP_TOKEN_SOUND_LOOPS,
8269   SETUP_TOKEN_SOUND_MUSIC,
8270   SETUP_TOKEN_SOUND_SIMPLE,
8271   SETUP_TOKEN_TOONS,
8272   SETUP_TOKEN_SCROLL_DELAY,
8273   SETUP_TOKEN_SCROLL_DELAY_VALUE,
8274   SETUP_TOKEN_ENGINE_SNAPSHOT_MODE,
8275   SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY,
8276   SETUP_TOKEN_FADE_SCREENS,
8277   SETUP_TOKEN_AUTORECORD,
8278   SETUP_TOKEN_SHOW_TITLESCREEN,
8279   SETUP_TOKEN_QUICK_DOORS,
8280   SETUP_TOKEN_TEAM_MODE,
8281   SETUP_TOKEN_HANDICAP,
8282   SETUP_TOKEN_SKIP_LEVELS,
8283   SETUP_TOKEN_INCREMENT_LEVELS,
8284   SETUP_TOKEN_AUTO_PLAY_NEXT_LEVEL,
8285   SETUP_TOKEN_SKIP_SCORES_AFTER_GAME,
8286   SETUP_TOKEN_TIME_LIMIT,
8287   SETUP_TOKEN_FULLSCREEN,
8288   SETUP_TOKEN_WINDOW_SCALING_PERCENT,
8289   SETUP_TOKEN_WINDOW_SCALING_QUALITY,
8290   SETUP_TOKEN_SCREEN_RENDERING_MODE,
8291   SETUP_TOKEN_ASK_ON_ESCAPE,
8292   SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR,
8293   SETUP_TOKEN_QUICK_SWITCH,
8294   SETUP_TOKEN_INPUT_ON_FOCUS,
8295   SETUP_TOKEN_PREFER_AGA_GRAPHICS,
8296   SETUP_TOKEN_GAME_FRAME_DELAY,
8297   SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS,
8298   SETUP_TOKEN_SMALL_GAME_GRAPHICS,
8299   SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS,
8300   SETUP_TOKEN_GRAPHICS_SET,
8301   SETUP_TOKEN_SOUNDS_SET,
8302   SETUP_TOKEN_MUSIC_SET,
8303   SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS,
8304   SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS,
8305   SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC,
8306   SETUP_TOKEN_VOLUME_SIMPLE,
8307   SETUP_TOKEN_VOLUME_LOOPS,
8308   SETUP_TOKEN_VOLUME_MUSIC,
8309   SETUP_TOKEN_NETWORK_MODE,
8310   SETUP_TOKEN_NETWORK_PLAYER_NR,
8311   SETUP_TOKEN_TOUCH_CONTROL_TYPE,
8312   SETUP_TOKEN_TOUCH_MOVE_DISTANCE,
8313   SETUP_TOKEN_TOUCH_DROP_DISTANCE,
8314   SETUP_TOKEN_TOUCH_TRANSPARENCY,
8315   SETUP_TOKEN_TOUCH_DRAW_OUTLINED,
8316   SETUP_TOKEN_TOUCH_DRAW_PRESSED,
8317   SETUP_TOKEN_TOUCH_GRID_XSIZE_0,
8318   SETUP_TOKEN_TOUCH_GRID_YSIZE_0,
8319   SETUP_TOKEN_TOUCH_GRID_XSIZE_1,
8320   SETUP_TOKEN_TOUCH_GRID_YSIZE_1,
8321
8322   NUM_GLOBAL_SETUP_TOKENS
8323 };
8324
8325 /* auto setup */
8326 enum
8327 {
8328   SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE = 0,
8329
8330   NUM_AUTO_SETUP_TOKENS
8331 };
8332
8333 /* editor setup */
8334 enum
8335 {
8336   SETUP_TOKEN_EDITOR_EL_CLASSIC = 0,
8337   SETUP_TOKEN_EDITOR_EL_CUSTOM,
8338   SETUP_TOKEN_EDITOR_EL_USER_DEFINED,
8339   SETUP_TOKEN_EDITOR_EL_DYNAMIC,
8340   SETUP_TOKEN_EDITOR_EL_HEADLINES,
8341   SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN,
8342
8343   NUM_EDITOR_SETUP_TOKENS
8344 };
8345
8346 /* editor cascade setup */
8347 enum
8348 {
8349   SETUP_TOKEN_EDITOR_CASCADE_BD = 0,
8350   SETUP_TOKEN_EDITOR_CASCADE_EM,
8351   SETUP_TOKEN_EDITOR_CASCADE_EMC,
8352   SETUP_TOKEN_EDITOR_CASCADE_RND,
8353   SETUP_TOKEN_EDITOR_CASCADE_SB,
8354   SETUP_TOKEN_EDITOR_CASCADE_SP,
8355   SETUP_TOKEN_EDITOR_CASCADE_DC,
8356   SETUP_TOKEN_EDITOR_CASCADE_DX,
8357   SETUP_TOKEN_EDITOR_CASCADE_TEXT,
8358   SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT,
8359   SETUP_TOKEN_EDITOR_CASCADE_CE,
8360   SETUP_TOKEN_EDITOR_CASCADE_GE,
8361   SETUP_TOKEN_EDITOR_CASCADE_REF,
8362   SETUP_TOKEN_EDITOR_CASCADE_USER,
8363   SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC,
8364
8365   NUM_EDITOR_CASCADE_SETUP_TOKENS
8366 };
8367
8368 /* shortcut setup */
8369 enum
8370 {
8371   SETUP_TOKEN_SHORTCUT_SAVE_GAME = 0,
8372   SETUP_TOKEN_SHORTCUT_LOAD_GAME,
8373   SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE,
8374   SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1,
8375   SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2,
8376   SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3,
8377   SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4,
8378   SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL,
8379   SETUP_TOKEN_SHORTCUT_TAPE_EJECT,
8380   SETUP_TOKEN_SHORTCUT_TAPE_EXTRA,
8381   SETUP_TOKEN_SHORTCUT_TAPE_STOP,
8382   SETUP_TOKEN_SHORTCUT_TAPE_PAUSE,
8383   SETUP_TOKEN_SHORTCUT_TAPE_RECORD,
8384   SETUP_TOKEN_SHORTCUT_TAPE_PLAY,
8385   SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE,
8386   SETUP_TOKEN_SHORTCUT_SOUND_LOOPS,
8387   SETUP_TOKEN_SHORTCUT_SOUND_MUSIC,
8388   SETUP_TOKEN_SHORTCUT_SNAP_LEFT,
8389   SETUP_TOKEN_SHORTCUT_SNAP_RIGHT,
8390   SETUP_TOKEN_SHORTCUT_SNAP_UP,
8391   SETUP_TOKEN_SHORTCUT_SNAP_DOWN,
8392
8393   NUM_SHORTCUT_SETUP_TOKENS
8394 };
8395
8396 /* player setup */
8397 enum
8398 {
8399   SETUP_TOKEN_PLAYER_USE_JOYSTICK = 0,
8400   SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME,
8401   SETUP_TOKEN_PLAYER_JOY_XLEFT,
8402   SETUP_TOKEN_PLAYER_JOY_XMIDDLE,
8403   SETUP_TOKEN_PLAYER_JOY_XRIGHT,
8404   SETUP_TOKEN_PLAYER_JOY_YUPPER,
8405   SETUP_TOKEN_PLAYER_JOY_YMIDDLE,
8406   SETUP_TOKEN_PLAYER_JOY_YLOWER,
8407   SETUP_TOKEN_PLAYER_JOY_SNAP,
8408   SETUP_TOKEN_PLAYER_JOY_DROP,
8409   SETUP_TOKEN_PLAYER_KEY_LEFT,
8410   SETUP_TOKEN_PLAYER_KEY_RIGHT,
8411   SETUP_TOKEN_PLAYER_KEY_UP,
8412   SETUP_TOKEN_PLAYER_KEY_DOWN,
8413   SETUP_TOKEN_PLAYER_KEY_SNAP,
8414   SETUP_TOKEN_PLAYER_KEY_DROP,
8415
8416   NUM_PLAYER_SETUP_TOKENS
8417 };
8418
8419 /* system setup */
8420 enum
8421 {
8422   SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER = 0,
8423   SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER,
8424   SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE,
8425
8426   NUM_SYSTEM_SETUP_TOKENS
8427 };
8428
8429 /* internal setup */
8430 enum
8431 {
8432   SETUP_TOKEN_INT_PROGRAM_TITLE = 0,
8433   SETUP_TOKEN_INT_PROGRAM_VERSION,
8434   SETUP_TOKEN_INT_PROGRAM_AUTHOR,
8435   SETUP_TOKEN_INT_PROGRAM_EMAIL,
8436   SETUP_TOKEN_INT_PROGRAM_WEBSITE,
8437   SETUP_TOKEN_INT_PROGRAM_COPYRIGHT,
8438   SETUP_TOKEN_INT_PROGRAM_COMPANY,
8439   SETUP_TOKEN_INT_PROGRAM_ICON_FILE,
8440   SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET,
8441   SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET,
8442   SETUP_TOKEN_INT_DEFAULT_MUSIC_SET,
8443   SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE,
8444   SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE,
8445   SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE,
8446   SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES,
8447   SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR,
8448   SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE,
8449   SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH,
8450   SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT,
8451
8452   NUM_INTERNAL_SETUP_TOKENS
8453 };
8454
8455 /* debug setup */
8456 enum
8457 {
8458   SETUP_TOKEN_DEBUG_FRAME_DELAY_0 = 0,
8459   SETUP_TOKEN_DEBUG_FRAME_DELAY_1,
8460   SETUP_TOKEN_DEBUG_FRAME_DELAY_2,
8461   SETUP_TOKEN_DEBUG_FRAME_DELAY_3,
8462   SETUP_TOKEN_DEBUG_FRAME_DELAY_4,
8463   SETUP_TOKEN_DEBUG_FRAME_DELAY_5,
8464   SETUP_TOKEN_DEBUG_FRAME_DELAY_6,
8465   SETUP_TOKEN_DEBUG_FRAME_DELAY_7,
8466   SETUP_TOKEN_DEBUG_FRAME_DELAY_8,
8467   SETUP_TOKEN_DEBUG_FRAME_DELAY_9,
8468   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0,
8469   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1,
8470   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2,
8471   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3,
8472   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4,
8473   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5,
8474   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6,
8475   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7,
8476   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8,
8477   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9,
8478   SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY,
8479   SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY,
8480   SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND,
8481
8482   NUM_DEBUG_SETUP_TOKENS
8483 };
8484
8485 /* options setup */
8486 enum
8487 {
8488   SETUP_TOKEN_OPTIONS_VERBOSE = 0,
8489
8490   NUM_OPTIONS_SETUP_TOKENS
8491 };
8492
8493
8494 static struct SetupInfo si;
8495 static struct SetupAutoSetupInfo sasi;
8496 static struct SetupEditorInfo sei;
8497 static struct SetupEditorCascadeInfo seci;
8498 static struct SetupShortcutInfo ssi;
8499 static struct SetupInputInfo sii;
8500 static struct SetupSystemInfo syi;
8501 static struct SetupInternalInfo sxi;
8502 static struct SetupDebugInfo sdi;
8503 static struct OptionInfo soi;
8504
8505 static struct TokenInfo global_setup_tokens[] =
8506 {
8507   { TYPE_STRING, &si.player_name,             "player_name"             },
8508   { TYPE_SWITCH, &si.sound,                   "sound"                   },
8509   { TYPE_SWITCH, &si.sound_loops,             "repeating_sound_loops"   },
8510   { TYPE_SWITCH, &si.sound_music,             "background_music"        },
8511   { TYPE_SWITCH, &si.sound_simple,            "simple_sound_effects"    },
8512   { TYPE_SWITCH, &si.toons,                   "toons"                   },
8513   { TYPE_SWITCH, &si.scroll_delay,            "scroll_delay"            },
8514   { TYPE_INTEGER,&si.scroll_delay_value,      "scroll_delay_value"      },
8515   { TYPE_STRING, &si.engine_snapshot_mode,    "engine_snapshot_mode"    },
8516   { TYPE_INTEGER,&si.engine_snapshot_memory,  "engine_snapshot_memory"  },
8517   { TYPE_SWITCH, &si.fade_screens,            "fade_screens"            },
8518   { TYPE_SWITCH, &si.autorecord,              "automatic_tape_recording"},
8519   { TYPE_SWITCH, &si.show_titlescreen,        "show_titlescreen"        },
8520   { TYPE_SWITCH, &si.quick_doors,             "quick_doors"             },
8521   { TYPE_SWITCH, &si.team_mode,               "team_mode"               },
8522   { TYPE_SWITCH, &si.handicap,                "handicap"                },
8523   { TYPE_SWITCH, &si.skip_levels,             "skip_levels"             },
8524   { TYPE_SWITCH, &si.increment_levels,        "increment_levels"        },
8525   { TYPE_SWITCH, &si.auto_play_next_level,    "auto_play_next_level"    },
8526   { TYPE_SWITCH, &si.skip_scores_after_game,  "skip_scores_after_game"  },
8527   { TYPE_SWITCH, &si.time_limit,              "time_limit"              },
8528   { TYPE_SWITCH, &si.fullscreen,              "fullscreen"              },
8529   { TYPE_INTEGER,&si.window_scaling_percent,  "window_scaling_percent"  },
8530   { TYPE_STRING, &si.window_scaling_quality,  "window_scaling_quality"  },
8531   { TYPE_STRING, &si.screen_rendering_mode,   "screen_rendering_mode"   },
8532   { TYPE_SWITCH, &si.ask_on_escape,           "ask_on_escape"           },
8533   { TYPE_SWITCH, &si.ask_on_escape_editor,    "ask_on_escape_editor"    },
8534   { TYPE_SWITCH, &si.quick_switch,            "quick_player_switch"     },
8535   { TYPE_SWITCH, &si.input_on_focus,          "input_on_focus"          },
8536   { TYPE_SWITCH, &si.prefer_aga_graphics,     "prefer_aga_graphics"     },
8537   { TYPE_INTEGER,&si.game_frame_delay,        "game_frame_delay"        },
8538   { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8539   { TYPE_SWITCH, &si.small_game_graphics,     "small_game_graphics"     },
8540   { TYPE_SWITCH, &si.show_snapshot_buttons,   "show_snapshot_buttons"   },
8541   { TYPE_STRING, &si.graphics_set,            "graphics_set"            },
8542   { TYPE_STRING, &si.sounds_set,              "sounds_set"              },
8543   { TYPE_STRING, &si.music_set,               "music_set"               },
8544   { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8545   { TYPE_SWITCH3,&si.override_level_sounds,   "override_level_sounds"   },
8546   { TYPE_SWITCH3,&si.override_level_music,    "override_level_music"    },
8547   { TYPE_INTEGER,&si.volume_simple,           "volume_simple"           },
8548   { TYPE_INTEGER,&si.volume_loops,            "volume_loops"            },
8549   { TYPE_INTEGER,&si.volume_music,            "volume_music"            },
8550   { TYPE_SWITCH, &si.network_mode,            "network_mode"            },
8551   { TYPE_PLAYER, &si.network_player_nr,       "network_player"          },
8552   { TYPE_STRING, &si.touch.control_type,      "touch.control_type"      },
8553   { TYPE_INTEGER,&si.touch.move_distance,     "touch.move_distance"     },
8554   { TYPE_INTEGER,&si.touch.drop_distance,     "touch.drop_distance"     },
8555   { TYPE_INTEGER,&si.touch.transparency,      "touch.transparency"      },
8556   { TYPE_INTEGER,&si.touch.draw_outlined,     "touch.draw_outlined"     },
8557   { TYPE_INTEGER,&si.touch.draw_pressed,      "touch.draw_pressed"      },
8558   { TYPE_INTEGER,&si.touch.grid_xsize[0],     "touch.virtual_buttons.0.xsize" },
8559   { TYPE_INTEGER,&si.touch.grid_ysize[0],     "touch.virtual_buttons.0.ysize" },
8560   { TYPE_INTEGER,&si.touch.grid_xsize[1],     "touch.virtual_buttons.1.xsize" },
8561   { TYPE_INTEGER,&si.touch.grid_ysize[1],     "touch.virtual_buttons.1.ysize" },
8562 };
8563
8564 static struct TokenInfo auto_setup_tokens[] =
8565 {
8566   { TYPE_INTEGER,&sasi.editor_zoom_tilesize,    "editor.zoom_tilesize"  },
8567 };
8568
8569 static struct TokenInfo editor_setup_tokens[] =
8570 {
8571   { TYPE_SWITCH, &sei.el_classic,       "editor.el_classic"             },
8572   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
8573   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
8574   { TYPE_SWITCH, &sei.el_dynamic,       "editor.el_dynamic"             },
8575   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
8576   { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token"    },
8577 };
8578
8579 static struct TokenInfo editor_cascade_setup_tokens[] =
8580 {
8581   { TYPE_SWITCH, &seci.el_bd,           "editor.cascade.el_bd"          },
8582   { TYPE_SWITCH, &seci.el_em,           "editor.cascade.el_em"          },
8583   { TYPE_SWITCH, &seci.el_emc,          "editor.cascade.el_emc"         },
8584   { TYPE_SWITCH, &seci.el_rnd,          "editor.cascade.el_rnd"         },
8585   { TYPE_SWITCH, &seci.el_sb,           "editor.cascade.el_sb"          },
8586   { TYPE_SWITCH, &seci.el_sp,           "editor.cascade.el_sp"          },
8587   { TYPE_SWITCH, &seci.el_dc,           "editor.cascade.el_dc"          },
8588   { TYPE_SWITCH, &seci.el_dx,           "editor.cascade.el_dx"          },
8589   { TYPE_SWITCH, &seci.el_mm,           "editor.cascade.el_mm"          },
8590   { TYPE_SWITCH, &seci.el_df,           "editor.cascade.el_df"          },
8591   { TYPE_SWITCH, &seci.el_chars,        "editor.cascade.el_chars"       },
8592   { TYPE_SWITCH, &seci.el_steel_chars,  "editor.cascade.el_steel_chars" },
8593   { TYPE_SWITCH, &seci.el_ce,           "editor.cascade.el_ce"          },
8594   { TYPE_SWITCH, &seci.el_ge,           "editor.cascade.el_ge"          },
8595   { TYPE_SWITCH, &seci.el_ref,          "editor.cascade.el_ref"         },
8596   { TYPE_SWITCH, &seci.el_user,         "editor.cascade.el_user"        },
8597   { TYPE_SWITCH, &seci.el_dynamic,      "editor.cascade.el_dynamic"     },
8598 };
8599
8600 static struct TokenInfo shortcut_setup_tokens[] =
8601 {
8602   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
8603   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
8604   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         },
8605   { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1"       },
8606   { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2"       },
8607   { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3"       },
8608   { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4"       },
8609   { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all"     },
8610   { TYPE_KEY_X11, &ssi.tape_eject,      "shortcut.tape_eject"           },
8611   { TYPE_KEY_X11, &ssi.tape_extra,      "shortcut.tape_extra"           },
8612   { TYPE_KEY_X11, &ssi.tape_stop,       "shortcut.tape_stop"            },
8613   { TYPE_KEY_X11, &ssi.tape_pause,      "shortcut.tape_pause"           },
8614   { TYPE_KEY_X11, &ssi.tape_record,     "shortcut.tape_record"          },
8615   { TYPE_KEY_X11, &ssi.tape_play,       "shortcut.tape_play"            },
8616   { TYPE_KEY_X11, &ssi.sound_simple,    "shortcut.sound_simple"         },
8617   { TYPE_KEY_X11, &ssi.sound_loops,     "shortcut.sound_loops"          },
8618   { TYPE_KEY_X11, &ssi.sound_music,     "shortcut.sound_music"          },
8619   { TYPE_KEY_X11, &ssi.snap_left,       "shortcut.snap_left"            },
8620   { TYPE_KEY_X11, &ssi.snap_right,      "shortcut.snap_right"           },
8621   { TYPE_KEY_X11, &ssi.snap_up,         "shortcut.snap_up"              },
8622   { TYPE_KEY_X11, &ssi.snap_down,       "shortcut.snap_down"            },
8623 };
8624
8625 static struct TokenInfo player_setup_tokens[] =
8626 {
8627   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
8628   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
8629   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
8630   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
8631   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
8632   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
8633   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
8634   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
8635   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
8636   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
8637   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
8638   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
8639   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
8640   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
8641   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
8642   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               },
8643 };
8644
8645 static struct TokenInfo system_setup_tokens[] =
8646 {
8647   { TYPE_STRING,  &syi.sdl_videodriver,    "system.sdl_videodriver"     },
8648   { TYPE_STRING,  &syi.sdl_audiodriver,    "system.sdl_audiodriver"     },
8649   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8650 };
8651
8652 static struct TokenInfo internal_setup_tokens[] =
8653 {
8654   { TYPE_STRING, &sxi.program_title,            "program_title"         },
8655   { TYPE_STRING, &sxi.program_version,          "program_version"       },
8656   { TYPE_STRING, &sxi.program_author,           "program_author"        },
8657   { TYPE_STRING, &sxi.program_email,            "program_email"         },
8658   { TYPE_STRING, &sxi.program_website,          "program_website"       },
8659   { TYPE_STRING, &sxi.program_copyright,        "program_copyright"     },
8660   { TYPE_STRING, &sxi.program_company,          "program_company"       },
8661   { TYPE_STRING, &sxi.program_icon_file,        "program_icon_file"     },
8662   { TYPE_STRING, &sxi.default_graphics_set,     "default_graphics_set"  },
8663   { TYPE_STRING, &sxi.default_sounds_set,       "default_sounds_set"    },
8664   { TYPE_STRING, &sxi.default_music_set,        "default_music_set"     },
8665   { TYPE_STRING, &sxi.fallback_graphics_file,   "fallback_graphics_file"},
8666   { TYPE_STRING, &sxi.fallback_sounds_file,     "fallback_sounds_file"  },
8667   { TYPE_STRING, &sxi.fallback_music_file,      "fallback_music_file"   },
8668   { TYPE_STRING, &sxi.default_level_series,     "default_level_series"  },
8669   { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8670   { TYPE_BOOLEAN,&sxi.show_scaling_in_title,    "show_scaling_in_title" },
8671   { TYPE_INTEGER,&sxi.default_window_width,     "default_window_width"  },
8672   { TYPE_INTEGER,&sxi.default_window_height,    "default_window_height" },
8673 };
8674
8675 static struct TokenInfo debug_setup_tokens[] =
8676 {
8677   { TYPE_INTEGER, &sdi.frame_delay[0],          "debug.frame_delay_0"   },
8678   { TYPE_INTEGER, &sdi.frame_delay[1],          "debug.frame_delay_1"   },
8679   { TYPE_INTEGER, &sdi.frame_delay[2],          "debug.frame_delay_2"   },
8680   { TYPE_INTEGER, &sdi.frame_delay[3],          "debug.frame_delay_3"   },
8681   { TYPE_INTEGER, &sdi.frame_delay[4],          "debug.frame_delay_4"   },
8682   { TYPE_INTEGER, &sdi.frame_delay[5],          "debug.frame_delay_5"   },
8683   { TYPE_INTEGER, &sdi.frame_delay[6],          "debug.frame_delay_6"   },
8684   { TYPE_INTEGER, &sdi.frame_delay[7],          "debug.frame_delay_7"   },
8685   { TYPE_INTEGER, &sdi.frame_delay[8],          "debug.frame_delay_8"   },
8686   { TYPE_INTEGER, &sdi.frame_delay[9],          "debug.frame_delay_9"   },
8687   { TYPE_KEY_X11, &sdi.frame_delay_key[0],      "debug.key.frame_delay_0" },
8688   { TYPE_KEY_X11, &sdi.frame_delay_key[1],      "debug.key.frame_delay_1" },
8689   { TYPE_KEY_X11, &sdi.frame_delay_key[2],      "debug.key.frame_delay_2" },
8690   { TYPE_KEY_X11, &sdi.frame_delay_key[3],      "debug.key.frame_delay_3" },
8691   { TYPE_KEY_X11, &sdi.frame_delay_key[4],      "debug.key.frame_delay_4" },
8692   { TYPE_KEY_X11, &sdi.frame_delay_key[5],      "debug.key.frame_delay_5" },
8693   { TYPE_KEY_X11, &sdi.frame_delay_key[6],      "debug.key.frame_delay_6" },
8694   { TYPE_KEY_X11, &sdi.frame_delay_key[7],      "debug.key.frame_delay_7" },
8695   { TYPE_KEY_X11, &sdi.frame_delay_key[8],      "debug.key.frame_delay_8" },
8696   { TYPE_KEY_X11, &sdi.frame_delay_key[9],      "debug.key.frame_delay_9" },
8697   { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8698   { TYPE_BOOLEAN, &sdi.frame_delay_game_only,  "debug.frame_delay.game_only" },
8699   { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8700 };
8701
8702 static struct TokenInfo options_setup_tokens[] =
8703 {
8704   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               },
8705 };
8706
8707 static char *get_corrected_login_name(char *login_name)
8708 {
8709   /* needed because player name must be a fixed length string */
8710   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8711
8712   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8713   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8714
8715   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
8716     if (strchr(login_name_new, ' '))
8717       *strchr(login_name_new, ' ') = '\0';
8718
8719   return login_name_new;
8720 }
8721
8722 static void setSetupInfoToDefaults(struct SetupInfo *si)
8723 {
8724   int i;
8725
8726   si->player_name = get_corrected_login_name(getLoginName());
8727
8728   si->sound = TRUE;
8729   si->sound_loops = TRUE;
8730   si->sound_music = TRUE;
8731   si->sound_simple = TRUE;
8732   si->toons = TRUE;
8733   si->scroll_delay = TRUE;
8734   si->scroll_delay_value = STD_SCROLL_DELAY;
8735   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8736   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8737   si->fade_screens = TRUE;
8738   si->autorecord = TRUE;
8739   si->show_titlescreen = TRUE;
8740   si->quick_doors = FALSE;
8741   si->team_mode = FALSE;
8742   si->handicap = TRUE;
8743   si->skip_levels = TRUE;
8744   si->increment_levels = TRUE;
8745   si->auto_play_next_level = TRUE;
8746   si->skip_scores_after_game = FALSE;
8747   si->time_limit = TRUE;
8748   si->fullscreen = FALSE;
8749   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8750   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8751   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8752   si->ask_on_escape = TRUE;
8753   si->ask_on_escape_editor = TRUE;
8754   si->quick_switch = FALSE;
8755   si->input_on_focus = FALSE;
8756   si->prefer_aga_graphics = TRUE;
8757   si->game_frame_delay = GAME_FRAME_DELAY;
8758   si->sp_show_border_elements = FALSE;
8759   si->small_game_graphics = FALSE;
8760   si->show_snapshot_buttons = FALSE;
8761
8762   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8763   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8764   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8765
8766   si->override_level_graphics = FALSE;
8767   si->override_level_sounds = FALSE;
8768   si->override_level_music = FALSE;
8769
8770   si->volume_simple = 100;              /* percent */
8771   si->volume_loops = 100;               /* percent */
8772   si->volume_music = 100;               /* percent */
8773
8774   si->network_mode = FALSE;
8775   si->network_player_nr = 0;            /* first player */
8776
8777   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8778   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        /* percent */
8779   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        /* percent */
8780   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          /* percent */
8781   si->touch.draw_outlined = TRUE;
8782   si->touch.draw_pressed = TRUE;
8783
8784   for (i = 0; i < 2; i++)
8785   {
8786     char *default_grid_button[6][2] =
8787     {
8788       { "      ", "  ^^  " },
8789       { "      ", "  ^^  " },
8790       { "      ", "<<  >>" },
8791       { "      ", "<<  >>" },
8792       { "111222", "  vv  " },
8793       { "111222", "  vv  " }
8794     };
8795     int grid_xsize = DEFAULT_GRID_XSIZE(i);
8796     int grid_ysize = DEFAULT_GRID_YSIZE(i);
8797     int min_xsize = MIN(6, grid_xsize);
8798     int min_ysize = MIN(6, grid_ysize);
8799     int startx = grid_xsize - min_xsize;
8800     int starty = grid_ysize - min_ysize;
8801     int x, y;
8802
8803     // virtual buttons grid can only be set to defaults if video is initialized
8804     // (this will be repeated if virtual buttons are not loaded from setup file)
8805     if (video.initialized)
8806     {
8807       si->touch.grid_xsize[i] = grid_xsize;
8808       si->touch.grid_ysize[i] = grid_ysize;
8809     }
8810     else
8811     {
8812       si->touch.grid_xsize[i] = -1;
8813       si->touch.grid_ysize[i] = -1;
8814     }
8815
8816     for (x = 0; x < MAX_GRID_XSIZE; x++)
8817       for (y = 0; y < MAX_GRID_YSIZE; y++)
8818         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
8819
8820     for (x = 0; x < min_xsize; x++)
8821       for (y = 0; y < min_ysize; y++)
8822         si->touch.grid_button[i][x][starty + y] =
8823           default_grid_button[y][0][x];
8824
8825     for (x = 0; x < min_xsize; x++)
8826       for (y = 0; y < min_ysize; y++)
8827         si->touch.grid_button[i][startx + x][starty + y] =
8828           default_grid_button[y][1][x];
8829   }
8830
8831   si->touch.grid_initialized            = video.initialized;
8832
8833   si->editor.el_boulderdash             = TRUE;
8834   si->editor.el_emerald_mine            = TRUE;
8835   si->editor.el_emerald_mine_club       = TRUE;
8836   si->editor.el_more                    = TRUE;
8837   si->editor.el_sokoban                 = TRUE;
8838   si->editor.el_supaplex                = TRUE;
8839   si->editor.el_diamond_caves           = TRUE;
8840   si->editor.el_dx_boulderdash          = TRUE;
8841
8842   si->editor.el_mirror_magic            = TRUE;
8843   si->editor.el_deflektor               = TRUE;
8844
8845   si->editor.el_chars                   = TRUE;
8846   si->editor.el_steel_chars             = TRUE;
8847
8848   si->editor.el_classic                 = TRUE;
8849   si->editor.el_custom                  = TRUE;
8850
8851   si->editor.el_user_defined            = FALSE;
8852   si->editor.el_dynamic                 = TRUE;
8853
8854   si->editor.el_headlines               = TRUE;
8855
8856   si->editor.show_element_token         = FALSE;
8857
8858   si->editor.use_template_for_new_levels = TRUE;
8859
8860   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
8861   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
8862   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
8863
8864   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
8865   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
8866   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
8867   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
8868   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8869
8870   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
8871   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
8872   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
8873   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
8874   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
8875   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
8876
8877   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
8878   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
8879   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
8880
8881   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
8882   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
8883   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
8884   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
8885
8886   for (i = 0; i < MAX_PLAYERS; i++)
8887   {
8888     si->input[i].use_joystick = FALSE;
8889     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8890     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
8891     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8892     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
8893     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
8894     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8895     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
8896     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
8897     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
8898     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
8899     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8900     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
8901     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
8902     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
8903     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
8904   }
8905
8906   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8907   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8908   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8909
8910   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
8911   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
8912   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
8913   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
8914   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
8915   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8916   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
8917
8918   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8919
8920   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8921   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8922   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8923
8924   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8925   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
8926   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
8927
8928   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8929   si->internal.choose_from_top_leveldir = FALSE;
8930   si->internal.show_scaling_in_title = TRUE;
8931
8932   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
8933   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8934
8935   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8936   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8937   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8938   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8939   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8940   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8941   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8942   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8943   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8944   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8945
8946   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8947   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8948   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8949   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8950   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8951   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8952   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8953   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8954   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8955   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8956
8957   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8958   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
8959
8960   si->debug.show_frames_per_second = FALSE;
8961
8962   si->options.verbose = FALSE;
8963
8964 #if defined(PLATFORM_ANDROID)
8965   si->fullscreen = TRUE;
8966 #endif
8967 }
8968
8969 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
8970 {
8971   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
8972 }
8973
8974 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8975 {
8976   si->editor_cascade.el_bd              = TRUE;
8977   si->editor_cascade.el_em              = TRUE;
8978   si->editor_cascade.el_emc             = TRUE;
8979   si->editor_cascade.el_rnd             = TRUE;
8980   si->editor_cascade.el_sb              = TRUE;
8981   si->editor_cascade.el_sp              = TRUE;
8982   si->editor_cascade.el_dc              = TRUE;
8983   si->editor_cascade.el_dx              = TRUE;
8984
8985   si->editor_cascade.el_mm              = TRUE;
8986   si->editor_cascade.el_df              = TRUE;
8987
8988   si->editor_cascade.el_chars           = FALSE;
8989   si->editor_cascade.el_steel_chars     = FALSE;
8990   si->editor_cascade.el_ce              = FALSE;
8991   si->editor_cascade.el_ge              = FALSE;
8992   si->editor_cascade.el_ref             = FALSE;
8993   si->editor_cascade.el_user            = FALSE;
8994   si->editor_cascade.el_dynamic         = FALSE;
8995 }
8996
8997 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
8998
8999 static char *getHideSetupToken(void *setup_value)
9000 {
9001   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9002
9003   if (setup_value != NULL)
9004     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9005
9006   return hide_setup_token;
9007 }
9008
9009 void setHideSetupEntry(void *setup_value)
9010 {
9011   char *hide_setup_token = getHideSetupToken(setup_value);
9012
9013   if (setup_value != NULL)
9014     setHashEntry(hide_setup_hash, hide_setup_token, "");
9015 }
9016
9017 static void setHideSetupEntryRaw(char *token_text, void *setup_value_raw)
9018 {
9019   /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
9020   void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
9021
9022   setHideSetupEntry(setup_value);
9023 }
9024
9025 boolean hideSetupEntry(void *setup_value)
9026 {
9027   char *hide_setup_token = getHideSetupToken(setup_value);
9028
9029   return (setup_value != NULL &&
9030           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9031 }
9032
9033 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9034                                       struct TokenInfo *token_info,
9035                                       int token_nr, char *token_text)
9036 {
9037   char *token_hide_text = getStringCat2(token_text, ".hide");
9038   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9039
9040   /* set the value of this setup option in the setup option structure */
9041   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9042
9043   /* check if this setup option should be hidden in the setup menu */
9044   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9045     setHideSetupEntryRaw(token_text, token_info[token_nr].value);
9046 }
9047
9048 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9049                                       struct TokenInfo *token_info,
9050                                       int token_nr)
9051 {
9052   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9053                             token_info[token_nr].text);
9054 }
9055
9056 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9057 {
9058   int i, pnr;
9059
9060   if (!setup_file_hash)
9061     return;
9062
9063   if (hide_setup_hash == NULL)
9064     hide_setup_hash = newSetupFileHash();
9065
9066   /* global setup */
9067   si = setup;
9068   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9069     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9070   setup = si;
9071
9072   /* virtual buttons setup */
9073   setup.touch.grid_initialized = TRUE;
9074   for (i = 0; i < 2; i++)
9075   {
9076     int grid_xsize = setup.touch.grid_xsize[i];
9077     int grid_ysize = setup.touch.grid_ysize[i];
9078     int x, y;
9079
9080     // if virtual buttons are not loaded from setup file, repeat initializing
9081     // virtual buttons grid with default values later when video is initialized
9082     if (grid_xsize == -1 ||
9083         grid_ysize == -1)
9084     {
9085       setup.touch.grid_initialized = FALSE;
9086
9087       continue;
9088     }
9089
9090     for (y = 0; y < grid_ysize; y++)
9091     {
9092       char token_string[MAX_LINE_LEN];
9093
9094       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9095
9096       char *value_string = getHashEntry(setup_file_hash, token_string);
9097
9098       if (value_string == NULL)
9099         continue;
9100
9101       for (x = 0; x < grid_xsize; x++)
9102       {
9103         char c = value_string[x];
9104
9105         setup.touch.grid_button[i][x][y] =
9106           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9107       }
9108     }
9109   }
9110
9111   /* editor setup */
9112   sei = setup.editor;
9113   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9114     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9115   setup.editor = sei;
9116
9117   /* shortcut setup */
9118   ssi = setup.shortcut;
9119   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9120     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9121   setup.shortcut = ssi;
9122
9123   /* player setup */
9124   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9125   {
9126     char prefix[30];
9127
9128     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9129
9130     sii = setup.input[pnr];
9131     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9132     {
9133       char full_token[100];
9134
9135       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9136       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9137                                 full_token);
9138     }
9139     setup.input[pnr] = sii;
9140   }
9141
9142   /* system setup */
9143   syi = setup.system;
9144   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9145     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9146   setup.system = syi;
9147
9148   /* internal setup */
9149   sxi = setup.internal;
9150   for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
9151     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9152   setup.internal = sxi;
9153
9154   /* debug setup */
9155   sdi = setup.debug;
9156   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9157     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9158   setup.debug = sdi;
9159
9160   /* options setup */
9161   soi = setup.options;
9162   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9163     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9164   setup.options = soi;
9165
9166   setHideRelatedSetupEntries();
9167 }
9168
9169 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9170 {
9171   int i;
9172
9173   if (!setup_file_hash)
9174     return;
9175
9176   /* auto setup */
9177   sasi = setup.auto_setup;
9178   for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9179     setSetupInfo(auto_setup_tokens, i,
9180                  getHashEntry(setup_file_hash,
9181                               auto_setup_tokens[i].text));
9182   setup.auto_setup = sasi;
9183 }
9184
9185 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9186 {
9187   int i;
9188
9189   if (!setup_file_hash)
9190     return;
9191
9192   /* editor cascade setup */
9193   seci = setup.editor_cascade;
9194   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9195     setSetupInfo(editor_cascade_setup_tokens, i,
9196                  getHashEntry(setup_file_hash,
9197                               editor_cascade_setup_tokens[i].text));
9198   setup.editor_cascade = seci;
9199 }
9200
9201 void LoadSetupFromFilename(char *filename)
9202 {
9203   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9204
9205   if (setup_file_hash)
9206   {
9207     decodeSetupFileHash(setup_file_hash);
9208
9209     freeSetupFileHash(setup_file_hash);
9210   }
9211   else
9212   {
9213     Error(ERR_DEBUG, "using default setup values");
9214   }
9215 }
9216
9217 static void LoadSetup_SpecialPostProcessing()
9218 {
9219   char *player_name_new;
9220
9221   /* needed to work around problems with fixed length strings */
9222   player_name_new = get_corrected_login_name(setup.player_name);
9223   free(setup.player_name);
9224   setup.player_name = player_name_new;
9225
9226   /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9227   if (setup.scroll_delay == FALSE)
9228   {
9229     setup.scroll_delay_value = MIN_SCROLL_DELAY;
9230     setup.scroll_delay = TRUE;                  /* now always "on" */
9231   }
9232
9233   /* make sure that scroll delay value stays inside valid range */
9234   setup.scroll_delay_value =
9235     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9236 }
9237
9238 void LoadSetup()
9239 {
9240   char *filename;
9241
9242   /* always start with reliable default values */
9243   setSetupInfoToDefaults(&setup);
9244
9245   /* try to load setup values from default setup file */
9246   filename = getDefaultSetupFilename();
9247
9248   if (fileExists(filename))
9249     LoadSetupFromFilename(filename);
9250
9251   /* try to load setup values from user setup file */
9252   filename = getSetupFilename();
9253
9254   LoadSetupFromFilename(filename);
9255
9256   LoadSetup_SpecialPostProcessing();
9257 }
9258
9259 void LoadSetup_AutoSetup()
9260 {
9261   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9262   SetupFileHash *setup_file_hash = NULL;
9263
9264   /* always start with reliable default values */
9265   setSetupInfoToDefaults_AutoSetup(&setup);
9266
9267   setup_file_hash = loadSetupFileHash(filename);
9268
9269   if (setup_file_hash)
9270   {
9271     decodeSetupFileHash_AutoSetup(setup_file_hash);
9272
9273     freeSetupFileHash(setup_file_hash);
9274   }
9275
9276   free(filename);
9277 }
9278
9279 void LoadSetup_EditorCascade()
9280 {
9281   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9282   SetupFileHash *setup_file_hash = NULL;
9283
9284   /* always start with reliable default values */
9285   setSetupInfoToDefaults_EditorCascade(&setup);
9286
9287   setup_file_hash = loadSetupFileHash(filename);
9288
9289   if (setup_file_hash)
9290   {
9291     decodeSetupFileHash_EditorCascade(setup_file_hash);
9292
9293     freeSetupFileHash(setup_file_hash);
9294   }
9295
9296   free(filename);
9297 }
9298
9299 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9300                                            char *mapping_line)
9301 {
9302   char mapping_guid[MAX_LINE_LEN];
9303   char *mapping_start, *mapping_end;
9304
9305   // get GUID from game controller mapping line: copy complete line
9306   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9307   mapping_guid[MAX_LINE_LEN - 1] = '\0';
9308
9309   // get GUID from game controller mapping line: cut after GUID part
9310   mapping_start = strchr(mapping_guid, ',');
9311   if (mapping_start != NULL)
9312     *mapping_start = '\0';
9313
9314   // cut newline from game controller mapping line
9315   mapping_end = strchr(mapping_line, '\n');
9316   if (mapping_end != NULL)
9317     *mapping_end = '\0';
9318
9319   // add mapping entry to game controller mappings hash
9320   setHashEntry(mappings_hash, mapping_guid, mapping_line);
9321 }
9322
9323 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9324                                                  char *filename)
9325 {
9326   FILE *file;
9327
9328   if (!(file = fopen(filename, MODE_READ)))
9329   {
9330     Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9331
9332     return;
9333   }
9334
9335   while (!feof(file))
9336   {
9337     char line[MAX_LINE_LEN];
9338
9339     if (!fgets(line, MAX_LINE_LEN, file))
9340       break;
9341
9342     addGameControllerMappingToHash(mappings_hash, line);
9343   }
9344
9345   fclose(file);
9346 }
9347
9348 void SaveSetup()
9349 {
9350   char *filename = getSetupFilename();
9351   FILE *file;
9352   int i, pnr;
9353
9354   InitUserDataDirectory();
9355
9356   if (!(file = fopen(filename, MODE_WRITE)))
9357   {
9358     Error(ERR_WARN, "cannot write setup file '%s'", filename);
9359     return;
9360   }
9361
9362   fprintFileHeader(file, SETUP_FILENAME);
9363
9364   /* global setup */
9365   si = setup;
9366   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9367   {
9368     /* just to make things nicer :) */
9369     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9370         i == SETUP_TOKEN_GRAPHICS_SET ||
9371         i == SETUP_TOKEN_VOLUME_SIMPLE ||
9372         i == SETUP_TOKEN_NETWORK_MODE ||
9373         i == SETUP_TOKEN_TOUCH_CONTROL_TYPE ||
9374         i == SETUP_TOKEN_TOUCH_GRID_XSIZE_0 ||
9375         i == SETUP_TOKEN_TOUCH_GRID_XSIZE_1)
9376       fprintf(file, "\n");
9377
9378     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9379   }
9380
9381   /* virtual buttons setup */
9382   for (i = 0; i < 2; i++)
9383   {
9384     int grid_xsize = setup.touch.grid_xsize[i];
9385     int grid_ysize = setup.touch.grid_ysize[i];
9386     int x, y;
9387
9388     fprintf(file, "\n");
9389
9390     for (y = 0; y < grid_ysize; y++)
9391     {
9392       char token_string[MAX_LINE_LEN];
9393       char value_string[MAX_LINE_LEN];
9394
9395       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9396
9397       for (x = 0; x < grid_xsize; x++)
9398       {
9399         char c = setup.touch.grid_button[i][x][y];
9400
9401         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9402       }
9403
9404       value_string[grid_xsize] = '\0';
9405
9406       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9407     }
9408   }
9409
9410   /* editor setup */
9411   sei = setup.editor;
9412   fprintf(file, "\n");
9413   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9414     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9415
9416   /* shortcut setup */
9417   ssi = setup.shortcut;
9418   fprintf(file, "\n");
9419   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9420     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9421
9422   /* player setup */
9423   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9424   {
9425     char prefix[30];
9426
9427     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9428     fprintf(file, "\n");
9429
9430     sii = setup.input[pnr];
9431     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9432       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9433   }
9434
9435   /* system setup */
9436   syi = setup.system;
9437   fprintf(file, "\n");
9438   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9439     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9440
9441   /* internal setup */
9442   /* (internal setup values not saved to user setup file) */
9443
9444   /* debug setup */
9445   sdi = setup.debug;
9446   fprintf(file, "\n");
9447   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9448     fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9449
9450   /* options setup */
9451   soi = setup.options;
9452   fprintf(file, "\n");
9453   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9454     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9455
9456   fclose(file);
9457
9458   SetFilePermissions(filename, PERMS_PRIVATE);
9459 }
9460
9461 void SaveSetup_AutoSetup()
9462 {
9463   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9464   FILE *file;
9465   int i;
9466
9467   InitUserDataDirectory();
9468
9469   if (!(file = fopen(filename, MODE_WRITE)))
9470   {
9471     Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9472     free(filename);
9473     return;
9474   }
9475
9476   fprintFileHeader(file, AUTOSETUP_FILENAME);
9477
9478   sasi = setup.auto_setup;
9479   for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9480     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9481
9482   fclose(file);
9483
9484   SetFilePermissions(filename, PERMS_PRIVATE);
9485
9486   free(filename);
9487 }
9488
9489 void SaveSetup_EditorCascade()
9490 {
9491   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9492   FILE *file;
9493   int i;
9494
9495   InitUserDataDirectory();
9496
9497   if (!(file = fopen(filename, MODE_WRITE)))
9498   {
9499     Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9500     free(filename);
9501     return;
9502   }
9503
9504   fprintFileHeader(file, EDITORCASCADE_FILENAME);
9505
9506   seci = setup.editor_cascade;
9507   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9508     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9509
9510   fclose(file);
9511
9512   SetFilePermissions(filename, PERMS_PRIVATE);
9513
9514   free(filename);
9515 }
9516
9517 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9518                                                   char *filename)
9519 {
9520   FILE *file;
9521
9522   if (!(file = fopen(filename, MODE_WRITE)))
9523   {
9524     Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9525
9526     return;
9527   }
9528
9529   BEGIN_HASH_ITERATION(mappings_hash, itr)
9530   {
9531     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9532   }
9533   END_HASH_ITERATION(mappings_hash, itr)
9534
9535   fclose(file);
9536 }
9537
9538 void SaveSetup_AddGameControllerMapping(char *mapping)
9539 {
9540   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9541   SetupFileHash *mappings_hash = newSetupFileHash();
9542
9543   InitUserDataDirectory();
9544
9545   // load existing personal game controller mappings
9546   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9547
9548   // add new mapping to personal game controller mappings
9549   addGameControllerMappingToHash(mappings_hash, mapping);
9550
9551   // save updated personal game controller mappings
9552   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9553
9554   freeSetupFileHash(mappings_hash);
9555   free(filename);
9556 }
9557
9558 void LoadCustomElementDescriptions()
9559 {
9560   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9561   SetupFileHash *setup_file_hash;
9562   int i;
9563
9564   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9565   {
9566     if (element_info[i].custom_description != NULL)
9567     {
9568       free(element_info[i].custom_description);
9569       element_info[i].custom_description = NULL;
9570     }
9571   }
9572
9573   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9574     return;
9575
9576   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9577   {
9578     char *token = getStringCat2(element_info[i].token_name, ".name");
9579     char *value = getHashEntry(setup_file_hash, token);
9580
9581     if (value != NULL)
9582       element_info[i].custom_description = getStringCopy(value);
9583
9584     free(token);
9585   }
9586
9587   freeSetupFileHash(setup_file_hash);
9588 }
9589
9590 static int getElementFromToken(char *token)
9591 {
9592   char *value = getHashEntry(element_token_hash, token);
9593
9594   if (value != NULL)
9595     return atoi(value);
9596
9597   Error(ERR_WARN, "unknown element token '%s'", token);
9598
9599   return EL_UNDEFINED;
9600 }
9601
9602 static int get_token_parameter_value(char *token, char *value_raw)
9603 {
9604   char *suffix;
9605
9606   if (token == NULL || value_raw == NULL)
9607     return ARG_UNDEFINED_VALUE;
9608
9609   suffix = strrchr(token, '.');
9610   if (suffix == NULL)
9611     suffix = token;
9612
9613   if (strEqual(suffix, ".element"))
9614     return getElementFromToken(value_raw);
9615
9616   /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9617   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9618 }
9619
9620 void InitMenuDesignSettings_Static()
9621 {
9622   int i;
9623
9624   /* always start with reliable default values from static default config */
9625   for (i = 0; image_config_vars[i].token != NULL; i++)
9626   {
9627     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9628
9629     if (value != NULL)
9630       *image_config_vars[i].value =
9631         get_token_parameter_value(image_config_vars[i].token, value);
9632   }
9633 }
9634
9635 static void InitMenuDesignSettings_SpecialPreProcessing()
9636 {
9637   int i;
9638
9639   /* the following initializes hierarchical values from static configuration */
9640
9641   /* special case: initialize "ARG_DEFAULT" values in static default config */
9642   /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9643   titlescreen_initial_first_default.fade_mode  =
9644     title_initial_first_default.fade_mode;
9645   titlescreen_initial_first_default.fade_delay =
9646     title_initial_first_default.fade_delay;
9647   titlescreen_initial_first_default.post_delay =
9648     title_initial_first_default.post_delay;
9649   titlescreen_initial_first_default.auto_delay =
9650     title_initial_first_default.auto_delay;
9651   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
9652   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9653   titlescreen_first_default.post_delay = title_first_default.post_delay;
9654   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9655   titlemessage_initial_first_default.fade_mode  =
9656     title_initial_first_default.fade_mode;
9657   titlemessage_initial_first_default.fade_delay =
9658     title_initial_first_default.fade_delay;
9659   titlemessage_initial_first_default.post_delay =
9660     title_initial_first_default.post_delay;
9661   titlemessage_initial_first_default.auto_delay =
9662     title_initial_first_default.auto_delay;
9663   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
9664   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9665   titlemessage_first_default.post_delay = title_first_default.post_delay;
9666   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9667
9668   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
9669   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9670   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9671   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9672   titlescreen_default.fade_mode  = title_default.fade_mode;
9673   titlescreen_default.fade_delay = title_default.fade_delay;
9674   titlescreen_default.post_delay = title_default.post_delay;
9675   titlescreen_default.auto_delay = title_default.auto_delay;
9676   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
9677   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9678   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9679   titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9680   titlemessage_default.fade_mode  = title_default.fade_mode;
9681   titlemessage_default.fade_delay = title_default.fade_delay;
9682   titlemessage_default.post_delay = title_default.post_delay;
9683   titlemessage_default.auto_delay = title_default.auto_delay;
9684
9685   /* special case: initialize "ARG_DEFAULT" values in static default config */
9686   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9687   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9688   {
9689     titlescreen_initial_first[i] = titlescreen_initial_first_default;
9690     titlescreen_first[i] = titlescreen_first_default;
9691     titlemessage_initial_first[i] = titlemessage_initial_first_default;
9692     titlemessage_first[i] = titlemessage_first_default;
9693
9694     titlescreen_initial[i] = titlescreen_initial_default;
9695     titlescreen[i] = titlescreen_default;
9696     titlemessage_initial[i] = titlemessage_initial_default;
9697     titlemessage[i] = titlemessage_default;
9698   }
9699
9700   /* special case: initialize "ARG_DEFAULT" values in static default config */
9701   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9702   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9703   {
9704     if (i == GFX_SPECIAL_ARG_TITLE)     /* title values already initialized */
9705       continue;
9706
9707     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9708     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9709     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9710   }
9711
9712   /* special case: initialize "ARG_DEFAULT" values in static default config */
9713   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9714   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9715   {
9716     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9717     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9718     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9719
9720     if (i == GFX_SPECIAL_ARG_EDITOR)    /* editor values already initialized */
9721       continue;
9722
9723     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9724   }
9725 }
9726
9727 static void InitMenuDesignSettings_SpecialPostProcessing()
9728 {
9729   static struct
9730   {
9731     struct XY *dst, *src;
9732   }
9733   game_buttons_xy[] =
9734   {
9735     { &game.button.save,        &game.button.stop       },
9736     { &game.button.pause2,      &game.button.pause      },
9737     { &game.button.load,        &game.button.play       },
9738     { &game.button.undo,        &game.button.stop       },
9739     { &game.button.redo,        &game.button.play       },
9740
9741     { NULL,                     NULL                    }
9742   };
9743   int i;
9744
9745   /* special case: initialize later added SETUP list size from LEVELS value */
9746   if (menu.list_size[GAME_MODE_SETUP] == -1)
9747     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9748
9749   /* set default position for snapshot buttons to stop/pause/play buttons */
9750   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9751     if ((*game_buttons_xy[i].dst).x == -1 &&
9752         (*game_buttons_xy[i].dst).y == -1)
9753       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9754 }
9755
9756 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9757 {
9758   static struct
9759   {
9760     struct XYTileSize *dst, *src;
9761     int graphic;
9762   }
9763   editor_buttons_xy[] =
9764   {
9765     {
9766       &editor.button.element_left,      &editor.palette.element_left,
9767       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9768     },
9769     {
9770       &editor.button.element_middle,    &editor.palette.element_middle,
9771       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9772     },
9773     {
9774       &editor.button.element_right,     &editor.palette.element_right,
9775       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9776     },
9777
9778     { NULL,                     NULL                    }
9779   };
9780   int i;
9781
9782   /* set default position for element buttons to element graphics */
9783   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9784   {
9785     if ((*editor_buttons_xy[i].dst).x == -1 &&
9786         (*editor_buttons_xy[i].dst).y == -1)
9787     {
9788       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9789
9790       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9791
9792       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9793     }
9794   }
9795 }
9796
9797 static void LoadMenuDesignSettingsFromFilename(char *filename)
9798 {
9799   static struct TitleFadingInfo tfi;
9800   static struct TitleMessageInfo tmi;
9801   static struct TokenInfo title_tokens[] =
9802   {
9803     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
9804     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
9805     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
9806     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
9807
9808     { -1,               NULL,                   NULL                    }
9809   };
9810   static struct TokenInfo titlemessage_tokens[] =
9811   {
9812     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
9813     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
9814     { TYPE_INTEGER,     &tmi.width,             ".width"                },
9815     { TYPE_INTEGER,     &tmi.height,            ".height"               },
9816     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
9817     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
9818     { TYPE_INTEGER,     &tmi.align,             ".align"                },
9819     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
9820     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
9821     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
9822     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
9823     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
9824     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
9825     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
9826     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
9827     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
9828     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
9829
9830     { -1,               NULL,                   NULL                    }
9831   };
9832   static struct
9833   {
9834     struct TitleFadingInfo *info;
9835     char *text;
9836   }
9837   title_info[] =
9838   {
9839     /* initialize first titles from "enter screen" definitions, if defined */
9840     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
9841     { &title_first_default,             "menu.enter_screen.TITLE"       },
9842
9843     /* initialize title screens from "next screen" definitions, if defined */
9844     { &title_initial_default,           "menu.next_screen.TITLE"        },
9845     { &title_default,                   "menu.next_screen.TITLE"        },
9846
9847     { NULL,                             NULL                            }
9848   };
9849   static struct
9850   {
9851     struct TitleMessageInfo *array;
9852     char *text;
9853   }
9854   titlemessage_arrays[] =
9855   {
9856     /* initialize first titles from "enter screen" definitions, if defined */
9857     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
9858     { titlescreen_first,                "menu.enter_screen.TITLE"       },
9859     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
9860     { titlemessage_first,               "menu.enter_screen.TITLE"       },
9861
9862     /* initialize titles from "next screen" definitions, if defined */
9863     { titlescreen_initial,              "menu.next_screen.TITLE"        },
9864     { titlescreen,                      "menu.next_screen.TITLE"        },
9865     { titlemessage_initial,             "menu.next_screen.TITLE"        },
9866     { titlemessage,                     "menu.next_screen.TITLE"        },
9867
9868     /* overwrite titles with title definitions, if defined */
9869     { titlescreen_initial_first,        "[title_initial]"               },
9870     { titlescreen_first,                "[title]"                       },
9871     { titlemessage_initial_first,       "[title_initial]"               },
9872     { titlemessage_first,               "[title]"                       },
9873
9874     { titlescreen_initial,              "[title_initial]"               },
9875     { titlescreen,                      "[title]"                       },
9876     { titlemessage_initial,             "[title_initial]"               },
9877     { titlemessage,                     "[title]"                       },
9878
9879     /* overwrite titles with title screen/message definitions, if defined */
9880     { titlescreen_initial_first,        "[titlescreen_initial]"         },
9881     { titlescreen_first,                "[titlescreen]"                 },
9882     { titlemessage_initial_first,       "[titlemessage_initial]"        },
9883     { titlemessage_first,               "[titlemessage]"                },
9884
9885     { titlescreen_initial,              "[titlescreen_initial]"         },
9886     { titlescreen,                      "[titlescreen]"                 },
9887     { titlemessage_initial,             "[titlemessage_initial]"        },
9888     { titlemessage,                     "[titlemessage]"                },
9889
9890     { NULL,                             NULL                            }
9891   };
9892   SetupFileHash *setup_file_hash;
9893   int i, j, k;
9894
9895   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9896     return;
9897
9898   /* the following initializes hierarchical values from dynamic configuration */
9899
9900   /* special case: initialize with default values that may be overwritten */
9901   /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9902   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9903   {
9904     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9905     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9906     char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9907
9908     if (value_1 != NULL)
9909       menu.draw_xoffset[i] = get_integer_from_string(value_1);
9910     if (value_2 != NULL)
9911       menu.draw_yoffset[i] = get_integer_from_string(value_2);
9912     if (value_3 != NULL)
9913       menu.list_size[i] = get_integer_from_string(value_3);
9914   }
9915
9916   /* special case: initialize with default values that may be overwritten */
9917   /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9918   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9919   {
9920     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9921     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9922
9923     if (value_1 != NULL)
9924       menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9925     if (value_2 != NULL)
9926       menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9927
9928     if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9929     {
9930       char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9931
9932       if (value_1 != NULL)
9933         menu.list_size_info[i] = get_integer_from_string(value_1);
9934     }
9935   }
9936
9937   /* special case: initialize with default values that may be overwritten */
9938   /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9939   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9940   {
9941     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9942     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9943
9944     if (value_1 != NULL)
9945       menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9946     if (value_2 != NULL)
9947       menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9948   }
9949
9950   /* special case: initialize with default values that may be overwritten */
9951   /* (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO") */
9952   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9953   {
9954     char *value_1 = getHashEntry(setup_file_hash,"menu.left_spacing.INFO");
9955     char *value_2 = getHashEntry(setup_file_hash,"menu.right_spacing.INFO");
9956     char *value_3 = getHashEntry(setup_file_hash,"menu.top_spacing.INFO");
9957     char *value_4 = getHashEntry(setup_file_hash,"menu.bottom_spacing.INFO");
9958     char *value_5 = getHashEntry(setup_file_hash,"menu.paragraph_spacing.INFO");
9959     char *value_6 = getHashEntry(setup_file_hash,"menu.headline1_spacing.INFO");
9960     char *value_7 = getHashEntry(setup_file_hash,"menu.headline2_spacing.INFO");
9961     char *value_8 = getHashEntry(setup_file_hash,"menu.line_spacing.INFO");
9962     char *value_9 = getHashEntry(setup_file_hash,"menu.extra_spacing.INFO");
9963
9964     if (value_1 != NULL)
9965       menu.left_spacing_info[i]      = get_integer_from_string(value_1);
9966     if (value_2 != NULL)
9967       menu.right_spacing_info[i]     = get_integer_from_string(value_2);
9968     if (value_3 != NULL)
9969       menu.top_spacing_info[i]       = get_integer_from_string(value_3);
9970     if (value_4 != NULL)
9971       menu.bottom_spacing_info[i]    = get_integer_from_string(value_4);
9972     if (value_5 != NULL)
9973       menu.paragraph_spacing_info[i] = get_integer_from_string(value_5);
9974     if (value_6 != NULL)
9975       menu.headline1_spacing_info[i] = get_integer_from_string(value_6);
9976     if (value_7 != NULL)
9977       menu.headline2_spacing_info[i] = get_integer_from_string(value_7);
9978     if (value_8 != NULL)
9979       menu.line_spacing_info[i]      = get_integer_from_string(value_8);
9980     if (value_9 != NULL)
9981       menu.extra_spacing_info[i]     = get_integer_from_string(value_9);
9982   }
9983
9984   /* special case: initialize with default values that may be overwritten */
9985   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9986   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9987   {
9988     char *token_1 = "menu.enter_screen.fade_mode";
9989     char *token_2 = "menu.enter_screen.fade_delay";
9990     char *token_3 = "menu.enter_screen.post_delay";
9991     char *token_4 = "menu.leave_screen.fade_mode";
9992     char *token_5 = "menu.leave_screen.fade_delay";
9993     char *token_6 = "menu.leave_screen.post_delay";
9994     char *token_7 = "menu.next_screen.fade_mode";
9995     char *token_8 = "menu.next_screen.fade_delay";
9996     char *token_9 = "menu.next_screen.post_delay";
9997     char *value_1 = getHashEntry(setup_file_hash, token_1);
9998     char *value_2 = getHashEntry(setup_file_hash, token_2);
9999     char *value_3 = getHashEntry(setup_file_hash, token_3);
10000     char *value_4 = getHashEntry(setup_file_hash, token_4);
10001     char *value_5 = getHashEntry(setup_file_hash, token_5);
10002     char *value_6 = getHashEntry(setup_file_hash, token_6);
10003     char *value_7 = getHashEntry(setup_file_hash, token_7);
10004     char *value_8 = getHashEntry(setup_file_hash, token_8);
10005     char *value_9 = getHashEntry(setup_file_hash, token_9);
10006
10007     if (value_1 != NULL)
10008       menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
10009                                                                  value_1);
10010     if (value_2 != NULL)
10011       menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
10012                                                                   value_2);
10013     if (value_3 != NULL)
10014       menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
10015                                                                   value_3);
10016     if (value_4 != NULL)
10017       menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
10018                                                                  value_4);
10019     if (value_5 != NULL)
10020       menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
10021                                                                   value_5);
10022     if (value_6 != NULL)
10023       menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
10024                                                                   value_6);
10025     if (value_7 != NULL)
10026       menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
10027                                                                 value_7);
10028     if (value_8 != NULL)
10029       menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
10030                                                                  value_8);
10031     if (value_9 != NULL)
10032       menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
10033                                                                  value_9);
10034   }
10035
10036   /* special case: initialize with default values that may be overwritten */
10037   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
10038   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10039   {
10040     char *token_w1 = "viewport.window.width";
10041     char *token_w2 = "viewport.window.height";
10042     char *token_01 = "viewport.playfield.x";
10043     char *token_02 = "viewport.playfield.y";
10044     char *token_03 = "viewport.playfield.width";
10045     char *token_04 = "viewport.playfield.height";
10046     char *token_05 = "viewport.playfield.border_size";
10047     char *token_06 = "viewport.door_1.x";
10048     char *token_07 = "viewport.door_1.y";
10049     char *token_08 = "viewport.door_1.width";
10050     char *token_09 = "viewport.door_1.height";
10051     char *token_10 = "viewport.door_1.border_size";
10052     char *token_11 = "viewport.door_2.x";
10053     char *token_12 = "viewport.door_2.y";
10054     char *token_13 = "viewport.door_2.width";
10055     char *token_14 = "viewport.door_2.height";
10056     char *token_15 = "viewport.door_2.border_size";
10057     char *value_w1 = getHashEntry(setup_file_hash, token_w1);
10058     char *value_w2 = getHashEntry(setup_file_hash, token_w2);
10059     char *value_01 = getHashEntry(setup_file_hash, token_01);
10060     char *value_02 = getHashEntry(setup_file_hash, token_02);
10061     char *value_03 = getHashEntry(setup_file_hash, token_03);
10062     char *value_04 = getHashEntry(setup_file_hash, token_04);
10063     char *value_05 = getHashEntry(setup_file_hash, token_05);
10064     char *value_06 = getHashEntry(setup_file_hash, token_06);
10065     char *value_07 = getHashEntry(setup_file_hash, token_07);
10066     char *value_08 = getHashEntry(setup_file_hash, token_08);
10067     char *value_09 = getHashEntry(setup_file_hash, token_09);
10068     char *value_10 = getHashEntry(setup_file_hash, token_10);
10069     char *value_11 = getHashEntry(setup_file_hash, token_11);
10070     char *value_12 = getHashEntry(setup_file_hash, token_12);
10071     char *value_13 = getHashEntry(setup_file_hash, token_13);
10072     char *value_14 = getHashEntry(setup_file_hash, token_14);
10073     char *value_15 = getHashEntry(setup_file_hash, token_15);
10074
10075     if (value_w1 != NULL)
10076       viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
10077     if (value_w2 != NULL)
10078       viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
10079     if (value_01 != NULL)
10080       viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
10081     if (value_02 != NULL)
10082       viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
10083     if (value_03 != NULL)
10084       viewport.playfield[i].width = get_token_parameter_value(token_03,
10085                                                               value_03);
10086     if (value_04 != NULL)
10087       viewport.playfield[i].height = get_token_parameter_value(token_04,
10088                                                                value_04);
10089     if (value_05 != NULL)
10090       viewport.playfield[i].border_size = get_token_parameter_value(token_05,
10091                                                                     value_05);
10092     if (value_06 != NULL)
10093       viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
10094     if (value_07 != NULL)
10095       viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
10096     if (value_08 != NULL)
10097       viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
10098     if (value_09 != NULL)
10099       viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
10100     if (value_10 != NULL)
10101       viewport.door_1[i].border_size = get_token_parameter_value(token_10,
10102                                                                  value_10);
10103     if (value_11 != NULL)
10104       viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
10105     if (value_12 != NULL)
10106       viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
10107     if (value_13 != NULL)
10108       viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
10109     if (value_14 != NULL)
10110       viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
10111     if (value_15 != NULL)
10112       viewport.door_1[i].border_size = get_token_parameter_value(token_15,
10113                                                                  value_15);
10114   }
10115
10116   /* special case: initialize with default values that may be overwritten */
10117   /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
10118   for (i = 0; title_info[i].info != NULL; i++)
10119   {
10120     struct TitleFadingInfo *info = title_info[i].info;
10121     char *base_token = title_info[i].text;
10122
10123     for (j = 0; title_tokens[j].type != -1; j++)
10124     {
10125       char *token = getStringCat2(base_token, title_tokens[j].text);
10126       char *value = getHashEntry(setup_file_hash, token);
10127
10128       if (value != NULL)
10129       {
10130         int parameter_value = get_token_parameter_value(token, value);
10131
10132         tfi = *info;
10133
10134         *(int *)title_tokens[j].value = (int)parameter_value;
10135
10136         *info = tfi;
10137       }
10138
10139       free(token);
10140     }
10141   }
10142
10143   /* special case: initialize with default values that may be overwritten */
10144   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
10145   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
10146   {
10147     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
10148     char *base_token = titlemessage_arrays[i].text;
10149
10150     for (j = 0; titlemessage_tokens[j].type != -1; j++)
10151     {
10152       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
10153       char *value = getHashEntry(setup_file_hash, token);
10154
10155       if (value != NULL)
10156       {
10157         int parameter_value = get_token_parameter_value(token, value);
10158
10159         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
10160         {
10161           tmi = array[k];
10162
10163           if (titlemessage_tokens[j].type == TYPE_INTEGER)
10164             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
10165           else
10166             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
10167
10168           array[k] = tmi;
10169         }
10170       }
10171
10172       free(token);
10173     }
10174   }
10175
10176   /* read (and overwrite with) values that may be specified in config file */
10177   for (i = 0; image_config_vars[i].token != NULL; i++)
10178   {
10179     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
10180
10181     /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
10182     if (value != NULL && !strEqual(value, ARG_DEFAULT))
10183       *image_config_vars[i].value =
10184         get_token_parameter_value(image_config_vars[i].token, value);
10185   }
10186
10187   freeSetupFileHash(setup_file_hash);
10188 }
10189
10190 void LoadMenuDesignSettings()
10191 {
10192   char *filename_base = UNDEFINED_FILENAME, *filename_local;
10193
10194   InitMenuDesignSettings_Static();
10195   InitMenuDesignSettings_SpecialPreProcessing();
10196
10197   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
10198   {
10199     /* first look for special settings configured in level series config */
10200     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
10201
10202     if (fileExists(filename_base))
10203       LoadMenuDesignSettingsFromFilename(filename_base);
10204   }
10205
10206   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10207
10208   if (filename_local != NULL && !strEqual(filename_base, filename_local))
10209     LoadMenuDesignSettingsFromFilename(filename_local);
10210
10211   InitMenuDesignSettings_SpecialPostProcessing();
10212 }
10213
10214 void LoadMenuDesignSettings_AfterGraphics()
10215 {
10216   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
10217 }
10218
10219 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
10220 {
10221   char *filename = getEditorSetupFilename();
10222   SetupFileList *setup_file_list, *list;
10223   SetupFileHash *element_hash;
10224   int num_unknown_tokens = 0;
10225   int i;
10226
10227   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
10228     return;
10229
10230   element_hash = newSetupFileHash();
10231
10232   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10233     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10234
10235   /* determined size may be larger than needed (due to unknown elements) */
10236   *num_elements = 0;
10237   for (list = setup_file_list; list != NULL; list = list->next)
10238     (*num_elements)++;
10239
10240   /* add space for up to 3 more elements for padding that may be needed */
10241   *num_elements += 3;
10242
10243   /* free memory for old list of elements, if needed */
10244   checked_free(*elements);
10245
10246   /* allocate memory for new list of elements */
10247   *elements = checked_malloc(*num_elements * sizeof(int));
10248
10249   *num_elements = 0;
10250   for (list = setup_file_list; list != NULL; list = list->next)
10251   {
10252     char *value = getHashEntry(element_hash, list->token);
10253
10254     if (value == NULL)          /* try to find obsolete token mapping */
10255     {
10256       char *mapped_token = get_mapped_token(list->token);
10257
10258       if (mapped_token != NULL)
10259       {
10260         value = getHashEntry(element_hash, mapped_token);
10261
10262         free(mapped_token);
10263       }
10264     }
10265
10266     if (value != NULL)
10267     {
10268       (*elements)[(*num_elements)++] = atoi(value);
10269     }
10270     else
10271     {
10272       if (num_unknown_tokens == 0)
10273       {
10274         Error(ERR_INFO_LINE, "-");
10275         Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10276         Error(ERR_INFO, "- config file: '%s'", filename);
10277
10278         num_unknown_tokens++;
10279       }
10280
10281       Error(ERR_INFO, "- token: '%s'", list->token);
10282     }
10283   }
10284
10285   if (num_unknown_tokens > 0)
10286     Error(ERR_INFO_LINE, "-");
10287
10288   while (*num_elements % 4)     /* pad with empty elements, if needed */
10289     (*elements)[(*num_elements)++] = EL_EMPTY;
10290
10291   freeSetupFileList(setup_file_list);
10292   freeSetupFileHash(element_hash);
10293
10294 #if 0
10295   for (i = 0; i < *num_elements; i++)
10296     printf("editor: element '%s' [%d]\n",
10297            element_info[(*elements)[i]].token_name, (*elements)[i]);
10298 #endif
10299 }
10300
10301 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10302                                                      boolean is_sound)
10303 {
10304   SetupFileHash *setup_file_hash = NULL;
10305   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10306   char *filename_music, *filename_prefix, *filename_info;
10307   struct
10308   {
10309     char *token;
10310     char **value_ptr;
10311   }
10312   token_to_value_ptr[] =
10313   {
10314     { "title_header",   &tmp_music_file_info.title_header       },
10315     { "artist_header",  &tmp_music_file_info.artist_header      },
10316     { "album_header",   &tmp_music_file_info.album_header       },
10317     { "year_header",    &tmp_music_file_info.year_header        },
10318
10319     { "title",          &tmp_music_file_info.title              },
10320     { "artist",         &tmp_music_file_info.artist             },
10321     { "album",          &tmp_music_file_info.album              },
10322     { "year",           &tmp_music_file_info.year               },
10323
10324     { NULL,             NULL                                    },
10325   };
10326   int i;
10327
10328   filename_music = (is_sound ? getCustomSoundFilename(basename) :
10329                     getCustomMusicFilename(basename));
10330
10331   if (filename_music == NULL)
10332     return NULL;
10333
10334   /* ---------- try to replace file extension ---------- */
10335
10336   filename_prefix = getStringCopy(filename_music);
10337   if (strrchr(filename_prefix, '.') != NULL)
10338     *strrchr(filename_prefix, '.') = '\0';
10339   filename_info = getStringCat2(filename_prefix, ".txt");
10340
10341   if (fileExists(filename_info))
10342     setup_file_hash = loadSetupFileHash(filename_info);
10343
10344   free(filename_prefix);
10345   free(filename_info);
10346
10347   if (setup_file_hash == NULL)
10348   {
10349     /* ---------- try to add file extension ---------- */
10350
10351     filename_prefix = getStringCopy(filename_music);
10352     filename_info = getStringCat2(filename_prefix, ".txt");
10353
10354     if (fileExists(filename_info))
10355       setup_file_hash = loadSetupFileHash(filename_info);
10356
10357     free(filename_prefix);
10358     free(filename_info);
10359   }
10360
10361   if (setup_file_hash == NULL)
10362     return NULL;
10363
10364   /* ---------- music file info found ---------- */
10365
10366   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10367
10368   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10369   {
10370     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10371
10372     *token_to_value_ptr[i].value_ptr =
10373       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10374   }
10375
10376   tmp_music_file_info.basename = getStringCopy(basename);
10377   tmp_music_file_info.music = music;
10378   tmp_music_file_info.is_sound = is_sound;
10379
10380   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10381   *new_music_file_info = tmp_music_file_info;
10382
10383   return new_music_file_info;
10384 }
10385
10386 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10387 {
10388   return get_music_file_info_ext(basename, music, FALSE);
10389 }
10390
10391 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10392 {
10393   return get_music_file_info_ext(basename, sound, TRUE);
10394 }
10395
10396 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10397                                      char *basename, boolean is_sound)
10398 {
10399   for (; list != NULL; list = list->next)
10400     if (list->is_sound == is_sound && strEqual(list->basename, basename))
10401       return TRUE;
10402
10403   return FALSE;
10404 }
10405
10406 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10407 {
10408   return music_info_listed_ext(list, basename, FALSE);
10409 }
10410
10411 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10412 {
10413   return music_info_listed_ext(list, basename, TRUE);
10414 }
10415
10416 void LoadMusicInfo()
10417 {
10418   char *music_directory = getCustomMusicDirectory();
10419   int num_music = getMusicListSize();
10420   int num_music_noconf = 0;
10421   int num_sounds = getSoundListSize();
10422   Directory *dir;
10423   DirectoryEntry *dir_entry;
10424   struct FileInfo *music, *sound;
10425   struct MusicFileInfo *next, **new;
10426   int i;
10427
10428   while (music_file_info != NULL)
10429   {
10430     next = music_file_info->next;
10431
10432     checked_free(music_file_info->basename);
10433
10434     checked_free(music_file_info->title_header);
10435     checked_free(music_file_info->artist_header);
10436     checked_free(music_file_info->album_header);
10437     checked_free(music_file_info->year_header);
10438
10439     checked_free(music_file_info->title);
10440     checked_free(music_file_info->artist);
10441     checked_free(music_file_info->album);
10442     checked_free(music_file_info->year);
10443
10444     free(music_file_info);
10445
10446     music_file_info = next;
10447   }
10448
10449   new = &music_file_info;
10450
10451   for (i = 0; i < num_music; i++)
10452   {
10453     music = getMusicListEntry(i);
10454
10455     if (music->filename == NULL)
10456       continue;
10457
10458     if (strEqual(music->filename, UNDEFINED_FILENAME))
10459       continue;
10460
10461     /* a configured file may be not recognized as music */
10462     if (!FileIsMusic(music->filename))
10463       continue;
10464
10465     if (!music_info_listed(music_file_info, music->filename))
10466     {
10467       *new = get_music_file_info(music->filename, i);
10468
10469       if (*new != NULL)
10470         new = &(*new)->next;
10471     }
10472   }
10473
10474   if ((dir = openDirectory(music_directory)) == NULL)
10475   {
10476     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10477     return;
10478   }
10479
10480   while ((dir_entry = readDirectory(dir)) != NULL)      /* loop all entries */
10481   {
10482     char *basename = dir_entry->basename;
10483     boolean music_already_used = FALSE;
10484     int i;
10485
10486     /* skip all music files that are configured in music config file */
10487     for (i = 0; i < num_music; i++)
10488     {
10489       music = getMusicListEntry(i);
10490
10491       if (music->filename == NULL)
10492         continue;
10493
10494       if (strEqual(basename, music->filename))
10495       {
10496         music_already_used = TRUE;
10497         break;
10498       }
10499     }
10500
10501     if (music_already_used)
10502       continue;
10503
10504     if (!FileIsMusic(dir_entry->filename))
10505       continue;
10506
10507     if (!music_info_listed(music_file_info, basename))
10508     {
10509       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10510
10511       if (*new != NULL)
10512         new = &(*new)->next;
10513     }
10514
10515     num_music_noconf++;
10516   }
10517
10518   closeDirectory(dir);
10519
10520   for (i = 0; i < num_sounds; i++)
10521   {
10522     sound = getSoundListEntry(i);
10523
10524     if (sound->filename == NULL)
10525       continue;
10526
10527     if (strEqual(sound->filename, UNDEFINED_FILENAME))
10528       continue;
10529
10530     /* a configured file may be not recognized as sound */
10531     if (!FileIsSound(sound->filename))
10532       continue;
10533
10534     if (!sound_info_listed(music_file_info, sound->filename))
10535     {
10536       *new = get_sound_file_info(sound->filename, i);
10537       if (*new != NULL)
10538         new = &(*new)->next;
10539     }
10540   }
10541 }
10542
10543 void add_helpanim_entry(int element, int action, int direction, int delay,
10544                         int *num_list_entries)
10545 {
10546   struct HelpAnimInfo *new_list_entry;
10547   (*num_list_entries)++;
10548
10549   helpanim_info =
10550     checked_realloc(helpanim_info,
10551                     *num_list_entries * sizeof(struct HelpAnimInfo));
10552   new_list_entry = &helpanim_info[*num_list_entries - 1];
10553
10554   new_list_entry->element = element;
10555   new_list_entry->action = action;
10556   new_list_entry->direction = direction;
10557   new_list_entry->delay = delay;
10558 }
10559
10560 void print_unknown_token(char *filename, char *token, int token_nr)
10561 {
10562   if (token_nr == 0)
10563   {
10564     Error(ERR_INFO_LINE, "-");
10565     Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10566     Error(ERR_INFO, "- config file: '%s'", filename);
10567   }
10568
10569   Error(ERR_INFO, "- token: '%s'", token);
10570 }
10571
10572 void print_unknown_token_end(int token_nr)
10573 {
10574   if (token_nr > 0)
10575     Error(ERR_INFO_LINE, "-");
10576 }
10577
10578 void LoadHelpAnimInfo()
10579 {
10580   char *filename = getHelpAnimFilename();
10581   SetupFileList *setup_file_list = NULL, *list;
10582   SetupFileHash *element_hash, *action_hash, *direction_hash;
10583   int num_list_entries = 0;
10584   int num_unknown_tokens = 0;
10585   int i;
10586
10587   if (fileExists(filename))
10588     setup_file_list = loadSetupFileList(filename);
10589
10590   if (setup_file_list == NULL)
10591   {
10592     /* use reliable default values from static configuration */
10593     SetupFileList *insert_ptr;
10594
10595     insert_ptr = setup_file_list =
10596       newSetupFileList(helpanim_config[0].token,
10597                        helpanim_config[0].value);
10598
10599     for (i = 1; helpanim_config[i].token; i++)
10600       insert_ptr = addListEntry(insert_ptr,
10601                                 helpanim_config[i].token,
10602                                 helpanim_config[i].value);
10603   }
10604
10605   element_hash   = newSetupFileHash();
10606   action_hash    = newSetupFileHash();
10607   direction_hash = newSetupFileHash();
10608
10609   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10610     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10611
10612   for (i = 0; i < NUM_ACTIONS; i++)
10613     setHashEntry(action_hash, element_action_info[i].suffix,
10614                  i_to_a(element_action_info[i].value));
10615
10616   /* do not store direction index (bit) here, but direction value! */
10617   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10618     setHashEntry(direction_hash, element_direction_info[i].suffix,
10619                  i_to_a(1 << element_direction_info[i].value));
10620
10621   for (list = setup_file_list; list != NULL; list = list->next)
10622   {
10623     char *element_token, *action_token, *direction_token;
10624     char *element_value, *action_value, *direction_value;
10625     int delay = atoi(list->value);
10626
10627     if (strEqual(list->token, "end"))
10628     {
10629       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10630
10631       continue;
10632     }
10633
10634     /* first try to break element into element/action/direction parts;
10635        if this does not work, also accept combined "element[.act][.dir]"
10636        elements (like "dynamite.active"), which are unique elements */
10637
10638     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
10639     {
10640       element_value = getHashEntry(element_hash, list->token);
10641       if (element_value != NULL)        /* element found */
10642         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10643                            &num_list_entries);
10644       else
10645       {
10646         /* no further suffixes found -- this is not an element */
10647         print_unknown_token(filename, list->token, num_unknown_tokens++);
10648       }
10649
10650       continue;
10651     }
10652
10653     /* token has format "<prefix>.<something>" */
10654
10655     action_token = strchr(list->token, '.');    /* suffix may be action ... */
10656     direction_token = action_token;             /* ... or direction */
10657
10658     element_token = getStringCopy(list->token);
10659     *strchr(element_token, '.') = '\0';
10660
10661     element_value = getHashEntry(element_hash, element_token);
10662
10663     if (element_value == NULL)          /* this is no element */
10664     {
10665       element_value = getHashEntry(element_hash, list->token);
10666       if (element_value != NULL)        /* combined element found */
10667         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10668                            &num_list_entries);
10669       else
10670         print_unknown_token(filename, list->token, num_unknown_tokens++);
10671
10672       free(element_token);
10673
10674       continue;
10675     }
10676
10677     action_value = getHashEntry(action_hash, action_token);
10678
10679     if (action_value != NULL)           /* action found */
10680     {
10681       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10682                     &num_list_entries);
10683
10684       free(element_token);
10685
10686       continue;
10687     }
10688
10689     direction_value = getHashEntry(direction_hash, direction_token);
10690
10691     if (direction_value != NULL)        /* direction found */
10692     {
10693       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10694                          &num_list_entries);
10695
10696       free(element_token);
10697
10698       continue;
10699     }
10700
10701     if (strchr(action_token + 1, '.') == NULL)
10702     {
10703       /* no further suffixes found -- this is not an action nor direction */
10704
10705       element_value = getHashEntry(element_hash, list->token);
10706       if (element_value != NULL)        /* combined element found */
10707         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10708                            &num_list_entries);
10709       else
10710         print_unknown_token(filename, list->token, num_unknown_tokens++);
10711
10712       free(element_token);
10713
10714       continue;
10715     }
10716
10717     /* token has format "<prefix>.<suffix>.<something>" */
10718
10719     direction_token = strchr(action_token + 1, '.');
10720
10721     action_token = getStringCopy(action_token);
10722     *strchr(action_token + 1, '.') = '\0';
10723
10724     action_value = getHashEntry(action_hash, action_token);
10725
10726     if (action_value == NULL)           /* this is no action */
10727     {
10728       element_value = getHashEntry(element_hash, list->token);
10729       if (element_value != NULL)        /* combined element found */
10730         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10731                            &num_list_entries);
10732       else
10733         print_unknown_token(filename, list->token, num_unknown_tokens++);
10734
10735       free(element_token);
10736       free(action_token);
10737
10738       continue;
10739     }
10740
10741     direction_value = getHashEntry(direction_hash, direction_token);
10742
10743     if (direction_value != NULL)        /* direction found */
10744     {
10745       add_helpanim_entry(atoi(element_value), atoi(action_value),
10746                          atoi(direction_value), delay, &num_list_entries);
10747
10748       free(element_token);
10749       free(action_token);
10750
10751       continue;
10752     }
10753
10754     /* this is no direction */
10755
10756     element_value = getHashEntry(element_hash, list->token);
10757     if (element_value != NULL)          /* combined element found */
10758       add_helpanim_entry(atoi(element_value), -1, -1, delay,
10759                          &num_list_entries);
10760     else
10761       print_unknown_token(filename, list->token, num_unknown_tokens++);
10762
10763     free(element_token);
10764     free(action_token);
10765   }
10766
10767   print_unknown_token_end(num_unknown_tokens);
10768
10769   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10770   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
10771
10772   freeSetupFileList(setup_file_list);
10773   freeSetupFileHash(element_hash);
10774   freeSetupFileHash(action_hash);
10775   freeSetupFileHash(direction_hash);
10776
10777 #if 0
10778   for (i = 0; i < num_list_entries; i++)
10779     printf("::: '%s': %d, %d, %d => %d\n",
10780            EL_NAME(helpanim_info[i].element),
10781            helpanim_info[i].element,
10782            helpanim_info[i].action,
10783            helpanim_info[i].direction,
10784            helpanim_info[i].delay);
10785 #endif
10786 }
10787
10788 void LoadHelpTextInfo()
10789 {
10790   char *filename = getHelpTextFilename();
10791   int i;
10792
10793   if (helptext_info != NULL)
10794   {
10795     freeSetupFileHash(helptext_info);
10796     helptext_info = NULL;
10797   }
10798
10799   if (fileExists(filename))
10800     helptext_info = loadSetupFileHash(filename);
10801
10802   if (helptext_info == NULL)
10803   {
10804     /* use reliable default values from static configuration */
10805     helptext_info = newSetupFileHash();
10806
10807     for (i = 0; helptext_config[i].token; i++)
10808       setHashEntry(helptext_info,
10809                    helptext_config[i].token,
10810                    helptext_config[i].value);
10811   }
10812
10813 #if 0
10814   BEGIN_HASH_ITERATION(helptext_info, itr)
10815   {
10816     printf("::: '%s' => '%s'\n",
10817            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10818   }
10819   END_HASH_ITERATION(hash, itr)
10820 #endif
10821 }
10822
10823
10824 /* ------------------------------------------------------------------------- */
10825 /* convert levels                                                            */
10826 /* ------------------------------------------------------------------------- */
10827
10828 #define MAX_NUM_CONVERT_LEVELS          1000
10829
10830 void ConvertLevels()
10831 {
10832   static LevelDirTree *convert_leveldir = NULL;
10833   static int convert_level_nr = -1;
10834   static int num_levels_handled = 0;
10835   static int num_levels_converted = 0;
10836   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10837   int i;
10838
10839   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10840                                                global.convert_leveldir);
10841
10842   if (convert_leveldir == NULL)
10843     Error(ERR_EXIT, "no such level identifier: '%s'",
10844           global.convert_leveldir);
10845
10846   leveldir_current = convert_leveldir;
10847
10848   if (global.convert_level_nr != -1)
10849   {
10850     convert_leveldir->first_level = global.convert_level_nr;
10851     convert_leveldir->last_level  = global.convert_level_nr;
10852   }
10853
10854   convert_level_nr = convert_leveldir->first_level;
10855
10856   PrintLine("=", 79);
10857   Print("Converting levels\n");
10858   PrintLine("-", 79);
10859   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10860   Print("Level series name:       '%s'\n", convert_leveldir->name);
10861   Print("Level series author:     '%s'\n", convert_leveldir->author);
10862   Print("Number of levels:        %d\n",   convert_leveldir->levels);
10863   PrintLine("=", 79);
10864   Print("\n");
10865
10866   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10867     levels_failed[i] = FALSE;
10868
10869   while (convert_level_nr <= convert_leveldir->last_level)
10870   {
10871     char *level_filename;
10872     boolean new_level;
10873
10874     level_nr = convert_level_nr++;
10875
10876     Print("Level %03d: ", level_nr);
10877
10878     LoadLevel(level_nr);
10879     if (level.no_level_file || level.no_valid_file)
10880     {
10881       Print("(no level)\n");
10882       continue;
10883     }
10884
10885     Print("converting level ... ");
10886
10887     level_filename = getDefaultLevelFilename(level_nr);
10888     new_level = !fileExists(level_filename);
10889
10890     if (new_level)
10891     {
10892       SaveLevel(level_nr);
10893
10894       num_levels_converted++;
10895
10896       Print("converted.\n");
10897     }
10898     else
10899     {
10900       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10901         levels_failed[level_nr] = TRUE;
10902
10903       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10904     }
10905
10906     num_levels_handled++;
10907   }
10908
10909   Print("\n");
10910   PrintLine("=", 79);
10911   Print("Number of levels handled: %d\n", num_levels_handled);
10912   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10913          (num_levels_handled ?
10914           num_levels_converted * 100 / num_levels_handled : 0));
10915   PrintLine("-", 79);
10916   Print("Summary (for automatic parsing by scripts):\n");
10917   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10918          convert_leveldir->identifier, num_levels_converted,
10919          num_levels_handled,
10920          (num_levels_handled ?
10921           num_levels_converted * 100 / num_levels_handled : 0));
10922
10923   if (num_levels_handled != num_levels_converted)
10924   {
10925     Print(", FAILED:");
10926     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10927       if (levels_failed[i])
10928         Print(" %03d", i);
10929   }
10930
10931   Print("\n");
10932   PrintLine("=", 79);
10933
10934   CloseAllAndExit(0);
10935 }
10936
10937
10938 /* ------------------------------------------------------------------------- */
10939 /* create and save images for use in level sketches (raw BMP format)         */
10940 /* ------------------------------------------------------------------------- */
10941
10942 void CreateLevelSketchImages()
10943 {
10944 #if defined(TARGET_SDL)
10945   Bitmap *bitmap1;
10946   Bitmap *bitmap2;
10947   int i;
10948
10949   InitElementPropertiesGfxElement();
10950
10951   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10952   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10953
10954   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10955   {
10956     Bitmap *src_bitmap;
10957     int src_x, src_y;
10958     int element = getMappedElement(i);
10959     int graphic = el2edimg(element);
10960     char basename1[16];
10961     char basename2[16];
10962     char *filename1;
10963     char *filename2;
10964
10965     sprintf(basename1, "%03d.bmp", i);
10966     sprintf(basename2, "%03ds.bmp", i);
10967
10968     filename1 = getPath2(global.create_images_dir, basename1);
10969     filename2 = getPath2(global.create_images_dir, basename2);
10970
10971     getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10972     BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10973                0, 0);
10974
10975     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10976       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10977
10978     getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10979     BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10980
10981     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10982       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10983
10984     free(filename1);
10985     free(filename2);
10986
10987     if (options.debug)
10988       printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10989   }
10990
10991   FreeBitmap(bitmap1);
10992   FreeBitmap(bitmap2);
10993
10994   if (options.debug)
10995     printf("\n");
10996
10997   Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10998
10999   CloseAllAndExit(0);
11000 #endif
11001 }
11002
11003
11004 /* ------------------------------------------------------------------------- */
11005 /* create and save images for custom and group elements (raw BMP format)     */
11006 /* ------------------------------------------------------------------------- */
11007
11008 void CreateCustomElementImages(char *directory)
11009 {
11010 #if defined(TARGET_SDL)
11011   char *src_basename = "RocksCE-template.ilbm";
11012   char *dst_basename = "RocksCE.bmp";
11013   char *src_filename = getPath2(directory, src_basename);
11014   char *dst_filename = getPath2(directory, dst_basename);
11015   Bitmap *src_bitmap;
11016   Bitmap *bitmap;
11017   int yoffset_ce = 0;
11018   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
11019   int i;
11020
11021   SDLInitVideoDisplay();
11022
11023   ReCreateBitmap(&backbuffer, video.width, video.height);
11024
11025   src_bitmap = LoadImage(src_filename);
11026
11027   bitmap = CreateBitmap(TILEX * 16 * 2,
11028                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
11029                         DEFAULT_DEPTH);
11030
11031   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11032   {
11033     int x = i % 16;
11034     int y = i / 16;
11035     int ii = i + 1;
11036     int j;
11037
11038     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11039                TILEX * x, TILEY * y + yoffset_ce);
11040
11041     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11042                TILEX, TILEY,
11043                TILEX * x + TILEX * 16,
11044                TILEY * y + yoffset_ce);
11045
11046     for (j = 2; j >= 0; j--)
11047     {
11048       int c = ii % 10;
11049
11050       BlitBitmap(src_bitmap, bitmap,
11051                  TILEX + c * 7, 0, 6, 10,
11052                  TILEX * x + 6 + j * 7,
11053                  TILEY * y + 11 + yoffset_ce);
11054
11055       BlitBitmap(src_bitmap, bitmap,
11056                  TILEX + c * 8, TILEY, 6, 10,
11057                  TILEX * 16 + TILEX * x + 6 + j * 8,
11058                  TILEY * y + 10 + yoffset_ce);
11059
11060       ii /= 10;
11061     }
11062   }
11063
11064   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
11065   {
11066     int x = i % 16;
11067     int y = i / 16;
11068     int ii = i + 1;
11069     int j;
11070
11071     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11072                TILEX * x, TILEY * y + yoffset_ge);
11073
11074     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11075                TILEX, TILEY,
11076                TILEX * x + TILEX * 16,
11077                TILEY * y + yoffset_ge);
11078
11079     for (j = 1; j >= 0; j--)
11080     {
11081       int c = ii % 10;
11082
11083       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
11084                  TILEX * x + 6 + j * 10,
11085                  TILEY * y + 11 + yoffset_ge);
11086
11087       BlitBitmap(src_bitmap, bitmap,
11088                  TILEX + c * 8, TILEY + 12, 6, 10,
11089                  TILEX * 16 + TILEX * x + 10 + j * 8,
11090                  TILEY * y + 10 + yoffset_ge);
11091
11092       ii /= 10;
11093     }
11094   }
11095
11096   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
11097     Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
11098
11099   FreeBitmap(bitmap);
11100
11101   CloseAllAndExit(0);
11102 #endif
11103 }