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