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