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