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