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