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