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