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