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