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