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