a7a8420a94889760fb4e54e67aa171868d6a148f
[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(void)
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(void)
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(void)
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(void)
2163 {
2164   return getDefaultLevelFilename(-1);
2165 }
2166
2167 char *getGlobalLevelTemplateFilename(void)
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 static 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 static 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 static 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 static 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 static 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 static 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(void);
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 static 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 static 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 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4097                                         boolean init)
4098 {
4099   static int last_data_encoded;
4100   static int offset1;
4101   static int offset2;
4102   int diff;
4103   int diff_hi, diff_lo;
4104   int data_hi, data_lo;
4105   unsigned short data_decoded;
4106
4107   if (init)
4108   {
4109     last_data_encoded = 0;
4110     offset1 = -1;
4111     offset2 = 0;
4112
4113     return 0;
4114   }
4115
4116   diff = data_encoded - last_data_encoded;
4117   diff_hi = diff & ~0xff;
4118   diff_lo = diff &  0xff;
4119
4120   offset2 += diff_lo;
4121
4122   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4123   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4124   data_hi = data_hi & 0xff00;
4125
4126   data_decoded = data_hi | data_lo;
4127
4128   last_data_encoded = data_encoded;
4129
4130   offset1 = (offset1 + 1) % 31;
4131   offset2 = offset2 & 0xff;
4132
4133   return data_decoded;
4134 }
4135
4136 static int getMappedElement_DC(int element)
4137 {
4138   switch (element)
4139   {
4140     case 0x0000:
4141       element = EL_ROCK;
4142       break;
4143
4144       /* 0x0117 - 0x036e: (?) */
4145       /* EL_DIAMOND */
4146
4147       /* 0x042d - 0x0684: (?) */
4148       /* EL_EMERALD */
4149
4150     case 0x06f1:
4151       element = EL_NUT;
4152       break;
4153
4154     case 0x074c:
4155       element = EL_BOMB;
4156       break;
4157
4158     case 0x07a4:
4159       element = EL_PEARL;
4160       break;
4161
4162     case 0x0823:
4163       element = EL_CRYSTAL;
4164       break;
4165
4166     case 0x0e77:        /* quicksand (boulder) */
4167       element = EL_QUICKSAND_FAST_FULL;
4168       break;
4169
4170     case 0x0e99:        /* slow quicksand (boulder) */
4171       element = EL_QUICKSAND_FULL;
4172       break;
4173
4174     case 0x0ed2:
4175       element = EL_EM_EXIT_OPEN;
4176       break;
4177
4178     case 0x0ee3:
4179       element = EL_EM_EXIT_CLOSED;
4180       break;
4181
4182     case 0x0eeb:
4183       element = EL_EM_STEEL_EXIT_OPEN;
4184       break;
4185
4186     case 0x0efc:
4187       element = EL_EM_STEEL_EXIT_CLOSED;
4188       break;
4189
4190     case 0x0f4f:        /* dynamite (lit 1) */
4191       element = EL_EM_DYNAMITE_ACTIVE;
4192       break;
4193
4194     case 0x0f57:        /* dynamite (lit 2) */
4195       element = EL_EM_DYNAMITE_ACTIVE;
4196       break;
4197
4198     case 0x0f5f:        /* dynamite (lit 3) */
4199       element = EL_EM_DYNAMITE_ACTIVE;
4200       break;
4201
4202     case 0x0f67:        /* dynamite (lit 4) */
4203       element = EL_EM_DYNAMITE_ACTIVE;
4204       break;
4205
4206     case 0x0f81:
4207     case 0x0f82:
4208     case 0x0f83:
4209     case 0x0f84:
4210       element = EL_AMOEBA_WET;
4211       break;
4212
4213     case 0x0f85:
4214       element = EL_AMOEBA_DROP;
4215       break;
4216
4217     case 0x0fb9:
4218       element = EL_DC_MAGIC_WALL;
4219       break;
4220
4221     case 0x0fd0:
4222       element = EL_SPACESHIP_UP;
4223       break;
4224
4225     case 0x0fd9:
4226       element = EL_SPACESHIP_DOWN;
4227       break;
4228
4229     case 0x0ff1:
4230       element = EL_SPACESHIP_LEFT;
4231       break;
4232
4233     case 0x0ff9:
4234       element = EL_SPACESHIP_RIGHT;
4235       break;
4236
4237     case 0x1057:
4238       element = EL_BUG_UP;
4239       break;
4240
4241     case 0x1060:
4242       element = EL_BUG_DOWN;
4243       break;
4244
4245     case 0x1078:
4246       element = EL_BUG_LEFT;
4247       break;
4248
4249     case 0x1080:
4250       element = EL_BUG_RIGHT;
4251       break;
4252
4253     case 0x10de:
4254       element = EL_MOLE_UP;
4255       break;
4256
4257     case 0x10e7:
4258       element = EL_MOLE_DOWN;
4259       break;
4260
4261     case 0x10ff:
4262       element = EL_MOLE_LEFT;
4263       break;
4264
4265     case 0x1107:
4266       element = EL_MOLE_RIGHT;
4267       break;
4268
4269     case 0x11c0:
4270       element = EL_ROBOT;
4271       break;
4272
4273     case 0x13f5:
4274       element = EL_YAMYAM;
4275       break;
4276
4277     case 0x1425:
4278       element = EL_SWITCHGATE_OPEN;
4279       break;
4280
4281     case 0x1426:
4282       element = EL_SWITCHGATE_CLOSED;
4283       break;
4284
4285     case 0x1437:
4286       element = EL_DC_SWITCHGATE_SWITCH_UP;
4287       break;
4288
4289     case 0x143a:
4290       element = EL_TIMEGATE_CLOSED;
4291       break;
4292
4293     case 0x144c:        /* conveyor belt switch (green) */
4294       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4295       break;
4296
4297     case 0x144f:        /* conveyor belt switch (red) */
4298       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4299       break;
4300
4301     case 0x1452:        /* conveyor belt switch (blue) */
4302       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4303       break;
4304
4305     case 0x145b:
4306       element = EL_CONVEYOR_BELT_3_MIDDLE;
4307       break;
4308
4309     case 0x1463:
4310       element = EL_CONVEYOR_BELT_3_LEFT;
4311       break;
4312
4313     case 0x146b:
4314       element = EL_CONVEYOR_BELT_3_RIGHT;
4315       break;
4316
4317     case 0x1473:
4318       element = EL_CONVEYOR_BELT_1_MIDDLE;
4319       break;
4320
4321     case 0x147b:
4322       element = EL_CONVEYOR_BELT_1_LEFT;
4323       break;
4324
4325     case 0x1483:
4326       element = EL_CONVEYOR_BELT_1_RIGHT;
4327       break;
4328
4329     case 0x148b:
4330       element = EL_CONVEYOR_BELT_4_MIDDLE;
4331       break;
4332
4333     case 0x1493:
4334       element = EL_CONVEYOR_BELT_4_LEFT;
4335       break;
4336
4337     case 0x149b:
4338       element = EL_CONVEYOR_BELT_4_RIGHT;
4339       break;
4340
4341     case 0x14ac:
4342       element = EL_EXPANDABLE_WALL_HORIZONTAL;
4343       break;
4344
4345     case 0x14bd:
4346       element = EL_EXPANDABLE_WALL_VERTICAL;
4347       break;
4348
4349     case 0x14c6:
4350       element = EL_EXPANDABLE_WALL_ANY;
4351       break;
4352
4353     case 0x14ce:        /* growing steel wall (left/right) */
4354       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4355       break;
4356
4357     case 0x14df:        /* growing steel wall (up/down) */
4358       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4359       break;
4360
4361     case 0x14e8:        /* growing steel wall (up/down/left/right) */
4362       element = EL_EXPANDABLE_STEELWALL_ANY;
4363       break;
4364
4365     case 0x14e9:
4366       element = EL_SHIELD_DEADLY;
4367       break;
4368
4369     case 0x1501:
4370       element = EL_EXTRA_TIME;
4371       break;
4372
4373     case 0x154f:
4374       element = EL_ACID;
4375       break;
4376
4377     case 0x1577:
4378       element = EL_EMPTY_SPACE;
4379       break;
4380
4381     case 0x1578:        /* quicksand (empty) */
4382       element = EL_QUICKSAND_FAST_EMPTY;
4383       break;
4384
4385     case 0x1579:        /* slow quicksand (empty) */
4386       element = EL_QUICKSAND_EMPTY;
4387       break;
4388
4389       /* 0x157c - 0x158b: */
4390       /* EL_SAND */
4391
4392       /* 0x1590 - 0x159f: */
4393       /* EL_DC_LANDMINE */
4394
4395     case 0x15a0:
4396       element = EL_EM_DYNAMITE;
4397       break;
4398
4399     case 0x15a1:        /* key (red) */
4400       element = EL_EM_KEY_1;
4401       break;
4402
4403     case 0x15a2:        /* key (yellow) */
4404       element = EL_EM_KEY_2;
4405       break;
4406
4407     case 0x15a3:        /* key (blue) */
4408       element = EL_EM_KEY_4;
4409       break;
4410
4411     case 0x15a4:        /* key (green) */
4412       element = EL_EM_KEY_3;
4413       break;
4414
4415     case 0x15a5:        /* key (white) */
4416       element = EL_DC_KEY_WHITE;
4417       break;
4418
4419     case 0x15a6:
4420       element = EL_WALL_SLIPPERY;
4421       break;
4422
4423     case 0x15a7:
4424       element = EL_WALL;
4425       break;
4426
4427     case 0x15a8:        /* wall (not round) */
4428       element = EL_WALL;
4429       break;
4430
4431     case 0x15a9:        /* (blue) */
4432       element = EL_CHAR_A;
4433       break;
4434
4435     case 0x15aa:        /* (blue) */
4436       element = EL_CHAR_B;
4437       break;
4438
4439     case 0x15ab:        /* (blue) */
4440       element = EL_CHAR_C;
4441       break;
4442
4443     case 0x15ac:        /* (blue) */
4444       element = EL_CHAR_D;
4445       break;
4446
4447     case 0x15ad:        /* (blue) */
4448       element = EL_CHAR_E;
4449       break;
4450
4451     case 0x15ae:        /* (blue) */
4452       element = EL_CHAR_F;
4453       break;
4454
4455     case 0x15af:        /* (blue) */
4456       element = EL_CHAR_G;
4457       break;
4458
4459     case 0x15b0:        /* (blue) */
4460       element = EL_CHAR_H;
4461       break;
4462
4463     case 0x15b1:        /* (blue) */
4464       element = EL_CHAR_I;
4465       break;
4466
4467     case 0x15b2:        /* (blue) */
4468       element = EL_CHAR_J;
4469       break;
4470
4471     case 0x15b3:        /* (blue) */
4472       element = EL_CHAR_K;
4473       break;
4474
4475     case 0x15b4:        /* (blue) */
4476       element = EL_CHAR_L;
4477       break;
4478
4479     case 0x15b5:        /* (blue) */
4480       element = EL_CHAR_M;
4481       break;
4482
4483     case 0x15b6:        /* (blue) */
4484       element = EL_CHAR_N;
4485       break;
4486
4487     case 0x15b7:        /* (blue) */
4488       element = EL_CHAR_O;
4489       break;
4490
4491     case 0x15b8:        /* (blue) */
4492       element = EL_CHAR_P;
4493       break;
4494
4495     case 0x15b9:        /* (blue) */
4496       element = EL_CHAR_Q;
4497       break;
4498
4499     case 0x15ba:        /* (blue) */
4500       element = EL_CHAR_R;
4501       break;
4502
4503     case 0x15bb:        /* (blue) */
4504       element = EL_CHAR_S;
4505       break;
4506
4507     case 0x15bc:        /* (blue) */
4508       element = EL_CHAR_T;
4509       break;
4510
4511     case 0x15bd:        /* (blue) */
4512       element = EL_CHAR_U;
4513       break;
4514
4515     case 0x15be:        /* (blue) */
4516       element = EL_CHAR_V;
4517       break;
4518
4519     case 0x15bf:        /* (blue) */
4520       element = EL_CHAR_W;
4521       break;
4522
4523     case 0x15c0:        /* (blue) */
4524       element = EL_CHAR_X;
4525       break;
4526
4527     case 0x15c1:        /* (blue) */
4528       element = EL_CHAR_Y;
4529       break;
4530
4531     case 0x15c2:        /* (blue) */
4532       element = EL_CHAR_Z;
4533       break;
4534
4535     case 0x15c3:        /* (blue) */
4536       element = EL_CHAR_AUMLAUT;
4537       break;
4538
4539     case 0x15c4:        /* (blue) */
4540       element = EL_CHAR_OUMLAUT;
4541       break;
4542
4543     case 0x15c5:        /* (blue) */
4544       element = EL_CHAR_UUMLAUT;
4545       break;
4546
4547     case 0x15c6:        /* (blue) */
4548       element = EL_CHAR_0;
4549       break;
4550
4551     case 0x15c7:        /* (blue) */
4552       element = EL_CHAR_1;
4553       break;
4554
4555     case 0x15c8:        /* (blue) */
4556       element = EL_CHAR_2;
4557       break;
4558
4559     case 0x15c9:        /* (blue) */
4560       element = EL_CHAR_3;
4561       break;
4562
4563     case 0x15ca:        /* (blue) */
4564       element = EL_CHAR_4;
4565       break;
4566
4567     case 0x15cb:        /* (blue) */
4568       element = EL_CHAR_5;
4569       break;
4570
4571     case 0x15cc:        /* (blue) */
4572       element = EL_CHAR_6;
4573       break;
4574
4575     case 0x15cd:        /* (blue) */
4576       element = EL_CHAR_7;
4577       break;
4578
4579     case 0x15ce:        /* (blue) */
4580       element = EL_CHAR_8;
4581       break;
4582
4583     case 0x15cf:        /* (blue) */
4584       element = EL_CHAR_9;
4585       break;
4586
4587     case 0x15d0:        /* (blue) */
4588       element = EL_CHAR_PERIOD;
4589       break;
4590
4591     case 0x15d1:        /* (blue) */
4592       element = EL_CHAR_EXCLAM;
4593       break;
4594
4595     case 0x15d2:        /* (blue) */
4596       element = EL_CHAR_COLON;
4597       break;
4598
4599     case 0x15d3:        /* (blue) */
4600       element = EL_CHAR_LESS;
4601       break;
4602
4603     case 0x15d4:        /* (blue) */
4604       element = EL_CHAR_GREATER;
4605       break;
4606
4607     case 0x15d5:        /* (blue) */
4608       element = EL_CHAR_QUESTION;
4609       break;
4610
4611     case 0x15d6:        /* (blue) */
4612       element = EL_CHAR_COPYRIGHT;
4613       break;
4614
4615     case 0x15d7:        /* (blue) */
4616       element = EL_CHAR_UP;
4617       break;
4618
4619     case 0x15d8:        /* (blue) */
4620       element = EL_CHAR_DOWN;
4621       break;
4622
4623     case 0x15d9:        /* (blue) */
4624       element = EL_CHAR_BUTTON;
4625       break;
4626
4627     case 0x15da:        /* (blue) */
4628       element = EL_CHAR_PLUS;
4629       break;
4630
4631     case 0x15db:        /* (blue) */
4632       element = EL_CHAR_MINUS;
4633       break;
4634
4635     case 0x15dc:        /* (blue) */
4636       element = EL_CHAR_APOSTROPHE;
4637       break;
4638
4639     case 0x15dd:        /* (blue) */
4640       element = EL_CHAR_PARENLEFT;
4641       break;
4642
4643     case 0x15de:        /* (blue) */
4644       element = EL_CHAR_PARENRIGHT;
4645       break;
4646
4647     case 0x15df:        /* (green) */
4648       element = EL_CHAR_A;
4649       break;
4650
4651     case 0x15e0:        /* (green) */
4652       element = EL_CHAR_B;
4653       break;
4654
4655     case 0x15e1:        /* (green) */
4656       element = EL_CHAR_C;
4657       break;
4658
4659     case 0x15e2:        /* (green) */
4660       element = EL_CHAR_D;
4661       break;
4662
4663     case 0x15e3:        /* (green) */
4664       element = EL_CHAR_E;
4665       break;
4666
4667     case 0x15e4:        /* (green) */
4668       element = EL_CHAR_F;
4669       break;
4670
4671     case 0x15e5:        /* (green) */
4672       element = EL_CHAR_G;
4673       break;
4674
4675     case 0x15e6:        /* (green) */
4676       element = EL_CHAR_H;
4677       break;
4678
4679     case 0x15e7:        /* (green) */
4680       element = EL_CHAR_I;
4681       break;
4682
4683     case 0x15e8:        /* (green) */
4684       element = EL_CHAR_J;
4685       break;
4686
4687     case 0x15e9:        /* (green) */
4688       element = EL_CHAR_K;
4689       break;
4690
4691     case 0x15ea:        /* (green) */
4692       element = EL_CHAR_L;
4693       break;
4694
4695     case 0x15eb:        /* (green) */
4696       element = EL_CHAR_M;
4697       break;
4698
4699     case 0x15ec:        /* (green) */
4700       element = EL_CHAR_N;
4701       break;
4702
4703     case 0x15ed:        /* (green) */
4704       element = EL_CHAR_O;
4705       break;
4706
4707     case 0x15ee:        /* (green) */
4708       element = EL_CHAR_P;
4709       break;
4710
4711     case 0x15ef:        /* (green) */
4712       element = EL_CHAR_Q;
4713       break;
4714
4715     case 0x15f0:        /* (green) */
4716       element = EL_CHAR_R;
4717       break;
4718
4719     case 0x15f1:        /* (green) */
4720       element = EL_CHAR_S;
4721       break;
4722
4723     case 0x15f2:        /* (green) */
4724       element = EL_CHAR_T;
4725       break;
4726
4727     case 0x15f3:        /* (green) */
4728       element = EL_CHAR_U;
4729       break;
4730
4731     case 0x15f4:        /* (green) */
4732       element = EL_CHAR_V;
4733       break;
4734
4735     case 0x15f5:        /* (green) */
4736       element = EL_CHAR_W;
4737       break;
4738
4739     case 0x15f6:        /* (green) */
4740       element = EL_CHAR_X;
4741       break;
4742
4743     case 0x15f7:        /* (green) */
4744       element = EL_CHAR_Y;
4745       break;
4746
4747     case 0x15f8:        /* (green) */
4748       element = EL_CHAR_Z;
4749       break;
4750
4751     case 0x15f9:        /* (green) */
4752       element = EL_CHAR_AUMLAUT;
4753       break;
4754
4755     case 0x15fa:        /* (green) */
4756       element = EL_CHAR_OUMLAUT;
4757       break;
4758
4759     case 0x15fb:        /* (green) */
4760       element = EL_CHAR_UUMLAUT;
4761       break;
4762
4763     case 0x15fc:        /* (green) */
4764       element = EL_CHAR_0;
4765       break;
4766
4767     case 0x15fd:        /* (green) */
4768       element = EL_CHAR_1;
4769       break;
4770
4771     case 0x15fe:        /* (green) */
4772       element = EL_CHAR_2;
4773       break;
4774
4775     case 0x15ff:        /* (green) */
4776       element = EL_CHAR_3;
4777       break;
4778
4779     case 0x1600:        /* (green) */
4780       element = EL_CHAR_4;
4781       break;
4782
4783     case 0x1601:        /* (green) */
4784       element = EL_CHAR_5;
4785       break;
4786
4787     case 0x1602:        /* (green) */
4788       element = EL_CHAR_6;
4789       break;
4790
4791     case 0x1603:        /* (green) */
4792       element = EL_CHAR_7;
4793       break;
4794
4795     case 0x1604:        /* (green) */
4796       element = EL_CHAR_8;
4797       break;
4798
4799     case 0x1605:        /* (green) */
4800       element = EL_CHAR_9;
4801       break;
4802
4803     case 0x1606:        /* (green) */
4804       element = EL_CHAR_PERIOD;
4805       break;
4806
4807     case 0x1607:        /* (green) */
4808       element = EL_CHAR_EXCLAM;
4809       break;
4810
4811     case 0x1608:        /* (green) */
4812       element = EL_CHAR_COLON;
4813       break;
4814
4815     case 0x1609:        /* (green) */
4816       element = EL_CHAR_LESS;
4817       break;
4818
4819     case 0x160a:        /* (green) */
4820       element = EL_CHAR_GREATER;
4821       break;
4822
4823     case 0x160b:        /* (green) */
4824       element = EL_CHAR_QUESTION;
4825       break;
4826
4827     case 0x160c:        /* (green) */
4828       element = EL_CHAR_COPYRIGHT;
4829       break;
4830
4831     case 0x160d:        /* (green) */
4832       element = EL_CHAR_UP;
4833       break;
4834
4835     case 0x160e:        /* (green) */
4836       element = EL_CHAR_DOWN;
4837       break;
4838
4839     case 0x160f:        /* (green) */
4840       element = EL_CHAR_BUTTON;
4841       break;
4842
4843     case 0x1610:        /* (green) */
4844       element = EL_CHAR_PLUS;
4845       break;
4846
4847     case 0x1611:        /* (green) */
4848       element = EL_CHAR_MINUS;
4849       break;
4850
4851     case 0x1612:        /* (green) */
4852       element = EL_CHAR_APOSTROPHE;
4853       break;
4854
4855     case 0x1613:        /* (green) */
4856       element = EL_CHAR_PARENLEFT;
4857       break;
4858
4859     case 0x1614:        /* (green) */
4860       element = EL_CHAR_PARENRIGHT;
4861       break;
4862
4863     case 0x1615:        /* (blue steel) */
4864       element = EL_STEEL_CHAR_A;
4865       break;
4866
4867     case 0x1616:        /* (blue steel) */
4868       element = EL_STEEL_CHAR_B;
4869       break;
4870
4871     case 0x1617:        /* (blue steel) */
4872       element = EL_STEEL_CHAR_C;
4873       break;
4874
4875     case 0x1618:        /* (blue steel) */
4876       element = EL_STEEL_CHAR_D;
4877       break;
4878
4879     case 0x1619:        /* (blue steel) */
4880       element = EL_STEEL_CHAR_E;
4881       break;
4882
4883     case 0x161a:        /* (blue steel) */
4884       element = EL_STEEL_CHAR_F;
4885       break;
4886
4887     case 0x161b:        /* (blue steel) */
4888       element = EL_STEEL_CHAR_G;
4889       break;
4890
4891     case 0x161c:        /* (blue steel) */
4892       element = EL_STEEL_CHAR_H;
4893       break;
4894
4895     case 0x161d:        /* (blue steel) */
4896       element = EL_STEEL_CHAR_I;
4897       break;
4898
4899     case 0x161e:        /* (blue steel) */
4900       element = EL_STEEL_CHAR_J;
4901       break;
4902
4903     case 0x161f:        /* (blue steel) */
4904       element = EL_STEEL_CHAR_K;
4905       break;
4906
4907     case 0x1620:        /* (blue steel) */
4908       element = EL_STEEL_CHAR_L;
4909       break;
4910
4911     case 0x1621:        /* (blue steel) */
4912       element = EL_STEEL_CHAR_M;
4913       break;
4914
4915     case 0x1622:        /* (blue steel) */
4916       element = EL_STEEL_CHAR_N;
4917       break;
4918
4919     case 0x1623:        /* (blue steel) */
4920       element = EL_STEEL_CHAR_O;
4921       break;
4922
4923     case 0x1624:        /* (blue steel) */
4924       element = EL_STEEL_CHAR_P;
4925       break;
4926
4927     case 0x1625:        /* (blue steel) */
4928       element = EL_STEEL_CHAR_Q;
4929       break;
4930
4931     case 0x1626:        /* (blue steel) */
4932       element = EL_STEEL_CHAR_R;
4933       break;
4934
4935     case 0x1627:        /* (blue steel) */
4936       element = EL_STEEL_CHAR_S;
4937       break;
4938
4939     case 0x1628:        /* (blue steel) */
4940       element = EL_STEEL_CHAR_T;
4941       break;
4942
4943     case 0x1629:        /* (blue steel) */
4944       element = EL_STEEL_CHAR_U;
4945       break;
4946
4947     case 0x162a:        /* (blue steel) */
4948       element = EL_STEEL_CHAR_V;
4949       break;
4950
4951     case 0x162b:        /* (blue steel) */
4952       element = EL_STEEL_CHAR_W;
4953       break;
4954
4955     case 0x162c:        /* (blue steel) */
4956       element = EL_STEEL_CHAR_X;
4957       break;
4958
4959     case 0x162d:        /* (blue steel) */
4960       element = EL_STEEL_CHAR_Y;
4961       break;
4962
4963     case 0x162e:        /* (blue steel) */
4964       element = EL_STEEL_CHAR_Z;
4965       break;
4966
4967     case 0x162f:        /* (blue steel) */
4968       element = EL_STEEL_CHAR_AUMLAUT;
4969       break;
4970
4971     case 0x1630:        /* (blue steel) */
4972       element = EL_STEEL_CHAR_OUMLAUT;
4973       break;
4974
4975     case 0x1631:        /* (blue steel) */
4976       element = EL_STEEL_CHAR_UUMLAUT;
4977       break;
4978
4979     case 0x1632:        /* (blue steel) */
4980       element = EL_STEEL_CHAR_0;
4981       break;
4982
4983     case 0x1633:        /* (blue steel) */
4984       element = EL_STEEL_CHAR_1;
4985       break;
4986
4987     case 0x1634:        /* (blue steel) */
4988       element = EL_STEEL_CHAR_2;
4989       break;
4990
4991     case 0x1635:        /* (blue steel) */
4992       element = EL_STEEL_CHAR_3;
4993       break;
4994
4995     case 0x1636:        /* (blue steel) */
4996       element = EL_STEEL_CHAR_4;
4997       break;
4998
4999     case 0x1637:        /* (blue steel) */
5000       element = EL_STEEL_CHAR_5;
5001       break;
5002
5003     case 0x1638:        /* (blue steel) */
5004       element = EL_STEEL_CHAR_6;
5005       break;
5006
5007     case 0x1639:        /* (blue steel) */
5008       element = EL_STEEL_CHAR_7;
5009       break;
5010
5011     case 0x163a:        /* (blue steel) */
5012       element = EL_STEEL_CHAR_8;
5013       break;
5014
5015     case 0x163b:        /* (blue steel) */
5016       element = EL_STEEL_CHAR_9;
5017       break;
5018
5019     case 0x163c:        /* (blue steel) */
5020       element = EL_STEEL_CHAR_PERIOD;
5021       break;
5022
5023     case 0x163d:        /* (blue steel) */
5024       element = EL_STEEL_CHAR_EXCLAM;
5025       break;
5026
5027     case 0x163e:        /* (blue steel) */
5028       element = EL_STEEL_CHAR_COLON;
5029       break;
5030
5031     case 0x163f:        /* (blue steel) */
5032       element = EL_STEEL_CHAR_LESS;
5033       break;
5034
5035     case 0x1640:        /* (blue steel) */
5036       element = EL_STEEL_CHAR_GREATER;
5037       break;
5038
5039     case 0x1641:        /* (blue steel) */
5040       element = EL_STEEL_CHAR_QUESTION;
5041       break;
5042
5043     case 0x1642:        /* (blue steel) */
5044       element = EL_STEEL_CHAR_COPYRIGHT;
5045       break;
5046
5047     case 0x1643:        /* (blue steel) */
5048       element = EL_STEEL_CHAR_UP;
5049       break;
5050
5051     case 0x1644:        /* (blue steel) */
5052       element = EL_STEEL_CHAR_DOWN;
5053       break;
5054
5055     case 0x1645:        /* (blue steel) */
5056       element = EL_STEEL_CHAR_BUTTON;
5057       break;
5058
5059     case 0x1646:        /* (blue steel) */
5060       element = EL_STEEL_CHAR_PLUS;
5061       break;
5062
5063     case 0x1647:        /* (blue steel) */
5064       element = EL_STEEL_CHAR_MINUS;
5065       break;
5066
5067     case 0x1648:        /* (blue steel) */
5068       element = EL_STEEL_CHAR_APOSTROPHE;
5069       break;
5070
5071     case 0x1649:        /* (blue steel) */
5072       element = EL_STEEL_CHAR_PARENLEFT;
5073       break;
5074
5075     case 0x164a:        /* (blue steel) */
5076       element = EL_STEEL_CHAR_PARENRIGHT;
5077       break;
5078
5079     case 0x164b:        /* (green steel) */
5080       element = EL_STEEL_CHAR_A;
5081       break;
5082
5083     case 0x164c:        /* (green steel) */
5084       element = EL_STEEL_CHAR_B;
5085       break;
5086
5087     case 0x164d:        /* (green steel) */
5088       element = EL_STEEL_CHAR_C;
5089       break;
5090
5091     case 0x164e:        /* (green steel) */
5092       element = EL_STEEL_CHAR_D;
5093       break;
5094
5095     case 0x164f:        /* (green steel) */
5096       element = EL_STEEL_CHAR_E;
5097       break;
5098
5099     case 0x1650:        /* (green steel) */
5100       element = EL_STEEL_CHAR_F;
5101       break;
5102
5103     case 0x1651:        /* (green steel) */
5104       element = EL_STEEL_CHAR_G;
5105       break;
5106
5107     case 0x1652:        /* (green steel) */
5108       element = EL_STEEL_CHAR_H;
5109       break;
5110
5111     case 0x1653:        /* (green steel) */
5112       element = EL_STEEL_CHAR_I;
5113       break;
5114
5115     case 0x1654:        /* (green steel) */
5116       element = EL_STEEL_CHAR_J;
5117       break;
5118
5119     case 0x1655:        /* (green steel) */
5120       element = EL_STEEL_CHAR_K;
5121       break;
5122
5123     case 0x1656:        /* (green steel) */
5124       element = EL_STEEL_CHAR_L;
5125       break;
5126
5127     case 0x1657:        /* (green steel) */
5128       element = EL_STEEL_CHAR_M;
5129       break;
5130
5131     case 0x1658:        /* (green steel) */
5132       element = EL_STEEL_CHAR_N;
5133       break;
5134
5135     case 0x1659:        /* (green steel) */
5136       element = EL_STEEL_CHAR_O;
5137       break;
5138
5139     case 0x165a:        /* (green steel) */
5140       element = EL_STEEL_CHAR_P;
5141       break;
5142
5143     case 0x165b:        /* (green steel) */
5144       element = EL_STEEL_CHAR_Q;
5145       break;
5146
5147     case 0x165c:        /* (green steel) */
5148       element = EL_STEEL_CHAR_R;
5149       break;
5150
5151     case 0x165d:        /* (green steel) */
5152       element = EL_STEEL_CHAR_S;
5153       break;
5154
5155     case 0x165e:        /* (green steel) */
5156       element = EL_STEEL_CHAR_T;
5157       break;
5158
5159     case 0x165f:        /* (green steel) */
5160       element = EL_STEEL_CHAR_U;
5161       break;
5162
5163     case 0x1660:        /* (green steel) */
5164       element = EL_STEEL_CHAR_V;
5165       break;
5166
5167     case 0x1661:        /* (green steel) */
5168       element = EL_STEEL_CHAR_W;
5169       break;
5170
5171     case 0x1662:        /* (green steel) */
5172       element = EL_STEEL_CHAR_X;
5173       break;
5174
5175     case 0x1663:        /* (green steel) */
5176       element = EL_STEEL_CHAR_Y;
5177       break;
5178
5179     case 0x1664:        /* (green steel) */
5180       element = EL_STEEL_CHAR_Z;
5181       break;
5182
5183     case 0x1665:        /* (green steel) */
5184       element = EL_STEEL_CHAR_AUMLAUT;
5185       break;
5186
5187     case 0x1666:        /* (green steel) */
5188       element = EL_STEEL_CHAR_OUMLAUT;
5189       break;
5190
5191     case 0x1667:        /* (green steel) */
5192       element = EL_STEEL_CHAR_UUMLAUT;
5193       break;
5194
5195     case 0x1668:        /* (green steel) */
5196       element = EL_STEEL_CHAR_0;
5197       break;
5198
5199     case 0x1669:        /* (green steel) */
5200       element = EL_STEEL_CHAR_1;
5201       break;
5202
5203     case 0x166a:        /* (green steel) */
5204       element = EL_STEEL_CHAR_2;
5205       break;
5206
5207     case 0x166b:        /* (green steel) */
5208       element = EL_STEEL_CHAR_3;
5209       break;
5210
5211     case 0x166c:        /* (green steel) */
5212       element = EL_STEEL_CHAR_4;
5213       break;
5214
5215     case 0x166d:        /* (green steel) */
5216       element = EL_STEEL_CHAR_5;
5217       break;
5218
5219     case 0x166e:        /* (green steel) */
5220       element = EL_STEEL_CHAR_6;
5221       break;
5222
5223     case 0x166f:        /* (green steel) */
5224       element = EL_STEEL_CHAR_7;
5225       break;
5226
5227     case 0x1670:        /* (green steel) */
5228       element = EL_STEEL_CHAR_8;
5229       break;
5230
5231     case 0x1671:        /* (green steel) */
5232       element = EL_STEEL_CHAR_9;
5233       break;
5234
5235     case 0x1672:        /* (green steel) */
5236       element = EL_STEEL_CHAR_PERIOD;
5237       break;
5238
5239     case 0x1673:        /* (green steel) */
5240       element = EL_STEEL_CHAR_EXCLAM;
5241       break;
5242
5243     case 0x1674:        /* (green steel) */
5244       element = EL_STEEL_CHAR_COLON;
5245       break;
5246
5247     case 0x1675:        /* (green steel) */
5248       element = EL_STEEL_CHAR_LESS;
5249       break;
5250
5251     case 0x1676:        /* (green steel) */
5252       element = EL_STEEL_CHAR_GREATER;
5253       break;
5254
5255     case 0x1677:        /* (green steel) */
5256       element = EL_STEEL_CHAR_QUESTION;
5257       break;
5258
5259     case 0x1678:        /* (green steel) */
5260       element = EL_STEEL_CHAR_COPYRIGHT;
5261       break;
5262
5263     case 0x1679:        /* (green steel) */
5264       element = EL_STEEL_CHAR_UP;
5265       break;
5266
5267     case 0x167a:        /* (green steel) */
5268       element = EL_STEEL_CHAR_DOWN;
5269       break;
5270
5271     case 0x167b:        /* (green steel) */
5272       element = EL_STEEL_CHAR_BUTTON;
5273       break;
5274
5275     case 0x167c:        /* (green steel) */
5276       element = EL_STEEL_CHAR_PLUS;
5277       break;
5278
5279     case 0x167d:        /* (green steel) */
5280       element = EL_STEEL_CHAR_MINUS;
5281       break;
5282
5283     case 0x167e:        /* (green steel) */
5284       element = EL_STEEL_CHAR_APOSTROPHE;
5285       break;
5286
5287     case 0x167f:        /* (green steel) */
5288       element = EL_STEEL_CHAR_PARENLEFT;
5289       break;
5290
5291     case 0x1680:        /* (green steel) */
5292       element = EL_STEEL_CHAR_PARENRIGHT;
5293       break;
5294
5295     case 0x1681:        /* gate (red) */
5296       element = EL_EM_GATE_1;
5297       break;
5298
5299     case 0x1682:        /* secret gate (red) */
5300       element = EL_GATE_1_GRAY;
5301       break;
5302
5303     case 0x1683:        /* gate (yellow) */
5304       element = EL_EM_GATE_2;
5305       break;
5306
5307     case 0x1684:        /* secret gate (yellow) */
5308       element = EL_GATE_2_GRAY;
5309       break;
5310
5311     case 0x1685:        /* gate (blue) */
5312       element = EL_EM_GATE_4;
5313       break;
5314
5315     case 0x1686:        /* secret gate (blue) */
5316       element = EL_GATE_4_GRAY;
5317       break;
5318
5319     case 0x1687:        /* gate (green) */
5320       element = EL_EM_GATE_3;
5321       break;
5322
5323     case 0x1688:        /* secret gate (green) */
5324       element = EL_GATE_3_GRAY;
5325       break;
5326
5327     case 0x1689:        /* gate (white) */
5328       element = EL_DC_GATE_WHITE;
5329       break;
5330
5331     case 0x168a:        /* secret gate (white) */
5332       element = EL_DC_GATE_WHITE_GRAY;
5333       break;
5334
5335     case 0x168b:        /* secret gate (no key) */
5336       element = EL_DC_GATE_FAKE_GRAY;
5337       break;
5338
5339     case 0x168c:
5340       element = EL_ROBOT_WHEEL;
5341       break;
5342
5343     case 0x168d:
5344       element = EL_DC_TIMEGATE_SWITCH;
5345       break;
5346
5347     case 0x168e:
5348       element = EL_ACID_POOL_BOTTOM;
5349       break;
5350
5351     case 0x168f:
5352       element = EL_ACID_POOL_TOPLEFT;
5353       break;
5354
5355     case 0x1690:
5356       element = EL_ACID_POOL_TOPRIGHT;
5357       break;
5358
5359     case 0x1691:
5360       element = EL_ACID_POOL_BOTTOMLEFT;
5361       break;
5362
5363     case 0x1692:
5364       element = EL_ACID_POOL_BOTTOMRIGHT;
5365       break;
5366
5367     case 0x1693:
5368       element = EL_STEELWALL;
5369       break;
5370
5371     case 0x1694:
5372       element = EL_STEELWALL_SLIPPERY;
5373       break;
5374
5375     case 0x1695:        /* steel wall (not round) */
5376       element = EL_STEELWALL;
5377       break;
5378
5379     case 0x1696:        /* steel wall (left) */
5380       element = EL_DC_STEELWALL_1_LEFT;
5381       break;
5382
5383     case 0x1697:        /* steel wall (bottom) */
5384       element = EL_DC_STEELWALL_1_BOTTOM;
5385       break;
5386
5387     case 0x1698:        /* steel wall (right) */
5388       element = EL_DC_STEELWALL_1_RIGHT;
5389       break;
5390
5391     case 0x1699:        /* steel wall (top) */
5392       element = EL_DC_STEELWALL_1_TOP;
5393       break;
5394
5395     case 0x169a:        /* steel wall (left/bottom) */
5396       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5397       break;
5398
5399     case 0x169b:        /* steel wall (right/bottom) */
5400       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5401       break;
5402
5403     case 0x169c:        /* steel wall (right/top) */
5404       element = EL_DC_STEELWALL_1_TOPRIGHT;
5405       break;
5406
5407     case 0x169d:        /* steel wall (left/top) */
5408       element = EL_DC_STEELWALL_1_TOPLEFT;
5409       break;
5410
5411     case 0x169e:        /* steel wall (right/bottom small) */
5412       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5413       break;
5414
5415     case 0x169f:        /* steel wall (left/bottom small) */
5416       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5417       break;
5418
5419     case 0x16a0:        /* steel wall (right/top small) */
5420       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5421       break;
5422
5423     case 0x16a1:        /* steel wall (left/top small) */
5424       element = EL_DC_STEELWALL_1_TOPLEFT_2;
5425       break;
5426
5427     case 0x16a2:        /* steel wall (left/right) */
5428       element = EL_DC_STEELWALL_1_VERTICAL;
5429       break;
5430
5431     case 0x16a3:        /* steel wall (top/bottom) */
5432       element = EL_DC_STEELWALL_1_HORIZONTAL;
5433       break;
5434
5435     case 0x16a4:        /* steel wall 2 (left end) */
5436       element = EL_DC_STEELWALL_2_LEFT;
5437       break;
5438
5439     case 0x16a5:        /* steel wall 2 (right end) */
5440       element = EL_DC_STEELWALL_2_RIGHT;
5441       break;
5442
5443     case 0x16a6:        /* steel wall 2 (top end) */
5444       element = EL_DC_STEELWALL_2_TOP;
5445       break;
5446
5447     case 0x16a7:        /* steel wall 2 (bottom end) */
5448       element = EL_DC_STEELWALL_2_BOTTOM;
5449       break;
5450
5451     case 0x16a8:        /* steel wall 2 (left/right) */
5452       element = EL_DC_STEELWALL_2_HORIZONTAL;
5453       break;
5454
5455     case 0x16a9:        /* steel wall 2 (up/down) */
5456       element = EL_DC_STEELWALL_2_VERTICAL;
5457       break;
5458
5459     case 0x16aa:        /* steel wall 2 (mid) */
5460       element = EL_DC_STEELWALL_2_MIDDLE;
5461       break;
5462
5463     case 0x16ab:
5464       element = EL_SIGN_EXCLAMATION;
5465       break;
5466
5467     case 0x16ac:
5468       element = EL_SIGN_RADIOACTIVITY;
5469       break;
5470
5471     case 0x16ad:
5472       element = EL_SIGN_STOP;
5473       break;
5474
5475     case 0x16ae:
5476       element = EL_SIGN_WHEELCHAIR;
5477       break;
5478
5479     case 0x16af:
5480       element = EL_SIGN_PARKING;
5481       break;
5482
5483     case 0x16b0:
5484       element = EL_SIGN_NO_ENTRY;
5485       break;
5486
5487     case 0x16b1:
5488       element = EL_SIGN_HEART;
5489       break;
5490
5491     case 0x16b2:
5492       element = EL_SIGN_GIVE_WAY;
5493       break;
5494
5495     case 0x16b3:
5496       element = EL_SIGN_ENTRY_FORBIDDEN;
5497       break;
5498
5499     case 0x16b4:
5500       element = EL_SIGN_EMERGENCY_EXIT;
5501       break;
5502
5503     case 0x16b5:
5504       element = EL_SIGN_YIN_YANG;
5505       break;
5506
5507     case 0x16b6:
5508       element = EL_WALL_EMERALD;
5509       break;
5510
5511     case 0x16b7:
5512       element = EL_WALL_DIAMOND;
5513       break;
5514
5515     case 0x16b8:
5516       element = EL_WALL_PEARL;
5517       break;
5518
5519     case 0x16b9:
5520       element = EL_WALL_CRYSTAL;
5521       break;
5522
5523     case 0x16ba:
5524       element = EL_INVISIBLE_WALL;
5525       break;
5526
5527     case 0x16bb:
5528       element = EL_INVISIBLE_STEELWALL;
5529       break;
5530
5531       /* 0x16bc - 0x16cb: */
5532       /* EL_INVISIBLE_SAND */
5533
5534     case 0x16cc:
5535       element = EL_LIGHT_SWITCH;
5536       break;
5537
5538     case 0x16cd:
5539       element = EL_ENVELOPE_1;
5540       break;
5541
5542     default:
5543       if (element >= 0x0117 && element <= 0x036e)       /* (?) */
5544         element = EL_DIAMOND;
5545       else if (element >= 0x042d && element <= 0x0684)  /* (?) */
5546         element = EL_EMERALD;
5547       else if (element >= 0x157c && element <= 0x158b)
5548         element = EL_SAND;
5549       else if (element >= 0x1590 && element <= 0x159f)
5550         element = EL_DC_LANDMINE;
5551       else if (element >= 0x16bc && element <= 0x16cb)
5552         element = EL_INVISIBLE_SAND;
5553       else
5554       {
5555         Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5556         element = EL_UNKNOWN;
5557       }
5558       break;
5559   }
5560
5561   return getMappedElement(element);
5562 }
5563
5564 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5565                                        int nr)
5566 {
5567   byte header[DC_LEVEL_HEADER_SIZE];
5568   int envelope_size;
5569   int envelope_header_pos = 62;
5570   int envelope_content_pos = 94;
5571   int level_name_pos = 251;
5572   int level_author_pos = 292;
5573   int envelope_header_len;
5574   int envelope_content_len;
5575   int level_name_len;
5576   int level_author_len;
5577   int fieldx, fieldy;
5578   int num_yamyam_contents;
5579   int i, x, y;
5580
5581   getDecodedWord_DC(0, TRUE);           /* initialize DC2 decoding engine */
5582
5583   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5584   {
5585     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5586
5587     header[i * 2 + 0] = header_word >> 8;
5588     header[i * 2 + 1] = header_word & 0xff;
5589   }
5590
5591   /* read some values from level header to check level decoding integrity */
5592   fieldx = header[6] | (header[7] << 8);
5593   fieldy = header[8] | (header[9] << 8);
5594   num_yamyam_contents = header[60] | (header[61] << 8);
5595
5596   /* do some simple sanity checks to ensure that level was correctly decoded */
5597   if (fieldx < 1 || fieldx > 256 ||
5598       fieldy < 1 || fieldy > 256 ||
5599       num_yamyam_contents < 1 || num_yamyam_contents > 8)
5600   {
5601     level->no_valid_file = TRUE;
5602
5603     Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5604
5605     return;
5606   }
5607
5608   /* maximum envelope header size is 31 bytes */
5609   envelope_header_len   = header[envelope_header_pos];
5610   /* maximum envelope content size is 110 (156?) bytes */
5611   envelope_content_len  = header[envelope_content_pos];
5612
5613   /* maximum level title size is 40 bytes */
5614   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
5615   /* maximum level author size is 30 (51?) bytes */
5616   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5617
5618   envelope_size = 0;
5619
5620   for (i = 0; i < envelope_header_len; i++)
5621     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5622       level->envelope[0].text[envelope_size++] =
5623         header[envelope_header_pos + 1 + i];
5624
5625   if (envelope_header_len > 0 && envelope_content_len > 0)
5626   {
5627     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5628       level->envelope[0].text[envelope_size++] = '\n';
5629     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5630       level->envelope[0].text[envelope_size++] = '\n';
5631   }
5632
5633   for (i = 0; i < envelope_content_len; i++)
5634     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5635       level->envelope[0].text[envelope_size++] =
5636         header[envelope_content_pos + 1 + i];
5637
5638   level->envelope[0].text[envelope_size] = '\0';
5639
5640   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5641   level->envelope[0].ysize = 10;
5642   level->envelope[0].autowrap = TRUE;
5643   level->envelope[0].centered = TRUE;
5644
5645   for (i = 0; i < level_name_len; i++)
5646     level->name[i] = header[level_name_pos + 1 + i];
5647   level->name[level_name_len] = '\0';
5648
5649   for (i = 0; i < level_author_len; i++)
5650     level->author[i] = header[level_author_pos + 1 + i];
5651   level->author[level_author_len] = '\0';
5652
5653   num_yamyam_contents = header[60] | (header[61] << 8);
5654   level->num_yamyam_contents =
5655     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5656
5657   for (i = 0; i < num_yamyam_contents; i++)
5658   {
5659     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5660     {
5661       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5662       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5663
5664       if (i < MAX_ELEMENT_CONTENTS)
5665         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5666     }
5667   }
5668
5669   fieldx = header[6] | (header[7] << 8);
5670   fieldy = header[8] | (header[9] << 8);
5671   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5672   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5673
5674   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5675   {
5676     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5677     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5678
5679     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5680       level->field[x][y] = getMappedElement_DC(element_dc);
5681   }
5682
5683   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5684   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5685   level->field[x][y] = EL_PLAYER_1;
5686
5687   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5688   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5689   level->field[x][y] = EL_PLAYER_2;
5690
5691   level->gems_needed            = header[18] | (header[19] << 8);
5692
5693   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
5694   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
5695   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
5696   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
5697   level->score[SC_NUT]          = header[28] | (header[29] << 8);
5698   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
5699   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
5700   level->score[SC_BUG]          = header[34] | (header[35] << 8);
5701   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
5702   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
5703   level->score[SC_KEY]          = header[40] | (header[41] << 8);
5704   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
5705
5706   level->time                   = header[44] | (header[45] << 8);
5707
5708   level->amoeba_speed           = header[46] | (header[47] << 8);
5709   level->time_light             = header[48] | (header[49] << 8);
5710   level->time_timegate          = header[50] | (header[51] << 8);
5711   level->time_wheel             = header[52] | (header[53] << 8);
5712   level->time_magic_wall        = header[54] | (header[55] << 8);
5713   level->extra_time             = header[56] | (header[57] << 8);
5714   level->shield_normal_time     = header[58] | (header[59] << 8);
5715
5716   /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5717      can slip down from flat walls, like normal walls and steel walls */
5718   level->em_slippery_gems = TRUE;
5719 }
5720
5721 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5722                                      struct LevelFileInfo *level_file_info,
5723                                      boolean level_info_only)
5724 {
5725   char *filename = level_file_info->filename;
5726   File *file;
5727   int num_magic_bytes = 8;
5728   char magic_bytes[num_magic_bytes + 1];
5729   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5730
5731   if (!(file = openFile(filename, MODE_READ)))
5732   {
5733     level->no_valid_file = TRUE;
5734
5735     if (!level_info_only)
5736       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5737
5738     return;
5739   }
5740
5741   // fseek(file, 0x0000, SEEK_SET);
5742
5743   if (level_file_info->packed)
5744   {
5745     /* read "magic bytes" from start of file */
5746     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5747       magic_bytes[0] = '\0';
5748
5749     /* check "magic bytes" for correct file format */
5750     if (!strPrefix(magic_bytes, "DC2"))
5751     {
5752       level->no_valid_file = TRUE;
5753
5754       Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5755             filename);
5756
5757       return;
5758     }
5759
5760     if (strPrefix(magic_bytes, "DC2Win95") ||
5761         strPrefix(magic_bytes, "DC2Win98"))
5762     {
5763       int position_first_level = 0x00fa;
5764       int extra_bytes = 4;
5765       int skip_bytes;
5766
5767       /* advance file stream to first level inside the level package */
5768       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5769
5770       /* each block of level data is followed by block of non-level data */
5771       num_levels_to_skip *= 2;
5772
5773       /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5774       while (num_levels_to_skip >= 0)
5775       {
5776         /* advance file stream to next level inside the level package */
5777         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5778         {
5779           level->no_valid_file = TRUE;
5780
5781           Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5782                 filename);
5783
5784           return;
5785         }
5786
5787         /* skip apparently unused extra bytes following each level */
5788         ReadUnusedBytesFromFile(file, extra_bytes);
5789
5790         /* read size of next level in level package */
5791         skip_bytes = getFile32BitLE(file);
5792
5793         num_levels_to_skip--;
5794       }
5795     }
5796     else
5797     {
5798       level->no_valid_file = TRUE;
5799
5800       Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5801             filename);
5802
5803       return;
5804     }
5805   }
5806
5807   LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5808
5809   closeFile(file);
5810 }
5811
5812
5813 /* ------------------------------------------------------------------------- */
5814 /* functions for loading SB level                                            */
5815 /* ------------------------------------------------------------------------- */
5816
5817 int getMappedElement_SB(int element_ascii, boolean use_ces)
5818 {
5819   static struct
5820   {
5821     int ascii;
5822     int sb;
5823     int ce;
5824   }
5825   sb_element_mapping[] =
5826   {
5827     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  /* floor (space) */
5828     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  /* wall */
5829     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  /* player */
5830     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  /* box */
5831     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  /* goal square */
5832     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  /* box on goal square */
5833     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  /* player on goal square */
5834     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  /* floor beyond border */
5835
5836     { 0,   -1,                      -1          },
5837   };
5838
5839   int i;
5840
5841   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5842     if (element_ascii == sb_element_mapping[i].ascii)
5843       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5844
5845   return EL_UNDEFINED;
5846 }
5847
5848 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5849                                      struct LevelFileInfo *level_file_info,
5850                                      boolean level_info_only)
5851 {
5852   char *filename = level_file_info->filename;
5853   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5854   char last_comment[MAX_LINE_LEN];
5855   char level_name[MAX_LINE_LEN];
5856   char *line_ptr;
5857   File *file;
5858   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5859   boolean read_continued_line = FALSE;
5860   boolean reading_playfield = FALSE;
5861   boolean got_valid_playfield_line = FALSE;
5862   boolean invalid_playfield_char = FALSE;
5863   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5864   int file_level_nr = 0;
5865   int line_nr = 0;
5866   int x = 0, y = 0;             /* initialized to make compilers happy */
5867
5868   last_comment[0] = '\0';
5869   level_name[0] = '\0';
5870
5871   if (!(file = openFile(filename, MODE_READ)))
5872   {
5873     level->no_valid_file = TRUE;
5874
5875     if (!level_info_only)
5876       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5877
5878     return;
5879   }
5880
5881   while (!checkEndOfFile(file))
5882   {
5883     /* level successfully read, but next level may follow here */
5884     if (!got_valid_playfield_line && reading_playfield)
5885     {
5886       /* read playfield from single level file -- skip remaining file */
5887       if (!level_file_info->packed)
5888         break;
5889
5890       if (file_level_nr >= num_levels_to_skip)
5891         break;
5892
5893       file_level_nr++;
5894
5895       last_comment[0] = '\0';
5896       level_name[0] = '\0';
5897
5898       reading_playfield = FALSE;
5899     }
5900
5901     got_valid_playfield_line = FALSE;
5902
5903     /* read next line of input file */
5904     if (!getStringFromFile(file, line, MAX_LINE_LEN))
5905       break;
5906
5907     /* check if line was completely read and is terminated by line break */
5908     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5909       line_nr++;
5910
5911     /* cut trailing line break (this can be newline and/or carriage return) */
5912     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5913       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5914         *line_ptr = '\0';
5915
5916     /* copy raw input line for later use (mainly debugging output) */
5917     strcpy(line_raw, line);
5918
5919     if (read_continued_line)
5920     {
5921       /* append new line to existing line, if there is enough space */
5922       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5923         strcat(previous_line, line_ptr);
5924
5925       strcpy(line, previous_line);      /* copy storage buffer to line */
5926
5927       read_continued_line = FALSE;
5928     }
5929
5930     /* if the last character is '\', continue at next line */
5931     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5932     {
5933       line[strlen(line) - 1] = '\0';    /* cut off trailing backslash */
5934       strcpy(previous_line, line);      /* copy line to storage buffer */
5935
5936       read_continued_line = TRUE;
5937
5938       continue;
5939     }
5940
5941     /* skip empty lines */
5942     if (line[0] == '\0')
5943       continue;
5944
5945     /* extract comment text from comment line */
5946     if (line[0] == ';')
5947     {
5948       for (line_ptr = line; *line_ptr; line_ptr++)
5949         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5950           break;
5951
5952       strcpy(last_comment, line_ptr);
5953
5954       continue;
5955     }
5956
5957     /* extract level title text from line containing level title */
5958     if (line[0] == '\'')
5959     {
5960       strcpy(level_name, &line[1]);
5961
5962       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5963         level_name[strlen(level_name) - 1] = '\0';
5964
5965       continue;
5966     }
5967
5968     /* skip lines containing only spaces (or empty lines) */
5969     for (line_ptr = line; *line_ptr; line_ptr++)
5970       if (*line_ptr != ' ')
5971         break;
5972     if (*line_ptr == '\0')
5973       continue;
5974
5975     /* at this point, we have found a line containing part of a playfield */
5976
5977     got_valid_playfield_line = TRUE;
5978
5979     if (!reading_playfield)
5980     {
5981       reading_playfield = TRUE;
5982       invalid_playfield_char = FALSE;
5983
5984       for (x = 0; x < MAX_LEV_FIELDX; x++)
5985         for (y = 0; y < MAX_LEV_FIELDY; y++)
5986           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5987
5988       level->fieldx = 0;
5989       level->fieldy = 0;
5990
5991       /* start with topmost tile row */
5992       y = 0;
5993     }
5994
5995     /* skip playfield line if larger row than allowed */
5996     if (y >= MAX_LEV_FIELDY)
5997       continue;
5998
5999     /* start with leftmost tile column */
6000     x = 0;
6001
6002     /* read playfield elements from line */
6003     for (line_ptr = line; *line_ptr; line_ptr++)
6004     {
6005       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6006
6007       /* stop parsing playfield line if larger column than allowed */
6008       if (x >= MAX_LEV_FIELDX)
6009         break;
6010
6011       if (mapped_sb_element == EL_UNDEFINED)
6012       {
6013         invalid_playfield_char = TRUE;
6014
6015         break;
6016       }
6017
6018       level->field[x][y] = mapped_sb_element;
6019
6020       /* continue with next tile column */
6021       x++;
6022
6023       level->fieldx = MAX(x, level->fieldx);
6024     }
6025
6026     if (invalid_playfield_char)
6027     {
6028       /* if first playfield line, treat invalid lines as comment lines */
6029       if (y == 0)
6030         reading_playfield = FALSE;
6031
6032       continue;
6033     }
6034
6035     /* continue with next tile row */
6036     y++;
6037   }
6038
6039   closeFile(file);
6040
6041   level->fieldy = y;
6042
6043   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6044   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6045
6046   if (!reading_playfield)
6047   {
6048     level->no_valid_file = TRUE;
6049
6050     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6051
6052     return;
6053   }
6054
6055   if (*level_name != '\0')
6056   {
6057     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6058     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6059   }
6060   else if (*last_comment != '\0')
6061   {
6062     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6063     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6064   }
6065   else
6066   {
6067     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6068   }
6069
6070   /* set all empty fields beyond the border walls to invisible steel wall */
6071   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6072   {
6073     if ((x == 0 || x == level->fieldx - 1 ||
6074          y == 0 || y == level->fieldy - 1) &&
6075         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6076       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6077                      level->field, level->fieldx, level->fieldy);
6078   }
6079
6080   /* set special level settings for Sokoban levels */
6081
6082   level->time = 0;
6083   level->use_step_counter = TRUE;
6084
6085   if (load_xsb_to_ces)
6086   {
6087     /* special global settings can now be set in level template */
6088
6089     level->use_custom_template = TRUE;
6090   }
6091 }
6092
6093
6094 /* ------------------------------------------------------------------------- */
6095 /* functions for handling native levels                                      */
6096 /* ------------------------------------------------------------------------- */
6097
6098 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6099                                      struct LevelFileInfo *level_file_info,
6100                                      boolean level_info_only)
6101 {
6102   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6103     level->no_valid_file = TRUE;
6104 }
6105
6106 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6107                                      struct LevelFileInfo *level_file_info,
6108                                      boolean level_info_only)
6109 {
6110   int pos = 0;
6111
6112   /* determine position of requested level inside level package */
6113   if (level_file_info->packed)
6114     pos = level_file_info->nr - leveldir_current->first_level;
6115
6116   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6117     level->no_valid_file = TRUE;
6118 }
6119
6120 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6121                                      struct LevelFileInfo *level_file_info,
6122                                      boolean level_info_only)
6123 {
6124   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6125     level->no_valid_file = TRUE;
6126 }
6127
6128 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6129 {
6130   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6131     CopyNativeLevel_RND_to_EM(level);
6132   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6133     CopyNativeLevel_RND_to_SP(level);
6134   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6135     CopyNativeLevel_RND_to_MM(level);
6136 }
6137
6138 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6139 {
6140   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6141     CopyNativeLevel_EM_to_RND(level);
6142   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6143     CopyNativeLevel_SP_to_RND(level);
6144   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6145     CopyNativeLevel_MM_to_RND(level);
6146 }
6147
6148 void SaveNativeLevel(struct LevelInfo *level)
6149 {
6150   if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6151   {
6152     char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6153     char *filename = getLevelFilenameFromBasename(basename);
6154
6155     CopyNativeLevel_RND_to_SP(level);
6156     CopyNativeTape_RND_to_SP(level);
6157
6158     SaveNativeLevel_SP(filename);
6159   }
6160 }
6161
6162
6163 /* ------------------------------------------------------------------------- */
6164 /* functions for loading generic level                                       */
6165 /* ------------------------------------------------------------------------- */
6166
6167 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6168                                   struct LevelFileInfo *level_file_info,
6169                                   boolean level_info_only)
6170 {
6171   /* always start with reliable default values */
6172   setLevelInfoToDefaults(level, level_info_only, TRUE);
6173
6174   switch (level_file_info->type)
6175   {
6176     case LEVEL_FILE_TYPE_RND:
6177       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6178       break;
6179
6180     case LEVEL_FILE_TYPE_EM:
6181       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6182       level->game_engine_type = GAME_ENGINE_TYPE_EM;
6183       break;
6184
6185     case LEVEL_FILE_TYPE_SP:
6186       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6187       level->game_engine_type = GAME_ENGINE_TYPE_SP;
6188       break;
6189
6190     case LEVEL_FILE_TYPE_MM:
6191       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6192       level->game_engine_type = GAME_ENGINE_TYPE_MM;
6193       break;
6194
6195     case LEVEL_FILE_TYPE_DC:
6196       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6197       break;
6198
6199     case LEVEL_FILE_TYPE_SB:
6200       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6201       break;
6202
6203     default:
6204       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6205       break;
6206   }
6207
6208   /* if level file is invalid, restore level structure to default values */
6209   if (level->no_valid_file)
6210     setLevelInfoToDefaults(level, level_info_only, FALSE);
6211
6212   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6213     level->game_engine_type = GAME_ENGINE_TYPE_RND;
6214
6215   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6216     CopyNativeLevel_Native_to_RND(level);
6217 }
6218
6219 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6220 {
6221   static struct LevelFileInfo level_file_info;
6222
6223   /* always start with reliable default values */
6224   setFileInfoToDefaults(&level_file_info);
6225
6226   level_file_info.nr = 0;                       /* unknown level number */
6227   level_file_info.type = LEVEL_FILE_TYPE_RND;   /* no others supported yet */
6228
6229   setString(&level_file_info.filename, filename);
6230
6231   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6232 }
6233
6234 static void LoadLevel_InitVersion(struct LevelInfo *level)
6235 {
6236   int i, j;
6237
6238   if (leveldir_current == NULL)         /* only when dumping level */
6239     return;
6240
6241   /* all engine modifications also valid for levels which use latest engine */
6242   if (level->game_version < VERSION_IDENT(3,2,0,5))
6243   {
6244     /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6245     level->score[SC_TIME_BONUS] /= 10;
6246   }
6247
6248   if (leveldir_current->latest_engine)
6249   {
6250     /* ---------- use latest game engine ----------------------------------- */
6251
6252     /* For all levels which are forced to use the latest game engine version
6253        (normally all but user contributed, private and undefined levels), set
6254        the game engine version to the actual version; this allows for actual
6255        corrections in the game engine to take effect for existing, converted
6256        levels (from "classic" or other existing games) to make the emulation
6257        of the corresponding game more accurate, while (hopefully) not breaking
6258        existing levels created from other players. */
6259
6260     level->game_version = GAME_VERSION_ACTUAL;
6261
6262     /* Set special EM style gems behaviour: EM style gems slip down from
6263        normal, steel and growing wall. As this is a more fundamental change,
6264        it seems better to set the default behaviour to "off" (as it is more
6265        natural) and make it configurable in the level editor (as a property
6266        of gem style elements). Already existing converted levels (neither
6267        private nor contributed levels) are changed to the new behaviour. */
6268
6269     if (level->file_version < FILE_VERSION_2_0)
6270       level->em_slippery_gems = TRUE;
6271
6272     return;
6273   }
6274
6275   /* ---------- use game engine the level was created with ----------------- */
6276
6277   /* For all levels which are not forced to use the latest game engine
6278      version (normally user contributed, private and undefined levels),
6279      use the version of the game engine the levels were created for.
6280
6281      Since 2.0.1, the game engine version is now directly stored
6282      in the level file (chunk "VERS"), so there is no need anymore
6283      to set the game version from the file version (except for old,
6284      pre-2.0 levels, where the game version is still taken from the
6285      file format version used to store the level -- see above). */
6286
6287   /* player was faster than enemies in 1.0.0 and before */
6288   if (level->file_version == FILE_VERSION_1_0)
6289     for (i = 0; i < MAX_PLAYERS; i++)
6290       level->initial_player_stepsize[i] = STEPSIZE_FAST;
6291
6292   /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6293   if (level->game_version == VERSION_IDENT(2,0,1,0))
6294     level->em_slippery_gems = TRUE;
6295
6296   /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6297   if (level->game_version < VERSION_IDENT(2,2,0,0))
6298     level->use_spring_bug = TRUE;
6299
6300   if (level->game_version < VERSION_IDENT(3,2,0,5))
6301   {
6302     /* time orb caused limited time in endless time levels before 3.2.0-5 */
6303     level->use_time_orb_bug = TRUE;
6304
6305     /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6306     level->block_snap_field = FALSE;
6307
6308     /* extra time score was same value as time left score before 3.2.0-5 */
6309     level->extra_time_score = level->score[SC_TIME_BONUS];
6310   }
6311
6312   if (level->game_version < VERSION_IDENT(3,2,0,7))
6313   {
6314     /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6315     level->continuous_snapping = FALSE;
6316   }
6317
6318   /* only few elements were able to actively move into acid before 3.1.0 */
6319   /* trigger settings did not exist before 3.1.0; set to default "any" */
6320   if (level->game_version < VERSION_IDENT(3,1,0,0))
6321   {
6322     /* correct "can move into acid" settings (all zero in old levels) */
6323
6324     level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6325     level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6326
6327     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
6328     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6329     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
6330     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
6331
6332     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6333       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6334
6335     /* correct trigger settings (stored as zero == "none" in old levels) */
6336
6337     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6338     {
6339       int element = EL_CUSTOM_START + i;
6340       struct ElementInfo *ei = &element_info[element];
6341
6342       for (j = 0; j < ei->num_change_pages; j++)
6343       {
6344         struct ElementChangeInfo *change = &ei->change_page[j];
6345
6346         change->trigger_player = CH_PLAYER_ANY;
6347         change->trigger_page = CH_PAGE_ANY;
6348       }
6349     }
6350   }
6351
6352   /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6353   {
6354     int element = EL_CUSTOM_256;
6355     struct ElementInfo *ei = &element_info[element];
6356     struct ElementChangeInfo *change = &ei->change_page[0];
6357
6358     /* This is needed to fix a problem that was caused by a bugfix in function
6359        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6360        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6361        not replace walkable elements, but instead just placed the player on it,
6362        without placing the Sokoban field under the player). Unfortunately, this
6363        breaks "Snake Bite" style levels when the snake is halfway through a door
6364        that just closes (the snake head is still alive and can be moved in this
6365        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6366        player (without Sokoban element) which then gets killed as designed). */
6367
6368     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6369          strncmp(ei->description, "pause b4 death", 14) == 0) &&
6370         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6371       change->target_element = EL_PLAYER_1;
6372   }
6373
6374   /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6375   if (level->game_version < VERSION_IDENT(3,2,5,0))
6376   {
6377     /* This is needed to fix a problem that was caused by a bugfix in function
6378        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6379        corrects the behaviour when a custom element changes to another custom
6380        element with a higher element number that has change actions defined.
6381        Normally, only one change per frame is allowed for custom elements.
6382        Therefore, it is checked if a custom element already changed in the
6383        current frame; if it did, subsequent changes are suppressed.
6384        Unfortunately, this is only checked for element changes, but not for
6385        change actions, which are still executed. As the function above loops
6386        through all custom elements from lower to higher, an element change
6387        resulting in a lower CE number won't be checked again, while a target
6388        element with a higher number will also be checked, and potential change
6389        actions will get executed for this CE, too (which is wrong), while
6390        further changes are ignored (which is correct). As this bugfix breaks
6391        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6392        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6393        behaviour for existing levels and tapes that make use of this bug */
6394
6395     level->use_action_after_change_bug = TRUE;
6396   }
6397
6398   /* not centering level after relocating player was default only in 3.2.3 */
6399   if (level->game_version == VERSION_IDENT(3,2,3,0))    /* (no pre-releases) */
6400     level->shifted_relocation = TRUE;
6401
6402   /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6403   if (level->game_version < VERSION_IDENT(3,2,6,0))
6404     level->em_explodes_by_fire = TRUE;
6405
6406   /* levels were solved by the first player entering an exit up to 4.1.0.0 */
6407   if (level->game_version <= VERSION_IDENT(4,1,0,0))
6408     level->solved_by_one_player = TRUE;
6409 }
6410
6411 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6412 {
6413   int i, x, y;
6414
6415   /* map elements that have changed in newer versions */
6416   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6417                                                     level->game_version);
6418   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6419     for (x = 0; x < 3; x++)
6420       for (y = 0; y < 3; y++)
6421         level->yamyam_content[i].e[x][y] =
6422           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6423                                     level->game_version);
6424
6425 }
6426
6427 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6428 {
6429   int i, j;
6430
6431   /* map custom element change events that have changed in newer versions
6432      (these following values were accidentally changed in version 3.0.1)
6433      (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6434   if (level->game_version <= VERSION_IDENT(3,0,0,0))
6435   {
6436     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6437     {
6438       int element = EL_CUSTOM_START + i;
6439
6440       /* order of checking and copying events to be mapped is important */
6441       /* (do not change the start and end value -- they are constant) */
6442       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6443       {
6444         if (HAS_CHANGE_EVENT(element, j - 2))
6445         {
6446           SET_CHANGE_EVENT(element, j - 2, FALSE);
6447           SET_CHANGE_EVENT(element, j, TRUE);
6448         }
6449       }
6450
6451       /* order of checking and copying events to be mapped is important */
6452       /* (do not change the start and end value -- they are constant) */
6453       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6454       {
6455         if (HAS_CHANGE_EVENT(element, j - 1))
6456         {
6457           SET_CHANGE_EVENT(element, j - 1, FALSE);
6458           SET_CHANGE_EVENT(element, j, TRUE);
6459         }
6460       }
6461     }
6462   }
6463
6464   /* initialize "can_change" field for old levels with only one change page */
6465   if (level->game_version <= VERSION_IDENT(3,0,2,0))
6466   {
6467     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6468     {
6469       int element = EL_CUSTOM_START + i;
6470
6471       if (CAN_CHANGE(element))
6472         element_info[element].change->can_change = TRUE;
6473     }
6474   }
6475
6476   /* correct custom element values (for old levels without these options) */
6477   if (level->game_version < VERSION_IDENT(3,1,1,0))
6478   {
6479     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6480     {
6481       int element = EL_CUSTOM_START + i;
6482       struct ElementInfo *ei = &element_info[element];
6483
6484       if (ei->access_direction == MV_NO_DIRECTION)
6485         ei->access_direction = MV_ALL_DIRECTIONS;
6486     }
6487   }
6488
6489   /* correct custom element values (fix invalid values for all versions) */
6490   if (1)
6491   {
6492     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6493     {
6494       int element = EL_CUSTOM_START + i;
6495       struct ElementInfo *ei = &element_info[element];
6496
6497       for (j = 0; j < ei->num_change_pages; j++)
6498       {
6499         struct ElementChangeInfo *change = &ei->change_page[j];
6500
6501         if (change->trigger_player == CH_PLAYER_NONE)
6502           change->trigger_player = CH_PLAYER_ANY;
6503
6504         if (change->trigger_side == CH_SIDE_NONE)
6505           change->trigger_side = CH_SIDE_ANY;
6506       }
6507     }
6508   }
6509
6510   /* initialize "can_explode" field for old levels which did not store this */
6511   /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6512   if (level->game_version <= VERSION_IDENT(3,1,0,0))
6513   {
6514     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6515     {
6516       int element = EL_CUSTOM_START + i;
6517
6518       if (EXPLODES_1X1_OLD(element))
6519         element_info[element].explosion_type = EXPLODES_1X1;
6520
6521       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6522                                              EXPLODES_SMASHED(element) ||
6523                                              EXPLODES_IMPACT(element)));
6524     }
6525   }
6526
6527   /* correct previously hard-coded move delay values for maze runner style */
6528   if (level->game_version < VERSION_IDENT(3,1,1,0))
6529   {
6530     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6531     {
6532       int element = EL_CUSTOM_START + i;
6533
6534       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6535       {
6536         /* previously hard-coded and therefore ignored */
6537         element_info[element].move_delay_fixed = 9;
6538         element_info[element].move_delay_random = 0;
6539       }
6540     }
6541   }
6542
6543   /* set some other uninitialized values of custom elements in older levels */
6544   if (level->game_version < VERSION_IDENT(3,1,0,0))
6545   {
6546     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6547     {
6548       int element = EL_CUSTOM_START + i;
6549
6550       element_info[element].access_direction = MV_ALL_DIRECTIONS;
6551
6552       element_info[element].explosion_delay = 17;
6553       element_info[element].ignition_delay = 8;
6554     }
6555   }
6556 }
6557
6558 static void LoadLevel_InitElements(struct LevelInfo *level)
6559 {
6560   LoadLevel_InitStandardElements(level);
6561
6562   if (level->file_has_custom_elements)
6563     LoadLevel_InitCustomElements(level);
6564
6565   /* initialize element properties for level editor etc. */
6566   InitElementPropertiesEngine(level->game_version);
6567   InitElementPropertiesGfxElement();
6568 }
6569
6570 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6571 {
6572   int x, y;
6573
6574   /* map elements that have changed in newer versions */
6575   for (y = 0; y < level->fieldy; y++)
6576     for (x = 0; x < level->fieldx; x++)
6577       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6578                                                      level->game_version);
6579
6580   /* clear unused playfield data (nicer if level gets resized in editor) */
6581   for (x = 0; x < MAX_LEV_FIELDX; x++)
6582     for (y = 0; y < MAX_LEV_FIELDY; y++)
6583       if (x >= level->fieldx || y >= level->fieldy)
6584         level->field[x][y] = EL_EMPTY;
6585
6586   /* copy elements to runtime playfield array */
6587   for (x = 0; x < MAX_LEV_FIELDX; x++)
6588     for (y = 0; y < MAX_LEV_FIELDY; y++)
6589       Feld[x][y] = level->field[x][y];
6590
6591   /* initialize level size variables for faster access */
6592   lev_fieldx = level->fieldx;
6593   lev_fieldy = level->fieldy;
6594
6595   /* determine border element for this level */
6596   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6597     BorderElement = EL_EMPTY;   /* (in editor, SetBorderElement() is used) */
6598   else
6599     SetBorderElement();
6600 }
6601
6602 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6603 {
6604   struct LevelFileInfo *level_file_info = &level->file_info;
6605
6606   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6607     CopyNativeLevel_RND_to_Native(level);
6608 }
6609
6610 static void LoadLevelTemplate_LoadAndInit(void)
6611 {
6612   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6613
6614   LoadLevel_InitVersion(&level_template);
6615   LoadLevel_InitElements(&level_template);
6616
6617   ActivateLevelTemplate();
6618 }
6619
6620 void LoadLevelTemplate(int nr)
6621 {
6622   if (!fileExists(getGlobalLevelTemplateFilename()))
6623   {
6624     Error(ERR_WARN, "no level template found for this level");
6625
6626     return;
6627   }
6628
6629   setLevelFileInfo(&level_template.file_info, nr);
6630
6631   LoadLevelTemplate_LoadAndInit();
6632 }
6633
6634 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6635 {
6636   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6637
6638   LoadLevelTemplate_LoadAndInit();
6639 }
6640
6641 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6642 {
6643   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6644
6645   if (level.use_custom_template)
6646   {
6647     if (network_level != NULL)
6648       LoadNetworkLevelTemplate(network_level);
6649     else
6650       LoadLevelTemplate(-1);
6651   }
6652
6653   LoadLevel_InitVersion(&level);
6654   LoadLevel_InitElements(&level);
6655   LoadLevel_InitPlayfield(&level);
6656
6657   LoadLevel_InitNativeEngines(&level);
6658 }
6659
6660 void LoadLevel(int nr)
6661 {
6662   SetLevelSetInfo(leveldir_current->identifier, nr);
6663
6664   setLevelFileInfo(&level.file_info, nr);
6665
6666   LoadLevel_LoadAndInit(NULL);
6667 }
6668
6669 void LoadLevelInfoOnly(int nr)
6670 {
6671   setLevelFileInfo(&level.file_info, nr);
6672
6673   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6674 }
6675
6676 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6677 {
6678   SetLevelSetInfo(network_level->leveldir_identifier,
6679                   network_level->file_info.nr);
6680
6681   copyLevelFileInfo(&network_level->file_info, &level.file_info);
6682
6683   LoadLevel_LoadAndInit(network_level);
6684 }
6685
6686 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6687 {
6688   int chunk_size = 0;
6689
6690   chunk_size += putFileVersion(file, level->file_version);
6691   chunk_size += putFileVersion(file, level->game_version);
6692
6693   return chunk_size;
6694 }
6695
6696 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6697 {
6698   int chunk_size = 0;
6699
6700   chunk_size += putFile16BitBE(file, level->creation_date.year);
6701   chunk_size += putFile8Bit(file,    level->creation_date.month);
6702   chunk_size += putFile8Bit(file,    level->creation_date.day);
6703
6704   return chunk_size;
6705 }
6706
6707 #if ENABLE_HISTORIC_CHUNKS
6708 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6709 {
6710   int i, x, y;
6711
6712   putFile8Bit(file, level->fieldx);
6713   putFile8Bit(file, level->fieldy);
6714
6715   putFile16BitBE(file, level->time);
6716   putFile16BitBE(file, level->gems_needed);
6717
6718   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6719     putFile8Bit(file, level->name[i]);
6720
6721   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6722     putFile8Bit(file, level->score[i]);
6723
6724   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6725     for (y = 0; y < 3; y++)
6726       for (x = 0; x < 3; x++)
6727         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6728                            level->yamyam_content[i].e[x][y]));
6729   putFile8Bit(file, level->amoeba_speed);
6730   putFile8Bit(file, level->time_magic_wall);
6731   putFile8Bit(file, level->time_wheel);
6732   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6733                      level->amoeba_content));
6734   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6735   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6736   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6737   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6738
6739   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6740
6741   putFile8Bit(file, (level->block_last_field ? 1 : 0));
6742   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6743   putFile32BitBE(file, level->can_move_into_acid_bits);
6744   putFile8Bit(file, level->dont_collide_with_bits);
6745
6746   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6747   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6748
6749   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6750   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6751   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6752
6753   putFile8Bit(file, level->game_engine_type);
6754
6755   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6756 }
6757 #endif
6758
6759 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6760 {
6761   int chunk_size = 0;
6762   int i;
6763
6764   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6765     chunk_size += putFile8Bit(file, level->name[i]);
6766
6767   return chunk_size;
6768 }
6769
6770 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6771 {
6772   int chunk_size = 0;
6773   int i;
6774
6775   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6776     chunk_size += putFile8Bit(file, level->author[i]);
6777
6778   return chunk_size;
6779 }
6780
6781 #if ENABLE_HISTORIC_CHUNKS
6782 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6783 {
6784   int chunk_size = 0;
6785   int x, y;
6786
6787   for (y = 0; y < level->fieldy; y++) 
6788     for (x = 0; x < level->fieldx; x++) 
6789       if (level->encoding_16bit_field)
6790         chunk_size += putFile16BitBE(file, level->field[x][y]);
6791       else
6792         chunk_size += putFile8Bit(file, level->field[x][y]);
6793
6794   return chunk_size;
6795 }
6796 #endif
6797
6798 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6799 {
6800   int chunk_size = 0;
6801   int x, y;
6802
6803   for (y = 0; y < level->fieldy; y++) 
6804     for (x = 0; x < level->fieldx; x++) 
6805       chunk_size += putFile16BitBE(file, level->field[x][y]);
6806
6807   return chunk_size;
6808 }
6809
6810 #if ENABLE_HISTORIC_CHUNKS
6811 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6812 {
6813   int i, x, y;
6814
6815   putFile8Bit(file, EL_YAMYAM);
6816   putFile8Bit(file, level->num_yamyam_contents);
6817   putFile8Bit(file, 0);
6818   putFile8Bit(file, 0);
6819
6820   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6821     for (y = 0; y < 3; y++)
6822       for (x = 0; x < 3; x++)
6823         if (level->encoding_16bit_field)
6824           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6825         else
6826           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6827 }
6828 #endif
6829
6830 #if ENABLE_HISTORIC_CHUNKS
6831 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6832 {
6833   int i, x, y;
6834   int num_contents, content_xsize, content_ysize;
6835   int content_array[MAX_ELEMENT_CONTENTS][3][3];
6836
6837   if (element == EL_YAMYAM)
6838   {
6839     num_contents = level->num_yamyam_contents;
6840     content_xsize = 3;
6841     content_ysize = 3;
6842
6843     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6844       for (y = 0; y < 3; y++)
6845         for (x = 0; x < 3; x++)
6846           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6847   }
6848   else if (element == EL_BD_AMOEBA)
6849   {
6850     num_contents = 1;
6851     content_xsize = 1;
6852     content_ysize = 1;
6853
6854     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6855       for (y = 0; y < 3; y++)
6856         for (x = 0; x < 3; x++)
6857           content_array[i][x][y] = EL_EMPTY;
6858     content_array[0][0][0] = level->amoeba_content;
6859   }
6860   else
6861   {
6862     /* chunk header already written -- write empty chunk data */
6863     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6864
6865     Error(ERR_WARN, "cannot save content for element '%d'", element);
6866     return;
6867   }
6868
6869   putFile16BitBE(file, element);
6870   putFile8Bit(file, num_contents);
6871   putFile8Bit(file, content_xsize);
6872   putFile8Bit(file, content_ysize);
6873
6874   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6875
6876   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6877     for (y = 0; y < 3; y++)
6878       for (x = 0; x < 3; x++)
6879         putFile16BitBE(file, content_array[i][x][y]);
6880 }
6881 #endif
6882
6883 #if ENABLE_HISTORIC_CHUNKS
6884 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6885 {
6886   int envelope_nr = element - EL_ENVELOPE_1;
6887   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6888   int chunk_size = 0;
6889   int i;
6890
6891   chunk_size += putFile16BitBE(file, element);
6892   chunk_size += putFile16BitBE(file, envelope_len);
6893   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6894   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6895
6896   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6897   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6898
6899   for (i = 0; i < envelope_len; i++)
6900     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6901
6902   return chunk_size;
6903 }
6904 #endif
6905
6906 #if ENABLE_HISTORIC_CHUNKS
6907 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6908                            int num_changed_custom_elements)
6909 {
6910   int i, check = 0;
6911
6912   putFile16BitBE(file, num_changed_custom_elements);
6913
6914   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6915   {
6916     int element = EL_CUSTOM_START + i;
6917
6918     struct ElementInfo *ei = &element_info[element];
6919
6920     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6921     {
6922       if (check < num_changed_custom_elements)
6923       {
6924         putFile16BitBE(file, element);
6925         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6926       }
6927
6928       check++;
6929     }
6930   }
6931
6932   if (check != num_changed_custom_elements)     /* should not happen */
6933     Error(ERR_WARN, "inconsistent number of custom element properties");
6934 }
6935 #endif
6936
6937 #if ENABLE_HISTORIC_CHUNKS
6938 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6939                            int num_changed_custom_elements)
6940 {
6941   int i, check = 0;
6942
6943   putFile16BitBE(file, num_changed_custom_elements);
6944
6945   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6946   {
6947     int element = EL_CUSTOM_START + i;
6948
6949     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6950     {
6951       if (check < num_changed_custom_elements)
6952       {
6953         putFile16BitBE(file, element);
6954         putFile16BitBE(file, element_info[element].change->target_element);
6955       }
6956
6957       check++;
6958     }
6959   }
6960
6961   if (check != num_changed_custom_elements)     /* should not happen */
6962     Error(ERR_WARN, "inconsistent number of custom target elements");
6963 }
6964 #endif
6965
6966 #if ENABLE_HISTORIC_CHUNKS
6967 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6968                            int num_changed_custom_elements)
6969 {
6970   int i, j, x, y, check = 0;
6971
6972   putFile16BitBE(file, num_changed_custom_elements);
6973
6974   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6975   {
6976     int element = EL_CUSTOM_START + i;
6977     struct ElementInfo *ei = &element_info[element];
6978
6979     if (ei->modified_settings)
6980     {
6981       if (check < num_changed_custom_elements)
6982       {
6983         putFile16BitBE(file, element);
6984
6985         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6986           putFile8Bit(file, ei->description[j]);
6987
6988         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6989
6990         /* some free bytes for future properties and padding */
6991         WriteUnusedBytesToFile(file, 7);
6992
6993         putFile8Bit(file, ei->use_gfx_element);
6994         putFile16BitBE(file, ei->gfx_element_initial);
6995
6996         putFile8Bit(file, ei->collect_score_initial);
6997         putFile8Bit(file, ei->collect_count_initial);
6998
6999         putFile16BitBE(file, ei->push_delay_fixed);
7000         putFile16BitBE(file, ei->push_delay_random);
7001         putFile16BitBE(file, ei->move_delay_fixed);
7002         putFile16BitBE(file, ei->move_delay_random);
7003
7004         putFile16BitBE(file, ei->move_pattern);
7005         putFile8Bit(file, ei->move_direction_initial);
7006         putFile8Bit(file, ei->move_stepsize);
7007
7008         for (y = 0; y < 3; y++)
7009           for (x = 0; x < 3; x++)
7010             putFile16BitBE(file, ei->content.e[x][y]);
7011
7012         putFile32BitBE(file, ei->change->events);
7013
7014         putFile16BitBE(file, ei->change->target_element);
7015
7016         putFile16BitBE(file, ei->change->delay_fixed);
7017         putFile16BitBE(file, ei->change->delay_random);
7018         putFile16BitBE(file, ei->change->delay_frames);
7019
7020         putFile16BitBE(file, ei->change->initial_trigger_element);
7021
7022         putFile8Bit(file, ei->change->explode);
7023         putFile8Bit(file, ei->change->use_target_content);
7024         putFile8Bit(file, ei->change->only_if_complete);
7025         putFile8Bit(file, ei->change->use_random_replace);
7026
7027         putFile8Bit(file, ei->change->random_percentage);
7028         putFile8Bit(file, ei->change->replace_when);
7029
7030         for (y = 0; y < 3; y++)
7031           for (x = 0; x < 3; x++)
7032             putFile16BitBE(file, ei->change->content.e[x][y]);
7033
7034         putFile8Bit(file, ei->slippery_type);
7035
7036         /* some free bytes for future properties and padding */
7037         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7038       }
7039
7040       check++;
7041     }
7042   }
7043
7044   if (check != num_changed_custom_elements)     /* should not happen */
7045     Error(ERR_WARN, "inconsistent number of custom element properties");
7046 }
7047 #endif
7048
7049 #if ENABLE_HISTORIC_CHUNKS
7050 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7051 {
7052   struct ElementInfo *ei = &element_info[element];
7053   int i, j, x, y;
7054
7055   /* ---------- custom element base property values (96 bytes) ------------- */
7056
7057   putFile16BitBE(file, element);
7058
7059   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7060     putFile8Bit(file, ei->description[i]);
7061
7062   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7063
7064   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
7065
7066   putFile8Bit(file, ei->num_change_pages);
7067
7068   putFile16BitBE(file, ei->ce_value_fixed_initial);
7069   putFile16BitBE(file, ei->ce_value_random_initial);
7070   putFile8Bit(file, ei->use_last_ce_value);
7071
7072   putFile8Bit(file, ei->use_gfx_element);
7073   putFile16BitBE(file, ei->gfx_element_initial);
7074
7075   putFile8Bit(file, ei->collect_score_initial);
7076   putFile8Bit(file, ei->collect_count_initial);
7077
7078   putFile8Bit(file, ei->drop_delay_fixed);
7079   putFile8Bit(file, ei->push_delay_fixed);
7080   putFile8Bit(file, ei->drop_delay_random);
7081   putFile8Bit(file, ei->push_delay_random);
7082   putFile16BitBE(file, ei->move_delay_fixed);
7083   putFile16BitBE(file, ei->move_delay_random);
7084
7085   /* bits 0 - 15 of "move_pattern" ... */
7086   putFile16BitBE(file, ei->move_pattern & 0xffff);
7087   putFile8Bit(file, ei->move_direction_initial);
7088   putFile8Bit(file, ei->move_stepsize);
7089
7090   putFile8Bit(file, ei->slippery_type);
7091
7092   for (y = 0; y < 3; y++)
7093     for (x = 0; x < 3; x++)
7094       putFile16BitBE(file, ei->content.e[x][y]);
7095
7096   putFile16BitBE(file, ei->move_enter_element);
7097   putFile16BitBE(file, ei->move_leave_element);
7098   putFile8Bit(file, ei->move_leave_type);
7099
7100   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
7101   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7102
7103   putFile8Bit(file, ei->access_direction);
7104
7105   putFile8Bit(file, ei->explosion_delay);
7106   putFile8Bit(file, ei->ignition_delay);
7107   putFile8Bit(file, ei->explosion_type);
7108
7109   /* some free bytes for future custom property values and padding */
7110   WriteUnusedBytesToFile(file, 1);
7111
7112   /* ---------- change page property values (48 bytes) --------------------- */
7113
7114   for (i = 0; i < ei->num_change_pages; i++)
7115   {
7116     struct ElementChangeInfo *change = &ei->change_page[i];
7117     unsigned int event_bits;
7118
7119     /* bits 0 - 31 of "has_event[]" ... */
7120     event_bits = 0;
7121     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7122       if (change->has_event[j])
7123         event_bits |= (1 << j);
7124     putFile32BitBE(file, event_bits);
7125
7126     putFile16BitBE(file, change->target_element);
7127
7128     putFile16BitBE(file, change->delay_fixed);
7129     putFile16BitBE(file, change->delay_random);
7130     putFile16BitBE(file, change->delay_frames);
7131
7132     putFile16BitBE(file, change->initial_trigger_element);
7133
7134     putFile8Bit(file, change->explode);
7135     putFile8Bit(file, change->use_target_content);
7136     putFile8Bit(file, change->only_if_complete);
7137     putFile8Bit(file, change->use_random_replace);
7138
7139     putFile8Bit(file, change->random_percentage);
7140     putFile8Bit(file, change->replace_when);
7141
7142     for (y = 0; y < 3; y++)
7143       for (x = 0; x < 3; x++)
7144         putFile16BitBE(file, change->target_content.e[x][y]);
7145
7146     putFile8Bit(file, change->can_change);
7147
7148     putFile8Bit(file, change->trigger_side);
7149
7150     putFile8Bit(file, change->trigger_player);
7151     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7152                        log_2(change->trigger_page)));
7153
7154     putFile8Bit(file, change->has_action);
7155     putFile8Bit(file, change->action_type);
7156     putFile8Bit(file, change->action_mode);
7157     putFile16BitBE(file, change->action_arg);
7158
7159     /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7160     event_bits = 0;
7161     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7162       if (change->has_event[j])
7163         event_bits |= (1 << (j - 32));
7164     putFile8Bit(file, event_bits);
7165   }
7166 }
7167 #endif
7168
7169 #if ENABLE_HISTORIC_CHUNKS
7170 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7171 {
7172   struct ElementInfo *ei = &element_info[element];
7173   struct ElementGroupInfo *group = ei->group;
7174   int i;
7175
7176   putFile16BitBE(file, element);
7177
7178   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7179     putFile8Bit(file, ei->description[i]);
7180
7181   putFile8Bit(file, group->num_elements);
7182
7183   putFile8Bit(file, ei->use_gfx_element);
7184   putFile16BitBE(file, ei->gfx_element_initial);
7185
7186   putFile8Bit(file, group->choice_mode);
7187
7188   /* some free bytes for future values and padding */
7189   WriteUnusedBytesToFile(file, 3);
7190
7191   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7192     putFile16BitBE(file, group->element[i]);
7193 }
7194 #endif
7195
7196 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7197                                 boolean write_element)
7198 {
7199   int save_type = entry->save_type;
7200   int data_type = entry->data_type;
7201   int conf_type = entry->conf_type;
7202   int byte_mask = conf_type & CONF_MASK_BYTES;
7203   int element = entry->element;
7204   int default_value = entry->default_value;
7205   int num_bytes = 0;
7206   boolean modified = FALSE;
7207
7208   if (byte_mask != CONF_MASK_MULTI_BYTES)
7209   {
7210     void *value_ptr = entry->value;
7211     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7212                  *(int *)value_ptr);
7213
7214     /* check if any settings have been modified before saving them */
7215     if (value != default_value)
7216       modified = TRUE;
7217
7218     /* do not save if explicitly told or if unmodified default settings */
7219     if ((save_type == SAVE_CONF_NEVER) ||
7220         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7221       return 0;
7222
7223     if (write_element)
7224       num_bytes += putFile16BitBE(file, element);
7225
7226     num_bytes += putFile8Bit(file, conf_type);
7227     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
7228                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7229                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7230                   0);
7231   }
7232   else if (data_type == TYPE_STRING)
7233   {
7234     char *default_string = entry->default_string;
7235     char *string = (char *)(entry->value);
7236     int string_length = strlen(string);
7237     int i;
7238
7239     /* check if any settings have been modified before saving them */
7240     if (!strEqual(string, default_string))
7241       modified = TRUE;
7242
7243     /* do not save if explicitly told or if unmodified default settings */
7244     if ((save_type == SAVE_CONF_NEVER) ||
7245         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7246       return 0;
7247
7248     if (write_element)
7249       num_bytes += putFile16BitBE(file, element);
7250
7251     num_bytes += putFile8Bit(file, conf_type);
7252     num_bytes += putFile16BitBE(file, string_length);
7253
7254     for (i = 0; i < string_length; i++)
7255       num_bytes += putFile8Bit(file, string[i]);
7256   }
7257   else if (data_type == TYPE_ELEMENT_LIST)
7258   {
7259     int *element_array = (int *)(entry->value);
7260     int num_elements = *(int *)(entry->num_entities);
7261     int i;
7262
7263     /* check if any settings have been modified before saving them */
7264     for (i = 0; i < num_elements; i++)
7265       if (element_array[i] != default_value)
7266         modified = TRUE;
7267
7268     /* do not save if explicitly told or if unmodified default settings */
7269     if ((save_type == SAVE_CONF_NEVER) ||
7270         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7271       return 0;
7272
7273     if (write_element)
7274       num_bytes += putFile16BitBE(file, element);
7275
7276     num_bytes += putFile8Bit(file, conf_type);
7277     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7278
7279     for (i = 0; i < num_elements; i++)
7280       num_bytes += putFile16BitBE(file, element_array[i]);
7281   }
7282   else if (data_type == TYPE_CONTENT_LIST)
7283   {
7284     struct Content *content = (struct Content *)(entry->value);
7285     int num_contents = *(int *)(entry->num_entities);
7286     int i, x, y;
7287
7288     /* check if any settings have been modified before saving them */
7289     for (i = 0; i < num_contents; i++)
7290       for (y = 0; y < 3; y++)
7291         for (x = 0; x < 3; x++)
7292           if (content[i].e[x][y] != default_value)
7293             modified = TRUE;
7294
7295     /* do not save if explicitly told or if unmodified default settings */
7296     if ((save_type == SAVE_CONF_NEVER) ||
7297         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7298       return 0;
7299
7300     if (write_element)
7301       num_bytes += putFile16BitBE(file, element);
7302
7303     num_bytes += putFile8Bit(file, conf_type);
7304     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7305
7306     for (i = 0; i < num_contents; i++)
7307       for (y = 0; y < 3; y++)
7308         for (x = 0; x < 3; x++)
7309           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7310   }
7311
7312   return num_bytes;
7313 }
7314
7315 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7316 {
7317   int chunk_size = 0;
7318   int i;
7319
7320   li = *level;          /* copy level data into temporary buffer */
7321
7322   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7323     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7324
7325   return chunk_size;
7326 }
7327
7328 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7329 {
7330   int chunk_size = 0;
7331   int i;
7332
7333   li = *level;          /* copy level data into temporary buffer */
7334
7335   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7336     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7337
7338   return chunk_size;
7339 }
7340
7341 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7342 {
7343   int envelope_nr = element - EL_ENVELOPE_1;
7344   int chunk_size = 0;
7345   int i;
7346
7347   chunk_size += putFile16BitBE(file, element);
7348
7349   /* copy envelope data into temporary buffer */
7350   xx_envelope = level->envelope[envelope_nr];
7351
7352   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7353     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7354
7355   return chunk_size;
7356 }
7357
7358 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7359 {
7360   struct ElementInfo *ei = &element_info[element];
7361   int chunk_size = 0;
7362   int i, j;
7363
7364   chunk_size += putFile16BitBE(file, element);
7365
7366   xx_ei = *ei;          /* copy element data into temporary buffer */
7367
7368   /* set default description string for this specific element */
7369   strcpy(xx_default_description, getDefaultElementDescription(ei));
7370
7371   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7372     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7373
7374   for (i = 0; i < ei->num_change_pages; i++)
7375   {
7376     struct ElementChangeInfo *change = &ei->change_page[i];
7377
7378     xx_current_change_page = i;
7379
7380     xx_change = *change;        /* copy change data into temporary buffer */
7381
7382     resetEventBits();
7383     setEventBitsFromEventFlags(change);
7384
7385     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7386       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7387                                          FALSE);
7388   }
7389
7390   return chunk_size;
7391 }
7392
7393 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7394 {
7395   struct ElementInfo *ei = &element_info[element];
7396   struct ElementGroupInfo *group = ei->group;
7397   int chunk_size = 0;
7398   int i;
7399
7400   chunk_size += putFile16BitBE(file, element);
7401
7402   xx_ei = *ei;          /* copy element data into temporary buffer */
7403   xx_group = *group;    /* copy group data into temporary buffer */
7404
7405   /* set default description string for this specific element */
7406   strcpy(xx_default_description, getDefaultElementDescription(ei));
7407
7408   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7409     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7410
7411   return chunk_size;
7412 }
7413
7414 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7415                                   boolean save_as_template)
7416 {
7417   int chunk_size;
7418   int i;
7419   FILE *file;
7420
7421   if (!(file = fopen(filename, MODE_WRITE)))
7422   {
7423     Error(ERR_WARN, "cannot save level file '%s'", filename);
7424     return;
7425   }
7426
7427   level->file_version = FILE_VERSION_ACTUAL;
7428   level->game_version = GAME_VERSION_ACTUAL;
7429
7430   level->creation_date = getCurrentDate();
7431
7432   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7433   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7434
7435   chunk_size = SaveLevel_VERS(NULL, level);
7436   putFileChunkBE(file, "VERS", chunk_size);
7437   SaveLevel_VERS(file, level);
7438
7439   chunk_size = SaveLevel_DATE(NULL, level);
7440   putFileChunkBE(file, "DATE", chunk_size);
7441   SaveLevel_DATE(file, level);
7442
7443   chunk_size = SaveLevel_NAME(NULL, level);
7444   putFileChunkBE(file, "NAME", chunk_size);
7445   SaveLevel_NAME(file, level);
7446
7447   chunk_size = SaveLevel_AUTH(NULL, level);
7448   putFileChunkBE(file, "AUTH", chunk_size);
7449   SaveLevel_AUTH(file, level);
7450
7451   chunk_size = SaveLevel_INFO(NULL, level);
7452   putFileChunkBE(file, "INFO", chunk_size);
7453   SaveLevel_INFO(file, level);
7454
7455   chunk_size = SaveLevel_BODY(NULL, level);
7456   putFileChunkBE(file, "BODY", chunk_size);
7457   SaveLevel_BODY(file, level);
7458
7459   chunk_size = SaveLevel_ELEM(NULL, level);
7460   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          /* save if changed */
7461   {
7462     putFileChunkBE(file, "ELEM", chunk_size);
7463     SaveLevel_ELEM(file, level);
7464   }
7465
7466   for (i = 0; i < NUM_ENVELOPES; i++)
7467   {
7468     int element = EL_ENVELOPE_1 + i;
7469
7470     chunk_size = SaveLevel_NOTE(NULL, level, element);
7471     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        /* save if changed */
7472     {
7473       putFileChunkBE(file, "NOTE", chunk_size);
7474       SaveLevel_NOTE(file, level, element);
7475     }
7476   }
7477
7478   /* if not using template level, check for non-default custom/group elements */
7479   if (!level->use_custom_template || save_as_template)
7480   {
7481     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7482     {
7483       int element = EL_CUSTOM_START + i;
7484
7485       chunk_size = SaveLevel_CUSX(NULL, level, element);
7486       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      /* save if changed */
7487       {
7488         putFileChunkBE(file, "CUSX", chunk_size);
7489         SaveLevel_CUSX(file, level, element);
7490       }
7491     }
7492
7493     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7494     {
7495       int element = EL_GROUP_START + i;
7496
7497       chunk_size = SaveLevel_GRPX(NULL, level, element);
7498       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      /* save if changed */
7499       {
7500         putFileChunkBE(file, "GRPX", chunk_size);
7501         SaveLevel_GRPX(file, level, element);
7502       }
7503     }
7504   }
7505
7506   fclose(file);
7507
7508   SetFilePermissions(filename, PERMS_PRIVATE);
7509 }
7510
7511 void SaveLevel(int nr)
7512 {
7513   char *filename = getDefaultLevelFilename(nr);
7514
7515   SaveLevelFromFilename(&level, filename, FALSE);
7516 }
7517
7518 void SaveLevelTemplate(void)
7519 {
7520   char *filename = getLocalLevelTemplateFilename();
7521
7522   SaveLevelFromFilename(&level, filename, TRUE);
7523 }
7524
7525 boolean SaveLevelChecked(int nr)
7526 {
7527   char *filename = getDefaultLevelFilename(nr);
7528   boolean new_level = !fileExists(filename);
7529   boolean level_saved = FALSE;
7530
7531   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7532   {
7533     SaveLevel(nr);
7534
7535     if (new_level)
7536       Request("Level saved!", REQ_CONFIRM);
7537
7538     level_saved = TRUE;
7539   }
7540
7541   return level_saved;
7542 }
7543
7544 void DumpLevel(struct LevelInfo *level)
7545 {
7546   if (level->no_level_file || level->no_valid_file)
7547   {
7548     Error(ERR_WARN, "cannot dump -- no valid level file found");
7549
7550     return;
7551   }
7552
7553   PrintLine("-", 79);
7554   Print("Level xxx (file version %08d, game version %08d)\n",
7555         level->file_version, level->game_version);
7556   PrintLine("-", 79);
7557
7558   Print("Level author: '%s'\n", level->author);
7559   Print("Level title:  '%s'\n", level->name);
7560   Print("\n");
7561   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7562   Print("\n");
7563   Print("Level time:  %d seconds\n", level->time);
7564   Print("Gems needed: %d\n", level->gems_needed);
7565   Print("\n");
7566   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7567   Print("Time for wheel:      %d seconds\n", level->time_wheel);
7568   Print("Time for light:      %d seconds\n", level->time_light);
7569   Print("Time for timegate:   %d seconds\n", level->time_timegate);
7570   Print("\n");
7571   Print("Amoeba speed: %d\n", level->amoeba_speed);
7572   Print("\n");
7573
7574   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
7575   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
7576   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7577   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7578   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7579
7580   PrintLine("-", 79);
7581 }
7582
7583
7584 /* ========================================================================= */
7585 /* tape file functions                                                       */
7586 /* ========================================================================= */
7587
7588 static void setTapeInfoToDefaults(void)
7589 {
7590   int i;
7591
7592   /* always start with reliable default values (empty tape) */
7593   TapeErase();
7594
7595   /* default values (also for pre-1.2 tapes) with only the first player */
7596   tape.player_participates[0] = TRUE;
7597   for (i = 1; i < MAX_PLAYERS; i++)
7598     tape.player_participates[i] = FALSE;
7599
7600   /* at least one (default: the first) player participates in every tape */
7601   tape.num_participating_players = 1;
7602
7603   tape.level_nr = level_nr;
7604   tape.counter = 0;
7605   tape.changed = FALSE;
7606
7607   tape.recording = FALSE;
7608   tape.playing = FALSE;
7609   tape.pausing = FALSE;
7610
7611   tape.no_valid_file = FALSE;
7612 }
7613
7614 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7615 {
7616   tape->file_version = getFileVersion(file);
7617   tape->game_version = getFileVersion(file);
7618
7619   return chunk_size;
7620 }
7621
7622 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7623 {
7624   int i;
7625
7626   tape->random_seed = getFile32BitBE(file);
7627   tape->date        = getFile32BitBE(file);
7628   tape->length      = getFile32BitBE(file);
7629
7630   /* read header fields that are new since version 1.2 */
7631   if (tape->file_version >= FILE_VERSION_1_2)
7632   {
7633     byte store_participating_players = getFile8Bit(file);
7634     int engine_version;
7635
7636     /* since version 1.2, tapes store which players participate in the tape */
7637     tape->num_participating_players = 0;
7638     for (i = 0; i < MAX_PLAYERS; i++)
7639     {
7640       tape->player_participates[i] = FALSE;
7641
7642       if (store_participating_players & (1 << i))
7643       {
7644         tape->player_participates[i] = TRUE;
7645         tape->num_participating_players++;
7646       }
7647     }
7648
7649     tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7650
7651     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7652
7653     engine_version = getFileVersion(file);
7654     if (engine_version > 0)
7655       tape->engine_version = engine_version;
7656     else
7657       tape->engine_version = tape->game_version;
7658   }
7659
7660   return chunk_size;
7661 }
7662
7663 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7664 {
7665   int level_identifier_size;
7666   int i;
7667
7668   level_identifier_size = getFile16BitBE(file);
7669
7670   tape->level_identifier =
7671     checked_realloc(tape->level_identifier, level_identifier_size);
7672
7673   for (i = 0; i < level_identifier_size; i++)
7674     tape->level_identifier[i] = getFile8Bit(file);
7675
7676   tape->level_nr = getFile16BitBE(file);
7677
7678   chunk_size = 2 + level_identifier_size + 2;
7679
7680   return chunk_size;
7681 }
7682
7683 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7684 {
7685   int i, j;
7686   int tape_pos_size =
7687     (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7688   int chunk_size_expected = tape_pos_size * tape->length;
7689
7690   if (chunk_size_expected != chunk_size)
7691   {
7692     ReadUnusedBytesFromFile(file, chunk_size);
7693     return chunk_size_expected;
7694   }
7695
7696   for (i = 0; i < tape->length; i++)
7697   {
7698     if (i >= MAX_TAPE_LEN)
7699     {
7700       Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7701             MAX_TAPE_LEN);
7702
7703       // tape too large; read and ignore remaining tape data from this chunk
7704       for (;i < tape->length; i++)
7705         ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7706
7707       break;
7708     }
7709
7710     if (tape->use_mouse)
7711     {
7712       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
7713       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
7714       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7715
7716       tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7717     }
7718     else
7719     {
7720       for (j = 0; j < MAX_PLAYERS; j++)
7721       {
7722         tape->pos[i].action[j] = MV_NONE;
7723
7724         if (tape->player_participates[j])
7725           tape->pos[i].action[j] = getFile8Bit(file);
7726       }
7727     }
7728
7729     tape->pos[i].delay = getFile8Bit(file);
7730
7731     if (tape->file_version == FILE_VERSION_1_0)
7732     {
7733       /* eliminate possible diagonal moves in old tapes */
7734       /* this is only for backward compatibility */
7735
7736       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7737       byte action = tape->pos[i].action[0];
7738       int k, num_moves = 0;
7739
7740       for (k = 0; k<4; k++)
7741       {
7742         if (action & joy_dir[k])
7743         {
7744           tape->pos[i + num_moves].action[0] = joy_dir[k];
7745           if (num_moves > 0)
7746             tape->pos[i + num_moves].delay = 0;
7747           num_moves++;
7748         }
7749       }
7750
7751       if (num_moves > 1)
7752       {
7753         num_moves--;
7754         i += num_moves;
7755         tape->length += num_moves;
7756       }
7757     }
7758     else if (tape->file_version < FILE_VERSION_2_0)
7759     {
7760       /* convert pre-2.0 tapes to new tape format */
7761
7762       if (tape->pos[i].delay > 1)
7763       {
7764         /* action part */
7765         tape->pos[i + 1] = tape->pos[i];
7766         tape->pos[i + 1].delay = 1;
7767
7768         /* delay part */
7769         for (j = 0; j < MAX_PLAYERS; j++)
7770           tape->pos[i].action[j] = MV_NONE;
7771         tape->pos[i].delay--;
7772
7773         i++;
7774         tape->length++;
7775       }
7776     }
7777
7778     if (checkEndOfFile(file))
7779       break;
7780   }
7781
7782   if (i != tape->length)
7783     chunk_size = tape_pos_size * i;
7784
7785   return chunk_size;
7786 }
7787
7788 static void LoadTape_SokobanSolution(char *filename)
7789 {
7790   File *file;
7791   int move_delay = TILESIZE / level.initial_player_stepsize[0];
7792
7793   if (!(file = openFile(filename, MODE_READ)))
7794   {
7795     tape.no_valid_file = TRUE;
7796
7797     return;
7798   }
7799
7800   while (!checkEndOfFile(file))
7801   {
7802     unsigned char c = getByteFromFile(file);
7803
7804     if (checkEndOfFile(file))
7805       break;
7806
7807     switch (c)
7808     {
7809       case 'u':
7810       case 'U':
7811         tape.pos[tape.length].action[0] = MV_UP;
7812         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7813         tape.length++;
7814         break;
7815
7816       case 'd':
7817       case 'D':
7818         tape.pos[tape.length].action[0] = MV_DOWN;
7819         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7820         tape.length++;
7821         break;
7822
7823       case 'l':
7824       case 'L':
7825         tape.pos[tape.length].action[0] = MV_LEFT;
7826         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7827         tape.length++;
7828         break;
7829
7830       case 'r':
7831       case 'R':
7832         tape.pos[tape.length].action[0] = MV_RIGHT;
7833         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7834         tape.length++;
7835         break;
7836
7837       case '\n':
7838       case '\r':
7839       case '\t':
7840       case ' ':
7841         /* ignore white-space characters */
7842         break;
7843
7844       default:
7845         tape.no_valid_file = TRUE;
7846
7847         Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7848
7849         break;
7850     }
7851   }
7852
7853   closeFile(file);
7854
7855   if (tape.no_valid_file)
7856     return;
7857
7858   tape.length_frames  = GetTapeLengthFrames();
7859   tape.length_seconds = GetTapeLengthSeconds();
7860 }
7861
7862 void LoadTapeFromFilename(char *filename)
7863 {
7864   char cookie[MAX_LINE_LEN];
7865   char chunk_name[CHUNK_ID_LEN + 1];
7866   File *file;
7867   int chunk_size;
7868
7869   /* always start with reliable default values */
7870   setTapeInfoToDefaults();
7871
7872   if (strSuffix(filename, ".sln"))
7873   {
7874     LoadTape_SokobanSolution(filename);
7875
7876     return;
7877   }
7878
7879   if (!(file = openFile(filename, MODE_READ)))
7880   {
7881     tape.no_valid_file = TRUE;
7882
7883     return;
7884   }
7885
7886   getFileChunkBE(file, chunk_name, NULL);
7887   if (strEqual(chunk_name, "RND1"))
7888   {
7889     getFile32BitBE(file);               /* not used */
7890
7891     getFileChunkBE(file, chunk_name, NULL);
7892     if (!strEqual(chunk_name, "TAPE"))
7893     {
7894       tape.no_valid_file = TRUE;
7895
7896       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7897
7898       closeFile(file);
7899
7900       return;
7901     }
7902   }
7903   else  /* check for pre-2.0 file format with cookie string */
7904   {
7905     strcpy(cookie, chunk_name);
7906     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7907       cookie[4] = '\0';
7908     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7909       cookie[strlen(cookie) - 1] = '\0';
7910
7911     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7912     {
7913       tape.no_valid_file = TRUE;
7914
7915       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7916
7917       closeFile(file);
7918
7919       return;
7920     }
7921
7922     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7923     {
7924       tape.no_valid_file = TRUE;
7925
7926       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7927
7928       closeFile(file);
7929
7930       return;
7931     }
7932
7933     /* pre-2.0 tape files have no game version, so use file version here */
7934     tape.game_version = tape.file_version;
7935   }
7936
7937   if (tape.file_version < FILE_VERSION_1_2)
7938   {
7939     /* tape files from versions before 1.2.0 without chunk structure */
7940     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7941     LoadTape_BODY(file, 2 * tape.length,      &tape);
7942   }
7943   else
7944   {
7945     static struct
7946     {
7947       char *name;
7948       int size;
7949       int (*loader)(File *, int, struct TapeInfo *);
7950     }
7951     chunk_info[] =
7952     {
7953       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
7954       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
7955       { "INFO", -1,                     LoadTape_INFO },
7956       { "BODY", -1,                     LoadTape_BODY },
7957       {  NULL,  0,                      NULL }
7958     };
7959
7960     while (getFileChunkBE(file, chunk_name, &chunk_size))
7961     {
7962       int i = 0;
7963
7964       while (chunk_info[i].name != NULL &&
7965              !strEqual(chunk_name, chunk_info[i].name))
7966         i++;
7967
7968       if (chunk_info[i].name == NULL)
7969       {
7970         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7971               chunk_name, filename);
7972         ReadUnusedBytesFromFile(file, chunk_size);
7973       }
7974       else if (chunk_info[i].size != -1 &&
7975                chunk_info[i].size != chunk_size)
7976       {
7977         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7978               chunk_size, chunk_name, filename);
7979         ReadUnusedBytesFromFile(file, chunk_size);
7980       }
7981       else
7982       {
7983         /* call function to load this tape chunk */
7984         int chunk_size_expected =
7985           (chunk_info[i].loader)(file, chunk_size, &tape);
7986
7987         /* the size of some chunks cannot be checked before reading other
7988            chunks first (like "HEAD" and "BODY") that contain some header
7989            information, so check them here */
7990         if (chunk_size_expected != chunk_size)
7991         {
7992           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7993                 chunk_size, chunk_name, filename);
7994         }
7995       }
7996     }
7997   }
7998
7999   closeFile(file);
8000
8001   tape.length_frames  = GetTapeLengthFrames();
8002   tape.length_seconds = GetTapeLengthSeconds();
8003
8004 #if 0
8005   printf("::: tape file version: %d\n",   tape.file_version);
8006   printf("::: tape game version: %d\n",   tape.game_version);
8007   printf("::: tape engine version: %d\n", tape.engine_version);
8008 #endif
8009 }
8010
8011 void LoadTape(int nr)
8012 {
8013   char *filename = getTapeFilename(nr);
8014
8015   LoadTapeFromFilename(filename);
8016 }
8017
8018 void LoadSolutionTape(int nr)
8019 {
8020   char *filename = getSolutionTapeFilename(nr);
8021
8022   LoadTapeFromFilename(filename);
8023
8024   if (TAPE_IS_EMPTY(tape) &&
8025       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8026       level.native_sp_level->demo.is_available)
8027     CopyNativeTape_SP_to_RND(&level);
8028 }
8029
8030 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8031 {
8032   putFileVersion(file, tape->file_version);
8033   putFileVersion(file, tape->game_version);
8034 }
8035
8036 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8037 {
8038   int i;
8039   byte store_participating_players = 0;
8040
8041   /* set bits for participating players for compact storage */
8042   for (i = 0; i < MAX_PLAYERS; i++)
8043     if (tape->player_participates[i])
8044       store_participating_players |= (1 << i);
8045
8046   putFile32BitBE(file, tape->random_seed);
8047   putFile32BitBE(file, tape->date);
8048   putFile32BitBE(file, tape->length);
8049
8050   putFile8Bit(file, store_participating_players);
8051
8052   putFile8Bit(file, (tape->use_mouse ? 1 : 0));
8053
8054   /* unused bytes not at the end here for 4-byte alignment of engine_version */
8055   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8056
8057   putFileVersion(file, tape->engine_version);
8058 }
8059
8060 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8061 {
8062   int level_identifier_size = strlen(tape->level_identifier) + 1;
8063   int i;
8064
8065   putFile16BitBE(file, level_identifier_size);
8066
8067   for (i = 0; i < level_identifier_size; i++)
8068     putFile8Bit(file, tape->level_identifier[i]);
8069
8070   putFile16BitBE(file, tape->level_nr);
8071 }
8072
8073 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8074 {
8075   int i, j;
8076
8077   for (i = 0; i < tape->length; i++)
8078   {
8079     if (tape->use_mouse)
8080     {
8081       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8082       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8083       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8084     }
8085     else
8086     {
8087       for (j = 0; j < MAX_PLAYERS; j++)
8088         if (tape->player_participates[j])
8089           putFile8Bit(file, tape->pos[i].action[j]);
8090     }
8091
8092     putFile8Bit(file, tape->pos[i].delay);
8093   }
8094 }
8095
8096 void SaveTape(int nr)
8097 {
8098   char *filename = getTapeFilename(nr);
8099   FILE *file;
8100   int num_participating_players = 0;
8101   int tape_pos_size;
8102   int info_chunk_size;
8103   int body_chunk_size;
8104   int i;
8105
8106   InitTapeDirectory(leveldir_current->subdir);
8107
8108   if (!(file = fopen(filename, MODE_WRITE)))
8109   {
8110     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8111     return;
8112   }
8113
8114   tape.file_version = FILE_VERSION_ACTUAL;
8115   tape.game_version = GAME_VERSION_ACTUAL;
8116
8117   /* count number of participating players  */
8118   for (i = 0; i < MAX_PLAYERS; i++)
8119     if (tape.player_participates[i])
8120       num_participating_players++;
8121
8122   tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
8123
8124   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8125   body_chunk_size = tape_pos_size * tape.length;
8126
8127   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8128   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8129
8130   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8131   SaveTape_VERS(file, &tape);
8132
8133   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8134   SaveTape_HEAD(file, &tape);
8135
8136   putFileChunkBE(file, "INFO", info_chunk_size);
8137   SaveTape_INFO(file, &tape);
8138
8139   putFileChunkBE(file, "BODY", body_chunk_size);
8140   SaveTape_BODY(file, &tape);
8141
8142   fclose(file);
8143
8144   SetFilePermissions(filename, PERMS_PRIVATE);
8145
8146   tape.changed = FALSE;
8147 }
8148
8149 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved)
8150 {
8151   char *filename = getTapeFilename(nr);
8152   boolean new_tape = !fileExists(filename);
8153   boolean tape_saved = FALSE;
8154
8155   if (new_tape || Request(msg_replace, REQ_ASK))
8156   {
8157     SaveTape(nr);
8158
8159     if (new_tape)
8160       Request(msg_saved, REQ_CONFIRM);
8161
8162     tape_saved = TRUE;
8163   }
8164
8165   return tape_saved;
8166 }
8167
8168 boolean SaveTapeChecked(int nr)
8169 {
8170   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!");
8171 }
8172
8173 boolean SaveTapeChecked_LevelSolved(int nr)
8174 {
8175   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8176                                 "Level solved! Tape saved!");
8177 }
8178
8179 void DumpTape(struct TapeInfo *tape)
8180 {
8181   int tape_frame_counter;
8182   int i, j;
8183
8184   if (tape->no_valid_file)
8185   {
8186     Error(ERR_WARN, "cannot dump -- no valid tape file found");
8187
8188     return;
8189   }
8190
8191   PrintLine("-", 79);
8192   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8193         tape->level_nr, tape->file_version, tape->game_version);
8194   Print("                  (effective engine version %08d)\n",
8195         tape->engine_version);
8196   Print("Level series identifier: '%s'\n", tape->level_identifier);
8197   PrintLine("-", 79);
8198
8199   tape_frame_counter = 0;
8200
8201   for (i = 0; i < tape->length; i++)
8202   {
8203     if (i >= MAX_TAPE_LEN)
8204       break;
8205
8206     Print("%04d: ", i);
8207
8208     for (j = 0; j < MAX_PLAYERS; j++)
8209     {
8210       if (tape->player_participates[j])
8211       {
8212         int action = tape->pos[i].action[j];
8213
8214         Print("%d:%02x ", j, action);
8215         Print("[%c%c%c%c|%c%c] - ",
8216               (action & JOY_LEFT ? '<' : ' '),
8217               (action & JOY_RIGHT ? '>' : ' '),
8218               (action & JOY_UP ? '^' : ' '),
8219               (action & JOY_DOWN ? 'v' : ' '),
8220               (action & JOY_BUTTON_1 ? '1' : ' '),
8221               (action & JOY_BUTTON_2 ? '2' : ' '));
8222       }
8223     }
8224
8225     Print("(%03d) ", tape->pos[i].delay);
8226     Print("[%05d]\n", tape_frame_counter);
8227
8228     tape_frame_counter += tape->pos[i].delay;
8229   }
8230
8231   PrintLine("-", 79);
8232 }
8233
8234
8235 /* ========================================================================= */
8236 /* score file functions                                                      */
8237 /* ========================================================================= */
8238
8239 void LoadScore(int nr)
8240 {
8241   int i;
8242   char *filename = getScoreFilename(nr);
8243   char cookie[MAX_LINE_LEN];
8244   char line[MAX_LINE_LEN];
8245   char *line_ptr;
8246   FILE *file;
8247
8248   /* always start with reliable default values */
8249   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8250   {
8251     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8252     highscore[i].Score = 0;
8253   }
8254
8255   if (!(file = fopen(filename, MODE_READ)))
8256     return;
8257
8258   /* check file identifier */
8259   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8260     cookie[0] = '\0';
8261   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8262     cookie[strlen(cookie) - 1] = '\0';
8263
8264   if (!checkCookieString(cookie, SCORE_COOKIE))
8265   {
8266     Error(ERR_WARN, "unknown format of score file '%s'", filename);
8267     fclose(file);
8268     return;
8269   }
8270
8271   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8272   {
8273     if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8274       Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8275     if (fgets(line, MAX_LINE_LEN, file) == NULL)
8276       line[0] = '\0';
8277
8278     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8279       line[strlen(line) - 1] = '\0';
8280
8281     for (line_ptr = line; *line_ptr; line_ptr++)
8282     {
8283       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8284       {
8285         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8286         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8287         break;
8288       }
8289     }
8290   }
8291
8292   fclose(file);
8293 }
8294
8295 void SaveScore(int nr)
8296 {
8297   int i;
8298   int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8299   char *filename = getScoreFilename(nr);
8300   FILE *file;
8301
8302   /* used instead of "leveldir_current->subdir" (for network games) */
8303   InitScoreDirectory(levelset.identifier);
8304
8305   if (!(file = fopen(filename, MODE_WRITE)))
8306   {
8307     Error(ERR_WARN, "cannot save score for level %d", nr);
8308     return;
8309   }
8310
8311   fprintf(file, "%s\n\n", SCORE_COOKIE);
8312
8313   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8314     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8315
8316   fclose(file);
8317
8318   SetFilePermissions(filename, permissions);
8319 }
8320
8321
8322 /* ========================================================================= */
8323 /* setup file functions                                                      */
8324 /* ========================================================================= */
8325
8326 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
8327
8328 /* global setup */
8329 enum
8330 {
8331   SETUP_TOKEN_PLAYER_NAME = 0,
8332   SETUP_TOKEN_SOUND,
8333   SETUP_TOKEN_SOUND_LOOPS,
8334   SETUP_TOKEN_SOUND_MUSIC,
8335   SETUP_TOKEN_SOUND_SIMPLE,
8336   SETUP_TOKEN_TOONS,
8337   SETUP_TOKEN_SCROLL_DELAY,
8338   SETUP_TOKEN_SCROLL_DELAY_VALUE,
8339   SETUP_TOKEN_ENGINE_SNAPSHOT_MODE,
8340   SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY,
8341   SETUP_TOKEN_FADE_SCREENS,
8342   SETUP_TOKEN_AUTORECORD,
8343   SETUP_TOKEN_SHOW_TITLESCREEN,
8344   SETUP_TOKEN_QUICK_DOORS,
8345   SETUP_TOKEN_TEAM_MODE,
8346   SETUP_TOKEN_HANDICAP,
8347   SETUP_TOKEN_SKIP_LEVELS,
8348   SETUP_TOKEN_INCREMENT_LEVELS,
8349   SETUP_TOKEN_AUTO_PLAY_NEXT_LEVEL,
8350   SETUP_TOKEN_SKIP_SCORES_AFTER_GAME,
8351   SETUP_TOKEN_TIME_LIMIT,
8352   SETUP_TOKEN_FULLSCREEN,
8353   SETUP_TOKEN_WINDOW_SCALING_PERCENT,
8354   SETUP_TOKEN_WINDOW_SCALING_QUALITY,
8355   SETUP_TOKEN_SCREEN_RENDERING_MODE,
8356   SETUP_TOKEN_VSYNC_MODE,
8357   SETUP_TOKEN_ASK_ON_ESCAPE,
8358   SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR,
8359   SETUP_TOKEN_QUICK_SWITCH,
8360   SETUP_TOKEN_INPUT_ON_FOCUS,
8361   SETUP_TOKEN_PREFER_AGA_GRAPHICS,
8362   SETUP_TOKEN_GAME_FRAME_DELAY,
8363   SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS,
8364   SETUP_TOKEN_SMALL_GAME_GRAPHICS,
8365   SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS,
8366   SETUP_TOKEN_GRAPHICS_SET,
8367   SETUP_TOKEN_SOUNDS_SET,
8368   SETUP_TOKEN_MUSIC_SET,
8369   SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS,
8370   SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS,
8371   SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC,
8372   SETUP_TOKEN_VOLUME_SIMPLE,
8373   SETUP_TOKEN_VOLUME_LOOPS,
8374   SETUP_TOKEN_VOLUME_MUSIC,
8375   SETUP_TOKEN_NETWORK_MODE,
8376   SETUP_TOKEN_NETWORK_PLAYER_NR,
8377   SETUP_TOKEN_TOUCH_CONTROL_TYPE,
8378   SETUP_TOKEN_TOUCH_MOVE_DISTANCE,
8379   SETUP_TOKEN_TOUCH_DROP_DISTANCE,
8380   SETUP_TOKEN_TOUCH_TRANSPARENCY,
8381   SETUP_TOKEN_TOUCH_DRAW_OUTLINED,
8382   SETUP_TOKEN_TOUCH_DRAW_PRESSED,
8383   SETUP_TOKEN_TOUCH_GRID_XSIZE_0,
8384   SETUP_TOKEN_TOUCH_GRID_YSIZE_0,
8385   SETUP_TOKEN_TOUCH_GRID_XSIZE_1,
8386   SETUP_TOKEN_TOUCH_GRID_YSIZE_1,
8387
8388   NUM_GLOBAL_SETUP_TOKENS
8389 };
8390
8391 /* auto setup */
8392 enum
8393 {
8394   SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE = 0,
8395
8396   NUM_AUTO_SETUP_TOKENS
8397 };
8398
8399 /* editor setup */
8400 enum
8401 {
8402   SETUP_TOKEN_EDITOR_EL_CLASSIC = 0,
8403   SETUP_TOKEN_EDITOR_EL_CUSTOM,
8404   SETUP_TOKEN_EDITOR_EL_USER_DEFINED,
8405   SETUP_TOKEN_EDITOR_EL_DYNAMIC,
8406   SETUP_TOKEN_EDITOR_EL_HEADLINES,
8407   SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN,
8408
8409   NUM_EDITOR_SETUP_TOKENS
8410 };
8411
8412 /* editor cascade setup */
8413 enum
8414 {
8415   SETUP_TOKEN_EDITOR_CASCADE_BD = 0,
8416   SETUP_TOKEN_EDITOR_CASCADE_EM,
8417   SETUP_TOKEN_EDITOR_CASCADE_EMC,
8418   SETUP_TOKEN_EDITOR_CASCADE_RND,
8419   SETUP_TOKEN_EDITOR_CASCADE_SB,
8420   SETUP_TOKEN_EDITOR_CASCADE_SP,
8421   SETUP_TOKEN_EDITOR_CASCADE_DC,
8422   SETUP_TOKEN_EDITOR_CASCADE_DX,
8423   SETUP_TOKEN_EDITOR_CASCADE_TEXT,
8424   SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT,
8425   SETUP_TOKEN_EDITOR_CASCADE_CE,
8426   SETUP_TOKEN_EDITOR_CASCADE_GE,
8427   SETUP_TOKEN_EDITOR_CASCADE_REF,
8428   SETUP_TOKEN_EDITOR_CASCADE_USER,
8429   SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC,
8430
8431   NUM_EDITOR_CASCADE_SETUP_TOKENS
8432 };
8433
8434 /* shortcut setup */
8435 enum
8436 {
8437   SETUP_TOKEN_SHORTCUT_SAVE_GAME = 0,
8438   SETUP_TOKEN_SHORTCUT_LOAD_GAME,
8439   SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE,
8440   SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1,
8441   SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2,
8442   SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3,
8443   SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4,
8444   SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL,
8445   SETUP_TOKEN_SHORTCUT_TAPE_EJECT,
8446   SETUP_TOKEN_SHORTCUT_TAPE_EXTRA,
8447   SETUP_TOKEN_SHORTCUT_TAPE_STOP,
8448   SETUP_TOKEN_SHORTCUT_TAPE_PAUSE,
8449   SETUP_TOKEN_SHORTCUT_TAPE_RECORD,
8450   SETUP_TOKEN_SHORTCUT_TAPE_PLAY,
8451   SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE,
8452   SETUP_TOKEN_SHORTCUT_SOUND_LOOPS,
8453   SETUP_TOKEN_SHORTCUT_SOUND_MUSIC,
8454   SETUP_TOKEN_SHORTCUT_SNAP_LEFT,
8455   SETUP_TOKEN_SHORTCUT_SNAP_RIGHT,
8456   SETUP_TOKEN_SHORTCUT_SNAP_UP,
8457   SETUP_TOKEN_SHORTCUT_SNAP_DOWN,
8458
8459   NUM_SHORTCUT_SETUP_TOKENS
8460 };
8461
8462 /* player setup */
8463 enum
8464 {
8465   SETUP_TOKEN_PLAYER_USE_JOYSTICK = 0,
8466   SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME,
8467   SETUP_TOKEN_PLAYER_JOY_XLEFT,
8468   SETUP_TOKEN_PLAYER_JOY_XMIDDLE,
8469   SETUP_TOKEN_PLAYER_JOY_XRIGHT,
8470   SETUP_TOKEN_PLAYER_JOY_YUPPER,
8471   SETUP_TOKEN_PLAYER_JOY_YMIDDLE,
8472   SETUP_TOKEN_PLAYER_JOY_YLOWER,
8473   SETUP_TOKEN_PLAYER_JOY_SNAP,
8474   SETUP_TOKEN_PLAYER_JOY_DROP,
8475   SETUP_TOKEN_PLAYER_KEY_LEFT,
8476   SETUP_TOKEN_PLAYER_KEY_RIGHT,
8477   SETUP_TOKEN_PLAYER_KEY_UP,
8478   SETUP_TOKEN_PLAYER_KEY_DOWN,
8479   SETUP_TOKEN_PLAYER_KEY_SNAP,
8480   SETUP_TOKEN_PLAYER_KEY_DROP,
8481
8482   NUM_PLAYER_SETUP_TOKENS
8483 };
8484
8485 /* system setup */
8486 enum
8487 {
8488   SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER = 0,
8489   SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER,
8490   SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE,
8491
8492   NUM_SYSTEM_SETUP_TOKENS
8493 };
8494
8495 /* internal setup */
8496 enum
8497 {
8498   SETUP_TOKEN_INT_PROGRAM_TITLE = 0,
8499   SETUP_TOKEN_INT_PROGRAM_VERSION,
8500   SETUP_TOKEN_INT_PROGRAM_AUTHOR,
8501   SETUP_TOKEN_INT_PROGRAM_EMAIL,
8502   SETUP_TOKEN_INT_PROGRAM_WEBSITE,
8503   SETUP_TOKEN_INT_PROGRAM_COPYRIGHT,
8504   SETUP_TOKEN_INT_PROGRAM_COMPANY,
8505   SETUP_TOKEN_INT_PROGRAM_ICON_FILE,
8506   SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET,
8507   SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET,
8508   SETUP_TOKEN_INT_DEFAULT_MUSIC_SET,
8509   SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE,
8510   SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE,
8511   SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE,
8512   SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES,
8513   SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR,
8514   SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE,
8515   SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH,
8516   SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT,
8517
8518   NUM_INTERNAL_SETUP_TOKENS
8519 };
8520
8521 /* debug setup */
8522 enum
8523 {
8524   SETUP_TOKEN_DEBUG_FRAME_DELAY_0 = 0,
8525   SETUP_TOKEN_DEBUG_FRAME_DELAY_1,
8526   SETUP_TOKEN_DEBUG_FRAME_DELAY_2,
8527   SETUP_TOKEN_DEBUG_FRAME_DELAY_3,
8528   SETUP_TOKEN_DEBUG_FRAME_DELAY_4,
8529   SETUP_TOKEN_DEBUG_FRAME_DELAY_5,
8530   SETUP_TOKEN_DEBUG_FRAME_DELAY_6,
8531   SETUP_TOKEN_DEBUG_FRAME_DELAY_7,
8532   SETUP_TOKEN_DEBUG_FRAME_DELAY_8,
8533   SETUP_TOKEN_DEBUG_FRAME_DELAY_9,
8534   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0,
8535   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1,
8536   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2,
8537   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3,
8538   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4,
8539   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5,
8540   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6,
8541   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7,
8542   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8,
8543   SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9,
8544   SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY,
8545   SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY,
8546   SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND,
8547
8548   NUM_DEBUG_SETUP_TOKENS
8549 };
8550
8551 /* options setup */
8552 enum
8553 {
8554   SETUP_TOKEN_OPTIONS_VERBOSE = 0,
8555
8556   NUM_OPTIONS_SETUP_TOKENS
8557 };
8558
8559
8560 static struct SetupInfo si;
8561 static struct SetupAutoSetupInfo sasi;
8562 static struct SetupEditorInfo sei;
8563 static struct SetupEditorCascadeInfo seci;
8564 static struct SetupShortcutInfo ssi;
8565 static struct SetupInputInfo sii;
8566 static struct SetupSystemInfo syi;
8567 static struct SetupInternalInfo sxi;
8568 static struct SetupDebugInfo sdi;
8569 static struct OptionInfo soi;
8570
8571 static struct TokenInfo global_setup_tokens[] =
8572 {
8573   { TYPE_STRING, &si.player_name,             "player_name"             },
8574   { TYPE_SWITCH, &si.sound,                   "sound"                   },
8575   { TYPE_SWITCH, &si.sound_loops,             "repeating_sound_loops"   },
8576   { TYPE_SWITCH, &si.sound_music,             "background_music"        },
8577   { TYPE_SWITCH, &si.sound_simple,            "simple_sound_effects"    },
8578   { TYPE_SWITCH, &si.toons,                   "toons"                   },
8579   { TYPE_SWITCH, &si.scroll_delay,            "scroll_delay"            },
8580   { TYPE_INTEGER,&si.scroll_delay_value,      "scroll_delay_value"      },
8581   { TYPE_STRING, &si.engine_snapshot_mode,    "engine_snapshot_mode"    },
8582   { TYPE_INTEGER,&si.engine_snapshot_memory,  "engine_snapshot_memory"  },
8583   { TYPE_SWITCH, &si.fade_screens,            "fade_screens"            },
8584   { TYPE_SWITCH, &si.autorecord,              "automatic_tape_recording"},
8585   { TYPE_SWITCH, &si.show_titlescreen,        "show_titlescreen"        },
8586   { TYPE_SWITCH, &si.quick_doors,             "quick_doors"             },
8587   { TYPE_SWITCH, &si.team_mode,               "team_mode"               },
8588   { TYPE_SWITCH, &si.handicap,                "handicap"                },
8589   { TYPE_SWITCH, &si.skip_levels,             "skip_levels"             },
8590   { TYPE_SWITCH, &si.increment_levels,        "increment_levels"        },
8591   { TYPE_SWITCH, &si.auto_play_next_level,    "auto_play_next_level"    },
8592   { TYPE_SWITCH, &si.skip_scores_after_game,  "skip_scores_after_game"  },
8593   { TYPE_SWITCH, &si.time_limit,              "time_limit"              },
8594   { TYPE_SWITCH, &si.fullscreen,              "fullscreen"              },
8595   { TYPE_INTEGER,&si.window_scaling_percent,  "window_scaling_percent"  },
8596   { TYPE_STRING, &si.window_scaling_quality,  "window_scaling_quality"  },
8597   { TYPE_STRING, &si.screen_rendering_mode,   "screen_rendering_mode"   },
8598   { TYPE_STRING, &si.vsync_mode,              "vsync_mode"              },
8599   { TYPE_SWITCH, &si.ask_on_escape,           "ask_on_escape"           },
8600   { TYPE_SWITCH, &si.ask_on_escape_editor,    "ask_on_escape_editor"    },
8601   { TYPE_SWITCH, &si.quick_switch,            "quick_player_switch"     },
8602   { TYPE_SWITCH, &si.input_on_focus,          "input_on_focus"          },
8603   { TYPE_SWITCH, &si.prefer_aga_graphics,     "prefer_aga_graphics"     },
8604   { TYPE_INTEGER,&si.game_frame_delay,        "game_frame_delay"        },
8605   { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8606   { TYPE_SWITCH, &si.small_game_graphics,     "small_game_graphics"     },
8607   { TYPE_SWITCH, &si.show_snapshot_buttons,   "show_snapshot_buttons"   },
8608   { TYPE_STRING, &si.graphics_set,            "graphics_set"            },
8609   { TYPE_STRING, &si.sounds_set,              "sounds_set"              },
8610   { TYPE_STRING, &si.music_set,               "music_set"               },
8611   { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8612   { TYPE_SWITCH3,&si.override_level_sounds,   "override_level_sounds"   },
8613   { TYPE_SWITCH3,&si.override_level_music,    "override_level_music"    },
8614   { TYPE_INTEGER,&si.volume_simple,           "volume_simple"           },
8615   { TYPE_INTEGER,&si.volume_loops,            "volume_loops"            },
8616   { TYPE_INTEGER,&si.volume_music,            "volume_music"            },
8617   { TYPE_SWITCH, &si.network_mode,            "network_mode"            },
8618   { TYPE_PLAYER, &si.network_player_nr,       "network_player"          },
8619   { TYPE_STRING, &si.touch.control_type,      "touch.control_type"      },
8620   { TYPE_INTEGER,&si.touch.move_distance,     "touch.move_distance"     },
8621   { TYPE_INTEGER,&si.touch.drop_distance,     "touch.drop_distance"     },
8622   { TYPE_INTEGER,&si.touch.transparency,      "touch.transparency"      },
8623   { TYPE_INTEGER,&si.touch.draw_outlined,     "touch.draw_outlined"     },
8624   { TYPE_INTEGER,&si.touch.draw_pressed,      "touch.draw_pressed"      },
8625   { TYPE_INTEGER,&si.touch.grid_xsize[0],     "touch.virtual_buttons.0.xsize" },
8626   { TYPE_INTEGER,&si.touch.grid_ysize[0],     "touch.virtual_buttons.0.ysize" },
8627   { TYPE_INTEGER,&si.touch.grid_xsize[1],     "touch.virtual_buttons.1.xsize" },
8628   { TYPE_INTEGER,&si.touch.grid_ysize[1],     "touch.virtual_buttons.1.ysize" },
8629 };
8630
8631 static struct TokenInfo auto_setup_tokens[] =
8632 {
8633   { TYPE_INTEGER,&sasi.editor_zoom_tilesize,    "editor.zoom_tilesize"  },
8634 };
8635
8636 static struct TokenInfo editor_setup_tokens[] =
8637 {
8638   { TYPE_SWITCH, &sei.el_classic,       "editor.el_classic"             },
8639   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
8640   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
8641   { TYPE_SWITCH, &sei.el_dynamic,       "editor.el_dynamic"             },
8642   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
8643   { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token"    },
8644 };
8645
8646 static struct TokenInfo editor_cascade_setup_tokens[] =
8647 {
8648   { TYPE_SWITCH, &seci.el_bd,           "editor.cascade.el_bd"          },
8649   { TYPE_SWITCH, &seci.el_em,           "editor.cascade.el_em"          },
8650   { TYPE_SWITCH, &seci.el_emc,          "editor.cascade.el_emc"         },
8651   { TYPE_SWITCH, &seci.el_rnd,          "editor.cascade.el_rnd"         },
8652   { TYPE_SWITCH, &seci.el_sb,           "editor.cascade.el_sb"          },
8653   { TYPE_SWITCH, &seci.el_sp,           "editor.cascade.el_sp"          },
8654   { TYPE_SWITCH, &seci.el_dc,           "editor.cascade.el_dc"          },
8655   { TYPE_SWITCH, &seci.el_dx,           "editor.cascade.el_dx"          },
8656   { TYPE_SWITCH, &seci.el_mm,           "editor.cascade.el_mm"          },
8657   { TYPE_SWITCH, &seci.el_df,           "editor.cascade.el_df"          },
8658   { TYPE_SWITCH, &seci.el_chars,        "editor.cascade.el_chars"       },
8659   { TYPE_SWITCH, &seci.el_steel_chars,  "editor.cascade.el_steel_chars" },
8660   { TYPE_SWITCH, &seci.el_ce,           "editor.cascade.el_ce"          },
8661   { TYPE_SWITCH, &seci.el_ge,           "editor.cascade.el_ge"          },
8662   { TYPE_SWITCH, &seci.el_ref,          "editor.cascade.el_ref"         },
8663   { TYPE_SWITCH, &seci.el_user,         "editor.cascade.el_user"        },
8664   { TYPE_SWITCH, &seci.el_dynamic,      "editor.cascade.el_dynamic"     },
8665 };
8666
8667 static struct TokenInfo shortcut_setup_tokens[] =
8668 {
8669   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
8670   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
8671   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         },
8672   { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1"       },
8673   { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2"       },
8674   { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3"       },
8675   { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4"       },
8676   { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all"     },
8677   { TYPE_KEY_X11, &ssi.tape_eject,      "shortcut.tape_eject"           },
8678   { TYPE_KEY_X11, &ssi.tape_extra,      "shortcut.tape_extra"           },
8679   { TYPE_KEY_X11, &ssi.tape_stop,       "shortcut.tape_stop"            },
8680   { TYPE_KEY_X11, &ssi.tape_pause,      "shortcut.tape_pause"           },
8681   { TYPE_KEY_X11, &ssi.tape_record,     "shortcut.tape_record"          },
8682   { TYPE_KEY_X11, &ssi.tape_play,       "shortcut.tape_play"            },
8683   { TYPE_KEY_X11, &ssi.sound_simple,    "shortcut.sound_simple"         },
8684   { TYPE_KEY_X11, &ssi.sound_loops,     "shortcut.sound_loops"          },
8685   { TYPE_KEY_X11, &ssi.sound_music,     "shortcut.sound_music"          },
8686   { TYPE_KEY_X11, &ssi.snap_left,       "shortcut.snap_left"            },
8687   { TYPE_KEY_X11, &ssi.snap_right,      "shortcut.snap_right"           },
8688   { TYPE_KEY_X11, &ssi.snap_up,         "shortcut.snap_up"              },
8689   { TYPE_KEY_X11, &ssi.snap_down,       "shortcut.snap_down"            },
8690 };
8691
8692 static struct TokenInfo player_setup_tokens[] =
8693 {
8694   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
8695   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
8696   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
8697   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
8698   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
8699   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
8700   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
8701   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
8702   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
8703   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
8704   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
8705   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
8706   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
8707   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
8708   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
8709   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               },
8710 };
8711
8712 static struct TokenInfo system_setup_tokens[] =
8713 {
8714   { TYPE_STRING,  &syi.sdl_videodriver,    "system.sdl_videodriver"     },
8715   { TYPE_STRING,  &syi.sdl_audiodriver,    "system.sdl_audiodriver"     },
8716   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8717 };
8718
8719 static struct TokenInfo internal_setup_tokens[] =
8720 {
8721   { TYPE_STRING, &sxi.program_title,            "program_title"         },
8722   { TYPE_STRING, &sxi.program_version,          "program_version"       },
8723   { TYPE_STRING, &sxi.program_author,           "program_author"        },
8724   { TYPE_STRING, &sxi.program_email,            "program_email"         },
8725   { TYPE_STRING, &sxi.program_website,          "program_website"       },
8726   { TYPE_STRING, &sxi.program_copyright,        "program_copyright"     },
8727   { TYPE_STRING, &sxi.program_company,          "program_company"       },
8728   { TYPE_STRING, &sxi.program_icon_file,        "program_icon_file"     },
8729   { TYPE_STRING, &sxi.default_graphics_set,     "default_graphics_set"  },
8730   { TYPE_STRING, &sxi.default_sounds_set,       "default_sounds_set"    },
8731   { TYPE_STRING, &sxi.default_music_set,        "default_music_set"     },
8732   { TYPE_STRING, &sxi.fallback_graphics_file,   "fallback_graphics_file"},
8733   { TYPE_STRING, &sxi.fallback_sounds_file,     "fallback_sounds_file"  },
8734   { TYPE_STRING, &sxi.fallback_music_file,      "fallback_music_file"   },
8735   { TYPE_STRING, &sxi.default_level_series,     "default_level_series"  },
8736   { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8737   { TYPE_BOOLEAN,&sxi.show_scaling_in_title,    "show_scaling_in_title" },
8738   { TYPE_INTEGER,&sxi.default_window_width,     "default_window_width"  },
8739   { TYPE_INTEGER,&sxi.default_window_height,    "default_window_height" },
8740 };
8741
8742 static struct TokenInfo debug_setup_tokens[] =
8743 {
8744   { TYPE_INTEGER, &sdi.frame_delay[0],          "debug.frame_delay_0"   },
8745   { TYPE_INTEGER, &sdi.frame_delay[1],          "debug.frame_delay_1"   },
8746   { TYPE_INTEGER, &sdi.frame_delay[2],          "debug.frame_delay_2"   },
8747   { TYPE_INTEGER, &sdi.frame_delay[3],          "debug.frame_delay_3"   },
8748   { TYPE_INTEGER, &sdi.frame_delay[4],          "debug.frame_delay_4"   },
8749   { TYPE_INTEGER, &sdi.frame_delay[5],          "debug.frame_delay_5"   },
8750   { TYPE_INTEGER, &sdi.frame_delay[6],          "debug.frame_delay_6"   },
8751   { TYPE_INTEGER, &sdi.frame_delay[7],          "debug.frame_delay_7"   },
8752   { TYPE_INTEGER, &sdi.frame_delay[8],          "debug.frame_delay_8"   },
8753   { TYPE_INTEGER, &sdi.frame_delay[9],          "debug.frame_delay_9"   },
8754   { TYPE_KEY_X11, &sdi.frame_delay_key[0],      "debug.key.frame_delay_0" },
8755   { TYPE_KEY_X11, &sdi.frame_delay_key[1],      "debug.key.frame_delay_1" },
8756   { TYPE_KEY_X11, &sdi.frame_delay_key[2],      "debug.key.frame_delay_2" },
8757   { TYPE_KEY_X11, &sdi.frame_delay_key[3],      "debug.key.frame_delay_3" },
8758   { TYPE_KEY_X11, &sdi.frame_delay_key[4],      "debug.key.frame_delay_4" },
8759   { TYPE_KEY_X11, &sdi.frame_delay_key[5],      "debug.key.frame_delay_5" },
8760   { TYPE_KEY_X11, &sdi.frame_delay_key[6],      "debug.key.frame_delay_6" },
8761   { TYPE_KEY_X11, &sdi.frame_delay_key[7],      "debug.key.frame_delay_7" },
8762   { TYPE_KEY_X11, &sdi.frame_delay_key[8],      "debug.key.frame_delay_8" },
8763   { TYPE_KEY_X11, &sdi.frame_delay_key[9],      "debug.key.frame_delay_9" },
8764   { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8765   { TYPE_BOOLEAN, &sdi.frame_delay_game_only,  "debug.frame_delay.game_only" },
8766   { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8767 };
8768
8769 static struct TokenInfo options_setup_tokens[] =
8770 {
8771   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               },
8772 };
8773
8774 static char *get_corrected_login_name(char *login_name)
8775 {
8776   /* needed because player name must be a fixed length string */
8777   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8778
8779   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8780   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8781
8782   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
8783     if (strchr(login_name_new, ' '))
8784       *strchr(login_name_new, ' ') = '\0';
8785
8786   return login_name_new;
8787 }
8788
8789 static void setSetupInfoToDefaults(struct SetupInfo *si)
8790 {
8791   int i;
8792
8793   si->player_name = get_corrected_login_name(getLoginName());
8794
8795   si->sound = TRUE;
8796   si->sound_loops = TRUE;
8797   si->sound_music = TRUE;
8798   si->sound_simple = TRUE;
8799   si->toons = TRUE;
8800   si->scroll_delay = TRUE;
8801   si->scroll_delay_value = STD_SCROLL_DELAY;
8802   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8803   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8804   si->fade_screens = TRUE;
8805   si->autorecord = TRUE;
8806   si->show_titlescreen = TRUE;
8807   si->quick_doors = FALSE;
8808   si->team_mode = FALSE;
8809   si->handicap = TRUE;
8810   si->skip_levels = TRUE;
8811   si->increment_levels = TRUE;
8812   si->auto_play_next_level = TRUE;
8813   si->skip_scores_after_game = FALSE;
8814   si->time_limit = TRUE;
8815   si->fullscreen = FALSE;
8816   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8817   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8818   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8819   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
8820   si->ask_on_escape = TRUE;
8821   si->ask_on_escape_editor = TRUE;
8822   si->quick_switch = FALSE;
8823   si->input_on_focus = FALSE;
8824   si->prefer_aga_graphics = TRUE;
8825   si->game_frame_delay = GAME_FRAME_DELAY;
8826   si->sp_show_border_elements = FALSE;
8827   si->small_game_graphics = FALSE;
8828   si->show_snapshot_buttons = FALSE;
8829
8830   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8831   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8832   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8833
8834   si->override_level_graphics = FALSE;
8835   si->override_level_sounds = FALSE;
8836   si->override_level_music = FALSE;
8837
8838   si->volume_simple = 100;              /* percent */
8839   si->volume_loops = 100;               /* percent */
8840   si->volume_music = 100;               /* percent */
8841
8842   si->network_mode = FALSE;
8843   si->network_player_nr = 0;            /* first player */
8844
8845   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8846   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        /* percent */
8847   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        /* percent */
8848   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          /* percent */
8849   si->touch.draw_outlined = TRUE;
8850   si->touch.draw_pressed = TRUE;
8851
8852   for (i = 0; i < 2; i++)
8853   {
8854     char *default_grid_button[6][2] =
8855     {
8856       { "      ", "  ^^  " },
8857       { "      ", "  ^^  " },
8858       { "      ", "<<  >>" },
8859       { "      ", "<<  >>" },
8860       { "111222", "  vv  " },
8861       { "111222", "  vv  " }
8862     };
8863     int grid_xsize = DEFAULT_GRID_XSIZE(i);
8864     int grid_ysize = DEFAULT_GRID_YSIZE(i);
8865     int min_xsize = MIN(6, grid_xsize);
8866     int min_ysize = MIN(6, grid_ysize);
8867     int startx = grid_xsize - min_xsize;
8868     int starty = grid_ysize - min_ysize;
8869     int x, y;
8870
8871     // virtual buttons grid can only be set to defaults if video is initialized
8872     // (this will be repeated if virtual buttons are not loaded from setup file)
8873     if (video.initialized)
8874     {
8875       si->touch.grid_xsize[i] = grid_xsize;
8876       si->touch.grid_ysize[i] = grid_ysize;
8877     }
8878     else
8879     {
8880       si->touch.grid_xsize[i] = -1;
8881       si->touch.grid_ysize[i] = -1;
8882     }
8883
8884     for (x = 0; x < MAX_GRID_XSIZE; x++)
8885       for (y = 0; y < MAX_GRID_YSIZE; y++)
8886         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
8887
8888     for (x = 0; x < min_xsize; x++)
8889       for (y = 0; y < min_ysize; y++)
8890         si->touch.grid_button[i][x][starty + y] =
8891           default_grid_button[y][0][x];
8892
8893     for (x = 0; x < min_xsize; x++)
8894       for (y = 0; y < min_ysize; y++)
8895         si->touch.grid_button[i][startx + x][starty + y] =
8896           default_grid_button[y][1][x];
8897   }
8898
8899   si->touch.grid_initialized            = video.initialized;
8900
8901   si->editor.el_boulderdash             = TRUE;
8902   si->editor.el_emerald_mine            = TRUE;
8903   si->editor.el_emerald_mine_club       = TRUE;
8904   si->editor.el_more                    = TRUE;
8905   si->editor.el_sokoban                 = TRUE;
8906   si->editor.el_supaplex                = TRUE;
8907   si->editor.el_diamond_caves           = TRUE;
8908   si->editor.el_dx_boulderdash          = TRUE;
8909
8910   si->editor.el_mirror_magic            = TRUE;
8911   si->editor.el_deflektor               = TRUE;
8912
8913   si->editor.el_chars                   = TRUE;
8914   si->editor.el_steel_chars             = TRUE;
8915
8916   si->editor.el_classic                 = TRUE;
8917   si->editor.el_custom                  = TRUE;
8918
8919   si->editor.el_user_defined            = FALSE;
8920   si->editor.el_dynamic                 = TRUE;
8921
8922   si->editor.el_headlines               = TRUE;
8923
8924   si->editor.show_element_token         = FALSE;
8925
8926   si->editor.use_template_for_new_levels = TRUE;
8927
8928   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
8929   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
8930   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
8931
8932   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
8933   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
8934   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
8935   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
8936   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8937
8938   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
8939   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
8940   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
8941   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
8942   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
8943   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
8944
8945   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
8946   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
8947   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
8948
8949   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
8950   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
8951   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
8952   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
8953
8954   for (i = 0; i < MAX_PLAYERS; i++)
8955   {
8956     si->input[i].use_joystick = FALSE;
8957     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8958     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
8959     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8960     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
8961     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
8962     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8963     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
8964     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
8965     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
8966     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
8967     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8968     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
8969     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
8970     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
8971     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
8972   }
8973
8974   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8975   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8976   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8977
8978   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
8979   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
8980   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
8981   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
8982   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
8983   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8984   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
8985
8986   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8987
8988   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8989   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8990   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8991
8992   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8993   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
8994   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
8995
8996   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8997   si->internal.choose_from_top_leveldir = FALSE;
8998   si->internal.show_scaling_in_title = TRUE;
8999
9000   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
9001   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9002
9003   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9004   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9005   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9006   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9007   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9008   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9009   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9010   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9011   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9012   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9013
9014   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9015   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9016   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9017   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9018   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9019   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9020   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9021   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9022   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9023   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9024
9025   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9026   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
9027
9028   si->debug.show_frames_per_second = FALSE;
9029
9030   si->options.verbose = FALSE;
9031
9032 #if defined(PLATFORM_ANDROID)
9033   si->fullscreen = TRUE;
9034 #endif
9035 }
9036
9037 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9038 {
9039   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9040 }
9041
9042 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9043 {
9044   si->editor_cascade.el_bd              = TRUE;
9045   si->editor_cascade.el_em              = TRUE;
9046   si->editor_cascade.el_emc             = TRUE;
9047   si->editor_cascade.el_rnd             = TRUE;
9048   si->editor_cascade.el_sb              = TRUE;
9049   si->editor_cascade.el_sp              = TRUE;
9050   si->editor_cascade.el_dc              = TRUE;
9051   si->editor_cascade.el_dx              = TRUE;
9052
9053   si->editor_cascade.el_mm              = TRUE;
9054   si->editor_cascade.el_df              = TRUE;
9055
9056   si->editor_cascade.el_chars           = FALSE;
9057   si->editor_cascade.el_steel_chars     = FALSE;
9058   si->editor_cascade.el_ce              = FALSE;
9059   si->editor_cascade.el_ge              = FALSE;
9060   si->editor_cascade.el_ref             = FALSE;
9061   si->editor_cascade.el_user            = FALSE;
9062   si->editor_cascade.el_dynamic         = FALSE;
9063 }
9064
9065 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
9066
9067 static char *getHideSetupToken(void *setup_value)
9068 {
9069   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9070
9071   if (setup_value != NULL)
9072     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9073
9074   return hide_setup_token;
9075 }
9076
9077 void setHideSetupEntry(void *setup_value)
9078 {
9079   char *hide_setup_token = getHideSetupToken(setup_value);
9080
9081   if (setup_value != NULL)
9082     setHashEntry(hide_setup_hash, hide_setup_token, "");
9083 }
9084
9085 static void setHideSetupEntryRaw(char *token_text, void *setup_value_raw)
9086 {
9087   /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
9088   void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
9089
9090   setHideSetupEntry(setup_value);
9091 }
9092
9093 boolean hideSetupEntry(void *setup_value)
9094 {
9095   char *hide_setup_token = getHideSetupToken(setup_value);
9096
9097   return (setup_value != NULL &&
9098           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9099 }
9100
9101 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9102                                       struct TokenInfo *token_info,
9103                                       int token_nr, char *token_text)
9104 {
9105   char *token_hide_text = getStringCat2(token_text, ".hide");
9106   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9107
9108   /* set the value of this setup option in the setup option structure */
9109   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9110
9111   /* check if this setup option should be hidden in the setup menu */
9112   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9113     setHideSetupEntryRaw(token_text, token_info[token_nr].value);
9114 }
9115
9116 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9117                                       struct TokenInfo *token_info,
9118                                       int token_nr)
9119 {
9120   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9121                             token_info[token_nr].text);
9122 }
9123
9124 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9125 {
9126   int i, pnr;
9127
9128   if (!setup_file_hash)
9129     return;
9130
9131   if (hide_setup_hash == NULL)
9132     hide_setup_hash = newSetupFileHash();
9133
9134   /* global setup */
9135   si = setup;
9136   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9137     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9138   setup = si;
9139
9140   /* virtual buttons setup */
9141   setup.touch.grid_initialized = TRUE;
9142   for (i = 0; i < 2; i++)
9143   {
9144     int grid_xsize = setup.touch.grid_xsize[i];
9145     int grid_ysize = setup.touch.grid_ysize[i];
9146     int x, y;
9147
9148     // if virtual buttons are not loaded from setup file, repeat initializing
9149     // virtual buttons grid with default values later when video is initialized
9150     if (grid_xsize == -1 ||
9151         grid_ysize == -1)
9152     {
9153       setup.touch.grid_initialized = FALSE;
9154
9155       continue;
9156     }
9157
9158     for (y = 0; y < grid_ysize; y++)
9159     {
9160       char token_string[MAX_LINE_LEN];
9161
9162       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9163
9164       char *value_string = getHashEntry(setup_file_hash, token_string);
9165
9166       if (value_string == NULL)
9167         continue;
9168
9169       for (x = 0; x < grid_xsize; x++)
9170       {
9171         char c = value_string[x];
9172
9173         setup.touch.grid_button[i][x][y] =
9174           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9175       }
9176     }
9177   }
9178
9179   /* editor setup */
9180   sei = setup.editor;
9181   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9182     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9183   setup.editor = sei;
9184
9185   /* shortcut setup */
9186   ssi = setup.shortcut;
9187   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9188     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9189   setup.shortcut = ssi;
9190
9191   /* player setup */
9192   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9193   {
9194     char prefix[30];
9195
9196     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9197
9198     sii = setup.input[pnr];
9199     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9200     {
9201       char full_token[100];
9202
9203       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9204       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9205                                 full_token);
9206     }
9207     setup.input[pnr] = sii;
9208   }
9209
9210   /* system setup */
9211   syi = setup.system;
9212   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9213     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9214   setup.system = syi;
9215
9216   /* internal setup */
9217   sxi = setup.internal;
9218   for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
9219     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9220   setup.internal = sxi;
9221
9222   /* debug setup */
9223   sdi = setup.debug;
9224   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9225     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9226   setup.debug = sdi;
9227
9228   /* options setup */
9229   soi = setup.options;
9230   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9231     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9232   setup.options = soi;
9233
9234   setHideRelatedSetupEntries();
9235 }
9236
9237 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9238 {
9239   int i;
9240
9241   if (!setup_file_hash)
9242     return;
9243
9244   /* auto setup */
9245   sasi = setup.auto_setup;
9246   for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9247     setSetupInfo(auto_setup_tokens, i,
9248                  getHashEntry(setup_file_hash,
9249                               auto_setup_tokens[i].text));
9250   setup.auto_setup = sasi;
9251 }
9252
9253 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9254 {
9255   int i;
9256
9257   if (!setup_file_hash)
9258     return;
9259
9260   /* editor cascade setup */
9261   seci = setup.editor_cascade;
9262   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9263     setSetupInfo(editor_cascade_setup_tokens, i,
9264                  getHashEntry(setup_file_hash,
9265                               editor_cascade_setup_tokens[i].text));
9266   setup.editor_cascade = seci;
9267 }
9268
9269 void LoadSetupFromFilename(char *filename)
9270 {
9271   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9272
9273   if (setup_file_hash)
9274   {
9275     decodeSetupFileHash(setup_file_hash);
9276
9277     freeSetupFileHash(setup_file_hash);
9278   }
9279   else
9280   {
9281     Error(ERR_DEBUG, "using default setup values");
9282   }
9283 }
9284
9285 static void LoadSetup_SpecialPostProcessing(void)
9286 {
9287   char *player_name_new;
9288
9289   /* needed to work around problems with fixed length strings */
9290   player_name_new = get_corrected_login_name(setup.player_name);
9291   free(setup.player_name);
9292   setup.player_name = player_name_new;
9293
9294   /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9295   if (setup.scroll_delay == FALSE)
9296   {
9297     setup.scroll_delay_value = MIN_SCROLL_DELAY;
9298     setup.scroll_delay = TRUE;                  /* now always "on" */
9299   }
9300
9301   /* make sure that scroll delay value stays inside valid range */
9302   setup.scroll_delay_value =
9303     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9304 }
9305
9306 void LoadSetup(void)
9307 {
9308   char *filename;
9309
9310   /* always start with reliable default values */
9311   setSetupInfoToDefaults(&setup);
9312
9313   /* try to load setup values from default setup file */
9314   filename = getDefaultSetupFilename();
9315
9316   if (fileExists(filename))
9317     LoadSetupFromFilename(filename);
9318
9319   /* try to load setup values from user setup file */
9320   filename = getSetupFilename();
9321
9322   LoadSetupFromFilename(filename);
9323
9324   LoadSetup_SpecialPostProcessing();
9325 }
9326
9327 void LoadSetup_AutoSetup(void)
9328 {
9329   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9330   SetupFileHash *setup_file_hash = NULL;
9331
9332   /* always start with reliable default values */
9333   setSetupInfoToDefaults_AutoSetup(&setup);
9334
9335   setup_file_hash = loadSetupFileHash(filename);
9336
9337   if (setup_file_hash)
9338   {
9339     decodeSetupFileHash_AutoSetup(setup_file_hash);
9340
9341     freeSetupFileHash(setup_file_hash);
9342   }
9343
9344   free(filename);
9345 }
9346
9347 void LoadSetup_EditorCascade(void)
9348 {
9349   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9350   SetupFileHash *setup_file_hash = NULL;
9351
9352   /* always start with reliable default values */
9353   setSetupInfoToDefaults_EditorCascade(&setup);
9354
9355   setup_file_hash = loadSetupFileHash(filename);
9356
9357   if (setup_file_hash)
9358   {
9359     decodeSetupFileHash_EditorCascade(setup_file_hash);
9360
9361     freeSetupFileHash(setup_file_hash);
9362   }
9363
9364   free(filename);
9365 }
9366
9367 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9368                                            char *mapping_line)
9369 {
9370   char mapping_guid[MAX_LINE_LEN];
9371   char *mapping_start, *mapping_end;
9372
9373   // get GUID from game controller mapping line: copy complete line
9374   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9375   mapping_guid[MAX_LINE_LEN - 1] = '\0';
9376
9377   // get GUID from game controller mapping line: cut after GUID part
9378   mapping_start = strchr(mapping_guid, ',');
9379   if (mapping_start != NULL)
9380     *mapping_start = '\0';
9381
9382   // cut newline from game controller mapping line
9383   mapping_end = strchr(mapping_line, '\n');
9384   if (mapping_end != NULL)
9385     *mapping_end = '\0';
9386
9387   // add mapping entry to game controller mappings hash
9388   setHashEntry(mappings_hash, mapping_guid, mapping_line);
9389 }
9390
9391 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9392                                                  char *filename)
9393 {
9394   FILE *file;
9395
9396   if (!(file = fopen(filename, MODE_READ)))
9397   {
9398     Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9399
9400     return;
9401   }
9402
9403   while (!feof(file))
9404   {
9405     char line[MAX_LINE_LEN];
9406
9407     if (!fgets(line, MAX_LINE_LEN, file))
9408       break;
9409
9410     addGameControllerMappingToHash(mappings_hash, line);
9411   }
9412
9413   fclose(file);
9414 }
9415
9416 void SaveSetup(void)
9417 {
9418   char *filename = getSetupFilename();
9419   FILE *file;
9420   int i, pnr;
9421
9422   InitUserDataDirectory();
9423
9424   if (!(file = fopen(filename, MODE_WRITE)))
9425   {
9426     Error(ERR_WARN, "cannot write setup file '%s'", filename);
9427     return;
9428   }
9429
9430   fprintFileHeader(file, SETUP_FILENAME);
9431
9432   /* global setup */
9433   si = setup;
9434   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9435   {
9436     /* just to make things nicer :) */
9437     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9438         i == SETUP_TOKEN_GRAPHICS_SET ||
9439         i == SETUP_TOKEN_VOLUME_SIMPLE ||
9440         i == SETUP_TOKEN_NETWORK_MODE ||
9441         i == SETUP_TOKEN_TOUCH_CONTROL_TYPE ||
9442         i == SETUP_TOKEN_TOUCH_GRID_XSIZE_0 ||
9443         i == SETUP_TOKEN_TOUCH_GRID_XSIZE_1)
9444       fprintf(file, "\n");
9445
9446     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9447   }
9448
9449   /* virtual buttons setup */
9450   for (i = 0; i < 2; i++)
9451   {
9452     int grid_xsize = setup.touch.grid_xsize[i];
9453     int grid_ysize = setup.touch.grid_ysize[i];
9454     int x, y;
9455
9456     fprintf(file, "\n");
9457
9458     for (y = 0; y < grid_ysize; y++)
9459     {
9460       char token_string[MAX_LINE_LEN];
9461       char value_string[MAX_LINE_LEN];
9462
9463       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9464
9465       for (x = 0; x < grid_xsize; x++)
9466       {
9467         char c = setup.touch.grid_button[i][x][y];
9468
9469         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9470       }
9471
9472       value_string[grid_xsize] = '\0';
9473
9474       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9475     }
9476   }
9477
9478   /* editor setup */
9479   sei = setup.editor;
9480   fprintf(file, "\n");
9481   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9482     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9483
9484   /* shortcut setup */
9485   ssi = setup.shortcut;
9486   fprintf(file, "\n");
9487   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9488     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9489
9490   /* player setup */
9491   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9492   {
9493     char prefix[30];
9494
9495     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9496     fprintf(file, "\n");
9497
9498     sii = setup.input[pnr];
9499     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9500       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9501   }
9502
9503   /* system setup */
9504   syi = setup.system;
9505   fprintf(file, "\n");
9506   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9507     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9508
9509   /* internal setup */
9510   /* (internal setup values not saved to user setup file) */
9511
9512   /* debug setup */
9513   sdi = setup.debug;
9514   fprintf(file, "\n");
9515   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9516     fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9517
9518   /* options setup */
9519   soi = setup.options;
9520   fprintf(file, "\n");
9521   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9522     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9523
9524   fclose(file);
9525
9526   SetFilePermissions(filename, PERMS_PRIVATE);
9527 }
9528
9529 void SaveSetup_AutoSetup(void)
9530 {
9531   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9532   FILE *file;
9533   int i;
9534
9535   InitUserDataDirectory();
9536
9537   if (!(file = fopen(filename, MODE_WRITE)))
9538   {
9539     Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9540     free(filename);
9541     return;
9542   }
9543
9544   fprintFileHeader(file, AUTOSETUP_FILENAME);
9545
9546   sasi = setup.auto_setup;
9547   for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9548     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9549
9550   fclose(file);
9551
9552   SetFilePermissions(filename, PERMS_PRIVATE);
9553
9554   free(filename);
9555 }
9556
9557 void SaveSetup_EditorCascade(void)
9558 {
9559   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9560   FILE *file;
9561   int i;
9562
9563   InitUserDataDirectory();
9564
9565   if (!(file = fopen(filename, MODE_WRITE)))
9566   {
9567     Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9568     free(filename);
9569     return;
9570   }
9571
9572   fprintFileHeader(file, EDITORCASCADE_FILENAME);
9573
9574   seci = setup.editor_cascade;
9575   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9576     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9577
9578   fclose(file);
9579
9580   SetFilePermissions(filename, PERMS_PRIVATE);
9581
9582   free(filename);
9583 }
9584
9585 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9586                                                   char *filename)
9587 {
9588   FILE *file;
9589
9590   if (!(file = fopen(filename, MODE_WRITE)))
9591   {
9592     Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9593
9594     return;
9595   }
9596
9597   BEGIN_HASH_ITERATION(mappings_hash, itr)
9598   {
9599     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9600   }
9601   END_HASH_ITERATION(mappings_hash, itr)
9602
9603   fclose(file);
9604 }
9605
9606 void SaveSetup_AddGameControllerMapping(char *mapping)
9607 {
9608   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9609   SetupFileHash *mappings_hash = newSetupFileHash();
9610
9611   InitUserDataDirectory();
9612
9613   // load existing personal game controller mappings
9614   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9615
9616   // add new mapping to personal game controller mappings
9617   addGameControllerMappingToHash(mappings_hash, mapping);
9618
9619   // save updated personal game controller mappings
9620   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9621
9622   freeSetupFileHash(mappings_hash);
9623   free(filename);
9624 }
9625
9626 void LoadCustomElementDescriptions(void)
9627 {
9628   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9629   SetupFileHash *setup_file_hash;
9630   int i;
9631
9632   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9633   {
9634     if (element_info[i].custom_description != NULL)
9635     {
9636       free(element_info[i].custom_description);
9637       element_info[i].custom_description = NULL;
9638     }
9639   }
9640
9641   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9642     return;
9643
9644   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9645   {
9646     char *token = getStringCat2(element_info[i].token_name, ".name");
9647     char *value = getHashEntry(setup_file_hash, token);
9648
9649     if (value != NULL)
9650       element_info[i].custom_description = getStringCopy(value);
9651
9652     free(token);
9653   }
9654
9655   freeSetupFileHash(setup_file_hash);
9656 }
9657
9658 static int getElementFromToken(char *token)
9659 {
9660   char *value = getHashEntry(element_token_hash, token);
9661
9662   if (value != NULL)
9663     return atoi(value);
9664
9665   Error(ERR_WARN, "unknown element token '%s'", token);
9666
9667   return EL_UNDEFINED;
9668 }
9669
9670 static int get_token_parameter_value(char *token, char *value_raw)
9671 {
9672   char *suffix;
9673
9674   if (token == NULL || value_raw == NULL)
9675     return ARG_UNDEFINED_VALUE;
9676
9677   suffix = strrchr(token, '.');
9678   if (suffix == NULL)
9679     suffix = token;
9680
9681   if (strEqual(suffix, ".element"))
9682     return getElementFromToken(value_raw);
9683
9684   /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9685   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9686 }
9687
9688 void InitMenuDesignSettings_Static(void)
9689 {
9690   int i;
9691
9692   /* always start with reliable default values from static default config */
9693   for (i = 0; image_config_vars[i].token != NULL; i++)
9694   {
9695     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9696
9697     if (value != NULL)
9698       *image_config_vars[i].value =
9699         get_token_parameter_value(image_config_vars[i].token, value);
9700   }
9701 }
9702
9703 static void InitMenuDesignSettings_SpecialPreProcessing(void)
9704 {
9705   int i;
9706
9707   /* the following initializes hierarchical values from static configuration */
9708
9709   /* special case: initialize "ARG_DEFAULT" values in static default config */
9710   /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9711   titlescreen_initial_first_default.fade_mode  =
9712     title_initial_first_default.fade_mode;
9713   titlescreen_initial_first_default.fade_delay =
9714     title_initial_first_default.fade_delay;
9715   titlescreen_initial_first_default.post_delay =
9716     title_initial_first_default.post_delay;
9717   titlescreen_initial_first_default.auto_delay =
9718     title_initial_first_default.auto_delay;
9719   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
9720   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9721   titlescreen_first_default.post_delay = title_first_default.post_delay;
9722   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9723   titlemessage_initial_first_default.fade_mode  =
9724     title_initial_first_default.fade_mode;
9725   titlemessage_initial_first_default.fade_delay =
9726     title_initial_first_default.fade_delay;
9727   titlemessage_initial_first_default.post_delay =
9728     title_initial_first_default.post_delay;
9729   titlemessage_initial_first_default.auto_delay =
9730     title_initial_first_default.auto_delay;
9731   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
9732   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9733   titlemessage_first_default.post_delay = title_first_default.post_delay;
9734   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9735
9736   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
9737   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9738   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9739   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9740   titlescreen_default.fade_mode  = title_default.fade_mode;
9741   titlescreen_default.fade_delay = title_default.fade_delay;
9742   titlescreen_default.post_delay = title_default.post_delay;
9743   titlescreen_default.auto_delay = title_default.auto_delay;
9744   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
9745   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9746   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9747   titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9748   titlemessage_default.fade_mode  = title_default.fade_mode;
9749   titlemessage_default.fade_delay = title_default.fade_delay;
9750   titlemessage_default.post_delay = title_default.post_delay;
9751   titlemessage_default.auto_delay = title_default.auto_delay;
9752
9753   /* special case: initialize "ARG_DEFAULT" values in static default config */
9754   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9755   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9756   {
9757     titlescreen_initial_first[i] = titlescreen_initial_first_default;
9758     titlescreen_first[i] = titlescreen_first_default;
9759     titlemessage_initial_first[i] = titlemessage_initial_first_default;
9760     titlemessage_first[i] = titlemessage_first_default;
9761
9762     titlescreen_initial[i] = titlescreen_initial_default;
9763     titlescreen[i] = titlescreen_default;
9764     titlemessage_initial[i] = titlemessage_initial_default;
9765     titlemessage[i] = titlemessage_default;
9766   }
9767
9768   /* special case: initialize "ARG_DEFAULT" values in static default config */
9769   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9770   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9771   {
9772     if (i == GFX_SPECIAL_ARG_TITLE)     /* title values already initialized */
9773       continue;
9774
9775     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9776     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9777     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9778   }
9779
9780   /* special case: initialize "ARG_DEFAULT" values in static default config */
9781   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9782   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9783   {
9784     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9785     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9786     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9787
9788     if (i == GFX_SPECIAL_ARG_EDITOR)    /* editor values already initialized */
9789       continue;
9790
9791     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9792   }
9793 }
9794
9795 static void InitMenuDesignSettings_SpecialPostProcessing(void)
9796 {
9797   static struct
9798   {
9799     struct XY *dst, *src;
9800   }
9801   game_buttons_xy[] =
9802   {
9803     { &game.button.save,        &game.button.stop       },
9804     { &game.button.pause2,      &game.button.pause      },
9805     { &game.button.load,        &game.button.play       },
9806     { &game.button.undo,        &game.button.stop       },
9807     { &game.button.redo,        &game.button.play       },
9808
9809     { NULL,                     NULL                    }
9810   };
9811   int i;
9812
9813   /* special case: initialize later added SETUP list size from LEVELS value */
9814   if (menu.list_size[GAME_MODE_SETUP] == -1)
9815     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9816
9817   /* set default position for snapshot buttons to stop/pause/play buttons */
9818   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9819     if ((*game_buttons_xy[i].dst).x == -1 &&
9820         (*game_buttons_xy[i].dst).y == -1)
9821       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9822 }
9823
9824 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
9825 {
9826   static struct
9827   {
9828     struct XYTileSize *dst, *src;
9829     int graphic;
9830   }
9831   editor_buttons_xy[] =
9832   {
9833     {
9834       &editor.button.element_left,      &editor.palette.element_left,
9835       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9836     },
9837     {
9838       &editor.button.element_middle,    &editor.palette.element_middle,
9839       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9840     },
9841     {
9842       &editor.button.element_right,     &editor.palette.element_right,
9843       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9844     },
9845
9846     { NULL,                     NULL                    }
9847   };
9848   int i;
9849
9850   /* set default position for element buttons to element graphics */
9851   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9852   {
9853     if ((*editor_buttons_xy[i].dst).x == -1 &&
9854         (*editor_buttons_xy[i].dst).y == -1)
9855     {
9856       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9857
9858       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9859
9860       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9861     }
9862   }
9863 }
9864
9865 static void LoadMenuDesignSettingsFromFilename(char *filename)
9866 {
9867   static struct TitleFadingInfo tfi;
9868   static struct TitleMessageInfo tmi;
9869   static struct TokenInfo title_tokens[] =
9870   {
9871     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
9872     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
9873     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
9874     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
9875
9876     { -1,               NULL,                   NULL                    }
9877   };
9878   static struct TokenInfo titlemessage_tokens[] =
9879   {
9880     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
9881     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
9882     { TYPE_INTEGER,     &tmi.width,             ".width"                },
9883     { TYPE_INTEGER,     &tmi.height,            ".height"               },
9884     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
9885     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
9886     { TYPE_INTEGER,     &tmi.align,             ".align"                },
9887     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
9888     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
9889     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
9890     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
9891     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
9892     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
9893     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
9894     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
9895     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
9896     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
9897
9898     { -1,               NULL,                   NULL                    }
9899   };
9900   static struct
9901   {
9902     struct TitleFadingInfo *info;
9903     char *text;
9904   }
9905   title_info[] =
9906   {
9907     /* initialize first titles from "enter screen" definitions, if defined */
9908     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
9909     { &title_first_default,             "menu.enter_screen.TITLE"       },
9910
9911     /* initialize title screens from "next screen" definitions, if defined */
9912     { &title_initial_default,           "menu.next_screen.TITLE"        },
9913     { &title_default,                   "menu.next_screen.TITLE"        },
9914
9915     { NULL,                             NULL                            }
9916   };
9917   static struct
9918   {
9919     struct TitleMessageInfo *array;
9920     char *text;
9921   }
9922   titlemessage_arrays[] =
9923   {
9924     /* initialize first titles from "enter screen" definitions, if defined */
9925     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
9926     { titlescreen_first,                "menu.enter_screen.TITLE"       },
9927     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
9928     { titlemessage_first,               "menu.enter_screen.TITLE"       },
9929
9930     /* initialize titles from "next screen" definitions, if defined */
9931     { titlescreen_initial,              "menu.next_screen.TITLE"        },
9932     { titlescreen,                      "menu.next_screen.TITLE"        },
9933     { titlemessage_initial,             "menu.next_screen.TITLE"        },
9934     { titlemessage,                     "menu.next_screen.TITLE"        },
9935
9936     /* overwrite titles with title definitions, if defined */
9937     { titlescreen_initial_first,        "[title_initial]"               },
9938     { titlescreen_first,                "[title]"                       },
9939     { titlemessage_initial_first,       "[title_initial]"               },
9940     { titlemessage_first,               "[title]"                       },
9941
9942     { titlescreen_initial,              "[title_initial]"               },
9943     { titlescreen,                      "[title]"                       },
9944     { titlemessage_initial,             "[title_initial]"               },
9945     { titlemessage,                     "[title]"                       },
9946
9947     /* overwrite titles with title screen/message definitions, if defined */
9948     { titlescreen_initial_first,        "[titlescreen_initial]"         },
9949     { titlescreen_first,                "[titlescreen]"                 },
9950     { titlemessage_initial_first,       "[titlemessage_initial]"        },
9951     { titlemessage_first,               "[titlemessage]"                },
9952
9953     { titlescreen_initial,              "[titlescreen_initial]"         },
9954     { titlescreen,                      "[titlescreen]"                 },
9955     { titlemessage_initial,             "[titlemessage_initial]"        },
9956     { titlemessage,                     "[titlemessage]"                },
9957
9958     { NULL,                             NULL                            }
9959   };
9960   SetupFileHash *setup_file_hash;
9961   int i, j, k;
9962
9963   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9964     return;
9965
9966   /* the following initializes hierarchical values from dynamic configuration */
9967
9968   /* special case: initialize with default values that may be overwritten */
9969   /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9970   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9971   {
9972     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9973     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9974     char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9975
9976     if (value_1 != NULL)
9977       menu.draw_xoffset[i] = get_integer_from_string(value_1);
9978     if (value_2 != NULL)
9979       menu.draw_yoffset[i] = get_integer_from_string(value_2);
9980     if (value_3 != NULL)
9981       menu.list_size[i] = get_integer_from_string(value_3);
9982   }
9983
9984   /* special case: initialize with default values that may be overwritten */
9985   /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9986   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9987   {
9988     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9989     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9990
9991     if (value_1 != NULL)
9992       menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9993     if (value_2 != NULL)
9994       menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9995
9996     if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9997     {
9998       char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9999
10000       if (value_1 != NULL)
10001         menu.list_size_info[i] = get_integer_from_string(value_1);
10002     }
10003   }
10004
10005   /* special case: initialize with default values that may be overwritten */
10006   /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
10007   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
10008   {
10009     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
10010     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
10011
10012     if (value_1 != NULL)
10013       menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
10014     if (value_2 != NULL)
10015       menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
10016   }
10017
10018   /* special case: initialize with default values that may be overwritten */
10019   /* (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO") */
10020   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
10021   {
10022     char *value_1 = getHashEntry(setup_file_hash,"menu.left_spacing.INFO");
10023     char *value_2 = getHashEntry(setup_file_hash,"menu.right_spacing.INFO");
10024     char *value_3 = getHashEntry(setup_file_hash,"menu.top_spacing.INFO");
10025     char *value_4 = getHashEntry(setup_file_hash,"menu.bottom_spacing.INFO");
10026     char *value_5 = getHashEntry(setup_file_hash,"menu.paragraph_spacing.INFO");
10027     char *value_6 = getHashEntry(setup_file_hash,"menu.headline1_spacing.INFO");
10028     char *value_7 = getHashEntry(setup_file_hash,"menu.headline2_spacing.INFO");
10029     char *value_8 = getHashEntry(setup_file_hash,"menu.line_spacing.INFO");
10030     char *value_9 = getHashEntry(setup_file_hash,"menu.extra_spacing.INFO");
10031
10032     if (value_1 != NULL)
10033       menu.left_spacing_info[i]      = get_integer_from_string(value_1);
10034     if (value_2 != NULL)
10035       menu.right_spacing_info[i]     = get_integer_from_string(value_2);
10036     if (value_3 != NULL)
10037       menu.top_spacing_info[i]       = get_integer_from_string(value_3);
10038     if (value_4 != NULL)
10039       menu.bottom_spacing_info[i]    = get_integer_from_string(value_4);
10040     if (value_5 != NULL)
10041       menu.paragraph_spacing_info[i] = get_integer_from_string(value_5);
10042     if (value_6 != NULL)
10043       menu.headline1_spacing_info[i] = get_integer_from_string(value_6);
10044     if (value_7 != NULL)
10045       menu.headline2_spacing_info[i] = get_integer_from_string(value_7);
10046     if (value_8 != NULL)
10047       menu.line_spacing_info[i]      = get_integer_from_string(value_8);
10048     if (value_9 != NULL)
10049       menu.extra_spacing_info[i]     = get_integer_from_string(value_9);
10050   }
10051
10052   /* special case: initialize with default values that may be overwritten */
10053   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
10054   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10055   {
10056     char *token_1 = "menu.enter_screen.fade_mode";
10057     char *token_2 = "menu.enter_screen.fade_delay";
10058     char *token_3 = "menu.enter_screen.post_delay";
10059     char *token_4 = "menu.leave_screen.fade_mode";
10060     char *token_5 = "menu.leave_screen.fade_delay";
10061     char *token_6 = "menu.leave_screen.post_delay";
10062     char *token_7 = "menu.next_screen.fade_mode";
10063     char *token_8 = "menu.next_screen.fade_delay";
10064     char *token_9 = "menu.next_screen.post_delay";
10065     char *value_1 = getHashEntry(setup_file_hash, token_1);
10066     char *value_2 = getHashEntry(setup_file_hash, token_2);
10067     char *value_3 = getHashEntry(setup_file_hash, token_3);
10068     char *value_4 = getHashEntry(setup_file_hash, token_4);
10069     char *value_5 = getHashEntry(setup_file_hash, token_5);
10070     char *value_6 = getHashEntry(setup_file_hash, token_6);
10071     char *value_7 = getHashEntry(setup_file_hash, token_7);
10072     char *value_8 = getHashEntry(setup_file_hash, token_8);
10073     char *value_9 = getHashEntry(setup_file_hash, token_9);
10074
10075     if (value_1 != NULL)
10076       menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
10077                                                                  value_1);
10078     if (value_2 != NULL)
10079       menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
10080                                                                   value_2);
10081     if (value_3 != NULL)
10082       menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
10083                                                                   value_3);
10084     if (value_4 != NULL)
10085       menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
10086                                                                  value_4);
10087     if (value_5 != NULL)
10088       menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
10089                                                                   value_5);
10090     if (value_6 != NULL)
10091       menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
10092                                                                   value_6);
10093     if (value_7 != NULL)
10094       menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
10095                                                                 value_7);
10096     if (value_8 != NULL)
10097       menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
10098                                                                  value_8);
10099     if (value_9 != NULL)
10100       menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
10101                                                                  value_9);
10102   }
10103
10104   /* special case: initialize with default values that may be overwritten */
10105   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
10106   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10107   {
10108     char *token_w1 = "viewport.window.width";
10109     char *token_w2 = "viewport.window.height";
10110     char *token_01 = "viewport.playfield.x";
10111     char *token_02 = "viewport.playfield.y";
10112     char *token_03 = "viewport.playfield.width";
10113     char *token_04 = "viewport.playfield.height";
10114     char *token_05 = "viewport.playfield.border_size";
10115     char *token_06 = "viewport.door_1.x";
10116     char *token_07 = "viewport.door_1.y";
10117     char *token_08 = "viewport.door_1.width";
10118     char *token_09 = "viewport.door_1.height";
10119     char *token_10 = "viewport.door_1.border_size";
10120     char *token_11 = "viewport.door_2.x";
10121     char *token_12 = "viewport.door_2.y";
10122     char *token_13 = "viewport.door_2.width";
10123     char *token_14 = "viewport.door_2.height";
10124     char *token_15 = "viewport.door_2.border_size";
10125     char *value_w1 = getHashEntry(setup_file_hash, token_w1);
10126     char *value_w2 = getHashEntry(setup_file_hash, token_w2);
10127     char *value_01 = getHashEntry(setup_file_hash, token_01);
10128     char *value_02 = getHashEntry(setup_file_hash, token_02);
10129     char *value_03 = getHashEntry(setup_file_hash, token_03);
10130     char *value_04 = getHashEntry(setup_file_hash, token_04);
10131     char *value_05 = getHashEntry(setup_file_hash, token_05);
10132     char *value_06 = getHashEntry(setup_file_hash, token_06);
10133     char *value_07 = getHashEntry(setup_file_hash, token_07);
10134     char *value_08 = getHashEntry(setup_file_hash, token_08);
10135     char *value_09 = getHashEntry(setup_file_hash, token_09);
10136     char *value_10 = getHashEntry(setup_file_hash, token_10);
10137     char *value_11 = getHashEntry(setup_file_hash, token_11);
10138     char *value_12 = getHashEntry(setup_file_hash, token_12);
10139     char *value_13 = getHashEntry(setup_file_hash, token_13);
10140     char *value_14 = getHashEntry(setup_file_hash, token_14);
10141     char *value_15 = getHashEntry(setup_file_hash, token_15);
10142
10143     if (value_w1 != NULL)
10144       viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
10145     if (value_w2 != NULL)
10146       viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
10147     if (value_01 != NULL)
10148       viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
10149     if (value_02 != NULL)
10150       viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
10151     if (value_03 != NULL)
10152       viewport.playfield[i].width = get_token_parameter_value(token_03,
10153                                                               value_03);
10154     if (value_04 != NULL)
10155       viewport.playfield[i].height = get_token_parameter_value(token_04,
10156                                                                value_04);
10157     if (value_05 != NULL)
10158       viewport.playfield[i].border_size = get_token_parameter_value(token_05,
10159                                                                     value_05);
10160     if (value_06 != NULL)
10161       viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
10162     if (value_07 != NULL)
10163       viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
10164     if (value_08 != NULL)
10165       viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
10166     if (value_09 != NULL)
10167       viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
10168     if (value_10 != NULL)
10169       viewport.door_1[i].border_size = get_token_parameter_value(token_10,
10170                                                                  value_10);
10171     if (value_11 != NULL)
10172       viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
10173     if (value_12 != NULL)
10174       viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
10175     if (value_13 != NULL)
10176       viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
10177     if (value_14 != NULL)
10178       viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
10179     if (value_15 != NULL)
10180       viewport.door_1[i].border_size = get_token_parameter_value(token_15,
10181                                                                  value_15);
10182   }
10183
10184   /* special case: initialize with default values that may be overwritten */
10185   /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
10186   for (i = 0; title_info[i].info != NULL; i++)
10187   {
10188     struct TitleFadingInfo *info = title_info[i].info;
10189     char *base_token = title_info[i].text;
10190
10191     for (j = 0; title_tokens[j].type != -1; j++)
10192     {
10193       char *token = getStringCat2(base_token, title_tokens[j].text);
10194       char *value = getHashEntry(setup_file_hash, token);
10195
10196       if (value != NULL)
10197       {
10198         int parameter_value = get_token_parameter_value(token, value);
10199
10200         tfi = *info;
10201
10202         *(int *)title_tokens[j].value = (int)parameter_value;
10203
10204         *info = tfi;
10205       }
10206
10207       free(token);
10208     }
10209   }
10210
10211   /* special case: initialize with default values that may be overwritten */
10212   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
10213   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
10214   {
10215     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
10216     char *base_token = titlemessage_arrays[i].text;
10217
10218     for (j = 0; titlemessage_tokens[j].type != -1; j++)
10219     {
10220       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
10221       char *value = getHashEntry(setup_file_hash, token);
10222
10223       if (value != NULL)
10224       {
10225         int parameter_value = get_token_parameter_value(token, value);
10226
10227         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
10228         {
10229           tmi = array[k];
10230
10231           if (titlemessage_tokens[j].type == TYPE_INTEGER)
10232             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
10233           else
10234             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
10235
10236           array[k] = tmi;
10237         }
10238       }
10239
10240       free(token);
10241     }
10242   }
10243
10244   /* read (and overwrite with) values that may be specified in config file */
10245   for (i = 0; image_config_vars[i].token != NULL; i++)
10246   {
10247     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
10248
10249     /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
10250     if (value != NULL && !strEqual(value, ARG_DEFAULT))
10251       *image_config_vars[i].value =
10252         get_token_parameter_value(image_config_vars[i].token, value);
10253   }
10254
10255   freeSetupFileHash(setup_file_hash);
10256 }
10257
10258 void LoadMenuDesignSettings(void)
10259 {
10260   char *filename_base = UNDEFINED_FILENAME, *filename_local;
10261
10262   InitMenuDesignSettings_Static();
10263   InitMenuDesignSettings_SpecialPreProcessing();
10264
10265   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
10266   {
10267     /* first look for special settings configured in level series config */
10268     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
10269
10270     if (fileExists(filename_base))
10271       LoadMenuDesignSettingsFromFilename(filename_base);
10272   }
10273
10274   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10275
10276   if (filename_local != NULL && !strEqual(filename_base, filename_local))
10277     LoadMenuDesignSettingsFromFilename(filename_local);
10278
10279   InitMenuDesignSettings_SpecialPostProcessing();
10280 }
10281
10282 void LoadMenuDesignSettings_AfterGraphics(void)
10283 {
10284   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
10285 }
10286
10287 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
10288 {
10289   char *filename = getEditorSetupFilename();
10290   SetupFileList *setup_file_list, *list;
10291   SetupFileHash *element_hash;
10292   int num_unknown_tokens = 0;
10293   int i;
10294
10295   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
10296     return;
10297
10298   element_hash = newSetupFileHash();
10299
10300   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10301     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10302
10303   /* determined size may be larger than needed (due to unknown elements) */
10304   *num_elements = 0;
10305   for (list = setup_file_list; list != NULL; list = list->next)
10306     (*num_elements)++;
10307
10308   /* add space for up to 3 more elements for padding that may be needed */
10309   *num_elements += 3;
10310
10311   /* free memory for old list of elements, if needed */
10312   checked_free(*elements);
10313
10314   /* allocate memory for new list of elements */
10315   *elements = checked_malloc(*num_elements * sizeof(int));
10316
10317   *num_elements = 0;
10318   for (list = setup_file_list; list != NULL; list = list->next)
10319   {
10320     char *value = getHashEntry(element_hash, list->token);
10321
10322     if (value == NULL)          /* try to find obsolete token mapping */
10323     {
10324       char *mapped_token = get_mapped_token(list->token);
10325
10326       if (mapped_token != NULL)
10327       {
10328         value = getHashEntry(element_hash, mapped_token);
10329
10330         free(mapped_token);
10331       }
10332     }
10333
10334     if (value != NULL)
10335     {
10336       (*elements)[(*num_elements)++] = atoi(value);
10337     }
10338     else
10339     {
10340       if (num_unknown_tokens == 0)
10341       {
10342         Error(ERR_INFO_LINE, "-");
10343         Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10344         Error(ERR_INFO, "- config file: '%s'", filename);
10345
10346         num_unknown_tokens++;
10347       }
10348
10349       Error(ERR_INFO, "- token: '%s'", list->token);
10350     }
10351   }
10352
10353   if (num_unknown_tokens > 0)
10354     Error(ERR_INFO_LINE, "-");
10355
10356   while (*num_elements % 4)     /* pad with empty elements, if needed */
10357     (*elements)[(*num_elements)++] = EL_EMPTY;
10358
10359   freeSetupFileList(setup_file_list);
10360   freeSetupFileHash(element_hash);
10361
10362 #if 0
10363   for (i = 0; i < *num_elements; i++)
10364     printf("editor: element '%s' [%d]\n",
10365            element_info[(*elements)[i]].token_name, (*elements)[i]);
10366 #endif
10367 }
10368
10369 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10370                                                      boolean is_sound)
10371 {
10372   SetupFileHash *setup_file_hash = NULL;
10373   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10374   char *filename_music, *filename_prefix, *filename_info;
10375   struct
10376   {
10377     char *token;
10378     char **value_ptr;
10379   }
10380   token_to_value_ptr[] =
10381   {
10382     { "title_header",   &tmp_music_file_info.title_header       },
10383     { "artist_header",  &tmp_music_file_info.artist_header      },
10384     { "album_header",   &tmp_music_file_info.album_header       },
10385     { "year_header",    &tmp_music_file_info.year_header        },
10386
10387     { "title",          &tmp_music_file_info.title              },
10388     { "artist",         &tmp_music_file_info.artist             },
10389     { "album",          &tmp_music_file_info.album              },
10390     { "year",           &tmp_music_file_info.year               },
10391
10392     { NULL,             NULL                                    },
10393   };
10394   int i;
10395
10396   filename_music = (is_sound ? getCustomSoundFilename(basename) :
10397                     getCustomMusicFilename(basename));
10398
10399   if (filename_music == NULL)
10400     return NULL;
10401
10402   /* ---------- try to replace file extension ---------- */
10403
10404   filename_prefix = getStringCopy(filename_music);
10405   if (strrchr(filename_prefix, '.') != NULL)
10406     *strrchr(filename_prefix, '.') = '\0';
10407   filename_info = getStringCat2(filename_prefix, ".txt");
10408
10409   if (fileExists(filename_info))
10410     setup_file_hash = loadSetupFileHash(filename_info);
10411
10412   free(filename_prefix);
10413   free(filename_info);
10414
10415   if (setup_file_hash == NULL)
10416   {
10417     /* ---------- try to add file extension ---------- */
10418
10419     filename_prefix = getStringCopy(filename_music);
10420     filename_info = getStringCat2(filename_prefix, ".txt");
10421
10422     if (fileExists(filename_info))
10423       setup_file_hash = loadSetupFileHash(filename_info);
10424
10425     free(filename_prefix);
10426     free(filename_info);
10427   }
10428
10429   if (setup_file_hash == NULL)
10430     return NULL;
10431
10432   /* ---------- music file info found ---------- */
10433
10434   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10435
10436   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10437   {
10438     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10439
10440     *token_to_value_ptr[i].value_ptr =
10441       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10442   }
10443
10444   tmp_music_file_info.basename = getStringCopy(basename);
10445   tmp_music_file_info.music = music;
10446   tmp_music_file_info.is_sound = is_sound;
10447
10448   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10449   *new_music_file_info = tmp_music_file_info;
10450
10451   return new_music_file_info;
10452 }
10453
10454 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10455 {
10456   return get_music_file_info_ext(basename, music, FALSE);
10457 }
10458
10459 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10460 {
10461   return get_music_file_info_ext(basename, sound, TRUE);
10462 }
10463
10464 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10465                                      char *basename, boolean is_sound)
10466 {
10467   for (; list != NULL; list = list->next)
10468     if (list->is_sound == is_sound && strEqual(list->basename, basename))
10469       return TRUE;
10470
10471   return FALSE;
10472 }
10473
10474 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10475 {
10476   return music_info_listed_ext(list, basename, FALSE);
10477 }
10478
10479 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10480 {
10481   return music_info_listed_ext(list, basename, TRUE);
10482 }
10483
10484 void LoadMusicInfo(void)
10485 {
10486   char *music_directory = getCustomMusicDirectory();
10487   int num_music = getMusicListSize();
10488   int num_music_noconf = 0;
10489   int num_sounds = getSoundListSize();
10490   Directory *dir;
10491   DirectoryEntry *dir_entry;
10492   struct FileInfo *music, *sound;
10493   struct MusicFileInfo *next, **new;
10494   int i;
10495
10496   while (music_file_info != NULL)
10497   {
10498     next = music_file_info->next;
10499
10500     checked_free(music_file_info->basename);
10501
10502     checked_free(music_file_info->title_header);
10503     checked_free(music_file_info->artist_header);
10504     checked_free(music_file_info->album_header);
10505     checked_free(music_file_info->year_header);
10506
10507     checked_free(music_file_info->title);
10508     checked_free(music_file_info->artist);
10509     checked_free(music_file_info->album);
10510     checked_free(music_file_info->year);
10511
10512     free(music_file_info);
10513
10514     music_file_info = next;
10515   }
10516
10517   new = &music_file_info;
10518
10519   for (i = 0; i < num_music; i++)
10520   {
10521     music = getMusicListEntry(i);
10522
10523     if (music->filename == NULL)
10524       continue;
10525
10526     if (strEqual(music->filename, UNDEFINED_FILENAME))
10527       continue;
10528
10529     /* a configured file may be not recognized as music */
10530     if (!FileIsMusic(music->filename))
10531       continue;
10532
10533     if (!music_info_listed(music_file_info, music->filename))
10534     {
10535       *new = get_music_file_info(music->filename, i);
10536
10537       if (*new != NULL)
10538         new = &(*new)->next;
10539     }
10540   }
10541
10542   if ((dir = openDirectory(music_directory)) == NULL)
10543   {
10544     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10545     return;
10546   }
10547
10548   while ((dir_entry = readDirectory(dir)) != NULL)      /* loop all entries */
10549   {
10550     char *basename = dir_entry->basename;
10551     boolean music_already_used = FALSE;
10552     int i;
10553
10554     /* skip all music files that are configured in music config file */
10555     for (i = 0; i < num_music; i++)
10556     {
10557       music = getMusicListEntry(i);
10558
10559       if (music->filename == NULL)
10560         continue;
10561
10562       if (strEqual(basename, music->filename))
10563       {
10564         music_already_used = TRUE;
10565         break;
10566       }
10567     }
10568
10569     if (music_already_used)
10570       continue;
10571
10572     if (!FileIsMusic(dir_entry->filename))
10573       continue;
10574
10575     if (!music_info_listed(music_file_info, basename))
10576     {
10577       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10578
10579       if (*new != NULL)
10580         new = &(*new)->next;
10581     }
10582
10583     num_music_noconf++;
10584   }
10585
10586   closeDirectory(dir);
10587
10588   for (i = 0; i < num_sounds; i++)
10589   {
10590     sound = getSoundListEntry(i);
10591
10592     if (sound->filename == NULL)
10593       continue;
10594
10595     if (strEqual(sound->filename, UNDEFINED_FILENAME))
10596       continue;
10597
10598     /* a configured file may be not recognized as sound */
10599     if (!FileIsSound(sound->filename))
10600       continue;
10601
10602     if (!sound_info_listed(music_file_info, sound->filename))
10603     {
10604       *new = get_sound_file_info(sound->filename, i);
10605       if (*new != NULL)
10606         new = &(*new)->next;
10607     }
10608   }
10609 }
10610
10611 static void add_helpanim_entry(int element, int action, int direction,
10612                                int delay, int *num_list_entries)
10613 {
10614   struct HelpAnimInfo *new_list_entry;
10615   (*num_list_entries)++;
10616
10617   helpanim_info =
10618     checked_realloc(helpanim_info,
10619                     *num_list_entries * sizeof(struct HelpAnimInfo));
10620   new_list_entry = &helpanim_info[*num_list_entries - 1];
10621
10622   new_list_entry->element = element;
10623   new_list_entry->action = action;
10624   new_list_entry->direction = direction;
10625   new_list_entry->delay = delay;
10626 }
10627
10628 static void print_unknown_token(char *filename, char *token, int token_nr)
10629 {
10630   if (token_nr == 0)
10631   {
10632     Error(ERR_INFO_LINE, "-");
10633     Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10634     Error(ERR_INFO, "- config file: '%s'", filename);
10635   }
10636
10637   Error(ERR_INFO, "- token: '%s'", token);
10638 }
10639
10640 static void print_unknown_token_end(int token_nr)
10641 {
10642   if (token_nr > 0)
10643     Error(ERR_INFO_LINE, "-");
10644 }
10645
10646 void LoadHelpAnimInfo(void)
10647 {
10648   char *filename = getHelpAnimFilename();
10649   SetupFileList *setup_file_list = NULL, *list;
10650   SetupFileHash *element_hash, *action_hash, *direction_hash;
10651   int num_list_entries = 0;
10652   int num_unknown_tokens = 0;
10653   int i;
10654
10655   if (fileExists(filename))
10656     setup_file_list = loadSetupFileList(filename);
10657
10658   if (setup_file_list == NULL)
10659   {
10660     /* use reliable default values from static configuration */
10661     SetupFileList *insert_ptr;
10662
10663     insert_ptr = setup_file_list =
10664       newSetupFileList(helpanim_config[0].token,
10665                        helpanim_config[0].value);
10666
10667     for (i = 1; helpanim_config[i].token; i++)
10668       insert_ptr = addListEntry(insert_ptr,
10669                                 helpanim_config[i].token,
10670                                 helpanim_config[i].value);
10671   }
10672
10673   element_hash   = newSetupFileHash();
10674   action_hash    = newSetupFileHash();
10675   direction_hash = newSetupFileHash();
10676
10677   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10678     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10679
10680   for (i = 0; i < NUM_ACTIONS; i++)
10681     setHashEntry(action_hash, element_action_info[i].suffix,
10682                  i_to_a(element_action_info[i].value));
10683
10684   /* do not store direction index (bit) here, but direction value! */
10685   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10686     setHashEntry(direction_hash, element_direction_info[i].suffix,
10687                  i_to_a(1 << element_direction_info[i].value));
10688
10689   for (list = setup_file_list; list != NULL; list = list->next)
10690   {
10691     char *element_token, *action_token, *direction_token;
10692     char *element_value, *action_value, *direction_value;
10693     int delay = atoi(list->value);
10694
10695     if (strEqual(list->token, "end"))
10696     {
10697       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10698
10699       continue;
10700     }
10701
10702     /* first try to break element into element/action/direction parts;
10703        if this does not work, also accept combined "element[.act][.dir]"
10704        elements (like "dynamite.active"), which are unique elements */
10705
10706     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
10707     {
10708       element_value = getHashEntry(element_hash, list->token);
10709       if (element_value != NULL)        /* element found */
10710         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10711                            &num_list_entries);
10712       else
10713       {
10714         /* no further suffixes found -- this is not an element */
10715         print_unknown_token(filename, list->token, num_unknown_tokens++);
10716       }
10717
10718       continue;
10719     }
10720
10721     /* token has format "<prefix>.<something>" */
10722
10723     action_token = strchr(list->token, '.');    /* suffix may be action ... */
10724     direction_token = action_token;             /* ... or direction */
10725
10726     element_token = getStringCopy(list->token);
10727     *strchr(element_token, '.') = '\0';
10728
10729     element_value = getHashEntry(element_hash, element_token);
10730
10731     if (element_value == NULL)          /* this is no element */
10732     {
10733       element_value = getHashEntry(element_hash, list->token);
10734       if (element_value != NULL)        /* combined element found */
10735         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10736                            &num_list_entries);
10737       else
10738         print_unknown_token(filename, list->token, num_unknown_tokens++);
10739
10740       free(element_token);
10741
10742       continue;
10743     }
10744
10745     action_value = getHashEntry(action_hash, action_token);
10746
10747     if (action_value != NULL)           /* action found */
10748     {
10749       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10750                     &num_list_entries);
10751
10752       free(element_token);
10753
10754       continue;
10755     }
10756
10757     direction_value = getHashEntry(direction_hash, direction_token);
10758
10759     if (direction_value != NULL)        /* direction found */
10760     {
10761       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10762                          &num_list_entries);
10763
10764       free(element_token);
10765
10766       continue;
10767     }
10768
10769     if (strchr(action_token + 1, '.') == NULL)
10770     {
10771       /* no further suffixes found -- this is not an action nor direction */
10772
10773       element_value = getHashEntry(element_hash, list->token);
10774       if (element_value != NULL)        /* combined element found */
10775         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10776                            &num_list_entries);
10777       else
10778         print_unknown_token(filename, list->token, num_unknown_tokens++);
10779
10780       free(element_token);
10781
10782       continue;
10783     }
10784
10785     /* token has format "<prefix>.<suffix>.<something>" */
10786
10787     direction_token = strchr(action_token + 1, '.');
10788
10789     action_token = getStringCopy(action_token);
10790     *strchr(action_token + 1, '.') = '\0';
10791
10792     action_value = getHashEntry(action_hash, action_token);
10793
10794     if (action_value == NULL)           /* this is no action */
10795     {
10796       element_value = getHashEntry(element_hash, list->token);
10797       if (element_value != NULL)        /* combined element found */
10798         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10799                            &num_list_entries);
10800       else
10801         print_unknown_token(filename, list->token, num_unknown_tokens++);
10802
10803       free(element_token);
10804       free(action_token);
10805
10806       continue;
10807     }
10808
10809     direction_value = getHashEntry(direction_hash, direction_token);
10810
10811     if (direction_value != NULL)        /* direction found */
10812     {
10813       add_helpanim_entry(atoi(element_value), atoi(action_value),
10814                          atoi(direction_value), delay, &num_list_entries);
10815
10816       free(element_token);
10817       free(action_token);
10818
10819       continue;
10820     }
10821
10822     /* this is no direction */
10823
10824     element_value = getHashEntry(element_hash, list->token);
10825     if (element_value != NULL)          /* combined element found */
10826       add_helpanim_entry(atoi(element_value), -1, -1, delay,
10827                          &num_list_entries);
10828     else
10829       print_unknown_token(filename, list->token, num_unknown_tokens++);
10830
10831     free(element_token);
10832     free(action_token);
10833   }
10834
10835   print_unknown_token_end(num_unknown_tokens);
10836
10837   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10838   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
10839
10840   freeSetupFileList(setup_file_list);
10841   freeSetupFileHash(element_hash);
10842   freeSetupFileHash(action_hash);
10843   freeSetupFileHash(direction_hash);
10844
10845 #if 0
10846   for (i = 0; i < num_list_entries; i++)
10847     printf("::: '%s': %d, %d, %d => %d\n",
10848            EL_NAME(helpanim_info[i].element),
10849            helpanim_info[i].element,
10850            helpanim_info[i].action,
10851            helpanim_info[i].direction,
10852            helpanim_info[i].delay);
10853 #endif
10854 }
10855
10856 void LoadHelpTextInfo(void)
10857 {
10858   char *filename = getHelpTextFilename();
10859   int i;
10860
10861   if (helptext_info != NULL)
10862   {
10863     freeSetupFileHash(helptext_info);
10864     helptext_info = NULL;
10865   }
10866
10867   if (fileExists(filename))
10868     helptext_info = loadSetupFileHash(filename);
10869
10870   if (helptext_info == NULL)
10871   {
10872     /* use reliable default values from static configuration */
10873     helptext_info = newSetupFileHash();
10874
10875     for (i = 0; helptext_config[i].token; i++)
10876       setHashEntry(helptext_info,
10877                    helptext_config[i].token,
10878                    helptext_config[i].value);
10879   }
10880
10881 #if 0
10882   BEGIN_HASH_ITERATION(helptext_info, itr)
10883   {
10884     printf("::: '%s' => '%s'\n",
10885            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10886   }
10887   END_HASH_ITERATION(hash, itr)
10888 #endif
10889 }
10890
10891
10892 /* ------------------------------------------------------------------------- */
10893 /* convert levels                                                            */
10894 /* ------------------------------------------------------------------------- */
10895
10896 #define MAX_NUM_CONVERT_LEVELS          1000
10897
10898 void ConvertLevels(void)
10899 {
10900   static LevelDirTree *convert_leveldir = NULL;
10901   static int convert_level_nr = -1;
10902   static int num_levels_handled = 0;
10903   static int num_levels_converted = 0;
10904   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10905   int i;
10906
10907   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10908                                                global.convert_leveldir);
10909
10910   if (convert_leveldir == NULL)
10911     Error(ERR_EXIT, "no such level identifier: '%s'",
10912           global.convert_leveldir);
10913
10914   leveldir_current = convert_leveldir;
10915
10916   if (global.convert_level_nr != -1)
10917   {
10918     convert_leveldir->first_level = global.convert_level_nr;
10919     convert_leveldir->last_level  = global.convert_level_nr;
10920   }
10921
10922   convert_level_nr = convert_leveldir->first_level;
10923
10924   PrintLine("=", 79);
10925   Print("Converting levels\n");
10926   PrintLine("-", 79);
10927   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10928   Print("Level series name:       '%s'\n", convert_leveldir->name);
10929   Print("Level series author:     '%s'\n", convert_leveldir->author);
10930   Print("Number of levels:        %d\n",   convert_leveldir->levels);
10931   PrintLine("=", 79);
10932   Print("\n");
10933
10934   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10935     levels_failed[i] = FALSE;
10936
10937   while (convert_level_nr <= convert_leveldir->last_level)
10938   {
10939     char *level_filename;
10940     boolean new_level;
10941
10942     level_nr = convert_level_nr++;
10943
10944     Print("Level %03d: ", level_nr);
10945
10946     LoadLevel(level_nr);
10947     if (level.no_level_file || level.no_valid_file)
10948     {
10949       Print("(no level)\n");
10950       continue;
10951     }
10952
10953     Print("converting level ... ");
10954
10955     level_filename = getDefaultLevelFilename(level_nr);
10956     new_level = !fileExists(level_filename);
10957
10958     if (new_level)
10959     {
10960       SaveLevel(level_nr);
10961
10962       num_levels_converted++;
10963
10964       Print("converted.\n");
10965     }
10966     else
10967     {
10968       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10969         levels_failed[level_nr] = TRUE;
10970
10971       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10972     }
10973
10974     num_levels_handled++;
10975   }
10976
10977   Print("\n");
10978   PrintLine("=", 79);
10979   Print("Number of levels handled: %d\n", num_levels_handled);
10980   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10981          (num_levels_handled ?
10982           num_levels_converted * 100 / num_levels_handled : 0));
10983   PrintLine("-", 79);
10984   Print("Summary (for automatic parsing by scripts):\n");
10985   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10986          convert_leveldir->identifier, num_levels_converted,
10987          num_levels_handled,
10988          (num_levels_handled ?
10989           num_levels_converted * 100 / num_levels_handled : 0));
10990
10991   if (num_levels_handled != num_levels_converted)
10992   {
10993     Print(", FAILED:");
10994     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10995       if (levels_failed[i])
10996         Print(" %03d", i);
10997   }
10998
10999   Print("\n");
11000   PrintLine("=", 79);
11001
11002   CloseAllAndExit(0);
11003 }
11004
11005
11006 /* ------------------------------------------------------------------------- */
11007 /* create and save images for use in level sketches (raw BMP format)         */
11008 /* ------------------------------------------------------------------------- */
11009
11010 void CreateLevelSketchImages(void)
11011 {
11012 #if defined(TARGET_SDL)
11013   Bitmap *bitmap1;
11014   Bitmap *bitmap2;
11015   int i;
11016
11017   InitElementPropertiesGfxElement();
11018
11019   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
11020   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
11021
11022   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11023   {
11024     Bitmap *src_bitmap;
11025     int src_x, src_y;
11026     int element = getMappedElement(i);
11027     int graphic = el2edimg(element);
11028     char basename1[16];
11029     char basename2[16];
11030     char *filename1;
11031     char *filename2;
11032
11033     sprintf(basename1, "%03d.bmp", i);
11034     sprintf(basename2, "%03ds.bmp", i);
11035
11036     filename1 = getPath2(global.create_images_dir, basename1);
11037     filename2 = getPath2(global.create_images_dir, basename2);
11038
11039     getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
11040     BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
11041                0, 0);
11042
11043     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
11044       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
11045
11046     getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
11047     BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
11048
11049     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
11050       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
11051
11052     free(filename1);
11053     free(filename2);
11054
11055     if (options.debug)
11056       printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
11057   }
11058
11059   FreeBitmap(bitmap1);
11060   FreeBitmap(bitmap2);
11061
11062   if (options.debug)
11063     printf("\n");
11064
11065   Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
11066
11067   CloseAllAndExit(0);
11068 #endif
11069 }
11070
11071
11072 /* ------------------------------------------------------------------------- */
11073 /* create and save images for custom and group elements (raw BMP format)     */
11074 /* ------------------------------------------------------------------------- */
11075
11076 void CreateCustomElementImages(char *directory)
11077 {
11078 #if defined(TARGET_SDL)
11079   char *src_basename = "RocksCE-template.ilbm";
11080   char *dst_basename = "RocksCE.bmp";
11081   char *src_filename = getPath2(directory, src_basename);
11082   char *dst_filename = getPath2(directory, dst_basename);
11083   Bitmap *src_bitmap;
11084   Bitmap *bitmap;
11085   int yoffset_ce = 0;
11086   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
11087   int i;
11088
11089   SDLInitVideoDisplay();
11090
11091   ReCreateBitmap(&backbuffer, video.width, video.height);
11092
11093   src_bitmap = LoadImage(src_filename);
11094
11095   bitmap = CreateBitmap(TILEX * 16 * 2,
11096                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
11097                         DEFAULT_DEPTH);
11098
11099   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11100   {
11101     int x = i % 16;
11102     int y = i / 16;
11103     int ii = i + 1;
11104     int j;
11105
11106     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11107                TILEX * x, TILEY * y + yoffset_ce);
11108
11109     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11110                TILEX, TILEY,
11111                TILEX * x + TILEX * 16,
11112                TILEY * y + yoffset_ce);
11113
11114     for (j = 2; j >= 0; j--)
11115     {
11116       int c = ii % 10;
11117
11118       BlitBitmap(src_bitmap, bitmap,
11119                  TILEX + c * 7, 0, 6, 10,
11120                  TILEX * x + 6 + j * 7,
11121                  TILEY * y + 11 + yoffset_ce);
11122
11123       BlitBitmap(src_bitmap, bitmap,
11124                  TILEX + c * 8, TILEY, 6, 10,
11125                  TILEX * 16 + TILEX * x + 6 + j * 8,
11126                  TILEY * y + 10 + yoffset_ce);
11127
11128       ii /= 10;
11129     }
11130   }
11131
11132   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
11133   {
11134     int x = i % 16;
11135     int y = i / 16;
11136     int ii = i + 1;
11137     int j;
11138
11139     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11140                TILEX * x, TILEY * y + yoffset_ge);
11141
11142     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11143                TILEX, TILEY,
11144                TILEX * x + TILEX * 16,
11145                TILEY * y + yoffset_ge);
11146
11147     for (j = 1; j >= 0; j--)
11148     {
11149       int c = ii % 10;
11150
11151       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
11152                  TILEX * x + 6 + j * 10,
11153                  TILEY * y + 11 + yoffset_ge);
11154
11155       BlitBitmap(src_bitmap, bitmap,
11156                  TILEX + c * 8, TILEY + 12, 6, 10,
11157                  TILEX * 16 + TILEX * x + 10 + j * 8,
11158                  TILEY * y + 10 + yoffset_ge);
11159
11160       ii /= 10;
11161     }
11162   }
11163
11164   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
11165     Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
11166
11167   FreeBitmap(bitmap);
11168
11169   CloseAllAndExit(0);
11170 #endif
11171 }