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