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