added option to draw pressed virtual buttons highlighted
[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 #define SETUP_TOKEN_PLAYER_NAME                 0
8265 #define SETUP_TOKEN_SOUND                       1
8266 #define SETUP_TOKEN_SOUND_LOOPS                 2
8267 #define SETUP_TOKEN_SOUND_MUSIC                 3
8268 #define SETUP_TOKEN_SOUND_SIMPLE                4
8269 #define SETUP_TOKEN_TOONS                       5
8270 #define SETUP_TOKEN_SCROLL_DELAY                6
8271 #define SETUP_TOKEN_SCROLL_DELAY_VALUE          7
8272 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE        8
8273 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY      9
8274 #define SETUP_TOKEN_FADE_SCREENS                10
8275 #define SETUP_TOKEN_AUTORECORD                  11
8276 #define SETUP_TOKEN_SHOW_TITLESCREEN            12
8277 #define SETUP_TOKEN_QUICK_DOORS                 13
8278 #define SETUP_TOKEN_TEAM_MODE                   14
8279 #define SETUP_TOKEN_HANDICAP                    15
8280 #define SETUP_TOKEN_SKIP_LEVELS                 16
8281 #define SETUP_TOKEN_INCREMENT_LEVELS            17
8282 #define SETUP_TOKEN_TIME_LIMIT                  18
8283 #define SETUP_TOKEN_FULLSCREEN                  19
8284 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT      20
8285 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY      21
8286 #define SETUP_TOKEN_SCREEN_RENDERING_MODE       22
8287 #define SETUP_TOKEN_ASK_ON_ESCAPE               23
8288 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR        24
8289 #define SETUP_TOKEN_QUICK_SWITCH                25
8290 #define SETUP_TOKEN_INPUT_ON_FOCUS              26
8291 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS         27
8292 #define SETUP_TOKEN_GAME_FRAME_DELAY            28
8293 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS     29
8294 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS         30
8295 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS       31
8296 #define SETUP_TOKEN_GRAPHICS_SET                32
8297 #define SETUP_TOKEN_SOUNDS_SET                  33
8298 #define SETUP_TOKEN_MUSIC_SET                   34
8299 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     35
8300 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       36
8301 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        37
8302 #define SETUP_TOKEN_VOLUME_SIMPLE               38
8303 #define SETUP_TOKEN_VOLUME_LOOPS                39
8304 #define SETUP_TOKEN_VOLUME_MUSIC                40
8305 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE          41
8306 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE         42
8307 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE         43
8308 #define SETUP_TOKEN_TOUCH_TRANSPARENCY          44
8309 #define SETUP_TOKEN_TOUCH_DRAW_OUTLINED         45
8310 #define SETUP_TOKEN_TOUCH_DRAW_PRESSED          46
8311 #define SETUP_TOKEN_TOUCH_GRID_XSIZE_0          47
8312 #define SETUP_TOKEN_TOUCH_GRID_YSIZE_0          48
8313 #define SETUP_TOKEN_TOUCH_GRID_XSIZE_1          49
8314 #define SETUP_TOKEN_TOUCH_GRID_YSIZE_1          50
8315
8316 #define NUM_GLOBAL_SETUP_TOKENS                 51
8317
8318 /* auto setup */
8319 #define SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE   0
8320
8321 #define NUM_AUTO_SETUP_TOKENS                   1
8322
8323 /* editor setup */
8324 #define SETUP_TOKEN_EDITOR_EL_CLASSIC           0
8325 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            1
8326 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      2
8327 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC           3
8328 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         4
8329 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN   5
8330
8331 #define NUM_EDITOR_SETUP_TOKENS                 6
8332
8333 /* editor cascade setup */
8334 #define SETUP_TOKEN_EDITOR_CASCADE_BD           0
8335 #define SETUP_TOKEN_EDITOR_CASCADE_EM           1
8336 #define SETUP_TOKEN_EDITOR_CASCADE_EMC          2
8337 #define SETUP_TOKEN_EDITOR_CASCADE_RND          3
8338 #define SETUP_TOKEN_EDITOR_CASCADE_SB           4
8339 #define SETUP_TOKEN_EDITOR_CASCADE_SP           5
8340 #define SETUP_TOKEN_EDITOR_CASCADE_DC           6
8341 #define SETUP_TOKEN_EDITOR_CASCADE_DX           7
8342 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT         8
8343 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT    9
8344 #define SETUP_TOKEN_EDITOR_CASCADE_CE           10
8345 #define SETUP_TOKEN_EDITOR_CASCADE_GE           11
8346 #define SETUP_TOKEN_EDITOR_CASCADE_REF          12
8347 #define SETUP_TOKEN_EDITOR_CASCADE_USER         13
8348 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC      14
8349
8350 #define NUM_EDITOR_CASCADE_SETUP_TOKENS         15
8351
8352 /* shortcut setup */
8353 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
8354 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
8355 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
8356 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1     3
8357 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2     4
8358 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3     5
8359 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4     6
8360 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL   7
8361 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT         8
8362 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA         9
8363 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP          10
8364 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE         11
8365 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD        12
8366 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY          13
8367 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE       14
8368 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS        15
8369 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC        16
8370 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT          17
8371 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT         18
8372 #define SETUP_TOKEN_SHORTCUT_SNAP_UP            19
8373 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN          20
8374
8375 #define NUM_SHORTCUT_SETUP_TOKENS               21
8376
8377 /* player setup */
8378 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
8379 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
8380 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
8381 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
8382 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
8383 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
8384 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
8385 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
8386 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
8387 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
8388 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
8389 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
8390 #define SETUP_TOKEN_PLAYER_KEY_UP               12
8391 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
8392 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
8393 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
8394
8395 #define NUM_PLAYER_SETUP_TOKENS                 16
8396
8397 /* system setup */
8398 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER      0
8399 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      1
8400 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  2
8401
8402 #define NUM_SYSTEM_SETUP_TOKENS                 3
8403
8404 /* internal setup */
8405 #define SETUP_TOKEN_INT_PROGRAM_TITLE           0
8406 #define SETUP_TOKEN_INT_PROGRAM_VERSION         1
8407 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR          2
8408 #define SETUP_TOKEN_INT_PROGRAM_EMAIL           3
8409 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE         4
8410 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT       5
8411 #define SETUP_TOKEN_INT_PROGRAM_COMPANY         6
8412 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE       7
8413 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET    8
8414 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET      9
8415 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET       10
8416 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE  11
8417 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE    12
8418 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE     13
8419 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES    14
8420 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
8421 #define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE   16
8422 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH    17
8423 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT   18
8424
8425 #define NUM_INTERNAL_SETUP_TOKENS               19
8426
8427 /* debug setup */
8428 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0         0
8429 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1         1
8430 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2         2
8431 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3         3
8432 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4         4
8433 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5         5
8434 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6         6
8435 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7         7
8436 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8         8
8437 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9         9
8438 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0     10
8439 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1     11
8440 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2     12
8441 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3     13
8442 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4     14
8443 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5     15
8444 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6     16
8445 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7     17
8446 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8     18
8447 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9     19
8448 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8449 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8450 #define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
8451
8452 #define NUM_DEBUG_SETUP_TOKENS                  23
8453
8454 /* options setup */
8455 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
8456
8457 #define NUM_OPTIONS_SETUP_TOKENS                1
8458
8459
8460 static struct SetupInfo si;
8461 static struct SetupAutoSetupInfo sasi;
8462 static struct SetupEditorInfo sei;
8463 static struct SetupEditorCascadeInfo seci;
8464 static struct SetupShortcutInfo ssi;
8465 static struct SetupInputInfo sii;
8466 static struct SetupSystemInfo syi;
8467 static struct SetupInternalInfo sxi;
8468 static struct SetupDebugInfo sdi;
8469 static struct OptionInfo soi;
8470
8471 static struct TokenInfo global_setup_tokens[] =
8472 {
8473   { TYPE_STRING, &si.player_name,             "player_name"             },
8474   { TYPE_SWITCH, &si.sound,                   "sound"                   },
8475   { TYPE_SWITCH, &si.sound_loops,             "repeating_sound_loops"   },
8476   { TYPE_SWITCH, &si.sound_music,             "background_music"        },
8477   { TYPE_SWITCH, &si.sound_simple,            "simple_sound_effects"    },
8478   { TYPE_SWITCH, &si.toons,                   "toons"                   },
8479   { TYPE_SWITCH, &si.scroll_delay,            "scroll_delay"            },
8480   { TYPE_INTEGER,&si.scroll_delay_value,      "scroll_delay_value"      },
8481   { TYPE_STRING, &si.engine_snapshot_mode,    "engine_snapshot_mode"    },
8482   { TYPE_INTEGER,&si.engine_snapshot_memory,  "engine_snapshot_memory"  },
8483   { TYPE_SWITCH, &si.fade_screens,            "fade_screens"            },
8484   { TYPE_SWITCH, &si.autorecord,              "automatic_tape_recording"},
8485   { TYPE_SWITCH, &si.show_titlescreen,        "show_titlescreen"        },
8486   { TYPE_SWITCH, &si.quick_doors,             "quick_doors"             },
8487   { TYPE_SWITCH, &si.team_mode,               "team_mode"               },
8488   { TYPE_SWITCH, &si.handicap,                "handicap"                },
8489   { TYPE_SWITCH, &si.skip_levels,             "skip_levels"             },
8490   { TYPE_SWITCH, &si.increment_levels,        "increment_levels"        },
8491   { TYPE_SWITCH, &si.time_limit,              "time_limit"              },
8492   { TYPE_SWITCH, &si.fullscreen,              "fullscreen"              },
8493   { TYPE_INTEGER,&si.window_scaling_percent,  "window_scaling_percent"  },
8494   { TYPE_STRING, &si.window_scaling_quality,  "window_scaling_quality"  },
8495   { TYPE_STRING, &si.screen_rendering_mode,   "screen_rendering_mode"   },
8496   { TYPE_SWITCH, &si.ask_on_escape,           "ask_on_escape"           },
8497   { TYPE_SWITCH, &si.ask_on_escape_editor,    "ask_on_escape_editor"    },
8498   { TYPE_SWITCH, &si.quick_switch,            "quick_player_switch"     },
8499   { TYPE_SWITCH, &si.input_on_focus,          "input_on_focus"          },
8500   { TYPE_SWITCH, &si.prefer_aga_graphics,     "prefer_aga_graphics"     },
8501   { TYPE_INTEGER,&si.game_frame_delay,        "game_frame_delay"        },
8502   { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8503   { TYPE_SWITCH, &si.small_game_graphics,     "small_game_graphics"     },
8504   { TYPE_SWITCH, &si.show_snapshot_buttons,   "show_snapshot_buttons"   },
8505   { TYPE_STRING, &si.graphics_set,            "graphics_set"            },
8506   { TYPE_STRING, &si.sounds_set,              "sounds_set"              },
8507   { TYPE_STRING, &si.music_set,               "music_set"               },
8508   { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8509   { TYPE_SWITCH3,&si.override_level_sounds,   "override_level_sounds"   },
8510   { TYPE_SWITCH3,&si.override_level_music,    "override_level_music"    },
8511   { TYPE_INTEGER,&si.volume_simple,           "volume_simple"           },
8512   { TYPE_INTEGER,&si.volume_loops,            "volume_loops"            },
8513   { TYPE_INTEGER,&si.volume_music,            "volume_music"            },
8514   { TYPE_STRING, &si.touch.control_type,      "touch.control_type"      },
8515   { TYPE_INTEGER,&si.touch.move_distance,     "touch.move_distance"     },
8516   { TYPE_INTEGER,&si.touch.drop_distance,     "touch.drop_distance"     },
8517   { TYPE_INTEGER,&si.touch.transparency,      "touch.transparency"      },
8518   { TYPE_INTEGER,&si.touch.draw_outlined,     "touch.draw_outlined"     },
8519   { TYPE_INTEGER,&si.touch.draw_pressed,      "touch.draw_pressed"      },
8520   { TYPE_INTEGER,&si.touch.grid_xsize[0],     "touch.virtual_buttons.0.xsize" },
8521   { TYPE_INTEGER,&si.touch.grid_ysize[0],     "touch.virtual_buttons.0.ysize" },
8522   { TYPE_INTEGER,&si.touch.grid_xsize[1],     "touch.virtual_buttons.1.xsize" },
8523   { TYPE_INTEGER,&si.touch.grid_ysize[1],     "touch.virtual_buttons.1.ysize" },
8524 };
8525
8526 static struct TokenInfo auto_setup_tokens[] =
8527 {
8528   { TYPE_INTEGER,&sasi.editor_zoom_tilesize,    "editor.zoom_tilesize"  },
8529 };
8530
8531 static struct TokenInfo editor_setup_tokens[] =
8532 {
8533   { TYPE_SWITCH, &sei.el_classic,       "editor.el_classic"             },
8534   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
8535   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
8536   { TYPE_SWITCH, &sei.el_dynamic,       "editor.el_dynamic"             },
8537   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
8538   { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token"    },
8539 };
8540
8541 static struct TokenInfo editor_cascade_setup_tokens[] =
8542 {
8543   { TYPE_SWITCH, &seci.el_bd,           "editor.cascade.el_bd"          },
8544   { TYPE_SWITCH, &seci.el_em,           "editor.cascade.el_em"          },
8545   { TYPE_SWITCH, &seci.el_emc,          "editor.cascade.el_emc"         },
8546   { TYPE_SWITCH, &seci.el_rnd,          "editor.cascade.el_rnd"         },
8547   { TYPE_SWITCH, &seci.el_sb,           "editor.cascade.el_sb"          },
8548   { TYPE_SWITCH, &seci.el_sp,           "editor.cascade.el_sp"          },
8549   { TYPE_SWITCH, &seci.el_dc,           "editor.cascade.el_dc"          },
8550   { TYPE_SWITCH, &seci.el_dx,           "editor.cascade.el_dx"          },
8551   { TYPE_SWITCH, &seci.el_mm,           "editor.cascade.el_mm"          },
8552   { TYPE_SWITCH, &seci.el_df,           "editor.cascade.el_df"          },
8553   { TYPE_SWITCH, &seci.el_chars,        "editor.cascade.el_chars"       },
8554   { TYPE_SWITCH, &seci.el_steel_chars,  "editor.cascade.el_steel_chars" },
8555   { TYPE_SWITCH, &seci.el_ce,           "editor.cascade.el_ce"          },
8556   { TYPE_SWITCH, &seci.el_ge,           "editor.cascade.el_ge"          },
8557   { TYPE_SWITCH, &seci.el_ref,          "editor.cascade.el_ref"         },
8558   { TYPE_SWITCH, &seci.el_user,         "editor.cascade.el_user"        },
8559   { TYPE_SWITCH, &seci.el_dynamic,      "editor.cascade.el_dynamic"     },
8560 };
8561
8562 static struct TokenInfo shortcut_setup_tokens[] =
8563 {
8564   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
8565   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
8566   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         },
8567   { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1"       },
8568   { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2"       },
8569   { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3"       },
8570   { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4"       },
8571   { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all"     },
8572   { TYPE_KEY_X11, &ssi.tape_eject,      "shortcut.tape_eject"           },
8573   { TYPE_KEY_X11, &ssi.tape_extra,      "shortcut.tape_extra"           },
8574   { TYPE_KEY_X11, &ssi.tape_stop,       "shortcut.tape_stop"            },
8575   { TYPE_KEY_X11, &ssi.tape_pause,      "shortcut.tape_pause"           },
8576   { TYPE_KEY_X11, &ssi.tape_record,     "shortcut.tape_record"          },
8577   { TYPE_KEY_X11, &ssi.tape_play,       "shortcut.tape_play"            },
8578   { TYPE_KEY_X11, &ssi.sound_simple,    "shortcut.sound_simple"         },
8579   { TYPE_KEY_X11, &ssi.sound_loops,     "shortcut.sound_loops"          },
8580   { TYPE_KEY_X11, &ssi.sound_music,     "shortcut.sound_music"          },
8581   { TYPE_KEY_X11, &ssi.snap_left,       "shortcut.snap_left"            },
8582   { TYPE_KEY_X11, &ssi.snap_right,      "shortcut.snap_right"           },
8583   { TYPE_KEY_X11, &ssi.snap_up,         "shortcut.snap_up"              },
8584   { TYPE_KEY_X11, &ssi.snap_down,       "shortcut.snap_down"            },
8585 };
8586
8587 static struct TokenInfo player_setup_tokens[] =
8588 {
8589   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
8590   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
8591   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
8592   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
8593   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
8594   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
8595   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
8596   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
8597   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
8598   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
8599   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
8600   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
8601   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
8602   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
8603   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
8604   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               },
8605 };
8606
8607 static struct TokenInfo system_setup_tokens[] =
8608 {
8609   { TYPE_STRING,  &syi.sdl_videodriver,    "system.sdl_videodriver"     },
8610   { TYPE_STRING,  &syi.sdl_audiodriver,    "system.sdl_audiodriver"     },
8611   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8612 };
8613
8614 static struct TokenInfo internal_setup_tokens[] =
8615 {
8616   { TYPE_STRING, &sxi.program_title,            "program_title"         },
8617   { TYPE_STRING, &sxi.program_version,          "program_version"       },
8618   { TYPE_STRING, &sxi.program_author,           "program_author"        },
8619   { TYPE_STRING, &sxi.program_email,            "program_email"         },
8620   { TYPE_STRING, &sxi.program_website,          "program_website"       },
8621   { TYPE_STRING, &sxi.program_copyright,        "program_copyright"     },
8622   { TYPE_STRING, &sxi.program_company,          "program_company"       },
8623   { TYPE_STRING, &sxi.program_icon_file,        "program_icon_file"     },
8624   { TYPE_STRING, &sxi.default_graphics_set,     "default_graphics_set"  },
8625   { TYPE_STRING, &sxi.default_sounds_set,       "default_sounds_set"    },
8626   { TYPE_STRING, &sxi.default_music_set,        "default_music_set"     },
8627   { TYPE_STRING, &sxi.fallback_graphics_file,   "fallback_graphics_file"},
8628   { TYPE_STRING, &sxi.fallback_sounds_file,     "fallback_sounds_file"  },
8629   { TYPE_STRING, &sxi.fallback_music_file,      "fallback_music_file"   },
8630   { TYPE_STRING, &sxi.default_level_series,     "default_level_series"  },
8631   { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8632   { TYPE_BOOLEAN,&sxi.show_scaling_in_title,    "show_scaling_in_title" },
8633   { TYPE_INTEGER,&sxi.default_window_width,     "default_window_width"  },
8634   { TYPE_INTEGER,&sxi.default_window_height,    "default_window_height" },
8635 };
8636
8637 static struct TokenInfo debug_setup_tokens[] =
8638 {
8639   { TYPE_INTEGER, &sdi.frame_delay[0],          "debug.frame_delay_0"   },
8640   { TYPE_INTEGER, &sdi.frame_delay[1],          "debug.frame_delay_1"   },
8641   { TYPE_INTEGER, &sdi.frame_delay[2],          "debug.frame_delay_2"   },
8642   { TYPE_INTEGER, &sdi.frame_delay[3],          "debug.frame_delay_3"   },
8643   { TYPE_INTEGER, &sdi.frame_delay[4],          "debug.frame_delay_4"   },
8644   { TYPE_INTEGER, &sdi.frame_delay[5],          "debug.frame_delay_5"   },
8645   { TYPE_INTEGER, &sdi.frame_delay[6],          "debug.frame_delay_6"   },
8646   { TYPE_INTEGER, &sdi.frame_delay[7],          "debug.frame_delay_7"   },
8647   { TYPE_INTEGER, &sdi.frame_delay[8],          "debug.frame_delay_8"   },
8648   { TYPE_INTEGER, &sdi.frame_delay[9],          "debug.frame_delay_9"   },
8649   { TYPE_KEY_X11, &sdi.frame_delay_key[0],      "debug.key.frame_delay_0" },
8650   { TYPE_KEY_X11, &sdi.frame_delay_key[1],      "debug.key.frame_delay_1" },
8651   { TYPE_KEY_X11, &sdi.frame_delay_key[2],      "debug.key.frame_delay_2" },
8652   { TYPE_KEY_X11, &sdi.frame_delay_key[3],      "debug.key.frame_delay_3" },
8653   { TYPE_KEY_X11, &sdi.frame_delay_key[4],      "debug.key.frame_delay_4" },
8654   { TYPE_KEY_X11, &sdi.frame_delay_key[5],      "debug.key.frame_delay_5" },
8655   { TYPE_KEY_X11, &sdi.frame_delay_key[6],      "debug.key.frame_delay_6" },
8656   { TYPE_KEY_X11, &sdi.frame_delay_key[7],      "debug.key.frame_delay_7" },
8657   { TYPE_KEY_X11, &sdi.frame_delay_key[8],      "debug.key.frame_delay_8" },
8658   { TYPE_KEY_X11, &sdi.frame_delay_key[9],      "debug.key.frame_delay_9" },
8659   { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8660   { TYPE_BOOLEAN, &sdi.frame_delay_game_only,  "debug.frame_delay.game_only" },
8661   { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8662 };
8663
8664 static struct TokenInfo options_setup_tokens[] =
8665 {
8666   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               },
8667 };
8668
8669 static char *get_corrected_login_name(char *login_name)
8670 {
8671   /* needed because player name must be a fixed length string */
8672   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8673
8674   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8675   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8676
8677   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
8678     if (strchr(login_name_new, ' '))
8679       *strchr(login_name_new, ' ') = '\0';
8680
8681   return login_name_new;
8682 }
8683
8684 static void setSetupInfoToDefaults(struct SetupInfo *si)
8685 {
8686   int i;
8687
8688   si->player_name = get_corrected_login_name(getLoginName());
8689
8690   si->sound = TRUE;
8691   si->sound_loops = TRUE;
8692   si->sound_music = TRUE;
8693   si->sound_simple = TRUE;
8694   si->toons = TRUE;
8695   si->scroll_delay = TRUE;
8696   si->scroll_delay_value = STD_SCROLL_DELAY;
8697   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8698   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8699   si->fade_screens = TRUE;
8700   si->autorecord = TRUE;
8701   si->show_titlescreen = TRUE;
8702   si->quick_doors = FALSE;
8703   si->team_mode = FALSE;
8704   si->handicap = TRUE;
8705   si->skip_levels = TRUE;
8706   si->increment_levels = TRUE;
8707   si->time_limit = TRUE;
8708   si->fullscreen = FALSE;
8709   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8710   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8711   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8712   si->ask_on_escape = TRUE;
8713   si->ask_on_escape_editor = TRUE;
8714   si->quick_switch = FALSE;
8715   si->input_on_focus = FALSE;
8716   si->prefer_aga_graphics = TRUE;
8717   si->game_frame_delay = GAME_FRAME_DELAY;
8718   si->sp_show_border_elements = FALSE;
8719   si->small_game_graphics = FALSE;
8720   si->show_snapshot_buttons = FALSE;
8721
8722   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8723   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8724   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8725
8726   si->override_level_graphics = FALSE;
8727   si->override_level_sounds = FALSE;
8728   si->override_level_music = FALSE;
8729
8730   si->volume_simple = 100;              /* percent */
8731   si->volume_loops = 100;               /* percent */
8732   si->volume_music = 100;               /* percent */
8733
8734   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8735   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        /* percent */
8736   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        /* percent */
8737   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          /* percent */
8738   si->touch.draw_outlined = TRUE;
8739   si->touch.draw_pressed = TRUE;
8740
8741   for (i = 0; i < 2; i++)
8742   {
8743     char *default_grid_button[6][2] =
8744     {
8745       { "      ", "  ^^  " },
8746       { "      ", "  ^^  " },
8747       { "      ", "<<  >>" },
8748       { "      ", "<<  >>" },
8749       { "111222", "  vv  " },
8750       { "111222", "  vv  " }
8751     };
8752     int grid_xsize = DEFAULT_GRID_XSIZE(i);
8753     int grid_ysize = DEFAULT_GRID_YSIZE(i);
8754     int min_xsize = MIN(6, grid_xsize);
8755     int min_ysize = MIN(6, grid_ysize);
8756     int startx = grid_xsize - min_xsize;
8757     int starty = grid_ysize - min_ysize;
8758     int x, y;
8759
8760     // virtual buttons grid can only be set to defaults if video is initialized
8761     // (this will be repeated if virtual buttons are not loaded from setup file)
8762     if (video.initialized)
8763     {
8764       si->touch.grid_xsize[i] = grid_xsize;
8765       si->touch.grid_ysize[i] = grid_ysize;
8766     }
8767     else
8768     {
8769       si->touch.grid_xsize[i] = -1;
8770       si->touch.grid_ysize[i] = -1;
8771     }
8772
8773     for (x = 0; x < MAX_GRID_XSIZE; x++)
8774       for (y = 0; y < MAX_GRID_YSIZE; y++)
8775         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
8776
8777     for (x = 0; x < min_xsize; x++)
8778       for (y = 0; y < min_ysize; y++)
8779         si->touch.grid_button[i][x][starty + y] =
8780           default_grid_button[y][0][x];
8781
8782     for (x = 0; x < min_xsize; x++)
8783       for (y = 0; y < min_ysize; y++)
8784         si->touch.grid_button[i][startx + x][starty + y] =
8785           default_grid_button[y][1][x];
8786   }
8787
8788   si->touch.grid_initialized            = video.initialized;
8789
8790   si->editor.el_boulderdash             = TRUE;
8791   si->editor.el_emerald_mine            = TRUE;
8792   si->editor.el_emerald_mine_club       = TRUE;
8793   si->editor.el_more                    = TRUE;
8794   si->editor.el_sokoban                 = TRUE;
8795   si->editor.el_supaplex                = TRUE;
8796   si->editor.el_diamond_caves           = TRUE;
8797   si->editor.el_dx_boulderdash          = TRUE;
8798
8799   si->editor.el_mirror_magic            = TRUE;
8800   si->editor.el_deflektor               = TRUE;
8801
8802   si->editor.el_chars                   = TRUE;
8803   si->editor.el_steel_chars             = TRUE;
8804
8805   si->editor.el_classic                 = TRUE;
8806   si->editor.el_custom                  = TRUE;
8807
8808   si->editor.el_user_defined            = FALSE;
8809   si->editor.el_dynamic                 = TRUE;
8810
8811   si->editor.el_headlines               = TRUE;
8812
8813   si->editor.show_element_token         = FALSE;
8814
8815   si->editor.use_template_for_new_levels = TRUE;
8816
8817   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
8818   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
8819   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
8820
8821   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
8822   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
8823   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
8824   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
8825   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8826
8827   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
8828   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
8829   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
8830   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
8831   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
8832   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
8833
8834   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
8835   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
8836   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
8837
8838   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
8839   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
8840   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
8841   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
8842
8843   for (i = 0; i < MAX_PLAYERS; i++)
8844   {
8845     si->input[i].use_joystick = FALSE;
8846     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8847     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
8848     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8849     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
8850     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
8851     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8852     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
8853     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
8854     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
8855     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
8856     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8857     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
8858     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
8859     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
8860     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
8861   }
8862
8863   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8864   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8865   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8866
8867   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
8868   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
8869   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
8870   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
8871   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
8872   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8873   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
8874
8875   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8876
8877   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8878   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8879   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8880
8881   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8882   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
8883   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
8884
8885   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8886   si->internal.choose_from_top_leveldir = FALSE;
8887   si->internal.show_scaling_in_title = TRUE;
8888
8889   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
8890   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8891
8892   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8893   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8894   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8895   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8896   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8897   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8898   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8899   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8900   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8901   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8902
8903   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8904   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8905   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8906   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8907   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8908   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8909   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8910   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8911   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8912   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8913
8914   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8915   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
8916
8917   si->debug.show_frames_per_second = FALSE;
8918
8919   si->options.verbose = FALSE;
8920
8921 #if defined(PLATFORM_ANDROID)
8922   si->fullscreen = TRUE;
8923 #endif
8924 }
8925
8926 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
8927 {
8928   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
8929 }
8930
8931 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8932 {
8933   si->editor_cascade.el_bd              = TRUE;
8934   si->editor_cascade.el_em              = TRUE;
8935   si->editor_cascade.el_emc             = TRUE;
8936   si->editor_cascade.el_rnd             = TRUE;
8937   si->editor_cascade.el_sb              = TRUE;
8938   si->editor_cascade.el_sp              = TRUE;
8939   si->editor_cascade.el_dc              = TRUE;
8940   si->editor_cascade.el_dx              = TRUE;
8941
8942   si->editor_cascade.el_mm              = TRUE;
8943   si->editor_cascade.el_df              = TRUE;
8944
8945   si->editor_cascade.el_chars           = FALSE;
8946   si->editor_cascade.el_steel_chars     = FALSE;
8947   si->editor_cascade.el_ce              = FALSE;
8948   si->editor_cascade.el_ge              = FALSE;
8949   si->editor_cascade.el_ref             = FALSE;
8950   si->editor_cascade.el_user            = FALSE;
8951   si->editor_cascade.el_dynamic         = FALSE;
8952 }
8953
8954 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
8955
8956 static char *getHideSetupToken(void *setup_value)
8957 {
8958   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
8959
8960   if (setup_value != NULL)
8961     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
8962
8963   return hide_setup_token;
8964 }
8965
8966 void setHideSetupEntry(void *setup_value)
8967 {
8968   char *hide_setup_token = getHideSetupToken(setup_value);
8969
8970   if (setup_value != NULL)
8971     setHashEntry(hide_setup_hash, hide_setup_token, "");
8972 }
8973
8974 static void setHideSetupEntryRaw(char *token_text, void *setup_value_raw)
8975 {
8976   /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
8977   void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
8978
8979   setHideSetupEntry(setup_value);
8980 }
8981
8982 boolean hideSetupEntry(void *setup_value)
8983 {
8984   char *hide_setup_token = getHideSetupToken(setup_value);
8985
8986   return (setup_value != NULL &&
8987           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
8988 }
8989
8990 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
8991                                       struct TokenInfo *token_info,
8992                                       int token_nr, char *token_text)
8993 {
8994   char *token_hide_text = getStringCat2(token_text, ".hide");
8995   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
8996
8997   /* set the value of this setup option in the setup option structure */
8998   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
8999
9000   /* check if this setup option should be hidden in the setup menu */
9001   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9002     setHideSetupEntryRaw(token_text, token_info[token_nr].value);
9003 }
9004
9005 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9006                                       struct TokenInfo *token_info,
9007                                       int token_nr)
9008 {
9009   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9010                             token_info[token_nr].text);
9011 }
9012
9013 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9014 {
9015   int i, pnr;
9016
9017   if (!setup_file_hash)
9018     return;
9019
9020   if (hide_setup_hash == NULL)
9021     hide_setup_hash = newSetupFileHash();
9022
9023   /* global setup */
9024   si = setup;
9025   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9026     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9027   setup = si;
9028
9029   /* virtual buttons setup */
9030   setup.touch.grid_initialized = TRUE;
9031   for (i = 0; i < 2; i++)
9032   {
9033     int grid_xsize = setup.touch.grid_xsize[i];
9034     int grid_ysize = setup.touch.grid_ysize[i];
9035     int x, y;
9036
9037     // if virtual buttons are not loaded from setup file, repeat initializing
9038     // virtual buttons grid with default values later when video is initialized
9039     if (grid_xsize == -1 ||
9040         grid_ysize == -1)
9041     {
9042       setup.touch.grid_initialized = FALSE;
9043
9044       continue;
9045     }
9046
9047     for (y = 0; y < grid_ysize; y++)
9048     {
9049       char token_string[MAX_LINE_LEN];
9050
9051       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9052
9053       char *value_string = getHashEntry(setup_file_hash, token_string);
9054
9055       if (value_string == NULL)
9056         continue;
9057
9058       for (x = 0; x < grid_xsize; x++)
9059       {
9060         char c = value_string[x];
9061
9062         setup.touch.grid_button[i][x][y] =
9063           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9064       }
9065     }
9066   }
9067
9068   /* editor setup */
9069   sei = setup.editor;
9070   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9071     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9072   setup.editor = sei;
9073
9074   /* shortcut setup */
9075   ssi = setup.shortcut;
9076   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9077     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9078   setup.shortcut = ssi;
9079
9080   /* player setup */
9081   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9082   {
9083     char prefix[30];
9084
9085     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9086
9087     sii = setup.input[pnr];
9088     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9089     {
9090       char full_token[100];
9091
9092       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9093       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9094                                 full_token);
9095     }
9096     setup.input[pnr] = sii;
9097   }
9098
9099   /* system setup */
9100   syi = setup.system;
9101   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9102     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9103   setup.system = syi;
9104
9105   /* internal setup */
9106   sxi = setup.internal;
9107   for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
9108     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9109   setup.internal = sxi;
9110
9111   /* debug setup */
9112   sdi = setup.debug;
9113   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9114     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9115   setup.debug = sdi;
9116
9117   /* options setup */
9118   soi = setup.options;
9119   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9120     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9121   setup.options = soi;
9122
9123   setHideRelatedSetupEntries();
9124 }
9125
9126 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9127 {
9128   int i;
9129
9130   if (!setup_file_hash)
9131     return;
9132
9133   /* auto setup */
9134   sasi = setup.auto_setup;
9135   for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9136     setSetupInfo(auto_setup_tokens, i,
9137                  getHashEntry(setup_file_hash,
9138                               auto_setup_tokens[i].text));
9139   setup.auto_setup = sasi;
9140 }
9141
9142 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9143 {
9144   int i;
9145
9146   if (!setup_file_hash)
9147     return;
9148
9149   /* editor cascade setup */
9150   seci = setup.editor_cascade;
9151   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9152     setSetupInfo(editor_cascade_setup_tokens, i,
9153                  getHashEntry(setup_file_hash,
9154                               editor_cascade_setup_tokens[i].text));
9155   setup.editor_cascade = seci;
9156 }
9157
9158 void LoadSetupFromFilename(char *filename)
9159 {
9160   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9161
9162   if (setup_file_hash)
9163   {
9164     decodeSetupFileHash(setup_file_hash);
9165
9166     freeSetupFileHash(setup_file_hash);
9167   }
9168   else
9169   {
9170     Error(ERR_DEBUG, "using default setup values");
9171   }
9172 }
9173
9174 static void LoadSetup_SpecialPostProcessing()
9175 {
9176   char *player_name_new;
9177
9178   /* needed to work around problems with fixed length strings */
9179   player_name_new = get_corrected_login_name(setup.player_name);
9180   free(setup.player_name);
9181   setup.player_name = player_name_new;
9182
9183   /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9184   if (setup.scroll_delay == FALSE)
9185   {
9186     setup.scroll_delay_value = MIN_SCROLL_DELAY;
9187     setup.scroll_delay = TRUE;                  /* now always "on" */
9188   }
9189
9190   /* make sure that scroll delay value stays inside valid range */
9191   setup.scroll_delay_value =
9192     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9193 }
9194
9195 void LoadSetup()
9196 {
9197   char *filename;
9198
9199   /* always start with reliable default values */
9200   setSetupInfoToDefaults(&setup);
9201
9202   /* try to load setup values from default setup file */
9203   filename = getDefaultSetupFilename();
9204
9205   if (fileExists(filename))
9206     LoadSetupFromFilename(filename);
9207
9208   /* try to load setup values from user setup file */
9209   filename = getSetupFilename();
9210
9211   LoadSetupFromFilename(filename);
9212
9213   LoadSetup_SpecialPostProcessing();
9214 }
9215
9216 void LoadSetup_AutoSetup()
9217 {
9218   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9219   SetupFileHash *setup_file_hash = NULL;
9220
9221   /* always start with reliable default values */
9222   setSetupInfoToDefaults_AutoSetup(&setup);
9223
9224   setup_file_hash = loadSetupFileHash(filename);
9225
9226   if (setup_file_hash)
9227   {
9228     decodeSetupFileHash_AutoSetup(setup_file_hash);
9229
9230     freeSetupFileHash(setup_file_hash);
9231   }
9232
9233   free(filename);
9234 }
9235
9236 void LoadSetup_EditorCascade()
9237 {
9238   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9239   SetupFileHash *setup_file_hash = NULL;
9240
9241   /* always start with reliable default values */
9242   setSetupInfoToDefaults_EditorCascade(&setup);
9243
9244   setup_file_hash = loadSetupFileHash(filename);
9245
9246   if (setup_file_hash)
9247   {
9248     decodeSetupFileHash_EditorCascade(setup_file_hash);
9249
9250     freeSetupFileHash(setup_file_hash);
9251   }
9252
9253   free(filename);
9254 }
9255
9256 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9257                                            char *mapping_line)
9258 {
9259   char mapping_guid[MAX_LINE_LEN];
9260   char *mapping_start, *mapping_end;
9261
9262   // get GUID from game controller mapping line: copy complete line
9263   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9264   mapping_guid[MAX_LINE_LEN - 1] = '\0';
9265
9266   // get GUID from game controller mapping line: cut after GUID part
9267   mapping_start = strchr(mapping_guid, ',');
9268   if (mapping_start != NULL)
9269     *mapping_start = '\0';
9270
9271   // cut newline from game controller mapping line
9272   mapping_end = strchr(mapping_line, '\n');
9273   if (mapping_end != NULL)
9274     *mapping_end = '\0';
9275
9276   // add mapping entry to game controller mappings hash
9277   setHashEntry(mappings_hash, mapping_guid, mapping_line);
9278 }
9279
9280 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9281                                                  char *filename)
9282 {
9283   FILE *file;
9284
9285   if (!(file = fopen(filename, MODE_READ)))
9286   {
9287     Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9288
9289     return;
9290   }
9291
9292   while (!feof(file))
9293   {
9294     char line[MAX_LINE_LEN];
9295
9296     if (!fgets(line, MAX_LINE_LEN, file))
9297       break;
9298
9299     addGameControllerMappingToHash(mappings_hash, line);
9300   }
9301
9302   fclose(file);
9303 }
9304
9305 void SaveSetup()
9306 {
9307   char *filename = getSetupFilename();
9308   FILE *file;
9309   int i, pnr;
9310
9311   InitUserDataDirectory();
9312
9313   if (!(file = fopen(filename, MODE_WRITE)))
9314   {
9315     Error(ERR_WARN, "cannot write setup file '%s'", filename);
9316     return;
9317   }
9318
9319   fprintFileHeader(file, SETUP_FILENAME);
9320
9321   /* global setup */
9322   si = setup;
9323   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9324   {
9325     /* just to make things nicer :) */
9326     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9327         i == SETUP_TOKEN_GRAPHICS_SET ||
9328         i == SETUP_TOKEN_VOLUME_SIMPLE ||
9329         i == SETUP_TOKEN_TOUCH_CONTROL_TYPE ||
9330         i == SETUP_TOKEN_TOUCH_GRID_XSIZE_0 ||
9331         i == SETUP_TOKEN_TOUCH_GRID_XSIZE_1)
9332       fprintf(file, "\n");
9333
9334     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9335   }
9336
9337   /* virtual buttons setup */
9338   for (i = 0; i < 2; i++)
9339   {
9340     int grid_xsize = setup.touch.grid_xsize[i];
9341     int grid_ysize = setup.touch.grid_ysize[i];
9342     int x, y;
9343
9344     fprintf(file, "\n");
9345
9346     for (y = 0; y < grid_ysize; y++)
9347     {
9348       char token_string[MAX_LINE_LEN];
9349       char value_string[MAX_LINE_LEN];
9350
9351       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9352
9353       for (x = 0; x < grid_xsize; x++)
9354       {
9355         char c = setup.touch.grid_button[i][x][y];
9356
9357         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9358       }
9359
9360       value_string[grid_xsize] = '\0';
9361
9362       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9363     }
9364   }
9365
9366   /* editor setup */
9367   sei = setup.editor;
9368   fprintf(file, "\n");
9369   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9370     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9371
9372   /* shortcut setup */
9373   ssi = setup.shortcut;
9374   fprintf(file, "\n");
9375   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9376     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9377
9378   /* player setup */
9379   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9380   {
9381     char prefix[30];
9382
9383     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9384     fprintf(file, "\n");
9385
9386     sii = setup.input[pnr];
9387     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9388       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9389   }
9390
9391   /* system setup */
9392   syi = setup.system;
9393   fprintf(file, "\n");
9394   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9395     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9396
9397   /* internal setup */
9398   /* (internal setup values not saved to user setup file) */
9399
9400   /* debug setup */
9401   sdi = setup.debug;
9402   fprintf(file, "\n");
9403   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9404     fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9405
9406   /* options setup */
9407   soi = setup.options;
9408   fprintf(file, "\n");
9409   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9410     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9411
9412   fclose(file);
9413
9414   SetFilePermissions(filename, PERMS_PRIVATE);
9415 }
9416
9417 void SaveSetup_AutoSetup()
9418 {
9419   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9420   FILE *file;
9421   int i;
9422
9423   InitUserDataDirectory();
9424
9425   if (!(file = fopen(filename, MODE_WRITE)))
9426   {
9427     Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9428     free(filename);
9429     return;
9430   }
9431
9432   fprintFileHeader(file, AUTOSETUP_FILENAME);
9433
9434   sasi = setup.auto_setup;
9435   for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9436     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9437
9438   fclose(file);
9439
9440   SetFilePermissions(filename, PERMS_PRIVATE);
9441
9442   free(filename);
9443 }
9444
9445 void SaveSetup_EditorCascade()
9446 {
9447   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9448   FILE *file;
9449   int i;
9450
9451   InitUserDataDirectory();
9452
9453   if (!(file = fopen(filename, MODE_WRITE)))
9454   {
9455     Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9456     free(filename);
9457     return;
9458   }
9459
9460   fprintFileHeader(file, EDITORCASCADE_FILENAME);
9461
9462   seci = setup.editor_cascade;
9463   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9464     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9465
9466   fclose(file);
9467
9468   SetFilePermissions(filename, PERMS_PRIVATE);
9469
9470   free(filename);
9471 }
9472
9473 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9474                                                   char *filename)
9475 {
9476   FILE *file;
9477
9478   if (!(file = fopen(filename, MODE_WRITE)))
9479   {
9480     Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9481
9482     return;
9483   }
9484
9485   BEGIN_HASH_ITERATION(mappings_hash, itr)
9486   {
9487     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9488   }
9489   END_HASH_ITERATION(mappings_hash, itr)
9490
9491   fclose(file);
9492 }
9493
9494 void SaveSetup_AddGameControllerMapping(char *mapping)
9495 {
9496   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9497   SetupFileHash *mappings_hash = newSetupFileHash();
9498
9499   InitUserDataDirectory();
9500
9501   // load existing personal game controller mappings
9502   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9503
9504   // add new mapping to personal game controller mappings
9505   addGameControllerMappingToHash(mappings_hash, mapping);
9506
9507   // save updated personal game controller mappings
9508   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9509
9510   freeSetupFileHash(mappings_hash);
9511   free(filename);
9512 }
9513
9514 void LoadCustomElementDescriptions()
9515 {
9516   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9517   SetupFileHash *setup_file_hash;
9518   int i;
9519
9520   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9521   {
9522     if (element_info[i].custom_description != NULL)
9523     {
9524       free(element_info[i].custom_description);
9525       element_info[i].custom_description = NULL;
9526     }
9527   }
9528
9529   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9530     return;
9531
9532   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9533   {
9534     char *token = getStringCat2(element_info[i].token_name, ".name");
9535     char *value = getHashEntry(setup_file_hash, token);
9536
9537     if (value != NULL)
9538       element_info[i].custom_description = getStringCopy(value);
9539
9540     free(token);
9541   }
9542
9543   freeSetupFileHash(setup_file_hash);
9544 }
9545
9546 static int getElementFromToken(char *token)
9547 {
9548   char *value = getHashEntry(element_token_hash, token);
9549
9550   if (value != NULL)
9551     return atoi(value);
9552
9553   Error(ERR_WARN, "unknown element token '%s'", token);
9554
9555   return EL_UNDEFINED;
9556 }
9557
9558 static int get_token_parameter_value(char *token, char *value_raw)
9559 {
9560   char *suffix;
9561
9562   if (token == NULL || value_raw == NULL)
9563     return ARG_UNDEFINED_VALUE;
9564
9565   suffix = strrchr(token, '.');
9566   if (suffix == NULL)
9567     suffix = token;
9568
9569   if (strEqual(suffix, ".element"))
9570     return getElementFromToken(value_raw);
9571
9572   /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9573   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9574 }
9575
9576 void InitMenuDesignSettings_Static()
9577 {
9578   int i;
9579
9580   /* always start with reliable default values from static default config */
9581   for (i = 0; image_config_vars[i].token != NULL; i++)
9582   {
9583     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9584
9585     if (value != NULL)
9586       *image_config_vars[i].value =
9587         get_token_parameter_value(image_config_vars[i].token, value);
9588   }
9589 }
9590
9591 static void InitMenuDesignSettings_SpecialPreProcessing()
9592 {
9593   int i;
9594
9595   /* the following initializes hierarchical values from static configuration */
9596
9597   /* special case: initialize "ARG_DEFAULT" values in static default config */
9598   /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9599   titlescreen_initial_first_default.fade_mode  =
9600     title_initial_first_default.fade_mode;
9601   titlescreen_initial_first_default.fade_delay =
9602     title_initial_first_default.fade_delay;
9603   titlescreen_initial_first_default.post_delay =
9604     title_initial_first_default.post_delay;
9605   titlescreen_initial_first_default.auto_delay =
9606     title_initial_first_default.auto_delay;
9607   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
9608   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9609   titlescreen_first_default.post_delay = title_first_default.post_delay;
9610   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9611   titlemessage_initial_first_default.fade_mode  =
9612     title_initial_first_default.fade_mode;
9613   titlemessage_initial_first_default.fade_delay =
9614     title_initial_first_default.fade_delay;
9615   titlemessage_initial_first_default.post_delay =
9616     title_initial_first_default.post_delay;
9617   titlemessage_initial_first_default.auto_delay =
9618     title_initial_first_default.auto_delay;
9619   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
9620   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9621   titlemessage_first_default.post_delay = title_first_default.post_delay;
9622   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9623
9624   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
9625   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9626   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9627   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9628   titlescreen_default.fade_mode  = title_default.fade_mode;
9629   titlescreen_default.fade_delay = title_default.fade_delay;
9630   titlescreen_default.post_delay = title_default.post_delay;
9631   titlescreen_default.auto_delay = title_default.auto_delay;
9632   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
9633   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9634   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9635   titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9636   titlemessage_default.fade_mode  = title_default.fade_mode;
9637   titlemessage_default.fade_delay = title_default.fade_delay;
9638   titlemessage_default.post_delay = title_default.post_delay;
9639   titlemessage_default.auto_delay = title_default.auto_delay;
9640
9641   /* special case: initialize "ARG_DEFAULT" values in static default config */
9642   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9643   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9644   {
9645     titlescreen_initial_first[i] = titlescreen_initial_first_default;
9646     titlescreen_first[i] = titlescreen_first_default;
9647     titlemessage_initial_first[i] = titlemessage_initial_first_default;
9648     titlemessage_first[i] = titlemessage_first_default;
9649
9650     titlescreen_initial[i] = titlescreen_initial_default;
9651     titlescreen[i] = titlescreen_default;
9652     titlemessage_initial[i] = titlemessage_initial_default;
9653     titlemessage[i] = titlemessage_default;
9654   }
9655
9656   /* special case: initialize "ARG_DEFAULT" values in static default config */
9657   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9658   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9659   {
9660     if (i == GFX_SPECIAL_ARG_TITLE)     /* title values already initialized */
9661       continue;
9662
9663     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9664     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9665     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9666   }
9667
9668   /* special case: initialize "ARG_DEFAULT" values in static default config */
9669   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9670   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9671   {
9672     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9673     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9674     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9675
9676     if (i == GFX_SPECIAL_ARG_EDITOR)    /* editor values already initialized */
9677       continue;
9678
9679     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9680   }
9681 }
9682
9683 static void InitMenuDesignSettings_SpecialPostProcessing()
9684 {
9685   static struct
9686   {
9687     struct XY *dst, *src;
9688   }
9689   game_buttons_xy[] =
9690   {
9691     { &game.button.save,        &game.button.stop       },
9692     { &game.button.pause2,      &game.button.pause      },
9693     { &game.button.load,        &game.button.play       },
9694     { &game.button.undo,        &game.button.stop       },
9695     { &game.button.redo,        &game.button.play       },
9696
9697     { NULL,                     NULL                    }
9698   };
9699   int i;
9700
9701   /* special case: initialize later added SETUP list size from LEVELS value */
9702   if (menu.list_size[GAME_MODE_SETUP] == -1)
9703     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9704
9705   /* set default position for snapshot buttons to stop/pause/play buttons */
9706   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9707     if ((*game_buttons_xy[i].dst).x == -1 &&
9708         (*game_buttons_xy[i].dst).y == -1)
9709       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9710 }
9711
9712 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9713 {
9714   static struct
9715   {
9716     struct XYTileSize *dst, *src;
9717     int graphic;
9718   }
9719   editor_buttons_xy[] =
9720   {
9721     {
9722       &editor.button.element_left,      &editor.palette.element_left,
9723       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9724     },
9725     {
9726       &editor.button.element_middle,    &editor.palette.element_middle,
9727       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9728     },
9729     {
9730       &editor.button.element_right,     &editor.palette.element_right,
9731       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9732     },
9733
9734     { NULL,                     NULL                    }
9735   };
9736   int i;
9737
9738   /* set default position for element buttons to element graphics */
9739   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9740   {
9741     if ((*editor_buttons_xy[i].dst).x == -1 &&
9742         (*editor_buttons_xy[i].dst).y == -1)
9743     {
9744       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9745
9746       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9747
9748       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9749     }
9750   }
9751 }
9752
9753 static void LoadMenuDesignSettingsFromFilename(char *filename)
9754 {
9755   static struct TitleFadingInfo tfi;
9756   static struct TitleMessageInfo tmi;
9757   static struct TokenInfo title_tokens[] =
9758   {
9759     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
9760     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
9761     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
9762     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
9763
9764     { -1,               NULL,                   NULL                    }
9765   };
9766   static struct TokenInfo titlemessage_tokens[] =
9767   {
9768     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
9769     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
9770     { TYPE_INTEGER,     &tmi.width,             ".width"                },
9771     { TYPE_INTEGER,     &tmi.height,            ".height"               },
9772     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
9773     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
9774     { TYPE_INTEGER,     &tmi.align,             ".align"                },
9775     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
9776     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
9777     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
9778     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
9779     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
9780     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
9781     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
9782     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
9783     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
9784     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
9785
9786     { -1,               NULL,                   NULL                    }
9787   };
9788   static struct
9789   {
9790     struct TitleFadingInfo *info;
9791     char *text;
9792   }
9793   title_info[] =
9794   {
9795     /* initialize first titles from "enter screen" definitions, if defined */
9796     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
9797     { &title_first_default,             "menu.enter_screen.TITLE"       },
9798
9799     /* initialize title screens from "next screen" definitions, if defined */
9800     { &title_initial_default,           "menu.next_screen.TITLE"        },
9801     { &title_default,                   "menu.next_screen.TITLE"        },
9802
9803     { NULL,                             NULL                            }
9804   };
9805   static struct
9806   {
9807     struct TitleMessageInfo *array;
9808     char *text;
9809   }
9810   titlemessage_arrays[] =
9811   {
9812     /* initialize first titles from "enter screen" definitions, if defined */
9813     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
9814     { titlescreen_first,                "menu.enter_screen.TITLE"       },
9815     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
9816     { titlemessage_first,               "menu.enter_screen.TITLE"       },
9817
9818     /* initialize titles from "next screen" definitions, if defined */
9819     { titlescreen_initial,              "menu.next_screen.TITLE"        },
9820     { titlescreen,                      "menu.next_screen.TITLE"        },
9821     { titlemessage_initial,             "menu.next_screen.TITLE"        },
9822     { titlemessage,                     "menu.next_screen.TITLE"        },
9823
9824     /* overwrite titles with title definitions, if defined */
9825     { titlescreen_initial_first,        "[title_initial]"               },
9826     { titlescreen_first,                "[title]"                       },
9827     { titlemessage_initial_first,       "[title_initial]"               },
9828     { titlemessage_first,               "[title]"                       },
9829
9830     { titlescreen_initial,              "[title_initial]"               },
9831     { titlescreen,                      "[title]"                       },
9832     { titlemessage_initial,             "[title_initial]"               },
9833     { titlemessage,                     "[title]"                       },
9834
9835     /* overwrite titles with title screen/message definitions, if defined */
9836     { titlescreen_initial_first,        "[titlescreen_initial]"         },
9837     { titlescreen_first,                "[titlescreen]"                 },
9838     { titlemessage_initial_first,       "[titlemessage_initial]"        },
9839     { titlemessage_first,               "[titlemessage]"                },
9840
9841     { titlescreen_initial,              "[titlescreen_initial]"         },
9842     { titlescreen,                      "[titlescreen]"                 },
9843     { titlemessage_initial,             "[titlemessage_initial]"        },
9844     { titlemessage,                     "[titlemessage]"                },
9845
9846     { NULL,                             NULL                            }
9847   };
9848   SetupFileHash *setup_file_hash;
9849   int i, j, k;
9850
9851   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9852     return;
9853
9854   /* the following initializes hierarchical values from dynamic configuration */
9855
9856   /* special case: initialize with default values that may be overwritten */
9857   /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9858   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9859   {
9860     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9861     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9862     char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9863
9864     if (value_1 != NULL)
9865       menu.draw_xoffset[i] = get_integer_from_string(value_1);
9866     if (value_2 != NULL)
9867       menu.draw_yoffset[i] = get_integer_from_string(value_2);
9868     if (value_3 != NULL)
9869       menu.list_size[i] = get_integer_from_string(value_3);
9870   }
9871
9872   /* special case: initialize with default values that may be overwritten */
9873   /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9874   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9875   {
9876     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9877     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9878
9879     if (value_1 != NULL)
9880       menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9881     if (value_2 != NULL)
9882       menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9883
9884     if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9885     {
9886       char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9887
9888       if (value_1 != NULL)
9889         menu.list_size_info[i] = get_integer_from_string(value_1);
9890     }
9891   }
9892
9893   /* special case: initialize with default values that may be overwritten */
9894   /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9895   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9896   {
9897     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9898     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9899
9900     if (value_1 != NULL)
9901       menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9902     if (value_2 != NULL)
9903       menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9904   }
9905
9906   /* special case: initialize with default values that may be overwritten */
9907   /* (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO") */
9908   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9909   {
9910     char *value_1 = getHashEntry(setup_file_hash,"menu.left_spacing.INFO");
9911     char *value_2 = getHashEntry(setup_file_hash,"menu.right_spacing.INFO");
9912     char *value_3 = getHashEntry(setup_file_hash,"menu.top_spacing.INFO");
9913     char *value_4 = getHashEntry(setup_file_hash,"menu.bottom_spacing.INFO");
9914     char *value_5 = getHashEntry(setup_file_hash,"menu.paragraph_spacing.INFO");
9915     char *value_6 = getHashEntry(setup_file_hash,"menu.headline1_spacing.INFO");
9916     char *value_7 = getHashEntry(setup_file_hash,"menu.headline2_spacing.INFO");
9917     char *value_8 = getHashEntry(setup_file_hash,"menu.line_spacing.INFO");
9918     char *value_9 = getHashEntry(setup_file_hash,"menu.extra_spacing.INFO");
9919
9920     if (value_1 != NULL)
9921       menu.left_spacing_info[i]      = get_integer_from_string(value_1);
9922     if (value_2 != NULL)
9923       menu.right_spacing_info[i]     = get_integer_from_string(value_2);
9924     if (value_3 != NULL)
9925       menu.top_spacing_info[i]       = get_integer_from_string(value_3);
9926     if (value_4 != NULL)
9927       menu.bottom_spacing_info[i]    = get_integer_from_string(value_4);
9928     if (value_5 != NULL)
9929       menu.paragraph_spacing_info[i] = get_integer_from_string(value_5);
9930     if (value_6 != NULL)
9931       menu.headline1_spacing_info[i] = get_integer_from_string(value_6);
9932     if (value_7 != NULL)
9933       menu.headline2_spacing_info[i] = get_integer_from_string(value_7);
9934     if (value_8 != NULL)
9935       menu.line_spacing_info[i]      = get_integer_from_string(value_8);
9936     if (value_9 != NULL)
9937       menu.extra_spacing_info[i]     = get_integer_from_string(value_9);
9938   }
9939
9940   /* special case: initialize with default values that may be overwritten */
9941   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9942   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9943   {
9944     char *token_1 = "menu.enter_screen.fade_mode";
9945     char *token_2 = "menu.enter_screen.fade_delay";
9946     char *token_3 = "menu.enter_screen.post_delay";
9947     char *token_4 = "menu.leave_screen.fade_mode";
9948     char *token_5 = "menu.leave_screen.fade_delay";
9949     char *token_6 = "menu.leave_screen.post_delay";
9950     char *token_7 = "menu.next_screen.fade_mode";
9951     char *token_8 = "menu.next_screen.fade_delay";
9952     char *token_9 = "menu.next_screen.post_delay";
9953     char *value_1 = getHashEntry(setup_file_hash, token_1);
9954     char *value_2 = getHashEntry(setup_file_hash, token_2);
9955     char *value_3 = getHashEntry(setup_file_hash, token_3);
9956     char *value_4 = getHashEntry(setup_file_hash, token_4);
9957     char *value_5 = getHashEntry(setup_file_hash, token_5);
9958     char *value_6 = getHashEntry(setup_file_hash, token_6);
9959     char *value_7 = getHashEntry(setup_file_hash, token_7);
9960     char *value_8 = getHashEntry(setup_file_hash, token_8);
9961     char *value_9 = getHashEntry(setup_file_hash, token_9);
9962
9963     if (value_1 != NULL)
9964       menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9965                                                                  value_1);
9966     if (value_2 != NULL)
9967       menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9968                                                                   value_2);
9969     if (value_3 != NULL)
9970       menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9971                                                                   value_3);
9972     if (value_4 != NULL)
9973       menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9974                                                                  value_4);
9975     if (value_5 != NULL)
9976       menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9977                                                                   value_5);
9978     if (value_6 != NULL)
9979       menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9980                                                                   value_6);
9981     if (value_7 != NULL)
9982       menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9983                                                                 value_7);
9984     if (value_8 != NULL)
9985       menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9986                                                                  value_8);
9987     if (value_9 != NULL)
9988       menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9989                                                                  value_9);
9990   }
9991
9992   /* special case: initialize with default values that may be overwritten */
9993   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9994   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9995   {
9996     char *token_w1 = "viewport.window.width";
9997     char *token_w2 = "viewport.window.height";
9998     char *token_01 = "viewport.playfield.x";
9999     char *token_02 = "viewport.playfield.y";
10000     char *token_03 = "viewport.playfield.width";
10001     char *token_04 = "viewport.playfield.height";
10002     char *token_05 = "viewport.playfield.border_size";
10003     char *token_06 = "viewport.door_1.x";
10004     char *token_07 = "viewport.door_1.y";
10005     char *token_08 = "viewport.door_1.width";
10006     char *token_09 = "viewport.door_1.height";
10007     char *token_10 = "viewport.door_1.border_size";
10008     char *token_11 = "viewport.door_2.x";
10009     char *token_12 = "viewport.door_2.y";
10010     char *token_13 = "viewport.door_2.width";
10011     char *token_14 = "viewport.door_2.height";
10012     char *token_15 = "viewport.door_2.border_size";
10013     char *value_w1 = getHashEntry(setup_file_hash, token_w1);
10014     char *value_w2 = getHashEntry(setup_file_hash, token_w2);
10015     char *value_01 = getHashEntry(setup_file_hash, token_01);
10016     char *value_02 = getHashEntry(setup_file_hash, token_02);
10017     char *value_03 = getHashEntry(setup_file_hash, token_03);
10018     char *value_04 = getHashEntry(setup_file_hash, token_04);
10019     char *value_05 = getHashEntry(setup_file_hash, token_05);
10020     char *value_06 = getHashEntry(setup_file_hash, token_06);
10021     char *value_07 = getHashEntry(setup_file_hash, token_07);
10022     char *value_08 = getHashEntry(setup_file_hash, token_08);
10023     char *value_09 = getHashEntry(setup_file_hash, token_09);
10024     char *value_10 = getHashEntry(setup_file_hash, token_10);
10025     char *value_11 = getHashEntry(setup_file_hash, token_11);
10026     char *value_12 = getHashEntry(setup_file_hash, token_12);
10027     char *value_13 = getHashEntry(setup_file_hash, token_13);
10028     char *value_14 = getHashEntry(setup_file_hash, token_14);
10029     char *value_15 = getHashEntry(setup_file_hash, token_15);
10030
10031     if (value_w1 != NULL)
10032       viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
10033     if (value_w2 != NULL)
10034       viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
10035     if (value_01 != NULL)
10036       viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
10037     if (value_02 != NULL)
10038       viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
10039     if (value_03 != NULL)
10040       viewport.playfield[i].width = get_token_parameter_value(token_03,
10041                                                               value_03);
10042     if (value_04 != NULL)
10043       viewport.playfield[i].height = get_token_parameter_value(token_04,
10044                                                                value_04);
10045     if (value_05 != NULL)
10046       viewport.playfield[i].border_size = get_token_parameter_value(token_05,
10047                                                                     value_05);
10048     if (value_06 != NULL)
10049       viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
10050     if (value_07 != NULL)
10051       viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
10052     if (value_08 != NULL)
10053       viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
10054     if (value_09 != NULL)
10055       viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
10056     if (value_10 != NULL)
10057       viewport.door_1[i].border_size = get_token_parameter_value(token_10,
10058                                                                  value_10);
10059     if (value_11 != NULL)
10060       viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
10061     if (value_12 != NULL)
10062       viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
10063     if (value_13 != NULL)
10064       viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
10065     if (value_14 != NULL)
10066       viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
10067     if (value_15 != NULL)
10068       viewport.door_1[i].border_size = get_token_parameter_value(token_15,
10069                                                                  value_15);
10070   }
10071
10072   /* special case: initialize with default values that may be overwritten */
10073   /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
10074   for (i = 0; title_info[i].info != NULL; i++)
10075   {
10076     struct TitleFadingInfo *info = title_info[i].info;
10077     char *base_token = title_info[i].text;
10078
10079     for (j = 0; title_tokens[j].type != -1; j++)
10080     {
10081       char *token = getStringCat2(base_token, title_tokens[j].text);
10082       char *value = getHashEntry(setup_file_hash, token);
10083
10084       if (value != NULL)
10085       {
10086         int parameter_value = get_token_parameter_value(token, value);
10087
10088         tfi = *info;
10089
10090         *(int *)title_tokens[j].value = (int)parameter_value;
10091
10092         *info = tfi;
10093       }
10094
10095       free(token);
10096     }
10097   }
10098
10099   /* special case: initialize with default values that may be overwritten */
10100   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
10101   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
10102   {
10103     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
10104     char *base_token = titlemessage_arrays[i].text;
10105
10106     for (j = 0; titlemessage_tokens[j].type != -1; j++)
10107     {
10108       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
10109       char *value = getHashEntry(setup_file_hash, token);
10110
10111       if (value != NULL)
10112       {
10113         int parameter_value = get_token_parameter_value(token, value);
10114
10115         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
10116         {
10117           tmi = array[k];
10118
10119           if (titlemessage_tokens[j].type == TYPE_INTEGER)
10120             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
10121           else
10122             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
10123
10124           array[k] = tmi;
10125         }
10126       }
10127
10128       free(token);
10129     }
10130   }
10131
10132   /* read (and overwrite with) values that may be specified in config file */
10133   for (i = 0; image_config_vars[i].token != NULL; i++)
10134   {
10135     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
10136
10137     /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
10138     if (value != NULL && !strEqual(value, ARG_DEFAULT))
10139       *image_config_vars[i].value =
10140         get_token_parameter_value(image_config_vars[i].token, value);
10141   }
10142
10143   freeSetupFileHash(setup_file_hash);
10144 }
10145
10146 void LoadMenuDesignSettings()
10147 {
10148   char *filename_base = UNDEFINED_FILENAME, *filename_local;
10149
10150   InitMenuDesignSettings_Static();
10151   InitMenuDesignSettings_SpecialPreProcessing();
10152
10153   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
10154   {
10155     /* first look for special settings configured in level series config */
10156     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
10157
10158     if (fileExists(filename_base))
10159       LoadMenuDesignSettingsFromFilename(filename_base);
10160   }
10161
10162   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10163
10164   if (filename_local != NULL && !strEqual(filename_base, filename_local))
10165     LoadMenuDesignSettingsFromFilename(filename_local);
10166
10167   InitMenuDesignSettings_SpecialPostProcessing();
10168 }
10169
10170 void LoadMenuDesignSettings_AfterGraphics()
10171 {
10172   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
10173 }
10174
10175 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
10176 {
10177   char *filename = getEditorSetupFilename();
10178   SetupFileList *setup_file_list, *list;
10179   SetupFileHash *element_hash;
10180   int num_unknown_tokens = 0;
10181   int i;
10182
10183   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
10184     return;
10185
10186   element_hash = newSetupFileHash();
10187
10188   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10189     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10190
10191   /* determined size may be larger than needed (due to unknown elements) */
10192   *num_elements = 0;
10193   for (list = setup_file_list; list != NULL; list = list->next)
10194     (*num_elements)++;
10195
10196   /* add space for up to 3 more elements for padding that may be needed */
10197   *num_elements += 3;
10198
10199   /* free memory for old list of elements, if needed */
10200   checked_free(*elements);
10201
10202   /* allocate memory for new list of elements */
10203   *elements = checked_malloc(*num_elements * sizeof(int));
10204
10205   *num_elements = 0;
10206   for (list = setup_file_list; list != NULL; list = list->next)
10207   {
10208     char *value = getHashEntry(element_hash, list->token);
10209
10210     if (value == NULL)          /* try to find obsolete token mapping */
10211     {
10212       char *mapped_token = get_mapped_token(list->token);
10213
10214       if (mapped_token != NULL)
10215       {
10216         value = getHashEntry(element_hash, mapped_token);
10217
10218         free(mapped_token);
10219       }
10220     }
10221
10222     if (value != NULL)
10223     {
10224       (*elements)[(*num_elements)++] = atoi(value);
10225     }
10226     else
10227     {
10228       if (num_unknown_tokens == 0)
10229       {
10230         Error(ERR_INFO_LINE, "-");
10231         Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10232         Error(ERR_INFO, "- config file: '%s'", filename);
10233
10234         num_unknown_tokens++;
10235       }
10236
10237       Error(ERR_INFO, "- token: '%s'", list->token);
10238     }
10239   }
10240
10241   if (num_unknown_tokens > 0)
10242     Error(ERR_INFO_LINE, "-");
10243
10244   while (*num_elements % 4)     /* pad with empty elements, if needed */
10245     (*elements)[(*num_elements)++] = EL_EMPTY;
10246
10247   freeSetupFileList(setup_file_list);
10248   freeSetupFileHash(element_hash);
10249
10250 #if 0
10251   for (i = 0; i < *num_elements; i++)
10252     printf("editor: element '%s' [%d]\n",
10253            element_info[(*elements)[i]].token_name, (*elements)[i]);
10254 #endif
10255 }
10256
10257 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10258                                                      boolean is_sound)
10259 {
10260   SetupFileHash *setup_file_hash = NULL;
10261   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10262   char *filename_music, *filename_prefix, *filename_info;
10263   struct
10264   {
10265     char *token;
10266     char **value_ptr;
10267   }
10268   token_to_value_ptr[] =
10269   {
10270     { "title_header",   &tmp_music_file_info.title_header       },
10271     { "artist_header",  &tmp_music_file_info.artist_header      },
10272     { "album_header",   &tmp_music_file_info.album_header       },
10273     { "year_header",    &tmp_music_file_info.year_header        },
10274
10275     { "title",          &tmp_music_file_info.title              },
10276     { "artist",         &tmp_music_file_info.artist             },
10277     { "album",          &tmp_music_file_info.album              },
10278     { "year",           &tmp_music_file_info.year               },
10279
10280     { NULL,             NULL                                    },
10281   };
10282   int i;
10283
10284   filename_music = (is_sound ? getCustomSoundFilename(basename) :
10285                     getCustomMusicFilename(basename));
10286
10287   if (filename_music == NULL)
10288     return NULL;
10289
10290   /* ---------- try to replace file extension ---------- */
10291
10292   filename_prefix = getStringCopy(filename_music);
10293   if (strrchr(filename_prefix, '.') != NULL)
10294     *strrchr(filename_prefix, '.') = '\0';
10295   filename_info = getStringCat2(filename_prefix, ".txt");
10296
10297   if (fileExists(filename_info))
10298     setup_file_hash = loadSetupFileHash(filename_info);
10299
10300   free(filename_prefix);
10301   free(filename_info);
10302
10303   if (setup_file_hash == NULL)
10304   {
10305     /* ---------- try to add file extension ---------- */
10306
10307     filename_prefix = getStringCopy(filename_music);
10308     filename_info = getStringCat2(filename_prefix, ".txt");
10309
10310     if (fileExists(filename_info))
10311       setup_file_hash = loadSetupFileHash(filename_info);
10312
10313     free(filename_prefix);
10314     free(filename_info);
10315   }
10316
10317   if (setup_file_hash == NULL)
10318     return NULL;
10319
10320   /* ---------- music file info found ---------- */
10321
10322   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10323
10324   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10325   {
10326     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10327
10328     *token_to_value_ptr[i].value_ptr =
10329       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10330   }
10331
10332   tmp_music_file_info.basename = getStringCopy(basename);
10333   tmp_music_file_info.music = music;
10334   tmp_music_file_info.is_sound = is_sound;
10335
10336   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10337   *new_music_file_info = tmp_music_file_info;
10338
10339   return new_music_file_info;
10340 }
10341
10342 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10343 {
10344   return get_music_file_info_ext(basename, music, FALSE);
10345 }
10346
10347 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10348 {
10349   return get_music_file_info_ext(basename, sound, TRUE);
10350 }
10351
10352 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10353                                      char *basename, boolean is_sound)
10354 {
10355   for (; list != NULL; list = list->next)
10356     if (list->is_sound == is_sound && strEqual(list->basename, basename))
10357       return TRUE;
10358
10359   return FALSE;
10360 }
10361
10362 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10363 {
10364   return music_info_listed_ext(list, basename, FALSE);
10365 }
10366
10367 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10368 {
10369   return music_info_listed_ext(list, basename, TRUE);
10370 }
10371
10372 void LoadMusicInfo()
10373 {
10374   char *music_directory = getCustomMusicDirectory();
10375   int num_music = getMusicListSize();
10376   int num_music_noconf = 0;
10377   int num_sounds = getSoundListSize();
10378   Directory *dir;
10379   DirectoryEntry *dir_entry;
10380   struct FileInfo *music, *sound;
10381   struct MusicFileInfo *next, **new;
10382   int i;
10383
10384   while (music_file_info != NULL)
10385   {
10386     next = music_file_info->next;
10387
10388     checked_free(music_file_info->basename);
10389
10390     checked_free(music_file_info->title_header);
10391     checked_free(music_file_info->artist_header);
10392     checked_free(music_file_info->album_header);
10393     checked_free(music_file_info->year_header);
10394
10395     checked_free(music_file_info->title);
10396     checked_free(music_file_info->artist);
10397     checked_free(music_file_info->album);
10398     checked_free(music_file_info->year);
10399
10400     free(music_file_info);
10401
10402     music_file_info = next;
10403   }
10404
10405   new = &music_file_info;
10406
10407   for (i = 0; i < num_music; i++)
10408   {
10409     music = getMusicListEntry(i);
10410
10411     if (music->filename == NULL)
10412       continue;
10413
10414     if (strEqual(music->filename, UNDEFINED_FILENAME))
10415       continue;
10416
10417     /* a configured file may be not recognized as music */
10418     if (!FileIsMusic(music->filename))
10419       continue;
10420
10421     if (!music_info_listed(music_file_info, music->filename))
10422     {
10423       *new = get_music_file_info(music->filename, i);
10424
10425       if (*new != NULL)
10426         new = &(*new)->next;
10427     }
10428   }
10429
10430   if ((dir = openDirectory(music_directory)) == NULL)
10431   {
10432     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10433     return;
10434   }
10435
10436   while ((dir_entry = readDirectory(dir)) != NULL)      /* loop all entries */
10437   {
10438     char *basename = dir_entry->basename;
10439     boolean music_already_used = FALSE;
10440     int i;
10441
10442     /* skip all music files that are configured in music config file */
10443     for (i = 0; i < num_music; i++)
10444     {
10445       music = getMusicListEntry(i);
10446
10447       if (music->filename == NULL)
10448         continue;
10449
10450       if (strEqual(basename, music->filename))
10451       {
10452         music_already_used = TRUE;
10453         break;
10454       }
10455     }
10456
10457     if (music_already_used)
10458       continue;
10459
10460     if (!FileIsMusic(dir_entry->filename))
10461       continue;
10462
10463     if (!music_info_listed(music_file_info, basename))
10464     {
10465       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10466
10467       if (*new != NULL)
10468         new = &(*new)->next;
10469     }
10470
10471     num_music_noconf++;
10472   }
10473
10474   closeDirectory(dir);
10475
10476   for (i = 0; i < num_sounds; i++)
10477   {
10478     sound = getSoundListEntry(i);
10479
10480     if (sound->filename == NULL)
10481       continue;
10482
10483     if (strEqual(sound->filename, UNDEFINED_FILENAME))
10484       continue;
10485
10486     /* a configured file may be not recognized as sound */
10487     if (!FileIsSound(sound->filename))
10488       continue;
10489
10490     if (!sound_info_listed(music_file_info, sound->filename))
10491     {
10492       *new = get_sound_file_info(sound->filename, i);
10493       if (*new != NULL)
10494         new = &(*new)->next;
10495     }
10496   }
10497 }
10498
10499 void add_helpanim_entry(int element, int action, int direction, int delay,
10500                         int *num_list_entries)
10501 {
10502   struct HelpAnimInfo *new_list_entry;
10503   (*num_list_entries)++;
10504
10505   helpanim_info =
10506     checked_realloc(helpanim_info,
10507                     *num_list_entries * sizeof(struct HelpAnimInfo));
10508   new_list_entry = &helpanim_info[*num_list_entries - 1];
10509
10510   new_list_entry->element = element;
10511   new_list_entry->action = action;
10512   new_list_entry->direction = direction;
10513   new_list_entry->delay = delay;
10514 }
10515
10516 void print_unknown_token(char *filename, char *token, int token_nr)
10517 {
10518   if (token_nr == 0)
10519   {
10520     Error(ERR_INFO_LINE, "-");
10521     Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10522     Error(ERR_INFO, "- config file: '%s'", filename);
10523   }
10524
10525   Error(ERR_INFO, "- token: '%s'", token);
10526 }
10527
10528 void print_unknown_token_end(int token_nr)
10529 {
10530   if (token_nr > 0)
10531     Error(ERR_INFO_LINE, "-");
10532 }
10533
10534 void LoadHelpAnimInfo()
10535 {
10536   char *filename = getHelpAnimFilename();
10537   SetupFileList *setup_file_list = NULL, *list;
10538   SetupFileHash *element_hash, *action_hash, *direction_hash;
10539   int num_list_entries = 0;
10540   int num_unknown_tokens = 0;
10541   int i;
10542
10543   if (fileExists(filename))
10544     setup_file_list = loadSetupFileList(filename);
10545
10546   if (setup_file_list == NULL)
10547   {
10548     /* use reliable default values from static configuration */
10549     SetupFileList *insert_ptr;
10550
10551     insert_ptr = setup_file_list =
10552       newSetupFileList(helpanim_config[0].token,
10553                        helpanim_config[0].value);
10554
10555     for (i = 1; helpanim_config[i].token; i++)
10556       insert_ptr = addListEntry(insert_ptr,
10557                                 helpanim_config[i].token,
10558                                 helpanim_config[i].value);
10559   }
10560
10561   element_hash   = newSetupFileHash();
10562   action_hash    = newSetupFileHash();
10563   direction_hash = newSetupFileHash();
10564
10565   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10566     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10567
10568   for (i = 0; i < NUM_ACTIONS; i++)
10569     setHashEntry(action_hash, element_action_info[i].suffix,
10570                  i_to_a(element_action_info[i].value));
10571
10572   /* do not store direction index (bit) here, but direction value! */
10573   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10574     setHashEntry(direction_hash, element_direction_info[i].suffix,
10575                  i_to_a(1 << element_direction_info[i].value));
10576
10577   for (list = setup_file_list; list != NULL; list = list->next)
10578   {
10579     char *element_token, *action_token, *direction_token;
10580     char *element_value, *action_value, *direction_value;
10581     int delay = atoi(list->value);
10582
10583     if (strEqual(list->token, "end"))
10584     {
10585       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10586
10587       continue;
10588     }
10589
10590     /* first try to break element into element/action/direction parts;
10591        if this does not work, also accept combined "element[.act][.dir]"
10592        elements (like "dynamite.active"), which are unique elements */
10593
10594     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
10595     {
10596       element_value = getHashEntry(element_hash, list->token);
10597       if (element_value != NULL)        /* element found */
10598         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10599                            &num_list_entries);
10600       else
10601       {
10602         /* no further suffixes found -- this is not an element */
10603         print_unknown_token(filename, list->token, num_unknown_tokens++);
10604       }
10605
10606       continue;
10607     }
10608
10609     /* token has format "<prefix>.<something>" */
10610
10611     action_token = strchr(list->token, '.');    /* suffix may be action ... */
10612     direction_token = action_token;             /* ... or direction */
10613
10614     element_token = getStringCopy(list->token);
10615     *strchr(element_token, '.') = '\0';
10616
10617     element_value = getHashEntry(element_hash, element_token);
10618
10619     if (element_value == NULL)          /* this is no element */
10620     {
10621       element_value = getHashEntry(element_hash, list->token);
10622       if (element_value != NULL)        /* combined element found */
10623         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10624                            &num_list_entries);
10625       else
10626         print_unknown_token(filename, list->token, num_unknown_tokens++);
10627
10628       free(element_token);
10629
10630       continue;
10631     }
10632
10633     action_value = getHashEntry(action_hash, action_token);
10634
10635     if (action_value != NULL)           /* action found */
10636     {
10637       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10638                     &num_list_entries);
10639
10640       free(element_token);
10641
10642       continue;
10643     }
10644
10645     direction_value = getHashEntry(direction_hash, direction_token);
10646
10647     if (direction_value != NULL)        /* direction found */
10648     {
10649       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10650                          &num_list_entries);
10651
10652       free(element_token);
10653
10654       continue;
10655     }
10656
10657     if (strchr(action_token + 1, '.') == NULL)
10658     {
10659       /* no further suffixes found -- this is not an action nor direction */
10660
10661       element_value = getHashEntry(element_hash, list->token);
10662       if (element_value != NULL)        /* combined element found */
10663         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10664                            &num_list_entries);
10665       else
10666         print_unknown_token(filename, list->token, num_unknown_tokens++);
10667
10668       free(element_token);
10669
10670       continue;
10671     }
10672
10673     /* token has format "<prefix>.<suffix>.<something>" */
10674
10675     direction_token = strchr(action_token + 1, '.');
10676
10677     action_token = getStringCopy(action_token);
10678     *strchr(action_token + 1, '.') = '\0';
10679
10680     action_value = getHashEntry(action_hash, action_token);
10681
10682     if (action_value == NULL)           /* this is no action */
10683     {
10684       element_value = getHashEntry(element_hash, list->token);
10685       if (element_value != NULL)        /* combined element found */
10686         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10687                            &num_list_entries);
10688       else
10689         print_unknown_token(filename, list->token, num_unknown_tokens++);
10690
10691       free(element_token);
10692       free(action_token);
10693
10694       continue;
10695     }
10696
10697     direction_value = getHashEntry(direction_hash, direction_token);
10698
10699     if (direction_value != NULL)        /* direction found */
10700     {
10701       add_helpanim_entry(atoi(element_value), atoi(action_value),
10702                          atoi(direction_value), delay, &num_list_entries);
10703
10704       free(element_token);
10705       free(action_token);
10706
10707       continue;
10708     }
10709
10710     /* this is no direction */
10711
10712     element_value = getHashEntry(element_hash, list->token);
10713     if (element_value != NULL)          /* combined element found */
10714       add_helpanim_entry(atoi(element_value), -1, -1, delay,
10715                          &num_list_entries);
10716     else
10717       print_unknown_token(filename, list->token, num_unknown_tokens++);
10718
10719     free(element_token);
10720     free(action_token);
10721   }
10722
10723   print_unknown_token_end(num_unknown_tokens);
10724
10725   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10726   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
10727
10728   freeSetupFileList(setup_file_list);
10729   freeSetupFileHash(element_hash);
10730   freeSetupFileHash(action_hash);
10731   freeSetupFileHash(direction_hash);
10732
10733 #if 0
10734   for (i = 0; i < num_list_entries; i++)
10735     printf("::: '%s': %d, %d, %d => %d\n",
10736            EL_NAME(helpanim_info[i].element),
10737            helpanim_info[i].element,
10738            helpanim_info[i].action,
10739            helpanim_info[i].direction,
10740            helpanim_info[i].delay);
10741 #endif
10742 }
10743
10744 void LoadHelpTextInfo()
10745 {
10746   char *filename = getHelpTextFilename();
10747   int i;
10748
10749   if (helptext_info != NULL)
10750   {
10751     freeSetupFileHash(helptext_info);
10752     helptext_info = NULL;
10753   }
10754
10755   if (fileExists(filename))
10756     helptext_info = loadSetupFileHash(filename);
10757
10758   if (helptext_info == NULL)
10759   {
10760     /* use reliable default values from static configuration */
10761     helptext_info = newSetupFileHash();
10762
10763     for (i = 0; helptext_config[i].token; i++)
10764       setHashEntry(helptext_info,
10765                    helptext_config[i].token,
10766                    helptext_config[i].value);
10767   }
10768
10769 #if 0
10770   BEGIN_HASH_ITERATION(helptext_info, itr)
10771   {
10772     printf("::: '%s' => '%s'\n",
10773            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10774   }
10775   END_HASH_ITERATION(hash, itr)
10776 #endif
10777 }
10778
10779
10780 /* ------------------------------------------------------------------------- */
10781 /* convert levels                                                            */
10782 /* ------------------------------------------------------------------------- */
10783
10784 #define MAX_NUM_CONVERT_LEVELS          1000
10785
10786 void ConvertLevels()
10787 {
10788   static LevelDirTree *convert_leveldir = NULL;
10789   static int convert_level_nr = -1;
10790   static int num_levels_handled = 0;
10791   static int num_levels_converted = 0;
10792   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10793   int i;
10794
10795   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10796                                                global.convert_leveldir);
10797
10798   if (convert_leveldir == NULL)
10799     Error(ERR_EXIT, "no such level identifier: '%s'",
10800           global.convert_leveldir);
10801
10802   leveldir_current = convert_leveldir;
10803
10804   if (global.convert_level_nr != -1)
10805   {
10806     convert_leveldir->first_level = global.convert_level_nr;
10807     convert_leveldir->last_level  = global.convert_level_nr;
10808   }
10809
10810   convert_level_nr = convert_leveldir->first_level;
10811
10812   PrintLine("=", 79);
10813   Print("Converting levels\n");
10814   PrintLine("-", 79);
10815   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10816   Print("Level series name:       '%s'\n", convert_leveldir->name);
10817   Print("Level series author:     '%s'\n", convert_leveldir->author);
10818   Print("Number of levels:        %d\n",   convert_leveldir->levels);
10819   PrintLine("=", 79);
10820   Print("\n");
10821
10822   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10823     levels_failed[i] = FALSE;
10824
10825   while (convert_level_nr <= convert_leveldir->last_level)
10826   {
10827     char *level_filename;
10828     boolean new_level;
10829
10830     level_nr = convert_level_nr++;
10831
10832     Print("Level %03d: ", level_nr);
10833
10834     LoadLevel(level_nr);
10835     if (level.no_level_file || level.no_valid_file)
10836     {
10837       Print("(no level)\n");
10838       continue;
10839     }
10840
10841     Print("converting level ... ");
10842
10843     level_filename = getDefaultLevelFilename(level_nr);
10844     new_level = !fileExists(level_filename);
10845
10846     if (new_level)
10847     {
10848       SaveLevel(level_nr);
10849
10850       num_levels_converted++;
10851
10852       Print("converted.\n");
10853     }
10854     else
10855     {
10856       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10857         levels_failed[level_nr] = TRUE;
10858
10859       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10860     }
10861
10862     num_levels_handled++;
10863   }
10864
10865   Print("\n");
10866   PrintLine("=", 79);
10867   Print("Number of levels handled: %d\n", num_levels_handled);
10868   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10869          (num_levels_handled ?
10870           num_levels_converted * 100 / num_levels_handled : 0));
10871   PrintLine("-", 79);
10872   Print("Summary (for automatic parsing by scripts):\n");
10873   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10874          convert_leveldir->identifier, num_levels_converted,
10875          num_levels_handled,
10876          (num_levels_handled ?
10877           num_levels_converted * 100 / num_levels_handled : 0));
10878
10879   if (num_levels_handled != num_levels_converted)
10880   {
10881     Print(", FAILED:");
10882     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10883       if (levels_failed[i])
10884         Print(" %03d", i);
10885   }
10886
10887   Print("\n");
10888   PrintLine("=", 79);
10889
10890   CloseAllAndExit(0);
10891 }
10892
10893
10894 /* ------------------------------------------------------------------------- */
10895 /* create and save images for use in level sketches (raw BMP format)         */
10896 /* ------------------------------------------------------------------------- */
10897
10898 void CreateLevelSketchImages()
10899 {
10900 #if defined(TARGET_SDL)
10901   Bitmap *bitmap1;
10902   Bitmap *bitmap2;
10903   int i;
10904
10905   InitElementPropertiesGfxElement();
10906
10907   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10908   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10909
10910   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10911   {
10912     Bitmap *src_bitmap;
10913     int src_x, src_y;
10914     int element = getMappedElement(i);
10915     int graphic = el2edimg(element);
10916     char basename1[16];
10917     char basename2[16];
10918     char *filename1;
10919     char *filename2;
10920
10921     sprintf(basename1, "%03d.bmp", i);
10922     sprintf(basename2, "%03ds.bmp", i);
10923
10924     filename1 = getPath2(global.create_images_dir, basename1);
10925     filename2 = getPath2(global.create_images_dir, basename2);
10926
10927     getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10928     BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10929                0, 0);
10930
10931     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10932       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10933
10934     getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10935     BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10936
10937     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10938       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10939
10940     free(filename1);
10941     free(filename2);
10942
10943     if (options.debug)
10944       printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10945   }
10946
10947   FreeBitmap(bitmap1);
10948   FreeBitmap(bitmap2);
10949
10950   if (options.debug)
10951     printf("\n");
10952
10953   Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10954
10955   CloseAllAndExit(0);
10956 #endif
10957 }
10958
10959
10960 /* ------------------------------------------------------------------------- */
10961 /* create and save images for custom and group elements (raw BMP format)     */
10962 /* ------------------------------------------------------------------------- */
10963
10964 void CreateCustomElementImages(char *directory)
10965 {
10966 #if defined(TARGET_SDL)
10967   char *src_basename = "RocksCE-template.ilbm";
10968   char *dst_basename = "RocksCE.bmp";
10969   char *src_filename = getPath2(directory, src_basename);
10970   char *dst_filename = getPath2(directory, dst_basename);
10971   Bitmap *src_bitmap;
10972   Bitmap *bitmap;
10973   int yoffset_ce = 0;
10974   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10975   int i;
10976
10977   SDLInitVideoDisplay();
10978
10979   ReCreateBitmap(&backbuffer, video.width, video.height);
10980
10981   src_bitmap = LoadImage(src_filename);
10982
10983   bitmap = CreateBitmap(TILEX * 16 * 2,
10984                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10985                         DEFAULT_DEPTH);
10986
10987   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10988   {
10989     int x = i % 16;
10990     int y = i / 16;
10991     int ii = i + 1;
10992     int j;
10993
10994     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10995                TILEX * x, TILEY * y + yoffset_ce);
10996
10997     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10998                TILEX, TILEY,
10999                TILEX * x + TILEX * 16,
11000                TILEY * y + yoffset_ce);
11001
11002     for (j = 2; j >= 0; j--)
11003     {
11004       int c = ii % 10;
11005
11006       BlitBitmap(src_bitmap, bitmap,
11007                  TILEX + c * 7, 0, 6, 10,
11008                  TILEX * x + 6 + j * 7,
11009                  TILEY * y + 11 + yoffset_ce);
11010
11011       BlitBitmap(src_bitmap, bitmap,
11012                  TILEX + c * 8, TILEY, 6, 10,
11013                  TILEX * 16 + TILEX * x + 6 + j * 8,
11014                  TILEY * y + 10 + yoffset_ce);
11015
11016       ii /= 10;
11017     }
11018   }
11019
11020   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
11021   {
11022     int x = i % 16;
11023     int y = i / 16;
11024     int ii = i + 1;
11025     int j;
11026
11027     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11028                TILEX * x, TILEY * y + yoffset_ge);
11029
11030     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11031                TILEX, TILEY,
11032                TILEX * x + TILEX * 16,
11033                TILEY * y + yoffset_ge);
11034
11035     for (j = 1; j >= 0; j--)
11036     {
11037       int c = ii % 10;
11038
11039       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
11040                  TILEX * x + 6 + j * 10,
11041                  TILEY * y + 11 + yoffset_ge);
11042
11043       BlitBitmap(src_bitmap, bitmap,
11044                  TILEX + c * 8, TILEY + 12, 6, 10,
11045                  TILEX * 16 + TILEX * x + 10 + j * 8,
11046                  TILEY * y + 10 + yoffset_ge);
11047
11048       ii /= 10;
11049     }
11050   }
11051
11052   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
11053     Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
11054
11055   FreeBitmap(bitmap);
11056
11057   CloseAllAndExit(0);
11058 #endif
11059 }