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