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