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