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