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