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