added support for scheduling type in BD engine to level editor
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "tools.h"
23 #include "tape.h"
24 #include "config.h"
25 #include "api.h"
26
27 #define ENABLE_UNUSED_CODE      0       // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
29 #define ENABLE_RESERVED_CODE    0       // reserved for later use
30
31 #define CHUNK_ID_LEN            4       // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
34
35 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
37
38 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
49
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
52
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
57
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
60
61 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
64
65 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
66
67 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
70
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER                 0
78 #define SAVE_CONF_ALWAYS                1
79 #define SAVE_CONF_WHEN_CHANGED          -1
80
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE                0x00
83 #define CONF_MASK_2_BYTE                0x40
84 #define CONF_MASK_4_BYTE                0x80
85 #define CONF_MASK_MULTI_BYTES           0xc0
86
87 #define CONF_MASK_BYTES                 0xc0
88 #define CONF_MASK_TOKEN                 0x3f
89
90 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
91 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
92 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
93 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
94
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
100
101 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
102                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
103                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
104
105 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES          (2)
108
109 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
110                                          (t) == TYPE_ELEMENT_LIST ?     \
111                                          CONF_ELEMENT_NUM_BYTES :       \
112                                          (t) == TYPE_CONTENT ||         \
113                                          (t) == TYPE_CONTENT_LIST ?     \
114                                          CONF_CONTENT_NUM_BYTES : 1)
115
116 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
121                                          (y) * 3 + (x))
122 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
123                                          CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
139
140 struct LevelFileConfigInfo
141 {
142   int element;                  // element for which data is to be stored
143   int save_type;                // save data always, never or when changed
144   int data_type;                // data type (used internally, not stored)
145   int conf_type;                // micro chunk identifier (stored in file)
146
147   // (mandatory)
148   void *value;                  // variable that holds the data to be stored
149   int default_value;            // initial default value for this variable
150
151   // (optional)
152   void *value_copy;             // variable that holds the data to be copied
153   void *num_entities;           // number of entities for multi-byte data
154   int default_num_entities;     // default number of entities for this data
155   int max_num_entities;         // maximal number of entities for this data
156   char *default_string;         // optional default string for string data
157 };
158
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 {
161   // ---------- values not related to single elements -------------------------
162
163   {
164     -1,                                 SAVE_CONF_ALWAYS,
165     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
166     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
167   },
168
169   {
170     -1,                                 SAVE_CONF_ALWAYS,
171     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
172     &li.fieldx,                         STD_LEV_FIELDX
173   },
174   {
175     -1,                                 SAVE_CONF_ALWAYS,
176     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
177     &li.fieldy,                         STD_LEV_FIELDY
178   },
179
180   {
181     -1,                                 SAVE_CONF_ALWAYS,
182     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
183     &li.time,                           100
184   },
185
186   {
187     -1,                                 SAVE_CONF_ALWAYS,
188     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
189     &li.gems_needed,                    0
190   },
191
192   {
193     -1,                                 -1,
194     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
195     &li.random_seed,                    0
196   },
197
198   {
199     -1,                                 -1,
200     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
201     &li.use_step_counter,               FALSE
202   },
203
204   {
205     -1,                                 -1,
206     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
207     &li.wind_direction_initial,         MV_NONE
208   },
209
210   {
211     -1,                                 -1,
212     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
213     &li.em_slippery_gems,               FALSE
214   },
215
216   {
217     -1,                                 -1,
218     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
219     &li.use_custom_template,            FALSE
220   },
221
222   {
223     -1,                                 -1,
224     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
225     &li.can_move_into_acid_bits,        ~0      // default: everything can
226   },
227
228   {
229     -1,                                 -1,
230     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
231     &li.dont_collide_with_bits,         ~0      // default: always deadly
232   },
233
234   {
235     -1,                                 -1,
236     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
237     &li.em_explodes_by_fire,            FALSE
238   },
239
240   {
241     -1,                                 -1,
242     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
243     &li.score[SC_TIME_BONUS],           1
244   },
245
246   {
247     -1,                                 -1,
248     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
249     &li.auto_exit_sokoban,              FALSE
250   },
251
252   {
253     -1,                                 -1,
254     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
255     &li.auto_count_gems,                FALSE
256   },
257
258   {
259     -1,                                 -1,
260     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
261     &li.solved_by_one_player,           FALSE
262   },
263
264   {
265     -1,                                 -1,
266     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
267     &li.time_score_base,                1
268   },
269
270   {
271     -1,                                 -1,
272     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
273     &li.rate_time_over_score,           FALSE
274   },
275
276   {
277     -1,                                 -1,
278     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
279     &li.bd_intermission,                FALSE
280   },
281
282   {
283     -1,                                 -1,
284     TYPE_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   strncpy(level->name, cave->name, MAX_LEVEL_NAME_LEN);
3775   level->name[MAX_LEVEL_NAME_LEN] = '\0';
3776
3777   for (x = 0; x < level->fieldx; x++)
3778     for (y = 0; y < level->fieldy; y++)
3779       level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3780 }
3781
3782 static void setTapeInfoToDefaults(void);
3783
3784 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3785 {
3786   struct LevelInfo_BD *level_bd = level->native_bd_level;
3787   GdCave *cave = level_bd->cave;
3788   GdReplay *replay = level_bd->replay;
3789   int i;
3790
3791   if (replay == NULL)
3792     return;
3793
3794   // always start with reliable default values
3795   setTapeInfoToDefaults();
3796
3797   tape.level_nr = level_nr;             // (currently not used)
3798   tape.random_seed = replay->seed;
3799
3800   TapeSetDateFromIsoDateString(replay->date);
3801
3802   tape.counter = 0;
3803   tape.pos[tape.counter].delay = 0;
3804
3805   tape.bd_replay = TRUE;
3806
3807   // all time calculations only used to display approximate tape time
3808   int cave_speed = cave->speed;
3809   int milliseconds_game = 0;
3810   int milliseconds_elapsed = 20;
3811
3812   for (i = 0; i < replay->movements->len; i++)
3813   {
3814     int replay_action = replay->movements->data[i];
3815     int tape_action = map_action_BD_to_RND(replay_action);
3816     byte action[MAX_TAPE_ACTIONS] = { tape_action };
3817     boolean success = 0;
3818
3819     while (1)
3820     {
3821       success = TapeAddAction(action);
3822
3823       milliseconds_game += milliseconds_elapsed;
3824
3825       if (milliseconds_game >= cave_speed)
3826       {
3827         milliseconds_game -= cave_speed;
3828
3829         break;
3830       }
3831     }
3832
3833     tape.counter++;
3834     tape.pos[tape.counter].delay = 0;
3835     tape.pos[tape.counter].action[0] = 0;
3836
3837     if (!success)
3838     {
3839       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3840
3841       break;
3842     }
3843   }
3844
3845   TapeHaltRecording();
3846 }
3847
3848
3849 // ----------------------------------------------------------------------------
3850 // functions for loading EM level
3851 // ----------------------------------------------------------------------------
3852
3853 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3854 {
3855   static int ball_xy[8][2] =
3856   {
3857     { 0, 0 },
3858     { 1, 0 },
3859     { 2, 0 },
3860     { 0, 1 },
3861     { 2, 1 },
3862     { 0, 2 },
3863     { 1, 2 },
3864     { 2, 2 },
3865   };
3866   struct LevelInfo_EM *level_em = level->native_em_level;
3867   struct CAVE *cav = level_em->cav;
3868   int i, j, x, y;
3869
3870   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3871   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3872
3873   cav->time_seconds     = level->time;
3874   cav->gems_needed      = level->gems_needed;
3875
3876   cav->emerald_score    = level->score[SC_EMERALD];
3877   cav->diamond_score    = level->score[SC_DIAMOND];
3878   cav->alien_score      = level->score[SC_ROBOT];
3879   cav->tank_score       = level->score[SC_SPACESHIP];
3880   cav->bug_score        = level->score[SC_BUG];
3881   cav->eater_score      = level->score[SC_YAMYAM];
3882   cav->nut_score        = level->score[SC_NUT];
3883   cav->dynamite_score   = level->score[SC_DYNAMITE];
3884   cav->key_score        = level->score[SC_KEY];
3885   cav->exit_score       = level->score[SC_TIME_BONUS];
3886
3887   cav->num_eater_arrays = level->num_yamyam_contents;
3888
3889   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3890     for (y = 0; y < 3; y++)
3891       for (x = 0; x < 3; x++)
3892         cav->eater_array[i][y * 3 + x] =
3893           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3894
3895   cav->amoeba_time              = level->amoeba_speed;
3896   cav->wonderwall_time          = level->time_magic_wall;
3897   cav->wheel_time               = level->time_wheel;
3898
3899   cav->android_move_time        = level->android_move_time;
3900   cav->android_clone_time       = level->android_clone_time;
3901   cav->ball_random              = level->ball_random;
3902   cav->ball_active              = level->ball_active_initial;
3903   cav->ball_time                = level->ball_time;
3904   cav->num_ball_arrays          = level->num_ball_contents;
3905
3906   cav->lenses_score             = level->lenses_score;
3907   cav->magnify_score            = level->magnify_score;
3908   cav->slurp_score              = level->slurp_score;
3909
3910   cav->lenses_time              = level->lenses_time;
3911   cav->magnify_time             = level->magnify_time;
3912
3913   cav->wind_time = 9999;
3914   cav->wind_direction =
3915     map_direction_RND_to_EM(level->wind_direction_initial);
3916
3917   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3918     for (j = 0; j < 8; j++)
3919       cav->ball_array[i][j] =
3920         map_element_RND_to_EM_cave(level->ball_content[i].
3921                                    e[ball_xy[j][0]][ball_xy[j][1]]);
3922
3923   map_android_clone_elements_RND_to_EM(level);
3924
3925   // first fill the complete playfield with the empty space element
3926   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3927     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3928       cav->cave[x][y] = Cblank;
3929
3930   // then copy the real level contents from level file into the playfield
3931   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3932   {
3933     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3934
3935     if (level->field[x][y] == EL_AMOEBA_DEAD)
3936       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3937
3938     cav->cave[x][y] = new_element;
3939   }
3940
3941   for (i = 0; i < MAX_PLAYERS; i++)
3942   {
3943     cav->player_x[i] = -1;
3944     cav->player_y[i] = -1;
3945   }
3946
3947   // initialize player positions and delete players from the playfield
3948   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3949   {
3950     if (IS_PLAYER_ELEMENT(level->field[x][y]))
3951     {
3952       int player_nr = GET_PLAYER_NR(level->field[x][y]);
3953
3954       cav->player_x[player_nr] = x;
3955       cav->player_y[player_nr] = y;
3956
3957       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3958     }
3959   }
3960 }
3961
3962 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3963 {
3964   static int ball_xy[8][2] =
3965   {
3966     { 0, 0 },
3967     { 1, 0 },
3968     { 2, 0 },
3969     { 0, 1 },
3970     { 2, 1 },
3971     { 0, 2 },
3972     { 1, 2 },
3973     { 2, 2 },
3974   };
3975   struct LevelInfo_EM *level_em = level->native_em_level;
3976   struct CAVE *cav = level_em->cav;
3977   int i, j, x, y;
3978
3979   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
3980   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3981
3982   level->time        = cav->time_seconds;
3983   level->gems_needed = cav->gems_needed;
3984
3985   sprintf(level->name, "Level %d", level->file_info.nr);
3986
3987   level->score[SC_EMERALD]      = cav->emerald_score;
3988   level->score[SC_DIAMOND]      = cav->diamond_score;
3989   level->score[SC_ROBOT]        = cav->alien_score;
3990   level->score[SC_SPACESHIP]    = cav->tank_score;
3991   level->score[SC_BUG]          = cav->bug_score;
3992   level->score[SC_YAMYAM]       = cav->eater_score;
3993   level->score[SC_NUT]          = cav->nut_score;
3994   level->score[SC_DYNAMITE]     = cav->dynamite_score;
3995   level->score[SC_KEY]          = cav->key_score;
3996   level->score[SC_TIME_BONUS]   = cav->exit_score;
3997
3998   level->num_yamyam_contents    = cav->num_eater_arrays;
3999
4000   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4001     for (y = 0; y < 3; y++)
4002       for (x = 0; x < 3; x++)
4003         level->yamyam_content[i].e[x][y] =
4004           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4005
4006   level->amoeba_speed           = cav->amoeba_time;
4007   level->time_magic_wall        = cav->wonderwall_time;
4008   level->time_wheel             = cav->wheel_time;
4009
4010   level->android_move_time      = cav->android_move_time;
4011   level->android_clone_time     = cav->android_clone_time;
4012   level->ball_random            = cav->ball_random;
4013   level->ball_active_initial    = cav->ball_active;
4014   level->ball_time              = cav->ball_time;
4015   level->num_ball_contents      = cav->num_ball_arrays;
4016
4017   level->lenses_score           = cav->lenses_score;
4018   level->magnify_score          = cav->magnify_score;
4019   level->slurp_score            = cav->slurp_score;
4020
4021   level->lenses_time            = cav->lenses_time;
4022   level->magnify_time           = cav->magnify_time;
4023
4024   level->wind_direction_initial =
4025     map_direction_EM_to_RND(cav->wind_direction);
4026
4027   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4028     for (j = 0; j < 8; j++)
4029       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4030         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4031
4032   map_android_clone_elements_EM_to_RND(level);
4033
4034   // convert the playfield (some elements need special treatment)
4035   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4036   {
4037     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4038
4039     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4040       new_element = EL_AMOEBA_DEAD;
4041
4042     level->field[x][y] = new_element;
4043   }
4044
4045   for (i = 0; i < MAX_PLAYERS; i++)
4046   {
4047     // in case of all players set to the same field, use the first player
4048     int nr = MAX_PLAYERS - i - 1;
4049     int jx = cav->player_x[nr];
4050     int jy = cav->player_y[nr];
4051
4052     if (jx != -1 && jy != -1)
4053       level->field[jx][jy] = EL_PLAYER_1 + nr;
4054   }
4055
4056   // time score is counted for each 10 seconds left in Emerald Mine levels
4057   level->time_score_base = 10;
4058 }
4059
4060
4061 // ----------------------------------------------------------------------------
4062 // functions for loading SP level
4063 // ----------------------------------------------------------------------------
4064
4065 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4066 {
4067   struct LevelInfo_SP *level_sp = level->native_sp_level;
4068   LevelInfoType *header = &level_sp->header;
4069   int i, x, y;
4070
4071   level_sp->width  = level->fieldx;
4072   level_sp->height = level->fieldy;
4073
4074   for (x = 0; x < level->fieldx; x++)
4075     for (y = 0; y < level->fieldy; y++)
4076       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4077
4078   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4079
4080   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4081     header->LevelTitle[i] = level->name[i];
4082   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4083
4084   header->InfotronsNeeded = level->gems_needed;
4085
4086   header->SpecialPortCount = 0;
4087
4088   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4089   {
4090     boolean gravity_port_found = FALSE;
4091     boolean gravity_port_valid = FALSE;
4092     int gravity_port_flag;
4093     int gravity_port_base_element;
4094     int element = level->field[x][y];
4095
4096     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4097         element <= EL_SP_GRAVITY_ON_PORT_UP)
4098     {
4099       gravity_port_found = TRUE;
4100       gravity_port_valid = TRUE;
4101       gravity_port_flag = 1;
4102       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4103     }
4104     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4105              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4106     {
4107       gravity_port_found = TRUE;
4108       gravity_port_valid = TRUE;
4109       gravity_port_flag = 0;
4110       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4111     }
4112     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4113              element <= EL_SP_GRAVITY_PORT_UP)
4114     {
4115       // change R'n'D style gravity inverting special port to normal port
4116       // (there are no gravity inverting ports in native Supaplex engine)
4117
4118       gravity_port_found = TRUE;
4119       gravity_port_valid = FALSE;
4120       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4121     }
4122
4123     if (gravity_port_found)
4124     {
4125       if (gravity_port_valid &&
4126           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4127       {
4128         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4129
4130         port->PortLocation = (y * level->fieldx + x) * 2;
4131         port->Gravity = gravity_port_flag;
4132
4133         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4134
4135         header->SpecialPortCount++;
4136       }
4137       else
4138       {
4139         // change special gravity port to normal port
4140
4141         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4142       }
4143
4144       level_sp->playfield[x][y] = element - EL_SP_START;
4145     }
4146   }
4147 }
4148
4149 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4150 {
4151   struct LevelInfo_SP *level_sp = level->native_sp_level;
4152   LevelInfoType *header = &level_sp->header;
4153   boolean num_invalid_elements = 0;
4154   int i, j, x, y;
4155
4156   level->fieldx = level_sp->width;
4157   level->fieldy = level_sp->height;
4158
4159   for (x = 0; x < level->fieldx; x++)
4160   {
4161     for (y = 0; y < level->fieldy; y++)
4162     {
4163       int element_old = level_sp->playfield[x][y];
4164       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4165
4166       if (element_new == EL_UNKNOWN)
4167       {
4168         num_invalid_elements++;
4169
4170         Debug("level:native:SP", "invalid element %d at position %d, %d",
4171               element_old, x, y);
4172       }
4173
4174       level->field[x][y] = element_new;
4175     }
4176   }
4177
4178   if (num_invalid_elements > 0)
4179     Warn("found %d invalid elements%s", num_invalid_elements,
4180          (!options.debug ? " (use '--debug' for more details)" : ""));
4181
4182   for (i = 0; i < MAX_PLAYERS; i++)
4183     level->initial_player_gravity[i] =
4184       (header->InitialGravity == 1 ? TRUE : FALSE);
4185
4186   // skip leading spaces
4187   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4188     if (header->LevelTitle[i] != ' ')
4189       break;
4190
4191   // copy level title
4192   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4193     level->name[j] = header->LevelTitle[i];
4194   level->name[j] = '\0';
4195
4196   // cut trailing spaces
4197   for (; j > 0; j--)
4198     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4199       level->name[j - 1] = '\0';
4200
4201   level->gems_needed = header->InfotronsNeeded;
4202
4203   for (i = 0; i < header->SpecialPortCount; i++)
4204   {
4205     SpecialPortType *port = &header->SpecialPort[i];
4206     int port_location = port->PortLocation;
4207     int gravity = port->Gravity;
4208     int port_x, port_y, port_element;
4209
4210     port_x = (port_location / 2) % level->fieldx;
4211     port_y = (port_location / 2) / level->fieldx;
4212
4213     if (port_x < 0 || port_x >= level->fieldx ||
4214         port_y < 0 || port_y >= level->fieldy)
4215     {
4216       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4217
4218       continue;
4219     }
4220
4221     port_element = level->field[port_x][port_y];
4222
4223     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4224         port_element > EL_SP_GRAVITY_PORT_UP)
4225     {
4226       Warn("no special port at position (%d, %d)", port_x, port_y);
4227
4228       continue;
4229     }
4230
4231     // change previous (wrong) gravity inverting special port to either
4232     // gravity enabling special port or gravity disabling special port
4233     level->field[port_x][port_y] +=
4234       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4235        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4236   }
4237
4238   // change special gravity ports without database entries to normal ports
4239   for (x = 0; x < level->fieldx; x++)
4240     for (y = 0; y < level->fieldy; y++)
4241       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4242           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4243         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4244
4245   level->time = 0;                      // no time limit
4246   level->amoeba_speed = 0;
4247   level->time_magic_wall = 0;
4248   level->time_wheel = 0;
4249   level->amoeba_content = EL_EMPTY;
4250
4251   // original Supaplex does not use score values -- rate by playing time
4252   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4253     level->score[i] = 0;
4254
4255   level->rate_time_over_score = TRUE;
4256
4257   // there are no yamyams in supaplex levels
4258   for (i = 0; i < level->num_yamyam_contents; i++)
4259     for (x = 0; x < 3; x++)
4260       for (y = 0; y < 3; y++)
4261         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4262 }
4263
4264 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4265 {
4266   struct LevelInfo_SP *level_sp = level->native_sp_level;
4267   struct DemoInfo_SP *demo = &level_sp->demo;
4268   int i, j;
4269
4270   // always start with reliable default values
4271   demo->is_available = FALSE;
4272   demo->length = 0;
4273
4274   if (TAPE_IS_EMPTY(tape))
4275     return;
4276
4277   demo->level_nr = tape.level_nr;       // (currently not used)
4278
4279   level_sp->header.DemoRandomSeed = tape.random_seed;
4280
4281   demo->length = 0;
4282
4283   for (i = 0; i < tape.length; i++)
4284   {
4285     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4286     int demo_repeat = tape.pos[i].delay;
4287     int demo_entries = (demo_repeat + 15) / 16;
4288
4289     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4290     {
4291       Warn("tape truncated: size exceeds maximum SP demo size %d",
4292            SP_MAX_TAPE_LEN);
4293
4294       break;
4295     }
4296
4297     for (j = 0; j < demo_repeat / 16; j++)
4298       demo->data[demo->length++] = 0xf0 | demo_action;
4299
4300     if (demo_repeat % 16)
4301       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4302   }
4303
4304   demo->is_available = TRUE;
4305 }
4306
4307 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4308 {
4309   struct LevelInfo_SP *level_sp = level->native_sp_level;
4310   struct DemoInfo_SP *demo = &level_sp->demo;
4311   char *filename = level->file_info.filename;
4312   int i;
4313
4314   // always start with reliable default values
4315   setTapeInfoToDefaults();
4316
4317   if (!demo->is_available)
4318     return;
4319
4320   tape.level_nr = demo->level_nr;       // (currently not used)
4321   tape.random_seed = level_sp->header.DemoRandomSeed;
4322
4323   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4324
4325   tape.counter = 0;
4326   tape.pos[tape.counter].delay = 0;
4327
4328   for (i = 0; i < demo->length; i++)
4329   {
4330     int demo_action = demo->data[i] & 0x0f;
4331     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4332     int tape_action = map_key_SP_to_RND(demo_action);
4333     int tape_repeat = demo_repeat + 1;
4334     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4335     boolean success = 0;
4336     int j;
4337
4338     for (j = 0; j < tape_repeat; j++)
4339       success = TapeAddAction(action);
4340
4341     if (!success)
4342     {
4343       Warn("SP demo truncated: size exceeds maximum tape size %d",
4344            MAX_TAPE_LEN);
4345
4346       break;
4347     }
4348   }
4349
4350   TapeHaltRecording();
4351 }
4352
4353
4354 // ----------------------------------------------------------------------------
4355 // functions for loading MM level
4356 // ----------------------------------------------------------------------------
4357
4358 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4359 {
4360   struct LevelInfo_MM *level_mm = level->native_mm_level;
4361   int i, x, y;
4362
4363   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4364   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4365
4366   level_mm->time = level->time;
4367   level_mm->kettles_needed = level->gems_needed;
4368   level_mm->auto_count_kettles = level->auto_count_gems;
4369
4370   level_mm->mm_laser_red   = level->mm_laser_red;
4371   level_mm->mm_laser_green = level->mm_laser_green;
4372   level_mm->mm_laser_blue  = level->mm_laser_blue;
4373
4374   level_mm->df_laser_red   = level->df_laser_red;
4375   level_mm->df_laser_green = level->df_laser_green;
4376   level_mm->df_laser_blue  = level->df_laser_blue;
4377
4378   strcpy(level_mm->name, level->name);
4379   strcpy(level_mm->author, level->author);
4380
4381   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
4382   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
4383   level_mm->score[SC_KEY]        = level->score[SC_KEY];
4384   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4385   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4386
4387   level_mm->amoeba_speed = level->amoeba_speed;
4388   level_mm->time_fuse    = level->mm_time_fuse;
4389   level_mm->time_bomb    = level->mm_time_bomb;
4390   level_mm->time_ball    = level->mm_time_ball;
4391   level_mm->time_block   = level->mm_time_block;
4392
4393   level_mm->num_ball_contents = level->num_mm_ball_contents;
4394   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4395   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4396   level_mm->explode_ball = level->explode_mm_ball;
4397
4398   for (i = 0; i < level->num_mm_ball_contents; i++)
4399     level_mm->ball_content[i] =
4400       map_element_RND_to_MM(level->mm_ball_content[i]);
4401
4402   for (x = 0; x < level->fieldx; x++)
4403     for (y = 0; y < level->fieldy; y++)
4404       Ur[x][y] =
4405         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4406 }
4407
4408 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4409 {
4410   struct LevelInfo_MM *level_mm = level->native_mm_level;
4411   int i, x, y;
4412
4413   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4414   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4415
4416   level->time = level_mm->time;
4417   level->gems_needed = level_mm->kettles_needed;
4418   level->auto_count_gems = level_mm->auto_count_kettles;
4419
4420   level->mm_laser_red   = level_mm->mm_laser_red;
4421   level->mm_laser_green = level_mm->mm_laser_green;
4422   level->mm_laser_blue  = level_mm->mm_laser_blue;
4423
4424   level->df_laser_red   = level_mm->df_laser_red;
4425   level->df_laser_green = level_mm->df_laser_green;
4426   level->df_laser_blue  = level_mm->df_laser_blue;
4427
4428   strcpy(level->name, level_mm->name);
4429
4430   // only overwrite author from 'levelinfo.conf' if author defined in level
4431   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4432     strcpy(level->author, level_mm->author);
4433
4434   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
4435   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
4436   level->score[SC_KEY]        = level_mm->score[SC_KEY];
4437   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4438   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4439
4440   level->amoeba_speed  = level_mm->amoeba_speed;
4441   level->mm_time_fuse  = level_mm->time_fuse;
4442   level->mm_time_bomb  = level_mm->time_bomb;
4443   level->mm_time_ball  = level_mm->time_ball;
4444   level->mm_time_block = level_mm->time_block;
4445
4446   level->num_mm_ball_contents = level_mm->num_ball_contents;
4447   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4448   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4449   level->explode_mm_ball = level_mm->explode_ball;
4450
4451   for (i = 0; i < level->num_mm_ball_contents; i++)
4452     level->mm_ball_content[i] =
4453       map_element_MM_to_RND(level_mm->ball_content[i]);
4454
4455   for (x = 0; x < level->fieldx; x++)
4456     for (y = 0; y < level->fieldy; y++)
4457       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4458 }
4459
4460
4461 // ----------------------------------------------------------------------------
4462 // functions for loading DC level
4463 // ----------------------------------------------------------------------------
4464
4465 #define DC_LEVEL_HEADER_SIZE            344
4466
4467 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4468                                         boolean init)
4469 {
4470   static int last_data_encoded;
4471   static int offset1;
4472   static int offset2;
4473   int diff;
4474   int diff_hi, diff_lo;
4475   int data_hi, data_lo;
4476   unsigned short data_decoded;
4477
4478   if (init)
4479   {
4480     last_data_encoded = 0;
4481     offset1 = -1;
4482     offset2 = 0;
4483
4484     return 0;
4485   }
4486
4487   diff = data_encoded - last_data_encoded;
4488   diff_hi = diff & ~0xff;
4489   diff_lo = diff &  0xff;
4490
4491   offset2 += diff_lo;
4492
4493   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4494   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4495   data_hi = data_hi & 0xff00;
4496
4497   data_decoded = data_hi | data_lo;
4498
4499   last_data_encoded = data_encoded;
4500
4501   offset1 = (offset1 + 1) % 31;
4502   offset2 = offset2 & 0xff;
4503
4504   return data_decoded;
4505 }
4506
4507 static int getMappedElement_DC(int element)
4508 {
4509   switch (element)
4510   {
4511     case 0x0000:
4512       element = EL_ROCK;
4513       break;
4514
4515       // 0x0117 - 0x036e: (?)
4516       // EL_DIAMOND
4517
4518       // 0x042d - 0x0684: (?)
4519       // EL_EMERALD
4520
4521     case 0x06f1:
4522       element = EL_NUT;
4523       break;
4524
4525     case 0x074c:
4526       element = EL_BOMB;
4527       break;
4528
4529     case 0x07a4:
4530       element = EL_PEARL;
4531       break;
4532
4533     case 0x0823:
4534       element = EL_CRYSTAL;
4535       break;
4536
4537     case 0x0e77:        // quicksand (boulder)
4538       element = EL_QUICKSAND_FAST_FULL;
4539       break;
4540
4541     case 0x0e99:        // slow quicksand (boulder)
4542       element = EL_QUICKSAND_FULL;
4543       break;
4544
4545     case 0x0ed2:
4546       element = EL_EM_EXIT_OPEN;
4547       break;
4548
4549     case 0x0ee3:
4550       element = EL_EM_EXIT_CLOSED;
4551       break;
4552
4553     case 0x0eeb:
4554       element = EL_EM_STEEL_EXIT_OPEN;
4555       break;
4556
4557     case 0x0efc:
4558       element = EL_EM_STEEL_EXIT_CLOSED;
4559       break;
4560
4561     case 0x0f4f:        // dynamite (lit 1)
4562       element = EL_EM_DYNAMITE_ACTIVE;
4563       break;
4564
4565     case 0x0f57:        // dynamite (lit 2)
4566       element = EL_EM_DYNAMITE_ACTIVE;
4567       break;
4568
4569     case 0x0f5f:        // dynamite (lit 3)
4570       element = EL_EM_DYNAMITE_ACTIVE;
4571       break;
4572
4573     case 0x0f67:        // dynamite (lit 4)
4574       element = EL_EM_DYNAMITE_ACTIVE;
4575       break;
4576
4577     case 0x0f81:
4578     case 0x0f82:
4579     case 0x0f83:
4580     case 0x0f84:
4581       element = EL_AMOEBA_WET;
4582       break;
4583
4584     case 0x0f85:
4585       element = EL_AMOEBA_DROP;
4586       break;
4587
4588     case 0x0fb9:
4589       element = EL_DC_MAGIC_WALL;
4590       break;
4591
4592     case 0x0fd0:
4593       element = EL_SPACESHIP_UP;
4594       break;
4595
4596     case 0x0fd9:
4597       element = EL_SPACESHIP_DOWN;
4598       break;
4599
4600     case 0x0ff1:
4601       element = EL_SPACESHIP_LEFT;
4602       break;
4603
4604     case 0x0ff9:
4605       element = EL_SPACESHIP_RIGHT;
4606       break;
4607
4608     case 0x1057:
4609       element = EL_BUG_UP;
4610       break;
4611
4612     case 0x1060:
4613       element = EL_BUG_DOWN;
4614       break;
4615
4616     case 0x1078:
4617       element = EL_BUG_LEFT;
4618       break;
4619
4620     case 0x1080:
4621       element = EL_BUG_RIGHT;
4622       break;
4623
4624     case 0x10de:
4625       element = EL_MOLE_UP;
4626       break;
4627
4628     case 0x10e7:
4629       element = EL_MOLE_DOWN;
4630       break;
4631
4632     case 0x10ff:
4633       element = EL_MOLE_LEFT;
4634       break;
4635
4636     case 0x1107:
4637       element = EL_MOLE_RIGHT;
4638       break;
4639
4640     case 0x11c0:
4641       element = EL_ROBOT;
4642       break;
4643
4644     case 0x13f5:
4645       element = EL_YAMYAM_UP;
4646       break;
4647
4648     case 0x1425:
4649       element = EL_SWITCHGATE_OPEN;
4650       break;
4651
4652     case 0x1426:
4653       element = EL_SWITCHGATE_CLOSED;
4654       break;
4655
4656     case 0x1437:
4657       element = EL_DC_SWITCHGATE_SWITCH_UP;
4658       break;
4659
4660     case 0x143a:
4661       element = EL_TIMEGATE_CLOSED;
4662       break;
4663
4664     case 0x144c:        // conveyor belt switch (green)
4665       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4666       break;
4667
4668     case 0x144f:        // conveyor belt switch (red)
4669       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4670       break;
4671
4672     case 0x1452:        // conveyor belt switch (blue)
4673       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4674       break;
4675
4676     case 0x145b:
4677       element = EL_CONVEYOR_BELT_3_MIDDLE;
4678       break;
4679
4680     case 0x1463:
4681       element = EL_CONVEYOR_BELT_3_LEFT;
4682       break;
4683
4684     case 0x146b:
4685       element = EL_CONVEYOR_BELT_3_RIGHT;
4686       break;
4687
4688     case 0x1473:
4689       element = EL_CONVEYOR_BELT_1_MIDDLE;
4690       break;
4691
4692     case 0x147b:
4693       element = EL_CONVEYOR_BELT_1_LEFT;
4694       break;
4695
4696     case 0x1483:
4697       element = EL_CONVEYOR_BELT_1_RIGHT;
4698       break;
4699
4700     case 0x148b:
4701       element = EL_CONVEYOR_BELT_4_MIDDLE;
4702       break;
4703
4704     case 0x1493:
4705       element = EL_CONVEYOR_BELT_4_LEFT;
4706       break;
4707
4708     case 0x149b:
4709       element = EL_CONVEYOR_BELT_4_RIGHT;
4710       break;
4711
4712     case 0x14ac:
4713       element = EL_EXPANDABLE_WALL_HORIZONTAL;
4714       break;
4715
4716     case 0x14bd:
4717       element = EL_EXPANDABLE_WALL_VERTICAL;
4718       break;
4719
4720     case 0x14c6:
4721       element = EL_EXPANDABLE_WALL_ANY;
4722       break;
4723
4724     case 0x14ce:        // growing steel wall (left/right)
4725       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4726       break;
4727
4728     case 0x14df:        // growing steel wall (up/down)
4729       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4730       break;
4731
4732     case 0x14e8:        // growing steel wall (up/down/left/right)
4733       element = EL_EXPANDABLE_STEELWALL_ANY;
4734       break;
4735
4736     case 0x14e9:
4737       element = EL_SHIELD_DEADLY;
4738       break;
4739
4740     case 0x1501:
4741       element = EL_EXTRA_TIME;
4742       break;
4743
4744     case 0x154f:
4745       element = EL_ACID;
4746       break;
4747
4748     case 0x1577:
4749       element = EL_EMPTY_SPACE;
4750       break;
4751
4752     case 0x1578:        // quicksand (empty)
4753       element = EL_QUICKSAND_FAST_EMPTY;
4754       break;
4755
4756     case 0x1579:        // slow quicksand (empty)
4757       element = EL_QUICKSAND_EMPTY;
4758       break;
4759
4760       // 0x157c - 0x158b:
4761       // EL_SAND
4762
4763       // 0x1590 - 0x159f:
4764       // EL_DC_LANDMINE
4765
4766     case 0x15a0:
4767       element = EL_EM_DYNAMITE;
4768       break;
4769
4770     case 0x15a1:        // key (red)
4771       element = EL_EM_KEY_1;
4772       break;
4773
4774     case 0x15a2:        // key (yellow)
4775       element = EL_EM_KEY_2;
4776       break;
4777
4778     case 0x15a3:        // key (blue)
4779       element = EL_EM_KEY_4;
4780       break;
4781
4782     case 0x15a4:        // key (green)
4783       element = EL_EM_KEY_3;
4784       break;
4785
4786     case 0x15a5:        // key (white)
4787       element = EL_DC_KEY_WHITE;
4788       break;
4789
4790     case 0x15a6:
4791       element = EL_WALL_SLIPPERY;
4792       break;
4793
4794     case 0x15a7:
4795       element = EL_WALL;
4796       break;
4797
4798     case 0x15a8:        // wall (not round)
4799       element = EL_WALL;
4800       break;
4801
4802     case 0x15a9:        // (blue)
4803       element = EL_CHAR_A;
4804       break;
4805
4806     case 0x15aa:        // (blue)
4807       element = EL_CHAR_B;
4808       break;
4809
4810     case 0x15ab:        // (blue)
4811       element = EL_CHAR_C;
4812       break;
4813
4814     case 0x15ac:        // (blue)
4815       element = EL_CHAR_D;
4816       break;
4817
4818     case 0x15ad:        // (blue)
4819       element = EL_CHAR_E;
4820       break;
4821
4822     case 0x15ae:        // (blue)
4823       element = EL_CHAR_F;
4824       break;
4825
4826     case 0x15af:        // (blue)
4827       element = EL_CHAR_G;
4828       break;
4829
4830     case 0x15b0:        // (blue)
4831       element = EL_CHAR_H;
4832       break;
4833
4834     case 0x15b1:        // (blue)
4835       element = EL_CHAR_I;
4836       break;
4837
4838     case 0x15b2:        // (blue)
4839       element = EL_CHAR_J;
4840       break;
4841
4842     case 0x15b3:        // (blue)
4843       element = EL_CHAR_K;
4844       break;
4845
4846     case 0x15b4:        // (blue)
4847       element = EL_CHAR_L;
4848       break;
4849
4850     case 0x15b5:        // (blue)
4851       element = EL_CHAR_M;
4852       break;
4853
4854     case 0x15b6:        // (blue)
4855       element = EL_CHAR_N;
4856       break;
4857
4858     case 0x15b7:        // (blue)
4859       element = EL_CHAR_O;
4860       break;
4861
4862     case 0x15b8:        // (blue)
4863       element = EL_CHAR_P;
4864       break;
4865
4866     case 0x15b9:        // (blue)
4867       element = EL_CHAR_Q;
4868       break;
4869
4870     case 0x15ba:        // (blue)
4871       element = EL_CHAR_R;
4872       break;
4873
4874     case 0x15bb:        // (blue)
4875       element = EL_CHAR_S;
4876       break;
4877
4878     case 0x15bc:        // (blue)
4879       element = EL_CHAR_T;
4880       break;
4881
4882     case 0x15bd:        // (blue)
4883       element = EL_CHAR_U;
4884       break;
4885
4886     case 0x15be:        // (blue)
4887       element = EL_CHAR_V;
4888       break;
4889
4890     case 0x15bf:        // (blue)
4891       element = EL_CHAR_W;
4892       break;
4893
4894     case 0x15c0:        // (blue)
4895       element = EL_CHAR_X;
4896       break;
4897
4898     case 0x15c1:        // (blue)
4899       element = EL_CHAR_Y;
4900       break;
4901
4902     case 0x15c2:        // (blue)
4903       element = EL_CHAR_Z;
4904       break;
4905
4906     case 0x15c3:        // (blue)
4907       element = EL_CHAR_AUMLAUT;
4908       break;
4909
4910     case 0x15c4:        // (blue)
4911       element = EL_CHAR_OUMLAUT;
4912       break;
4913
4914     case 0x15c5:        // (blue)
4915       element = EL_CHAR_UUMLAUT;
4916       break;
4917
4918     case 0x15c6:        // (blue)
4919       element = EL_CHAR_0;
4920       break;
4921
4922     case 0x15c7:        // (blue)
4923       element = EL_CHAR_1;
4924       break;
4925
4926     case 0x15c8:        // (blue)
4927       element = EL_CHAR_2;
4928       break;
4929
4930     case 0x15c9:        // (blue)
4931       element = EL_CHAR_3;
4932       break;
4933
4934     case 0x15ca:        // (blue)
4935       element = EL_CHAR_4;
4936       break;
4937
4938     case 0x15cb:        // (blue)
4939       element = EL_CHAR_5;
4940       break;
4941
4942     case 0x15cc:        // (blue)
4943       element = EL_CHAR_6;
4944       break;
4945
4946     case 0x15cd:        // (blue)
4947       element = EL_CHAR_7;
4948       break;
4949
4950     case 0x15ce:        // (blue)
4951       element = EL_CHAR_8;
4952       break;
4953
4954     case 0x15cf:        // (blue)
4955       element = EL_CHAR_9;
4956       break;
4957
4958     case 0x15d0:        // (blue)
4959       element = EL_CHAR_PERIOD;
4960       break;
4961
4962     case 0x15d1:        // (blue)
4963       element = EL_CHAR_EXCLAM;
4964       break;
4965
4966     case 0x15d2:        // (blue)
4967       element = EL_CHAR_COLON;
4968       break;
4969
4970     case 0x15d3:        // (blue)
4971       element = EL_CHAR_LESS;
4972       break;
4973
4974     case 0x15d4:        // (blue)
4975       element = EL_CHAR_GREATER;
4976       break;
4977
4978     case 0x15d5:        // (blue)
4979       element = EL_CHAR_QUESTION;
4980       break;
4981
4982     case 0x15d6:        // (blue)
4983       element = EL_CHAR_COPYRIGHT;
4984       break;
4985
4986     case 0x15d7:        // (blue)
4987       element = EL_CHAR_UP;
4988       break;
4989
4990     case 0x15d8:        // (blue)
4991       element = EL_CHAR_DOWN;
4992       break;
4993
4994     case 0x15d9:        // (blue)
4995       element = EL_CHAR_BUTTON;
4996       break;
4997
4998     case 0x15da:        // (blue)
4999       element = EL_CHAR_PLUS;
5000       break;
5001
5002     case 0x15db:        // (blue)
5003       element = EL_CHAR_MINUS;
5004       break;
5005
5006     case 0x15dc:        // (blue)
5007       element = EL_CHAR_APOSTROPHE;
5008       break;
5009
5010     case 0x15dd:        // (blue)
5011       element = EL_CHAR_PARENLEFT;
5012       break;
5013
5014     case 0x15de:        // (blue)
5015       element = EL_CHAR_PARENRIGHT;
5016       break;
5017
5018     case 0x15df:        // (green)
5019       element = EL_CHAR_A;
5020       break;
5021
5022     case 0x15e0:        // (green)
5023       element = EL_CHAR_B;
5024       break;
5025
5026     case 0x15e1:        // (green)
5027       element = EL_CHAR_C;
5028       break;
5029
5030     case 0x15e2:        // (green)
5031       element = EL_CHAR_D;
5032       break;
5033
5034     case 0x15e3:        // (green)
5035       element = EL_CHAR_E;
5036       break;
5037
5038     case 0x15e4:        // (green)
5039       element = EL_CHAR_F;
5040       break;
5041
5042     case 0x15e5:        // (green)
5043       element = EL_CHAR_G;
5044       break;
5045
5046     case 0x15e6:        // (green)
5047       element = EL_CHAR_H;
5048       break;
5049
5050     case 0x15e7:        // (green)
5051       element = EL_CHAR_I;
5052       break;
5053
5054     case 0x15e8:        // (green)
5055       element = EL_CHAR_J;
5056       break;
5057
5058     case 0x15e9:        // (green)
5059       element = EL_CHAR_K;
5060       break;
5061
5062     case 0x15ea:        // (green)
5063       element = EL_CHAR_L;
5064       break;
5065
5066     case 0x15eb:        // (green)
5067       element = EL_CHAR_M;
5068       break;
5069
5070     case 0x15ec:        // (green)
5071       element = EL_CHAR_N;
5072       break;
5073
5074     case 0x15ed:        // (green)
5075       element = EL_CHAR_O;
5076       break;
5077
5078     case 0x15ee:        // (green)
5079       element = EL_CHAR_P;
5080       break;
5081
5082     case 0x15ef:        // (green)
5083       element = EL_CHAR_Q;
5084       break;
5085
5086     case 0x15f0:        // (green)
5087       element = EL_CHAR_R;
5088       break;
5089
5090     case 0x15f1:        // (green)
5091       element = EL_CHAR_S;
5092       break;
5093
5094     case 0x15f2:        // (green)
5095       element = EL_CHAR_T;
5096       break;
5097
5098     case 0x15f3:        // (green)
5099       element = EL_CHAR_U;
5100       break;
5101
5102     case 0x15f4:        // (green)
5103       element = EL_CHAR_V;
5104       break;
5105
5106     case 0x15f5:        // (green)
5107       element = EL_CHAR_W;
5108       break;
5109
5110     case 0x15f6:        // (green)
5111       element = EL_CHAR_X;
5112       break;
5113
5114     case 0x15f7:        // (green)
5115       element = EL_CHAR_Y;
5116       break;
5117
5118     case 0x15f8:        // (green)
5119       element = EL_CHAR_Z;
5120       break;
5121
5122     case 0x15f9:        // (green)
5123       element = EL_CHAR_AUMLAUT;
5124       break;
5125
5126     case 0x15fa:        // (green)
5127       element = EL_CHAR_OUMLAUT;
5128       break;
5129
5130     case 0x15fb:        // (green)
5131       element = EL_CHAR_UUMLAUT;
5132       break;
5133
5134     case 0x15fc:        // (green)
5135       element = EL_CHAR_0;
5136       break;
5137
5138     case 0x15fd:        // (green)
5139       element = EL_CHAR_1;
5140       break;
5141
5142     case 0x15fe:        // (green)
5143       element = EL_CHAR_2;
5144       break;
5145
5146     case 0x15ff:        // (green)
5147       element = EL_CHAR_3;
5148       break;
5149
5150     case 0x1600:        // (green)
5151       element = EL_CHAR_4;
5152       break;
5153
5154     case 0x1601:        // (green)
5155       element = EL_CHAR_5;
5156       break;
5157
5158     case 0x1602:        // (green)
5159       element = EL_CHAR_6;
5160       break;
5161
5162     case 0x1603:        // (green)
5163       element = EL_CHAR_7;
5164       break;
5165
5166     case 0x1604:        // (green)
5167       element = EL_CHAR_8;
5168       break;
5169
5170     case 0x1605:        // (green)
5171       element = EL_CHAR_9;
5172       break;
5173
5174     case 0x1606:        // (green)
5175       element = EL_CHAR_PERIOD;
5176       break;
5177
5178     case 0x1607:        // (green)
5179       element = EL_CHAR_EXCLAM;
5180       break;
5181
5182     case 0x1608:        // (green)
5183       element = EL_CHAR_COLON;
5184       break;
5185
5186     case 0x1609:        // (green)
5187       element = EL_CHAR_LESS;
5188       break;
5189
5190     case 0x160a:        // (green)
5191       element = EL_CHAR_GREATER;
5192       break;
5193
5194     case 0x160b:        // (green)
5195       element = EL_CHAR_QUESTION;
5196       break;
5197
5198     case 0x160c:        // (green)
5199       element = EL_CHAR_COPYRIGHT;
5200       break;
5201
5202     case 0x160d:        // (green)
5203       element = EL_CHAR_UP;
5204       break;
5205
5206     case 0x160e:        // (green)
5207       element = EL_CHAR_DOWN;
5208       break;
5209
5210     case 0x160f:        // (green)
5211       element = EL_CHAR_BUTTON;
5212       break;
5213
5214     case 0x1610:        // (green)
5215       element = EL_CHAR_PLUS;
5216       break;
5217
5218     case 0x1611:        // (green)
5219       element = EL_CHAR_MINUS;
5220       break;
5221
5222     case 0x1612:        // (green)
5223       element = EL_CHAR_APOSTROPHE;
5224       break;
5225
5226     case 0x1613:        // (green)
5227       element = EL_CHAR_PARENLEFT;
5228       break;
5229
5230     case 0x1614:        // (green)
5231       element = EL_CHAR_PARENRIGHT;
5232       break;
5233
5234     case 0x1615:        // (blue steel)
5235       element = EL_STEEL_CHAR_A;
5236       break;
5237
5238     case 0x1616:        // (blue steel)
5239       element = EL_STEEL_CHAR_B;
5240       break;
5241
5242     case 0x1617:        // (blue steel)
5243       element = EL_STEEL_CHAR_C;
5244       break;
5245
5246     case 0x1618:        // (blue steel)
5247       element = EL_STEEL_CHAR_D;
5248       break;
5249
5250     case 0x1619:        // (blue steel)
5251       element = EL_STEEL_CHAR_E;
5252       break;
5253
5254     case 0x161a:        // (blue steel)
5255       element = EL_STEEL_CHAR_F;
5256       break;
5257
5258     case 0x161b:        // (blue steel)
5259       element = EL_STEEL_CHAR_G;
5260       break;
5261
5262     case 0x161c:        // (blue steel)
5263       element = EL_STEEL_CHAR_H;
5264       break;
5265
5266     case 0x161d:        // (blue steel)
5267       element = EL_STEEL_CHAR_I;
5268       break;
5269
5270     case 0x161e:        // (blue steel)
5271       element = EL_STEEL_CHAR_J;
5272       break;
5273
5274     case 0x161f:        // (blue steel)
5275       element = EL_STEEL_CHAR_K;
5276       break;
5277
5278     case 0x1620:        // (blue steel)
5279       element = EL_STEEL_CHAR_L;
5280       break;
5281
5282     case 0x1621:        // (blue steel)
5283       element = EL_STEEL_CHAR_M;
5284       break;
5285
5286     case 0x1622:        // (blue steel)
5287       element = EL_STEEL_CHAR_N;
5288       break;
5289
5290     case 0x1623:        // (blue steel)
5291       element = EL_STEEL_CHAR_O;
5292       break;
5293
5294     case 0x1624:        // (blue steel)
5295       element = EL_STEEL_CHAR_P;
5296       break;
5297
5298     case 0x1625:        // (blue steel)
5299       element = EL_STEEL_CHAR_Q;
5300       break;
5301
5302     case 0x1626:        // (blue steel)
5303       element = EL_STEEL_CHAR_R;
5304       break;
5305
5306     case 0x1627:        // (blue steel)
5307       element = EL_STEEL_CHAR_S;
5308       break;
5309
5310     case 0x1628:        // (blue steel)
5311       element = EL_STEEL_CHAR_T;
5312       break;
5313
5314     case 0x1629:        // (blue steel)
5315       element = EL_STEEL_CHAR_U;
5316       break;
5317
5318     case 0x162a:        // (blue steel)
5319       element = EL_STEEL_CHAR_V;
5320       break;
5321
5322     case 0x162b:        // (blue steel)
5323       element = EL_STEEL_CHAR_W;
5324       break;
5325
5326     case 0x162c:        // (blue steel)
5327       element = EL_STEEL_CHAR_X;
5328       break;
5329
5330     case 0x162d:        // (blue steel)
5331       element = EL_STEEL_CHAR_Y;
5332       break;
5333
5334     case 0x162e:        // (blue steel)
5335       element = EL_STEEL_CHAR_Z;
5336       break;
5337
5338     case 0x162f:        // (blue steel)
5339       element = EL_STEEL_CHAR_AUMLAUT;
5340       break;
5341
5342     case 0x1630:        // (blue steel)
5343       element = EL_STEEL_CHAR_OUMLAUT;
5344       break;
5345
5346     case 0x1631:        // (blue steel)
5347       element = EL_STEEL_CHAR_UUMLAUT;
5348       break;
5349
5350     case 0x1632:        // (blue steel)
5351       element = EL_STEEL_CHAR_0;
5352       break;
5353
5354     case 0x1633:        // (blue steel)
5355       element = EL_STEEL_CHAR_1;
5356       break;
5357
5358     case 0x1634:        // (blue steel)
5359       element = EL_STEEL_CHAR_2;
5360       break;
5361
5362     case 0x1635:        // (blue steel)
5363       element = EL_STEEL_CHAR_3;
5364       break;
5365
5366     case 0x1636:        // (blue steel)
5367       element = EL_STEEL_CHAR_4;
5368       break;
5369
5370     case 0x1637:        // (blue steel)
5371       element = EL_STEEL_CHAR_5;
5372       break;
5373
5374     case 0x1638:        // (blue steel)
5375       element = EL_STEEL_CHAR_6;
5376       break;
5377
5378     case 0x1639:        // (blue steel)
5379       element = EL_STEEL_CHAR_7;
5380       break;
5381
5382     case 0x163a:        // (blue steel)
5383       element = EL_STEEL_CHAR_8;
5384       break;
5385
5386     case 0x163b:        // (blue steel)
5387       element = EL_STEEL_CHAR_9;
5388       break;
5389
5390     case 0x163c:        // (blue steel)
5391       element = EL_STEEL_CHAR_PERIOD;
5392       break;
5393
5394     case 0x163d:        // (blue steel)
5395       element = EL_STEEL_CHAR_EXCLAM;
5396       break;
5397
5398     case 0x163e:        // (blue steel)
5399       element = EL_STEEL_CHAR_COLON;
5400       break;
5401
5402     case 0x163f:        // (blue steel)
5403       element = EL_STEEL_CHAR_LESS;
5404       break;
5405
5406     case 0x1640:        // (blue steel)
5407       element = EL_STEEL_CHAR_GREATER;
5408       break;
5409
5410     case 0x1641:        // (blue steel)
5411       element = EL_STEEL_CHAR_QUESTION;
5412       break;
5413
5414     case 0x1642:        // (blue steel)
5415       element = EL_STEEL_CHAR_COPYRIGHT;
5416       break;
5417
5418     case 0x1643:        // (blue steel)
5419       element = EL_STEEL_CHAR_UP;
5420       break;
5421
5422     case 0x1644:        // (blue steel)
5423       element = EL_STEEL_CHAR_DOWN;
5424       break;
5425
5426     case 0x1645:        // (blue steel)
5427       element = EL_STEEL_CHAR_BUTTON;
5428       break;
5429
5430     case 0x1646:        // (blue steel)
5431       element = EL_STEEL_CHAR_PLUS;
5432       break;
5433
5434     case 0x1647:        // (blue steel)
5435       element = EL_STEEL_CHAR_MINUS;
5436       break;
5437
5438     case 0x1648:        // (blue steel)
5439       element = EL_STEEL_CHAR_APOSTROPHE;
5440       break;
5441
5442     case 0x1649:        // (blue steel)
5443       element = EL_STEEL_CHAR_PARENLEFT;
5444       break;
5445
5446     case 0x164a:        // (blue steel)
5447       element = EL_STEEL_CHAR_PARENRIGHT;
5448       break;
5449
5450     case 0x164b:        // (green steel)
5451       element = EL_STEEL_CHAR_A;
5452       break;
5453
5454     case 0x164c:        // (green steel)
5455       element = EL_STEEL_CHAR_B;
5456       break;
5457
5458     case 0x164d:        // (green steel)
5459       element = EL_STEEL_CHAR_C;
5460       break;
5461
5462     case 0x164e:        // (green steel)
5463       element = EL_STEEL_CHAR_D;
5464       break;
5465
5466     case 0x164f:        // (green steel)
5467       element = EL_STEEL_CHAR_E;
5468       break;
5469
5470     case 0x1650:        // (green steel)
5471       element = EL_STEEL_CHAR_F;
5472       break;
5473
5474     case 0x1651:        // (green steel)
5475       element = EL_STEEL_CHAR_G;
5476       break;
5477
5478     case 0x1652:        // (green steel)
5479       element = EL_STEEL_CHAR_H;
5480       break;
5481
5482     case 0x1653:        // (green steel)
5483       element = EL_STEEL_CHAR_I;
5484       break;
5485
5486     case 0x1654:        // (green steel)
5487       element = EL_STEEL_CHAR_J;
5488       break;
5489
5490     case 0x1655:        // (green steel)
5491       element = EL_STEEL_CHAR_K;
5492       break;
5493
5494     case 0x1656:        // (green steel)
5495       element = EL_STEEL_CHAR_L;
5496       break;
5497
5498     case 0x1657:        // (green steel)
5499       element = EL_STEEL_CHAR_M;
5500       break;
5501
5502     case 0x1658:        // (green steel)
5503       element = EL_STEEL_CHAR_N;
5504       break;
5505
5506     case 0x1659:        // (green steel)
5507       element = EL_STEEL_CHAR_O;
5508       break;
5509
5510     case 0x165a:        // (green steel)
5511       element = EL_STEEL_CHAR_P;
5512       break;
5513
5514     case 0x165b:        // (green steel)
5515       element = EL_STEEL_CHAR_Q;
5516       break;
5517
5518     case 0x165c:        // (green steel)
5519       element = EL_STEEL_CHAR_R;
5520       break;
5521
5522     case 0x165d:        // (green steel)
5523       element = EL_STEEL_CHAR_S;
5524       break;
5525
5526     case 0x165e:        // (green steel)
5527       element = EL_STEEL_CHAR_T;
5528       break;
5529
5530     case 0x165f:        // (green steel)
5531       element = EL_STEEL_CHAR_U;
5532       break;
5533
5534     case 0x1660:        // (green steel)
5535       element = EL_STEEL_CHAR_V;
5536       break;
5537
5538     case 0x1661:        // (green steel)
5539       element = EL_STEEL_CHAR_W;
5540       break;
5541
5542     case 0x1662:        // (green steel)
5543       element = EL_STEEL_CHAR_X;
5544       break;
5545
5546     case 0x1663:        // (green steel)
5547       element = EL_STEEL_CHAR_Y;
5548       break;
5549
5550     case 0x1664:        // (green steel)
5551       element = EL_STEEL_CHAR_Z;
5552       break;
5553
5554     case 0x1665:        // (green steel)
5555       element = EL_STEEL_CHAR_AUMLAUT;
5556       break;
5557
5558     case 0x1666:        // (green steel)
5559       element = EL_STEEL_CHAR_OUMLAUT;
5560       break;
5561
5562     case 0x1667:        // (green steel)
5563       element = EL_STEEL_CHAR_UUMLAUT;
5564       break;
5565
5566     case 0x1668:        // (green steel)
5567       element = EL_STEEL_CHAR_0;
5568       break;
5569
5570     case 0x1669:        // (green steel)
5571       element = EL_STEEL_CHAR_1;
5572       break;
5573
5574     case 0x166a:        // (green steel)
5575       element = EL_STEEL_CHAR_2;
5576       break;
5577
5578     case 0x166b:        // (green steel)
5579       element = EL_STEEL_CHAR_3;
5580       break;
5581
5582     case 0x166c:        // (green steel)
5583       element = EL_STEEL_CHAR_4;
5584       break;
5585
5586     case 0x166d:        // (green steel)
5587       element = EL_STEEL_CHAR_5;
5588       break;
5589
5590     case 0x166e:        // (green steel)
5591       element = EL_STEEL_CHAR_6;
5592       break;
5593
5594     case 0x166f:        // (green steel)
5595       element = EL_STEEL_CHAR_7;
5596       break;
5597
5598     case 0x1670:        // (green steel)
5599       element = EL_STEEL_CHAR_8;
5600       break;
5601
5602     case 0x1671:        // (green steel)
5603       element = EL_STEEL_CHAR_9;
5604       break;
5605
5606     case 0x1672:        // (green steel)
5607       element = EL_STEEL_CHAR_PERIOD;
5608       break;
5609
5610     case 0x1673:        // (green steel)
5611       element = EL_STEEL_CHAR_EXCLAM;
5612       break;
5613
5614     case 0x1674:        // (green steel)
5615       element = EL_STEEL_CHAR_COLON;
5616       break;
5617
5618     case 0x1675:        // (green steel)
5619       element = EL_STEEL_CHAR_LESS;
5620       break;
5621
5622     case 0x1676:        // (green steel)
5623       element = EL_STEEL_CHAR_GREATER;
5624       break;
5625
5626     case 0x1677:        // (green steel)
5627       element = EL_STEEL_CHAR_QUESTION;
5628       break;
5629
5630     case 0x1678:        // (green steel)
5631       element = EL_STEEL_CHAR_COPYRIGHT;
5632       break;
5633
5634     case 0x1679:        // (green steel)
5635       element = EL_STEEL_CHAR_UP;
5636       break;
5637
5638     case 0x167a:        // (green steel)
5639       element = EL_STEEL_CHAR_DOWN;
5640       break;
5641
5642     case 0x167b:        // (green steel)
5643       element = EL_STEEL_CHAR_BUTTON;
5644       break;
5645
5646     case 0x167c:        // (green steel)
5647       element = EL_STEEL_CHAR_PLUS;
5648       break;
5649
5650     case 0x167d:        // (green steel)
5651       element = EL_STEEL_CHAR_MINUS;
5652       break;
5653
5654     case 0x167e:        // (green steel)
5655       element = EL_STEEL_CHAR_APOSTROPHE;
5656       break;
5657
5658     case 0x167f:        // (green steel)
5659       element = EL_STEEL_CHAR_PARENLEFT;
5660       break;
5661
5662     case 0x1680:        // (green steel)
5663       element = EL_STEEL_CHAR_PARENRIGHT;
5664       break;
5665
5666     case 0x1681:        // gate (red)
5667       element = EL_EM_GATE_1;
5668       break;
5669
5670     case 0x1682:        // secret gate (red)
5671       element = EL_EM_GATE_1_GRAY;
5672       break;
5673
5674     case 0x1683:        // gate (yellow)
5675       element = EL_EM_GATE_2;
5676       break;
5677
5678     case 0x1684:        // secret gate (yellow)
5679       element = EL_EM_GATE_2_GRAY;
5680       break;
5681
5682     case 0x1685:        // gate (blue)
5683       element = EL_EM_GATE_4;
5684       break;
5685
5686     case 0x1686:        // secret gate (blue)
5687       element = EL_EM_GATE_4_GRAY;
5688       break;
5689
5690     case 0x1687:        // gate (green)
5691       element = EL_EM_GATE_3;
5692       break;
5693
5694     case 0x1688:        // secret gate (green)
5695       element = EL_EM_GATE_3_GRAY;
5696       break;
5697
5698     case 0x1689:        // gate (white)
5699       element = EL_DC_GATE_WHITE;
5700       break;
5701
5702     case 0x168a:        // secret gate (white)
5703       element = EL_DC_GATE_WHITE_GRAY;
5704       break;
5705
5706     case 0x168b:        // secret gate (no key)
5707       element = EL_DC_GATE_FAKE_GRAY;
5708       break;
5709
5710     case 0x168c:
5711       element = EL_ROBOT_WHEEL;
5712       break;
5713
5714     case 0x168d:
5715       element = EL_DC_TIMEGATE_SWITCH;
5716       break;
5717
5718     case 0x168e:
5719       element = EL_ACID_POOL_BOTTOM;
5720       break;
5721
5722     case 0x168f:
5723       element = EL_ACID_POOL_TOPLEFT;
5724       break;
5725
5726     case 0x1690:
5727       element = EL_ACID_POOL_TOPRIGHT;
5728       break;
5729
5730     case 0x1691:
5731       element = EL_ACID_POOL_BOTTOMLEFT;
5732       break;
5733
5734     case 0x1692:
5735       element = EL_ACID_POOL_BOTTOMRIGHT;
5736       break;
5737
5738     case 0x1693:
5739       element = EL_STEELWALL;
5740       break;
5741
5742     case 0x1694:
5743       element = EL_STEELWALL_SLIPPERY;
5744       break;
5745
5746     case 0x1695:        // steel wall (not round)
5747       element = EL_STEELWALL;
5748       break;
5749
5750     case 0x1696:        // steel wall (left)
5751       element = EL_DC_STEELWALL_1_LEFT;
5752       break;
5753
5754     case 0x1697:        // steel wall (bottom)
5755       element = EL_DC_STEELWALL_1_BOTTOM;
5756       break;
5757
5758     case 0x1698:        // steel wall (right)
5759       element = EL_DC_STEELWALL_1_RIGHT;
5760       break;
5761
5762     case 0x1699:        // steel wall (top)
5763       element = EL_DC_STEELWALL_1_TOP;
5764       break;
5765
5766     case 0x169a:        // steel wall (left/bottom)
5767       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5768       break;
5769
5770     case 0x169b:        // steel wall (right/bottom)
5771       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5772       break;
5773
5774     case 0x169c:        // steel wall (right/top)
5775       element = EL_DC_STEELWALL_1_TOPRIGHT;
5776       break;
5777
5778     case 0x169d:        // steel wall (left/top)
5779       element = EL_DC_STEELWALL_1_TOPLEFT;
5780       break;
5781
5782     case 0x169e:        // steel wall (right/bottom small)
5783       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5784       break;
5785
5786     case 0x169f:        // steel wall (left/bottom small)
5787       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5788       break;
5789
5790     case 0x16a0:        // steel wall (right/top small)
5791       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5792       break;
5793
5794     case 0x16a1:        // steel wall (left/top small)
5795       element = EL_DC_STEELWALL_1_TOPLEFT_2;
5796       break;
5797
5798     case 0x16a2:        // steel wall (left/right)
5799       element = EL_DC_STEELWALL_1_VERTICAL;
5800       break;
5801
5802     case 0x16a3:        // steel wall (top/bottom)
5803       element = EL_DC_STEELWALL_1_HORIZONTAL;
5804       break;
5805
5806     case 0x16a4:        // steel wall 2 (left end)
5807       element = EL_DC_STEELWALL_2_LEFT;
5808       break;
5809
5810     case 0x16a5:        // steel wall 2 (right end)
5811       element = EL_DC_STEELWALL_2_RIGHT;
5812       break;
5813
5814     case 0x16a6:        // steel wall 2 (top end)
5815       element = EL_DC_STEELWALL_2_TOP;
5816       break;
5817
5818     case 0x16a7:        // steel wall 2 (bottom end)
5819       element = EL_DC_STEELWALL_2_BOTTOM;
5820       break;
5821
5822     case 0x16a8:        // steel wall 2 (left/right)
5823       element = EL_DC_STEELWALL_2_HORIZONTAL;
5824       break;
5825
5826     case 0x16a9:        // steel wall 2 (up/down)
5827       element = EL_DC_STEELWALL_2_VERTICAL;
5828       break;
5829
5830     case 0x16aa:        // steel wall 2 (mid)
5831       element = EL_DC_STEELWALL_2_MIDDLE;
5832       break;
5833
5834     case 0x16ab:
5835       element = EL_SIGN_EXCLAMATION;
5836       break;
5837
5838     case 0x16ac:
5839       element = EL_SIGN_RADIOACTIVITY;
5840       break;
5841
5842     case 0x16ad:
5843       element = EL_SIGN_STOP;
5844       break;
5845
5846     case 0x16ae:
5847       element = EL_SIGN_WHEELCHAIR;
5848       break;
5849
5850     case 0x16af:
5851       element = EL_SIGN_PARKING;
5852       break;
5853
5854     case 0x16b0:
5855       element = EL_SIGN_NO_ENTRY;
5856       break;
5857
5858     case 0x16b1:
5859       element = EL_SIGN_HEART;
5860       break;
5861
5862     case 0x16b2:
5863       element = EL_SIGN_GIVE_WAY;
5864       break;
5865
5866     case 0x16b3:
5867       element = EL_SIGN_ENTRY_FORBIDDEN;
5868       break;
5869
5870     case 0x16b4:
5871       element = EL_SIGN_EMERGENCY_EXIT;
5872       break;
5873
5874     case 0x16b5:
5875       element = EL_SIGN_YIN_YANG;
5876       break;
5877
5878     case 0x16b6:
5879       element = EL_WALL_EMERALD;
5880       break;
5881
5882     case 0x16b7:
5883       element = EL_WALL_DIAMOND;
5884       break;
5885
5886     case 0x16b8:
5887       element = EL_WALL_PEARL;
5888       break;
5889
5890     case 0x16b9:
5891       element = EL_WALL_CRYSTAL;
5892       break;
5893
5894     case 0x16ba:
5895       element = EL_INVISIBLE_WALL;
5896       break;
5897
5898     case 0x16bb:
5899       element = EL_INVISIBLE_STEELWALL;
5900       break;
5901
5902       // 0x16bc - 0x16cb:
5903       // EL_INVISIBLE_SAND
5904
5905     case 0x16cc:
5906       element = EL_LIGHT_SWITCH;
5907       break;
5908
5909     case 0x16cd:
5910       element = EL_ENVELOPE_1;
5911       break;
5912
5913     default:
5914       if (element >= 0x0117 && element <= 0x036e)       // (?)
5915         element = EL_DIAMOND;
5916       else if (element >= 0x042d && element <= 0x0684)  // (?)
5917         element = EL_EMERALD;
5918       else if (element >= 0x157c && element <= 0x158b)
5919         element = EL_SAND;
5920       else if (element >= 0x1590 && element <= 0x159f)
5921         element = EL_DC_LANDMINE;
5922       else if (element >= 0x16bc && element <= 0x16cb)
5923         element = EL_INVISIBLE_SAND;
5924       else
5925       {
5926         Warn("unknown Diamond Caves element 0x%04x", element);
5927
5928         element = EL_UNKNOWN;
5929       }
5930       break;
5931   }
5932
5933   return getMappedElement(element);
5934 }
5935
5936 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5937 {
5938   byte header[DC_LEVEL_HEADER_SIZE];
5939   int envelope_size;
5940   int envelope_header_pos = 62;
5941   int envelope_content_pos = 94;
5942   int level_name_pos = 251;
5943   int level_author_pos = 292;
5944   int envelope_header_len;
5945   int envelope_content_len;
5946   int level_name_len;
5947   int level_author_len;
5948   int fieldx, fieldy;
5949   int num_yamyam_contents;
5950   int i, x, y;
5951
5952   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
5953
5954   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5955   {
5956     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5957
5958     header[i * 2 + 0] = header_word >> 8;
5959     header[i * 2 + 1] = header_word & 0xff;
5960   }
5961
5962   // read some values from level header to check level decoding integrity
5963   fieldx = header[6] | (header[7] << 8);
5964   fieldy = header[8] | (header[9] << 8);
5965   num_yamyam_contents = header[60] | (header[61] << 8);
5966
5967   // do some simple sanity checks to ensure that level was correctly decoded
5968   if (fieldx < 1 || fieldx > 256 ||
5969       fieldy < 1 || fieldy > 256 ||
5970       num_yamyam_contents < 1 || num_yamyam_contents > 8)
5971   {
5972     level->no_valid_file = TRUE;
5973
5974     Warn("cannot decode level from stream -- using empty level");
5975
5976     return;
5977   }
5978
5979   // maximum envelope header size is 31 bytes
5980   envelope_header_len   = header[envelope_header_pos];
5981   // maximum envelope content size is 110 (156?) bytes
5982   envelope_content_len  = header[envelope_content_pos];
5983
5984   // maximum level title size is 40 bytes
5985   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
5986   // maximum level author size is 30 (51?) bytes
5987   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5988
5989   envelope_size = 0;
5990
5991   for (i = 0; i < envelope_header_len; i++)
5992     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5993       level->envelope[0].text[envelope_size++] =
5994         header[envelope_header_pos + 1 + i];
5995
5996   if (envelope_header_len > 0 && envelope_content_len > 0)
5997   {
5998     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5999       level->envelope[0].text[envelope_size++] = '\n';
6000     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6001       level->envelope[0].text[envelope_size++] = '\n';
6002   }
6003
6004   for (i = 0; i < envelope_content_len; i++)
6005     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6006       level->envelope[0].text[envelope_size++] =
6007         header[envelope_content_pos + 1 + i];
6008
6009   level->envelope[0].text[envelope_size] = '\0';
6010
6011   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6012   level->envelope[0].ysize = 10;
6013   level->envelope[0].autowrap = TRUE;
6014   level->envelope[0].centered = TRUE;
6015
6016   for (i = 0; i < level_name_len; i++)
6017     level->name[i] = header[level_name_pos + 1 + i];
6018   level->name[level_name_len] = '\0';
6019
6020   for (i = 0; i < level_author_len; i++)
6021     level->author[i] = header[level_author_pos + 1 + i];
6022   level->author[level_author_len] = '\0';
6023
6024   num_yamyam_contents = header[60] | (header[61] << 8);
6025   level->num_yamyam_contents =
6026     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6027
6028   for (i = 0; i < num_yamyam_contents; i++)
6029   {
6030     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6031     {
6032       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6033       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6034
6035       if (i < MAX_ELEMENT_CONTENTS)
6036         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6037     }
6038   }
6039
6040   fieldx = header[6] | (header[7] << 8);
6041   fieldy = header[8] | (header[9] << 8);
6042   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6043   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6044
6045   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6046   {
6047     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6048     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6049
6050     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6051       level->field[x][y] = getMappedElement_DC(element_dc);
6052   }
6053
6054   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6055   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6056   level->field[x][y] = EL_PLAYER_1;
6057
6058   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6059   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6060   level->field[x][y] = EL_PLAYER_2;
6061
6062   level->gems_needed            = header[18] | (header[19] << 8);
6063
6064   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6065   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6066   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6067   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6068   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6069   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6070   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6071   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6072   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6073   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6074   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6075   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6076
6077   level->time                   = header[44] | (header[45] << 8);
6078
6079   level->amoeba_speed           = header[46] | (header[47] << 8);
6080   level->time_light             = header[48] | (header[49] << 8);
6081   level->time_timegate          = header[50] | (header[51] << 8);
6082   level->time_wheel             = header[52] | (header[53] << 8);
6083   level->time_magic_wall        = header[54] | (header[55] << 8);
6084   level->extra_time             = header[56] | (header[57] << 8);
6085   level->shield_normal_time     = header[58] | (header[59] << 8);
6086
6087   // shield and extra time elements do not have a score
6088   level->score[SC_SHIELD]       = 0;
6089   level->extra_time_score       = 0;
6090
6091   // set time for normal and deadly shields to the same value
6092   level->shield_deadly_time     = level->shield_normal_time;
6093
6094   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6095   // can slip down from flat walls, like normal walls and steel walls
6096   level->em_slippery_gems = TRUE;
6097
6098   // time score is counted for each 10 seconds left in Diamond Caves levels
6099   level->time_score_base = 10;
6100 }
6101
6102 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6103                                      struct LevelFileInfo *level_file_info,
6104                                      boolean level_info_only)
6105 {
6106   char *filename = level_file_info->filename;
6107   File *file;
6108   int num_magic_bytes = 8;
6109   char magic_bytes[num_magic_bytes + 1];
6110   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6111
6112   if (!(file = openFile(filename, MODE_READ)))
6113   {
6114     level->no_valid_file = TRUE;
6115
6116     if (!level_info_only)
6117       Warn("cannot read level '%s' -- using empty level", filename);
6118
6119     return;
6120   }
6121
6122   // fseek(file, 0x0000, SEEK_SET);
6123
6124   if (level_file_info->packed)
6125   {
6126     // read "magic bytes" from start of file
6127     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6128       magic_bytes[0] = '\0';
6129
6130     // check "magic bytes" for correct file format
6131     if (!strPrefix(magic_bytes, "DC2"))
6132     {
6133       level->no_valid_file = TRUE;
6134
6135       Warn("unknown DC level file '%s' -- using empty level", filename);
6136
6137       return;
6138     }
6139
6140     if (strPrefix(magic_bytes, "DC2Win95") ||
6141         strPrefix(magic_bytes, "DC2Win98"))
6142     {
6143       int position_first_level = 0x00fa;
6144       int extra_bytes = 4;
6145       int skip_bytes;
6146
6147       // advance file stream to first level inside the level package
6148       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6149
6150       // each block of level data is followed by block of non-level data
6151       num_levels_to_skip *= 2;
6152
6153       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6154       while (num_levels_to_skip >= 0)
6155       {
6156         // advance file stream to next level inside the level package
6157         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6158         {
6159           level->no_valid_file = TRUE;
6160
6161           Warn("cannot fseek in file '%s' -- using empty level", filename);
6162
6163           return;
6164         }
6165
6166         // skip apparently unused extra bytes following each level
6167         ReadUnusedBytesFromFile(file, extra_bytes);
6168
6169         // read size of next level in level package
6170         skip_bytes = getFile32BitLE(file);
6171
6172         num_levels_to_skip--;
6173       }
6174     }
6175     else
6176     {
6177       level->no_valid_file = TRUE;
6178
6179       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6180
6181       return;
6182     }
6183   }
6184
6185   LoadLevelFromFileStream_DC(file, level);
6186
6187   closeFile(file);
6188 }
6189
6190
6191 // ----------------------------------------------------------------------------
6192 // functions for loading SB level
6193 // ----------------------------------------------------------------------------
6194
6195 int getMappedElement_SB(int element_ascii, boolean use_ces)
6196 {
6197   static struct
6198   {
6199     int ascii;
6200     int sb;
6201     int ce;
6202   }
6203   sb_element_mapping[] =
6204   {
6205     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6206     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6207     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6208     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6209     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6210     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6211     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6212     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6213
6214     { 0,   -1,                      -1          },
6215   };
6216
6217   int i;
6218
6219   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6220     if (element_ascii == sb_element_mapping[i].ascii)
6221       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6222
6223   return EL_UNDEFINED;
6224 }
6225
6226 static void SetLevelSettings_SB(struct LevelInfo *level)
6227 {
6228   // time settings
6229   level->time = 0;
6230   level->use_step_counter = TRUE;
6231
6232   // score settings
6233   level->score[SC_TIME_BONUS] = 0;
6234   level->time_score_base = 1;
6235   level->rate_time_over_score = TRUE;
6236
6237   // game settings
6238   level->auto_exit_sokoban = TRUE;
6239 }
6240
6241 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6242                                      struct LevelFileInfo *level_file_info,
6243                                      boolean level_info_only)
6244 {
6245   char *filename = level_file_info->filename;
6246   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6247   char last_comment[MAX_LINE_LEN];
6248   char level_name[MAX_LINE_LEN];
6249   char *line_ptr;
6250   File *file;
6251   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6252   boolean read_continued_line = FALSE;
6253   boolean reading_playfield = FALSE;
6254   boolean got_valid_playfield_line = FALSE;
6255   boolean invalid_playfield_char = FALSE;
6256   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6257   int file_level_nr = 0;
6258   int x = 0, y = 0;             // initialized to make compilers happy
6259
6260   last_comment[0] = '\0';
6261   level_name[0] = '\0';
6262
6263   if (!(file = openFile(filename, MODE_READ)))
6264   {
6265     level->no_valid_file = TRUE;
6266
6267     if (!level_info_only)
6268       Warn("cannot read level '%s' -- using empty level", filename);
6269
6270     return;
6271   }
6272
6273   while (!checkEndOfFile(file))
6274   {
6275     // level successfully read, but next level may follow here
6276     if (!got_valid_playfield_line && reading_playfield)
6277     {
6278       // read playfield from single level file -- skip remaining file
6279       if (!level_file_info->packed)
6280         break;
6281
6282       if (file_level_nr >= num_levels_to_skip)
6283         break;
6284
6285       file_level_nr++;
6286
6287       last_comment[0] = '\0';
6288       level_name[0] = '\0';
6289
6290       reading_playfield = FALSE;
6291     }
6292
6293     got_valid_playfield_line = FALSE;
6294
6295     // read next line of input file
6296     if (!getStringFromFile(file, line, MAX_LINE_LEN))
6297       break;
6298
6299     // cut trailing line break (this can be newline and/or carriage return)
6300     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6301       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6302         *line_ptr = '\0';
6303
6304     // copy raw input line for later use (mainly debugging output)
6305     strcpy(line_raw, line);
6306
6307     if (read_continued_line)
6308     {
6309       // append new line to existing line, if there is enough space
6310       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6311         strcat(previous_line, line_ptr);
6312
6313       strcpy(line, previous_line);      // copy storage buffer to line
6314
6315       read_continued_line = FALSE;
6316     }
6317
6318     // if the last character is '\', continue at next line
6319     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6320     {
6321       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
6322       strcpy(previous_line, line);      // copy line to storage buffer
6323
6324       read_continued_line = TRUE;
6325
6326       continue;
6327     }
6328
6329     // skip empty lines
6330     if (line[0] == '\0')
6331       continue;
6332
6333     // extract comment text from comment line
6334     if (line[0] == ';')
6335     {
6336       for (line_ptr = line; *line_ptr; line_ptr++)
6337         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6338           break;
6339
6340       strcpy(last_comment, line_ptr);
6341
6342       continue;
6343     }
6344
6345     // extract level title text from line containing level title
6346     if (line[0] == '\'')
6347     {
6348       strcpy(level_name, &line[1]);
6349
6350       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6351         level_name[strlen(level_name) - 1] = '\0';
6352
6353       continue;
6354     }
6355
6356     // skip lines containing only spaces (or empty lines)
6357     for (line_ptr = line; *line_ptr; line_ptr++)
6358       if (*line_ptr != ' ')
6359         break;
6360     if (*line_ptr == '\0')
6361       continue;
6362
6363     // at this point, we have found a line containing part of a playfield
6364
6365     got_valid_playfield_line = TRUE;
6366
6367     if (!reading_playfield)
6368     {
6369       reading_playfield = TRUE;
6370       invalid_playfield_char = FALSE;
6371
6372       for (x = 0; x < MAX_LEV_FIELDX; x++)
6373         for (y = 0; y < MAX_LEV_FIELDY; y++)
6374           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6375
6376       level->fieldx = 0;
6377       level->fieldy = 0;
6378
6379       // start with topmost tile row
6380       y = 0;
6381     }
6382
6383     // skip playfield line if larger row than allowed
6384     if (y >= MAX_LEV_FIELDY)
6385       continue;
6386
6387     // start with leftmost tile column
6388     x = 0;
6389
6390     // read playfield elements from line
6391     for (line_ptr = line; *line_ptr; line_ptr++)
6392     {
6393       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6394
6395       // stop parsing playfield line if larger column than allowed
6396       if (x >= MAX_LEV_FIELDX)
6397         break;
6398
6399       if (mapped_sb_element == EL_UNDEFINED)
6400       {
6401         invalid_playfield_char = TRUE;
6402
6403         break;
6404       }
6405
6406       level->field[x][y] = mapped_sb_element;
6407
6408       // continue with next tile column
6409       x++;
6410
6411       level->fieldx = MAX(x, level->fieldx);
6412     }
6413
6414     if (invalid_playfield_char)
6415     {
6416       // if first playfield line, treat invalid lines as comment lines
6417       if (y == 0)
6418         reading_playfield = FALSE;
6419
6420       continue;
6421     }
6422
6423     // continue with next tile row
6424     y++;
6425   }
6426
6427   closeFile(file);
6428
6429   level->fieldy = y;
6430
6431   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6432   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6433
6434   if (!reading_playfield)
6435   {
6436     level->no_valid_file = TRUE;
6437
6438     Warn("cannot read level '%s' -- using empty level", filename);
6439
6440     return;
6441   }
6442
6443   if (*level_name != '\0')
6444   {
6445     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6446     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6447   }
6448   else if (*last_comment != '\0')
6449   {
6450     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6451     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6452   }
6453   else
6454   {
6455     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6456   }
6457
6458   // set all empty fields beyond the border walls to invisible steel wall
6459   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6460   {
6461     if ((x == 0 || x == level->fieldx - 1 ||
6462          y == 0 || y == level->fieldy - 1) &&
6463         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6464       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6465                      level->field, level->fieldx, level->fieldy);
6466   }
6467
6468   // set special level settings for Sokoban levels
6469   SetLevelSettings_SB(level);
6470
6471   if (load_xsb_to_ces)
6472   {
6473     // special global settings can now be set in level template
6474     level->use_custom_template = TRUE;
6475   }
6476 }
6477
6478
6479 // -------------------------------------------------------------------------
6480 // functions for handling native levels
6481 // -------------------------------------------------------------------------
6482
6483 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6484                                      struct LevelFileInfo *level_file_info,
6485                                      boolean level_info_only)
6486 {
6487   int pos = 0;
6488
6489   // determine position of requested level inside level package
6490   if (level_file_info->packed)
6491     pos = level_file_info->nr - leveldir_current->first_level;
6492
6493   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6494     level->no_valid_file = TRUE;
6495 }
6496
6497 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6498                                      struct LevelFileInfo *level_file_info,
6499                                      boolean level_info_only)
6500 {
6501   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6502     level->no_valid_file = TRUE;
6503 }
6504
6505 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6506                                      struct LevelFileInfo *level_file_info,
6507                                      boolean level_info_only)
6508 {
6509   int pos = 0;
6510
6511   // determine position of requested level inside level package
6512   if (level_file_info->packed)
6513     pos = level_file_info->nr - leveldir_current->first_level;
6514
6515   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6516     level->no_valid_file = TRUE;
6517 }
6518
6519 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6520                                      struct LevelFileInfo *level_file_info,
6521                                      boolean level_info_only)
6522 {
6523   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6524     level->no_valid_file = TRUE;
6525 }
6526
6527 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6528 {
6529   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6530     CopyNativeLevel_RND_to_BD(level);
6531   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6532     CopyNativeLevel_RND_to_EM(level);
6533   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6534     CopyNativeLevel_RND_to_SP(level);
6535   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6536     CopyNativeLevel_RND_to_MM(level);
6537 }
6538
6539 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6540 {
6541   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6542     CopyNativeLevel_BD_to_RND(level);
6543   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6544     CopyNativeLevel_EM_to_RND(level);
6545   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6546     CopyNativeLevel_SP_to_RND(level);
6547   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6548     CopyNativeLevel_MM_to_RND(level);
6549 }
6550
6551 void SaveNativeLevel(struct LevelInfo *level)
6552 {
6553   // saving native level files only supported for some game engines
6554   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6555       level->game_engine_type != GAME_ENGINE_TYPE_SP)
6556     return;
6557
6558   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6559                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6560   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6561   char *filename = getLevelFilenameFromBasename(basename);
6562
6563   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6564     return;
6565
6566   boolean success = FALSE;
6567
6568   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6569   {
6570     CopyNativeLevel_RND_to_BD(level);
6571     // CopyNativeTape_RND_to_BD(level);
6572
6573     success = SaveNativeLevel_BD(filename);
6574   }
6575   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6576   {
6577     CopyNativeLevel_RND_to_SP(level);
6578     CopyNativeTape_RND_to_SP(level);
6579
6580     success = SaveNativeLevel_SP(filename);
6581   }
6582
6583   if (success)
6584     Request("Native level file saved!", REQ_CONFIRM);
6585   else
6586     Request("Failed to save native level file!", REQ_CONFIRM);
6587 }
6588
6589
6590 // ----------------------------------------------------------------------------
6591 // functions for loading generic level
6592 // ----------------------------------------------------------------------------
6593
6594 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6595                                   struct LevelFileInfo *level_file_info,
6596                                   boolean level_info_only)
6597 {
6598   // always start with reliable default values
6599   setLevelInfoToDefaults(level, level_info_only, TRUE);
6600
6601   switch (level_file_info->type)
6602   {
6603     case LEVEL_FILE_TYPE_RND:
6604       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6605       break;
6606
6607     case LEVEL_FILE_TYPE_BD:
6608       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6609       level->game_engine_type = GAME_ENGINE_TYPE_BD;
6610       break;
6611
6612     case LEVEL_FILE_TYPE_EM:
6613       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6614       level->game_engine_type = GAME_ENGINE_TYPE_EM;
6615       break;
6616
6617     case LEVEL_FILE_TYPE_SP:
6618       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6619       level->game_engine_type = GAME_ENGINE_TYPE_SP;
6620       break;
6621
6622     case LEVEL_FILE_TYPE_MM:
6623       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6624       level->game_engine_type = GAME_ENGINE_TYPE_MM;
6625       break;
6626
6627     case LEVEL_FILE_TYPE_DC:
6628       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6629       break;
6630
6631     case LEVEL_FILE_TYPE_SB:
6632       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6633       break;
6634
6635     default:
6636       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6637       break;
6638   }
6639
6640   // if level file is invalid, restore level structure to default values
6641   if (level->no_valid_file)
6642     setLevelInfoToDefaults(level, level_info_only, FALSE);
6643
6644   if (check_special_flags("use_native_bd_game_engine"))
6645     level->game_engine_type = GAME_ENGINE_TYPE_BD;
6646
6647   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6648     level->game_engine_type = GAME_ENGINE_TYPE_RND;
6649
6650   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6651     CopyNativeLevel_Native_to_RND(level);
6652 }
6653
6654 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6655 {
6656   static struct LevelFileInfo level_file_info;
6657
6658   // always start with reliable default values
6659   setFileInfoToDefaults(&level_file_info);
6660
6661   level_file_info.nr = 0;                       // unknown level number
6662   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
6663
6664   setString(&level_file_info.filename, filename);
6665
6666   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6667 }
6668
6669 static void LoadLevel_InitVersion(struct LevelInfo *level)
6670 {
6671   int i, j;
6672
6673   if (leveldir_current == NULL)         // only when dumping level
6674     return;
6675
6676   // all engine modifications also valid for levels which use latest engine
6677   if (level->game_version < VERSION_IDENT(3,2,0,5))
6678   {
6679     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6680     level->time_score_base = 10;
6681   }
6682
6683   if (leveldir_current->latest_engine)
6684   {
6685     // ---------- use latest game engine --------------------------------------
6686
6687     /* For all levels which are forced to use the latest game engine version
6688        (normally all but user contributed, private and undefined levels), set
6689        the game engine version to the actual version; this allows for actual
6690        corrections in the game engine to take effect for existing, converted
6691        levels (from "classic" or other existing games) to make the emulation
6692        of the corresponding game more accurate, while (hopefully) not breaking
6693        existing levels created from other players. */
6694
6695     level->game_version = GAME_VERSION_ACTUAL;
6696
6697     /* Set special EM style gems behaviour: EM style gems slip down from
6698        normal, steel and growing wall. As this is a more fundamental change,
6699        it seems better to set the default behaviour to "off" (as it is more
6700        natural) and make it configurable in the level editor (as a property
6701        of gem style elements). Already existing converted levels (neither
6702        private nor contributed levels) are changed to the new behaviour. */
6703
6704     if (level->file_version < FILE_VERSION_2_0)
6705       level->em_slippery_gems = TRUE;
6706
6707     return;
6708   }
6709
6710   // ---------- use game engine the level was created with --------------------
6711
6712   /* For all levels which are not forced to use the latest game engine
6713      version (normally user contributed, private and undefined levels),
6714      use the version of the game engine the levels were created for.
6715
6716      Since 2.0.1, the game engine version is now directly stored
6717      in the level file (chunk "VERS"), so there is no need anymore
6718      to set the game version from the file version (except for old,
6719      pre-2.0 levels, where the game version is still taken from the
6720      file format version used to store the level -- see above). */
6721
6722   // player was faster than enemies in 1.0.0 and before
6723   if (level->file_version == FILE_VERSION_1_0)
6724     for (i = 0; i < MAX_PLAYERS; i++)
6725       level->initial_player_stepsize[i] = STEPSIZE_FAST;
6726
6727   // default behaviour for EM style gems was "slippery" only in 2.0.1
6728   if (level->game_version == VERSION_IDENT(2,0,1,0))
6729     level->em_slippery_gems = TRUE;
6730
6731   // springs could be pushed over pits before (pre-release version) 2.2.0
6732   if (level->game_version < VERSION_IDENT(2,2,0,0))
6733     level->use_spring_bug = TRUE;
6734
6735   if (level->game_version < VERSION_IDENT(3,2,0,5))
6736   {
6737     // time orb caused limited time in endless time levels before 3.2.0-5
6738     level->use_time_orb_bug = TRUE;
6739
6740     // default behaviour for snapping was "no snap delay" before 3.2.0-5
6741     level->block_snap_field = FALSE;
6742
6743     // extra time score was same value as time left score before 3.2.0-5
6744     level->extra_time_score = level->score[SC_TIME_BONUS];
6745   }
6746
6747   if (level->game_version < VERSION_IDENT(3,2,0,7))
6748   {
6749     // default behaviour for snapping was "not continuous" before 3.2.0-7
6750     level->continuous_snapping = FALSE;
6751   }
6752
6753   // only few elements were able to actively move into acid before 3.1.0
6754   // trigger settings did not exist before 3.1.0; set to default "any"
6755   if (level->game_version < VERSION_IDENT(3,1,0,0))
6756   {
6757     // correct "can move into acid" settings (all zero in old levels)
6758
6759     level->can_move_into_acid_bits = 0; // nothing can move into acid
6760     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6761
6762     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
6763     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6764     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
6765     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
6766
6767     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6768       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6769
6770     // correct trigger settings (stored as zero == "none" in old levels)
6771
6772     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6773     {
6774       int element = EL_CUSTOM_START + i;
6775       struct ElementInfo *ei = &element_info[element];
6776
6777       for (j = 0; j < ei->num_change_pages; j++)
6778       {
6779         struct ElementChangeInfo *change = &ei->change_page[j];
6780
6781         change->trigger_player = CH_PLAYER_ANY;
6782         change->trigger_page = CH_PAGE_ANY;
6783       }
6784     }
6785   }
6786
6787   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6788   {
6789     int element = EL_CUSTOM_256;
6790     struct ElementInfo *ei = &element_info[element];
6791     struct ElementChangeInfo *change = &ei->change_page[0];
6792
6793     /* This is needed to fix a problem that was caused by a bugfix in function
6794        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6795        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6796        not replace walkable elements, but instead just placed the player on it,
6797        without placing the Sokoban field under the player). Unfortunately, this
6798        breaks "Snake Bite" style levels when the snake is halfway through a door
6799        that just closes (the snake head is still alive and can be moved in this
6800        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6801        player (without Sokoban element) which then gets killed as designed). */
6802
6803     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6804          strncmp(ei->description, "pause b4 death", 14) == 0) &&
6805         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6806       change->target_element = EL_PLAYER_1;
6807   }
6808
6809   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6810   if (level->game_version < VERSION_IDENT(3,2,5,0))
6811   {
6812     /* This is needed to fix a problem that was caused by a bugfix in function
6813        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6814        corrects the behaviour when a custom element changes to another custom
6815        element with a higher element number that has change actions defined.
6816        Normally, only one change per frame is allowed for custom elements.
6817        Therefore, it is checked if a custom element already changed in the
6818        current frame; if it did, subsequent changes are suppressed.
6819        Unfortunately, this is only checked for element changes, but not for
6820        change actions, which are still executed. As the function above loops
6821        through all custom elements from lower to higher, an element change
6822        resulting in a lower CE number won't be checked again, while a target
6823        element with a higher number will also be checked, and potential change
6824        actions will get executed for this CE, too (which is wrong), while
6825        further changes are ignored (which is correct). As this bugfix breaks
6826        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6827        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6828        behaviour for existing levels and tapes that make use of this bug */
6829
6830     level->use_action_after_change_bug = TRUE;
6831   }
6832
6833   // not centering level after relocating player was default only in 3.2.3
6834   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
6835     level->shifted_relocation = TRUE;
6836
6837   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6838   if (level->game_version < VERSION_IDENT(3,2,6,0))
6839     level->em_explodes_by_fire = TRUE;
6840
6841   // levels were solved by the first player entering an exit up to 4.1.0.0
6842   if (level->game_version <= VERSION_IDENT(4,1,0,0))
6843     level->solved_by_one_player = TRUE;
6844
6845   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6846   if (level->game_version < VERSION_IDENT(4,1,1,1))
6847     level->use_life_bugs = TRUE;
6848
6849   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6850   if (level->game_version < VERSION_IDENT(4,1,1,1))
6851     level->sb_objects_needed = FALSE;
6852
6853   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6854   if (level->game_version <= VERSION_IDENT(4,2,2,0))
6855     level->finish_dig_collect = FALSE;
6856
6857   // CE changing to player was kept under the player if walkable up to 4.2.3.1
6858   if (level->game_version <= VERSION_IDENT(4,2,3,1))
6859     level->keep_walkable_ce = TRUE;
6860 }
6861
6862 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6863 {
6864   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
6865   int x, y;
6866
6867   // check if this level is (not) a Sokoban level
6868   for (y = 0; y < level->fieldy; y++)
6869     for (x = 0; x < level->fieldx; x++)
6870       if (!IS_SB_ELEMENT(Tile[x][y]))
6871         is_sokoban_level = FALSE;
6872
6873   if (is_sokoban_level)
6874   {
6875     // set special level settings for Sokoban levels
6876     SetLevelSettings_SB(level);
6877   }
6878 }
6879
6880 static void LoadLevel_InitSettings(struct LevelInfo *level)
6881 {
6882   // adjust level settings for (non-native) Sokoban-style levels
6883   LoadLevel_InitSettings_SB(level);
6884
6885   // rename levels with title "nameless level" or if renaming is forced
6886   if (leveldir_current->empty_level_name != NULL &&
6887       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6888        leveldir_current->force_level_name))
6889     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6890              leveldir_current->empty_level_name, level_nr);
6891 }
6892
6893 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6894 {
6895   int i, x, y;
6896
6897   // map elements that have changed in newer versions
6898   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6899                                                     level->game_version);
6900   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6901     for (x = 0; x < 3; x++)
6902       for (y = 0; y < 3; y++)
6903         level->yamyam_content[i].e[x][y] =
6904           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6905                                     level->game_version);
6906
6907 }
6908
6909 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6910 {
6911   int i, j;
6912
6913   // map custom element change events that have changed in newer versions
6914   // (these following values were accidentally changed in version 3.0.1)
6915   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6916   if (level->game_version <= VERSION_IDENT(3,0,0,0))
6917   {
6918     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6919     {
6920       int element = EL_CUSTOM_START + i;
6921
6922       // order of checking and copying events to be mapped is important
6923       // (do not change the start and end value -- they are constant)
6924       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6925       {
6926         if (HAS_CHANGE_EVENT(element, j - 2))
6927         {
6928           SET_CHANGE_EVENT(element, j - 2, FALSE);
6929           SET_CHANGE_EVENT(element, j, TRUE);
6930         }
6931       }
6932
6933       // order of checking and copying events to be mapped is important
6934       // (do not change the start and end value -- they are constant)
6935       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6936       {
6937         if (HAS_CHANGE_EVENT(element, j - 1))
6938         {
6939           SET_CHANGE_EVENT(element, j - 1, FALSE);
6940           SET_CHANGE_EVENT(element, j, TRUE);
6941         }
6942       }
6943     }
6944   }
6945
6946   // initialize "can_change" field for old levels with only one change page
6947   if (level->game_version <= VERSION_IDENT(3,0,2,0))
6948   {
6949     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6950     {
6951       int element = EL_CUSTOM_START + i;
6952
6953       if (CAN_CHANGE(element))
6954         element_info[element].change->can_change = TRUE;
6955     }
6956   }
6957
6958   // correct custom element values (for old levels without these options)
6959   if (level->game_version < VERSION_IDENT(3,1,1,0))
6960   {
6961     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6962     {
6963       int element = EL_CUSTOM_START + i;
6964       struct ElementInfo *ei = &element_info[element];
6965
6966       if (ei->access_direction == MV_NO_DIRECTION)
6967         ei->access_direction = MV_ALL_DIRECTIONS;
6968     }
6969   }
6970
6971   // correct custom element values (fix invalid values for all versions)
6972   if (1)
6973   {
6974     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6975     {
6976       int element = EL_CUSTOM_START + i;
6977       struct ElementInfo *ei = &element_info[element];
6978
6979       for (j = 0; j < ei->num_change_pages; j++)
6980       {
6981         struct ElementChangeInfo *change = &ei->change_page[j];
6982
6983         if (change->trigger_player == CH_PLAYER_NONE)
6984           change->trigger_player = CH_PLAYER_ANY;
6985
6986         if (change->trigger_side == CH_SIDE_NONE)
6987           change->trigger_side = CH_SIDE_ANY;
6988       }
6989     }
6990   }
6991
6992   // initialize "can_explode" field for old levels which did not store this
6993   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6994   if (level->game_version <= VERSION_IDENT(3,1,0,0))
6995   {
6996     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6997     {
6998       int element = EL_CUSTOM_START + i;
6999
7000       if (EXPLODES_1X1_OLD(element))
7001         element_info[element].explosion_type = EXPLODES_1X1;
7002
7003       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7004                                              EXPLODES_SMASHED(element) ||
7005                                              EXPLODES_IMPACT(element)));
7006     }
7007   }
7008
7009   // correct previously hard-coded move delay values for maze runner style
7010   if (level->game_version < VERSION_IDENT(3,1,1,0))
7011   {
7012     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7013     {
7014       int element = EL_CUSTOM_START + i;
7015
7016       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7017       {
7018         // previously hard-coded and therefore ignored
7019         element_info[element].move_delay_fixed = 9;
7020         element_info[element].move_delay_random = 0;
7021       }
7022     }
7023   }
7024
7025   // set some other uninitialized values of custom elements in older levels
7026   if (level->game_version < VERSION_IDENT(3,1,0,0))
7027   {
7028     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7029     {
7030       int element = EL_CUSTOM_START + i;
7031
7032       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7033
7034       element_info[element].explosion_delay = 17;
7035       element_info[element].ignition_delay = 8;
7036     }
7037   }
7038
7039   // set mouse click change events to work for left/middle/right mouse button
7040   if (level->game_version < VERSION_IDENT(4,2,3,0))
7041   {
7042     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7043     {
7044       int element = EL_CUSTOM_START + i;
7045       struct ElementInfo *ei = &element_info[element];
7046
7047       for (j = 0; j < ei->num_change_pages; j++)
7048       {
7049         struct ElementChangeInfo *change = &ei->change_page[j];
7050
7051         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7052             change->has_event[CE_PRESSED_BY_MOUSE] ||
7053             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7054             change->has_event[CE_MOUSE_PRESSED_ON_X])
7055           change->trigger_side = CH_SIDE_ANY;
7056       }
7057     }
7058   }
7059 }
7060
7061 static void LoadLevel_InitElements(struct LevelInfo *level)
7062 {
7063   LoadLevel_InitStandardElements(level);
7064
7065   if (level->file_has_custom_elements)
7066     LoadLevel_InitCustomElements(level);
7067
7068   // initialize element properties for level editor etc.
7069   InitElementPropertiesEngine(level->game_version);
7070   InitElementPropertiesGfxElement();
7071 }
7072
7073 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7074 {
7075   int x, y;
7076
7077   // map elements that have changed in newer versions
7078   for (y = 0; y < level->fieldy; y++)
7079     for (x = 0; x < level->fieldx; x++)
7080       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7081                                                      level->game_version);
7082
7083   // clear unused playfield data (nicer if level gets resized in editor)
7084   for (x = 0; x < MAX_LEV_FIELDX; x++)
7085     for (y = 0; y < MAX_LEV_FIELDY; y++)
7086       if (x >= level->fieldx || y >= level->fieldy)
7087         level->field[x][y] = EL_EMPTY;
7088
7089   // copy elements to runtime playfield array
7090   for (x = 0; x < MAX_LEV_FIELDX; x++)
7091     for (y = 0; y < MAX_LEV_FIELDY; y++)
7092       Tile[x][y] = level->field[x][y];
7093
7094   // initialize level size variables for faster access
7095   lev_fieldx = level->fieldx;
7096   lev_fieldy = level->fieldy;
7097
7098   // determine border element for this level
7099   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7100     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7101   else
7102     SetBorderElement();
7103 }
7104
7105 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7106 {
7107   struct LevelFileInfo *level_file_info = &level->file_info;
7108
7109   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7110     CopyNativeLevel_RND_to_Native(level);
7111 }
7112
7113 static void LoadLevelTemplate_LoadAndInit(void)
7114 {
7115   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7116
7117   LoadLevel_InitVersion(&level_template);
7118   LoadLevel_InitElements(&level_template);
7119   LoadLevel_InitSettings(&level_template);
7120
7121   ActivateLevelTemplate();
7122 }
7123
7124 void LoadLevelTemplate(int nr)
7125 {
7126   if (!fileExists(getGlobalLevelTemplateFilename()))
7127   {
7128     Warn("no level template found for this level");
7129
7130     return;
7131   }
7132
7133   setLevelFileInfo(&level_template.file_info, nr);
7134
7135   LoadLevelTemplate_LoadAndInit();
7136 }
7137
7138 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7139 {
7140   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7141
7142   LoadLevelTemplate_LoadAndInit();
7143 }
7144
7145 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7146 {
7147   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7148
7149   if (level.use_custom_template)
7150   {
7151     if (network_level != NULL)
7152       LoadNetworkLevelTemplate(network_level);
7153     else
7154       LoadLevelTemplate(-1);
7155   }
7156
7157   LoadLevel_InitVersion(&level);
7158   LoadLevel_InitElements(&level);
7159   LoadLevel_InitPlayfield(&level);
7160   LoadLevel_InitSettings(&level);
7161
7162   LoadLevel_InitNativeEngines(&level);
7163 }
7164
7165 void LoadLevel(int nr)
7166 {
7167   SetLevelSetInfo(leveldir_current->identifier, nr);
7168
7169   setLevelFileInfo(&level.file_info, nr);
7170
7171   LoadLevel_LoadAndInit(NULL);
7172 }
7173
7174 void LoadLevelInfoOnly(int nr)
7175 {
7176   setLevelFileInfo(&level.file_info, nr);
7177
7178   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7179 }
7180
7181 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7182 {
7183   SetLevelSetInfo(network_level->leveldir_identifier,
7184                   network_level->file_info.nr);
7185
7186   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7187
7188   LoadLevel_LoadAndInit(network_level);
7189 }
7190
7191 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7192 {
7193   int chunk_size = 0;
7194
7195   chunk_size += putFileVersion(file, level->file_version);
7196   chunk_size += putFileVersion(file, level->game_version);
7197
7198   return chunk_size;
7199 }
7200
7201 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7202 {
7203   int chunk_size = 0;
7204
7205   chunk_size += putFile16BitBE(file, level->creation_date.year);
7206   chunk_size += putFile8Bit(file,    level->creation_date.month);
7207   chunk_size += putFile8Bit(file,    level->creation_date.day);
7208
7209   return chunk_size;
7210 }
7211
7212 #if ENABLE_HISTORIC_CHUNKS
7213 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7214 {
7215   int i, x, y;
7216
7217   putFile8Bit(file, level->fieldx);
7218   putFile8Bit(file, level->fieldy);
7219
7220   putFile16BitBE(file, level->time);
7221   putFile16BitBE(file, level->gems_needed);
7222
7223   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7224     putFile8Bit(file, level->name[i]);
7225
7226   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7227     putFile8Bit(file, level->score[i]);
7228
7229   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7230     for (y = 0; y < 3; y++)
7231       for (x = 0; x < 3; x++)
7232         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7233                            level->yamyam_content[i].e[x][y]));
7234   putFile8Bit(file, level->amoeba_speed);
7235   putFile8Bit(file, level->time_magic_wall);
7236   putFile8Bit(file, level->time_wheel);
7237   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7238                      level->amoeba_content));
7239   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7240   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7241   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7242   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7243
7244   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7245
7246   putFile8Bit(file, (level->block_last_field ? 1 : 0));
7247   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7248   putFile32BitBE(file, level->can_move_into_acid_bits);
7249   putFile8Bit(file, level->dont_collide_with_bits);
7250
7251   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7252   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7253
7254   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7255   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7256   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7257
7258   putFile8Bit(file, level->game_engine_type);
7259
7260   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7261 }
7262 #endif
7263
7264 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7265 {
7266   int chunk_size = 0;
7267   int i;
7268
7269   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7270     chunk_size += putFile8Bit(file, level->name[i]);
7271
7272   return chunk_size;
7273 }
7274
7275 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7276 {
7277   int chunk_size = 0;
7278   int i;
7279
7280   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7281     chunk_size += putFile8Bit(file, level->author[i]);
7282
7283   return chunk_size;
7284 }
7285
7286 #if ENABLE_HISTORIC_CHUNKS
7287 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7288 {
7289   int chunk_size = 0;
7290   int x, y;
7291
7292   for (y = 0; y < level->fieldy; y++)
7293     for (x = 0; x < level->fieldx; x++)
7294       if (level->encoding_16bit_field)
7295         chunk_size += putFile16BitBE(file, level->field[x][y]);
7296       else
7297         chunk_size += putFile8Bit(file, level->field[x][y]);
7298
7299   return chunk_size;
7300 }
7301 #endif
7302
7303 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7304 {
7305   int chunk_size = 0;
7306   int x, y;
7307
7308   for (y = 0; y < level->fieldy; y++) 
7309     for (x = 0; x < level->fieldx; x++) 
7310       chunk_size += putFile16BitBE(file, level->field[x][y]);
7311
7312   return chunk_size;
7313 }
7314
7315 #if ENABLE_HISTORIC_CHUNKS
7316 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7317 {
7318   int i, x, y;
7319
7320   putFile8Bit(file, EL_YAMYAM);
7321   putFile8Bit(file, level->num_yamyam_contents);
7322   putFile8Bit(file, 0);
7323   putFile8Bit(file, 0);
7324
7325   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7326     for (y = 0; y < 3; y++)
7327       for (x = 0; x < 3; x++)
7328         if (level->encoding_16bit_field)
7329           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7330         else
7331           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7332 }
7333 #endif
7334
7335 #if ENABLE_HISTORIC_CHUNKS
7336 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7337 {
7338   int i, x, y;
7339   int num_contents, content_xsize, content_ysize;
7340   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7341
7342   if (element == EL_YAMYAM)
7343   {
7344     num_contents = level->num_yamyam_contents;
7345     content_xsize = 3;
7346     content_ysize = 3;
7347
7348     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7349       for (y = 0; y < 3; y++)
7350         for (x = 0; x < 3; x++)
7351           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7352   }
7353   else if (element == EL_BD_AMOEBA)
7354   {
7355     num_contents = 1;
7356     content_xsize = 1;
7357     content_ysize = 1;
7358
7359     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7360       for (y = 0; y < 3; y++)
7361         for (x = 0; x < 3; x++)
7362           content_array[i][x][y] = EL_EMPTY;
7363     content_array[0][0][0] = level->amoeba_content;
7364   }
7365   else
7366   {
7367     // chunk header already written -- write empty chunk data
7368     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7369
7370     Warn("cannot save content for element '%d'", element);
7371
7372     return;
7373   }
7374
7375   putFile16BitBE(file, element);
7376   putFile8Bit(file, num_contents);
7377   putFile8Bit(file, content_xsize);
7378   putFile8Bit(file, content_ysize);
7379
7380   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7381
7382   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7383     for (y = 0; y < 3; y++)
7384       for (x = 0; x < 3; x++)
7385         putFile16BitBE(file, content_array[i][x][y]);
7386 }
7387 #endif
7388
7389 #if ENABLE_HISTORIC_CHUNKS
7390 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7391 {
7392   int envelope_nr = element - EL_ENVELOPE_1;
7393   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7394   int chunk_size = 0;
7395   int i;
7396
7397   chunk_size += putFile16BitBE(file, element);
7398   chunk_size += putFile16BitBE(file, envelope_len);
7399   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7400   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7401
7402   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7403   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7404
7405   for (i = 0; i < envelope_len; i++)
7406     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7407
7408   return chunk_size;
7409 }
7410 #endif
7411
7412 #if ENABLE_HISTORIC_CHUNKS
7413 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7414                            int num_changed_custom_elements)
7415 {
7416   int i, check = 0;
7417
7418   putFile16BitBE(file, num_changed_custom_elements);
7419
7420   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7421   {
7422     int element = EL_CUSTOM_START + i;
7423
7424     struct ElementInfo *ei = &element_info[element];
7425
7426     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7427     {
7428       if (check < num_changed_custom_elements)
7429       {
7430         putFile16BitBE(file, element);
7431         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7432       }
7433
7434       check++;
7435     }
7436   }
7437
7438   if (check != num_changed_custom_elements)     // should not happen
7439     Warn("inconsistent number of custom element properties");
7440 }
7441 #endif
7442
7443 #if ENABLE_HISTORIC_CHUNKS
7444 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7445                            int num_changed_custom_elements)
7446 {
7447   int i, check = 0;
7448
7449   putFile16BitBE(file, num_changed_custom_elements);
7450
7451   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7452   {
7453     int element = EL_CUSTOM_START + i;
7454
7455     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7456     {
7457       if (check < num_changed_custom_elements)
7458       {
7459         putFile16BitBE(file, element);
7460         putFile16BitBE(file, element_info[element].change->target_element);
7461       }
7462
7463       check++;
7464     }
7465   }
7466
7467   if (check != num_changed_custom_elements)     // should not happen
7468     Warn("inconsistent number of custom target elements");
7469 }
7470 #endif
7471
7472 #if ENABLE_HISTORIC_CHUNKS
7473 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7474                            int num_changed_custom_elements)
7475 {
7476   int i, j, x, y, check = 0;
7477
7478   putFile16BitBE(file, num_changed_custom_elements);
7479
7480   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7481   {
7482     int element = EL_CUSTOM_START + i;
7483     struct ElementInfo *ei = &element_info[element];
7484
7485     if (ei->modified_settings)
7486     {
7487       if (check < num_changed_custom_elements)
7488       {
7489         putFile16BitBE(file, element);
7490
7491         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7492           putFile8Bit(file, ei->description[j]);
7493
7494         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7495
7496         // some free bytes for future properties and padding
7497         WriteUnusedBytesToFile(file, 7);
7498
7499         putFile8Bit(file, ei->use_gfx_element);
7500         putFile16BitBE(file, ei->gfx_element_initial);
7501
7502         putFile8Bit(file, ei->collect_score_initial);
7503         putFile8Bit(file, ei->collect_count_initial);
7504
7505         putFile16BitBE(file, ei->push_delay_fixed);
7506         putFile16BitBE(file, ei->push_delay_random);
7507         putFile16BitBE(file, ei->move_delay_fixed);
7508         putFile16BitBE(file, ei->move_delay_random);
7509
7510         putFile16BitBE(file, ei->move_pattern);
7511         putFile8Bit(file, ei->move_direction_initial);
7512         putFile8Bit(file, ei->move_stepsize);
7513
7514         for (y = 0; y < 3; y++)
7515           for (x = 0; x < 3; x++)
7516             putFile16BitBE(file, ei->content.e[x][y]);
7517
7518         putFile32BitBE(file, ei->change->events);
7519
7520         putFile16BitBE(file, ei->change->target_element);
7521
7522         putFile16BitBE(file, ei->change->delay_fixed);
7523         putFile16BitBE(file, ei->change->delay_random);
7524         putFile16BitBE(file, ei->change->delay_frames);
7525
7526         putFile16BitBE(file, ei->change->initial_trigger_element);
7527
7528         putFile8Bit(file, ei->change->explode);
7529         putFile8Bit(file, ei->change->use_target_content);
7530         putFile8Bit(file, ei->change->only_if_complete);
7531         putFile8Bit(file, ei->change->use_random_replace);
7532
7533         putFile8Bit(file, ei->change->random_percentage);
7534         putFile8Bit(file, ei->change->replace_when);
7535
7536         for (y = 0; y < 3; y++)
7537           for (x = 0; x < 3; x++)
7538             putFile16BitBE(file, ei->change->content.e[x][y]);
7539
7540         putFile8Bit(file, ei->slippery_type);
7541
7542         // some free bytes for future properties and padding
7543         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7544       }
7545
7546       check++;
7547     }
7548   }
7549
7550   if (check != num_changed_custom_elements)     // should not happen
7551     Warn("inconsistent number of custom element properties");
7552 }
7553 #endif
7554
7555 #if ENABLE_HISTORIC_CHUNKS
7556 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7557 {
7558   struct ElementInfo *ei = &element_info[element];
7559   int i, j, x, y;
7560
7561   // ---------- custom element base property values (96 bytes) ----------------
7562
7563   putFile16BitBE(file, element);
7564
7565   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7566     putFile8Bit(file, ei->description[i]);
7567
7568   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7569
7570   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
7571
7572   putFile8Bit(file, ei->num_change_pages);
7573
7574   putFile16BitBE(file, ei->ce_value_fixed_initial);
7575   putFile16BitBE(file, ei->ce_value_random_initial);
7576   putFile8Bit(file, ei->use_last_ce_value);
7577
7578   putFile8Bit(file, ei->use_gfx_element);
7579   putFile16BitBE(file, ei->gfx_element_initial);
7580
7581   putFile8Bit(file, ei->collect_score_initial);
7582   putFile8Bit(file, ei->collect_count_initial);
7583
7584   putFile8Bit(file, ei->drop_delay_fixed);
7585   putFile8Bit(file, ei->push_delay_fixed);
7586   putFile8Bit(file, ei->drop_delay_random);
7587   putFile8Bit(file, ei->push_delay_random);
7588   putFile16BitBE(file, ei->move_delay_fixed);
7589   putFile16BitBE(file, ei->move_delay_random);
7590
7591   // bits 0 - 15 of "move_pattern" ...
7592   putFile16BitBE(file, ei->move_pattern & 0xffff);
7593   putFile8Bit(file, ei->move_direction_initial);
7594   putFile8Bit(file, ei->move_stepsize);
7595
7596   putFile8Bit(file, ei->slippery_type);
7597
7598   for (y = 0; y < 3; y++)
7599     for (x = 0; x < 3; x++)
7600       putFile16BitBE(file, ei->content.e[x][y]);
7601
7602   putFile16BitBE(file, ei->move_enter_element);
7603   putFile16BitBE(file, ei->move_leave_element);
7604   putFile8Bit(file, ei->move_leave_type);
7605
7606   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7607   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7608
7609   putFile8Bit(file, ei->access_direction);
7610
7611   putFile8Bit(file, ei->explosion_delay);
7612   putFile8Bit(file, ei->ignition_delay);
7613   putFile8Bit(file, ei->explosion_type);
7614
7615   // some free bytes for future custom property values and padding
7616   WriteUnusedBytesToFile(file, 1);
7617
7618   // ---------- change page property values (48 bytes) ------------------------
7619
7620   for (i = 0; i < ei->num_change_pages; i++)
7621   {
7622     struct ElementChangeInfo *change = &ei->change_page[i];
7623     unsigned int event_bits;
7624
7625     // bits 0 - 31 of "has_event[]" ...
7626     event_bits = 0;
7627     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7628       if (change->has_event[j])
7629         event_bits |= (1u << j);
7630     putFile32BitBE(file, event_bits);
7631
7632     putFile16BitBE(file, change->target_element);
7633
7634     putFile16BitBE(file, change->delay_fixed);
7635     putFile16BitBE(file, change->delay_random);
7636     putFile16BitBE(file, change->delay_frames);
7637
7638     putFile16BitBE(file, change->initial_trigger_element);
7639
7640     putFile8Bit(file, change->explode);
7641     putFile8Bit(file, change->use_target_content);
7642     putFile8Bit(file, change->only_if_complete);
7643     putFile8Bit(file, change->use_random_replace);
7644
7645     putFile8Bit(file, change->random_percentage);
7646     putFile8Bit(file, change->replace_when);
7647
7648     for (y = 0; y < 3; y++)
7649       for (x = 0; x < 3; x++)
7650         putFile16BitBE(file, change->target_content.e[x][y]);
7651
7652     putFile8Bit(file, change->can_change);
7653
7654     putFile8Bit(file, change->trigger_side);
7655
7656     putFile8Bit(file, change->trigger_player);
7657     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7658                        log_2(change->trigger_page)));
7659
7660     putFile8Bit(file, change->has_action);
7661     putFile8Bit(file, change->action_type);
7662     putFile8Bit(file, change->action_mode);
7663     putFile16BitBE(file, change->action_arg);
7664
7665     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7666     event_bits = 0;
7667     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7668       if (change->has_event[j])
7669         event_bits |= (1u << (j - 32));
7670     putFile8Bit(file, event_bits);
7671   }
7672 }
7673 #endif
7674
7675 #if ENABLE_HISTORIC_CHUNKS
7676 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7677 {
7678   struct ElementInfo *ei = &element_info[element];
7679   struct ElementGroupInfo *group = ei->group;
7680   int i;
7681
7682   putFile16BitBE(file, element);
7683
7684   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7685     putFile8Bit(file, ei->description[i]);
7686
7687   putFile8Bit(file, group->num_elements);
7688
7689   putFile8Bit(file, ei->use_gfx_element);
7690   putFile16BitBE(file, ei->gfx_element_initial);
7691
7692   putFile8Bit(file, group->choice_mode);
7693
7694   // some free bytes for future values and padding
7695   WriteUnusedBytesToFile(file, 3);
7696
7697   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7698     putFile16BitBE(file, group->element[i]);
7699 }
7700 #endif
7701
7702 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7703                                 boolean write_element)
7704 {
7705   int save_type = entry->save_type;
7706   int data_type = entry->data_type;
7707   int conf_type = entry->conf_type;
7708   int byte_mask = conf_type & CONF_MASK_BYTES;
7709   int element = entry->element;
7710   int default_value = entry->default_value;
7711   int num_bytes = 0;
7712   boolean modified = FALSE;
7713
7714   if (byte_mask != CONF_MASK_MULTI_BYTES)
7715   {
7716     void *value_ptr = entry->value;
7717     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7718                  *(int *)value_ptr);
7719
7720     // check if any settings have been modified before saving them
7721     if (value != default_value)
7722       modified = TRUE;
7723
7724     // do not save if explicitly told or if unmodified default settings
7725     if ((save_type == SAVE_CONF_NEVER) ||
7726         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7727       return 0;
7728
7729     if (write_element)
7730       num_bytes += putFile16BitBE(file, element);
7731
7732     num_bytes += putFile8Bit(file, conf_type);
7733     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
7734                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7735                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7736                   0);
7737   }
7738   else if (data_type == TYPE_STRING)
7739   {
7740     char *default_string = entry->default_string;
7741     char *string = (char *)(entry->value);
7742     int string_length = strlen(string);
7743     int i;
7744
7745     // check if any settings have been modified before saving them
7746     if (!strEqual(string, default_string))
7747       modified = TRUE;
7748
7749     // do not save if explicitly told or if unmodified default settings
7750     if ((save_type == SAVE_CONF_NEVER) ||
7751         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7752       return 0;
7753
7754     if (write_element)
7755       num_bytes += putFile16BitBE(file, element);
7756
7757     num_bytes += putFile8Bit(file, conf_type);
7758     num_bytes += putFile16BitBE(file, string_length);
7759
7760     for (i = 0; i < string_length; i++)
7761       num_bytes += putFile8Bit(file, string[i]);
7762   }
7763   else if (data_type == TYPE_ELEMENT_LIST)
7764   {
7765     int *element_array = (int *)(entry->value);
7766     int num_elements = *(int *)(entry->num_entities);
7767     int i;
7768
7769     // check if any settings have been modified before saving them
7770     for (i = 0; i < num_elements; i++)
7771       if (element_array[i] != default_value)
7772         modified = TRUE;
7773
7774     // do not save if explicitly told or if unmodified default settings
7775     if ((save_type == SAVE_CONF_NEVER) ||
7776         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7777       return 0;
7778
7779     if (write_element)
7780       num_bytes += putFile16BitBE(file, element);
7781
7782     num_bytes += putFile8Bit(file, conf_type);
7783     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7784
7785     for (i = 0; i < num_elements; i++)
7786       num_bytes += putFile16BitBE(file, element_array[i]);
7787   }
7788   else if (data_type == TYPE_CONTENT_LIST)
7789   {
7790     struct Content *content = (struct Content *)(entry->value);
7791     int num_contents = *(int *)(entry->num_entities);
7792     int i, x, y;
7793
7794     // check if any settings have been modified before saving them
7795     for (i = 0; i < num_contents; i++)
7796       for (y = 0; y < 3; y++)
7797         for (x = 0; x < 3; x++)
7798           if (content[i].e[x][y] != default_value)
7799             modified = TRUE;
7800
7801     // do not save if explicitly told or if unmodified default settings
7802     if ((save_type == SAVE_CONF_NEVER) ||
7803         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7804       return 0;
7805
7806     if (write_element)
7807       num_bytes += putFile16BitBE(file, element);
7808
7809     num_bytes += putFile8Bit(file, conf_type);
7810     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7811
7812     for (i = 0; i < num_contents; i++)
7813       for (y = 0; y < 3; y++)
7814         for (x = 0; x < 3; x++)
7815           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7816   }
7817
7818   return num_bytes;
7819 }
7820
7821 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7822 {
7823   int chunk_size = 0;
7824   int i;
7825
7826   li = *level;          // copy level data into temporary buffer
7827
7828   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7829     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7830
7831   return chunk_size;
7832 }
7833
7834 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7835 {
7836   int chunk_size = 0;
7837   int i;
7838
7839   li = *level;          // copy level data into temporary buffer
7840
7841   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7842     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7843
7844   return chunk_size;
7845 }
7846
7847 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7848 {
7849   int envelope_nr = element - EL_ENVELOPE_1;
7850   int chunk_size = 0;
7851   int i;
7852
7853   chunk_size += putFile16BitBE(file, element);
7854
7855   // copy envelope data into temporary buffer
7856   xx_envelope = level->envelope[envelope_nr];
7857
7858   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7859     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7860
7861   return chunk_size;
7862 }
7863
7864 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7865 {
7866   struct ElementInfo *ei = &element_info[element];
7867   int chunk_size = 0;
7868   int i, j;
7869
7870   chunk_size += putFile16BitBE(file, element);
7871
7872   xx_ei = *ei;          // copy element data into temporary buffer
7873
7874   // set default description string for this specific element
7875   strcpy(xx_default_description, getDefaultElementDescription(ei));
7876
7877   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7878     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7879
7880   for (i = 0; i < ei->num_change_pages; i++)
7881   {
7882     struct ElementChangeInfo *change = &ei->change_page[i];
7883
7884     xx_current_change_page = i;
7885
7886     xx_change = *change;        // copy change data into temporary buffer
7887
7888     resetEventBits();
7889     setEventBitsFromEventFlags(change);
7890
7891     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7892       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7893                                          FALSE);
7894   }
7895
7896   return chunk_size;
7897 }
7898
7899 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7900 {
7901   struct ElementInfo *ei = &element_info[element];
7902   struct ElementGroupInfo *group = ei->group;
7903   int chunk_size = 0;
7904   int i;
7905
7906   chunk_size += putFile16BitBE(file, element);
7907
7908   xx_ei = *ei;          // copy element data into temporary buffer
7909   xx_group = *group;    // copy group data into temporary buffer
7910
7911   // set default description string for this specific element
7912   strcpy(xx_default_description, getDefaultElementDescription(ei));
7913
7914   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7915     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7916
7917   return chunk_size;
7918 }
7919
7920 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7921 {
7922   struct ElementInfo *ei = &element_info[element];
7923   int chunk_size = 0;
7924   int i;
7925
7926   chunk_size += putFile16BitBE(file, element);
7927
7928   xx_ei = *ei;          // copy element data into temporary buffer
7929
7930   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7931     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7932
7933   return chunk_size;
7934 }
7935
7936 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7937                                   boolean save_as_template)
7938 {
7939   int chunk_size;
7940   int i;
7941   FILE *file;
7942
7943   if (!(file = fopen(filename, MODE_WRITE)))
7944   {
7945     Warn("cannot save level file '%s'", filename);
7946
7947     return;
7948   }
7949
7950   level->file_version = FILE_VERSION_ACTUAL;
7951   level->game_version = GAME_VERSION_ACTUAL;
7952
7953   level->creation_date = getCurrentDate();
7954
7955   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7956   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7957
7958   chunk_size = SaveLevel_VERS(NULL, level);
7959   putFileChunkBE(file, "VERS", chunk_size);
7960   SaveLevel_VERS(file, level);
7961
7962   chunk_size = SaveLevel_DATE(NULL, level);
7963   putFileChunkBE(file, "DATE", chunk_size);
7964   SaveLevel_DATE(file, level);
7965
7966   chunk_size = SaveLevel_NAME(NULL, level);
7967   putFileChunkBE(file, "NAME", chunk_size);
7968   SaveLevel_NAME(file, level);
7969
7970   chunk_size = SaveLevel_AUTH(NULL, level);
7971   putFileChunkBE(file, "AUTH", chunk_size);
7972   SaveLevel_AUTH(file, level);
7973
7974   chunk_size = SaveLevel_INFO(NULL, level);
7975   putFileChunkBE(file, "INFO", chunk_size);
7976   SaveLevel_INFO(file, level);
7977
7978   chunk_size = SaveLevel_BODY(NULL, level);
7979   putFileChunkBE(file, "BODY", chunk_size);
7980   SaveLevel_BODY(file, level);
7981
7982   chunk_size = SaveLevel_ELEM(NULL, level);
7983   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
7984   {
7985     putFileChunkBE(file, "ELEM", chunk_size);
7986     SaveLevel_ELEM(file, level);
7987   }
7988
7989   for (i = 0; i < NUM_ENVELOPES; i++)
7990   {
7991     int element = EL_ENVELOPE_1 + i;
7992
7993     chunk_size = SaveLevel_NOTE(NULL, level, element);
7994     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
7995     {
7996       putFileChunkBE(file, "NOTE", chunk_size);
7997       SaveLevel_NOTE(file, level, element);
7998     }
7999   }
8000
8001   // if not using template level, check for non-default custom/group elements
8002   if (!level->use_custom_template || save_as_template)
8003   {
8004     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8005     {
8006       int element = EL_CUSTOM_START + i;
8007
8008       chunk_size = SaveLevel_CUSX(NULL, level, element);
8009       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8010       {
8011         putFileChunkBE(file, "CUSX", chunk_size);
8012         SaveLevel_CUSX(file, level, element);
8013       }
8014     }
8015
8016     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8017     {
8018       int element = EL_GROUP_START + i;
8019
8020       chunk_size = SaveLevel_GRPX(NULL, level, element);
8021       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8022       {
8023         putFileChunkBE(file, "GRPX", chunk_size);
8024         SaveLevel_GRPX(file, level, element);
8025       }
8026     }
8027
8028     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8029     {
8030       int element = GET_EMPTY_ELEMENT(i);
8031
8032       chunk_size = SaveLevel_EMPX(NULL, level, element);
8033       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8034       {
8035         putFileChunkBE(file, "EMPX", chunk_size);
8036         SaveLevel_EMPX(file, level, element);
8037       }
8038     }
8039   }
8040
8041   fclose(file);
8042
8043   SetFilePermissions(filename, PERMS_PRIVATE);
8044 }
8045
8046 void SaveLevel(int nr)
8047 {
8048   char *filename = getDefaultLevelFilename(nr);
8049
8050   SaveLevelFromFilename(&level, filename, FALSE);
8051 }
8052
8053 void SaveLevelTemplate(void)
8054 {
8055   char *filename = getLocalLevelTemplateFilename();
8056
8057   SaveLevelFromFilename(&level, filename, TRUE);
8058 }
8059
8060 boolean SaveLevelChecked(int nr)
8061 {
8062   char *filename = getDefaultLevelFilename(nr);
8063   boolean new_level = !fileExists(filename);
8064   boolean level_saved = FALSE;
8065
8066   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8067   {
8068     SaveLevel(nr);
8069
8070     if (new_level)
8071       Request("Level saved!", REQ_CONFIRM);
8072
8073     level_saved = TRUE;
8074   }
8075
8076   return level_saved;
8077 }
8078
8079 void DumpLevel(struct LevelInfo *level)
8080 {
8081   if (level->no_level_file || level->no_valid_file)
8082   {
8083     Warn("cannot dump -- no valid level file found");
8084
8085     return;
8086   }
8087
8088   PrintLine("-", 79);
8089   Print("Level xxx (file version %08d, game version %08d)\n",
8090         level->file_version, level->game_version);
8091   PrintLine("-", 79);
8092
8093   Print("Level author: '%s'\n", level->author);
8094   Print("Level title:  '%s'\n", level->name);
8095   Print("\n");
8096   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8097   Print("\n");
8098   Print("Level time:  %d seconds\n", level->time);
8099   Print("Gems needed: %d\n", level->gems_needed);
8100   Print("\n");
8101   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8102   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8103   Print("Time for light:      %d seconds\n", level->time_light);
8104   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8105   Print("\n");
8106   Print("Amoeba speed: %d\n", level->amoeba_speed);
8107   Print("\n");
8108
8109   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8110   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8111   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8112   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8113   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8114   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8115
8116   if (options.debug)
8117   {
8118     int i, j;
8119
8120     for (i = 0; i < NUM_ENVELOPES; i++)
8121     {
8122       char *text = level->envelope[i].text;
8123       int text_len = strlen(text);
8124       boolean has_text = FALSE;
8125
8126       for (j = 0; j < text_len; j++)
8127         if (text[j] != ' ' && text[j] != '\n')
8128           has_text = TRUE;
8129
8130       if (has_text)
8131       {
8132         Print("\n");
8133         Print("Envelope %d:\n'%s'\n", i + 1, text);
8134       }
8135     }
8136   }
8137
8138   PrintLine("-", 79);
8139 }
8140
8141 void DumpLevels(void)
8142 {
8143   static LevelDirTree *dumplevel_leveldir = NULL;
8144
8145   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8146                                                  global.dumplevel_leveldir);
8147
8148   if (dumplevel_leveldir == NULL)
8149     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8150
8151   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8152       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8153     Fail("no such level number: %d", global.dumplevel_level_nr);
8154
8155   leveldir_current = dumplevel_leveldir;
8156
8157   LoadLevel(global.dumplevel_level_nr);
8158   DumpLevel(&level);
8159
8160   CloseAllAndExit(0);
8161 }
8162
8163
8164 // ============================================================================
8165 // tape file functions
8166 // ============================================================================
8167
8168 static void setTapeInfoToDefaults(void)
8169 {
8170   int i;
8171
8172   // always start with reliable default values (empty tape)
8173   TapeErase();
8174
8175   // default values (also for pre-1.2 tapes) with only the first player
8176   tape.player_participates[0] = TRUE;
8177   for (i = 1; i < MAX_PLAYERS; i++)
8178     tape.player_participates[i] = FALSE;
8179
8180   // at least one (default: the first) player participates in every tape
8181   tape.num_participating_players = 1;
8182
8183   tape.property_bits = TAPE_PROPERTY_NONE;
8184
8185   tape.level_nr = level_nr;
8186   tape.counter = 0;
8187   tape.changed = FALSE;
8188   tape.solved = FALSE;
8189
8190   tape.recording = FALSE;
8191   tape.playing = FALSE;
8192   tape.pausing = FALSE;
8193
8194   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8195   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8196
8197   tape.no_info_chunk = TRUE;
8198   tape.no_valid_file = FALSE;
8199 }
8200
8201 static int getTapePosSize(struct TapeInfo *tape)
8202 {
8203   int tape_pos_size = 0;
8204
8205   if (tape->use_key_actions)
8206     tape_pos_size += tape->num_participating_players;
8207
8208   if (tape->use_mouse_actions)
8209     tape_pos_size += 3;         // x and y position and mouse button mask
8210
8211   tape_pos_size += 1;           // tape action delay value
8212
8213   return tape_pos_size;
8214 }
8215
8216 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8217 {
8218   tape->use_key_actions = FALSE;
8219   tape->use_mouse_actions = FALSE;
8220
8221   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8222     tape->use_key_actions = TRUE;
8223
8224   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8225     tape->use_mouse_actions = TRUE;
8226 }
8227
8228 static int getTapeActionValue(struct TapeInfo *tape)
8229 {
8230   return (tape->use_key_actions &&
8231           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8232           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
8233           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8234           TAPE_ACTIONS_DEFAULT);
8235 }
8236
8237 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8238 {
8239   tape->file_version = getFileVersion(file);
8240   tape->game_version = getFileVersion(file);
8241
8242   return chunk_size;
8243 }
8244
8245 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8246 {
8247   int i;
8248
8249   tape->random_seed = getFile32BitBE(file);
8250   tape->date        = getFile32BitBE(file);
8251   tape->length      = getFile32BitBE(file);
8252
8253   // read header fields that are new since version 1.2
8254   if (tape->file_version >= FILE_VERSION_1_2)
8255   {
8256     byte store_participating_players = getFile8Bit(file);
8257     int engine_version;
8258
8259     // since version 1.2, tapes store which players participate in the tape
8260     tape->num_participating_players = 0;
8261     for (i = 0; i < MAX_PLAYERS; i++)
8262     {
8263       tape->player_participates[i] = FALSE;
8264
8265       if (store_participating_players & (1 << i))
8266       {
8267         tape->player_participates[i] = TRUE;
8268         tape->num_participating_players++;
8269       }
8270     }
8271
8272     setTapeActionFlags(tape, getFile8Bit(file));
8273
8274     tape->property_bits = getFile8Bit(file);
8275     tape->solved = getFile8Bit(file);
8276
8277     engine_version = getFileVersion(file);
8278     if (engine_version > 0)
8279       tape->engine_version = engine_version;
8280     else
8281       tape->engine_version = tape->game_version;
8282   }
8283
8284   return chunk_size;
8285 }
8286
8287 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8288 {
8289   tape->scr_fieldx = getFile8Bit(file);
8290   tape->scr_fieldy = getFile8Bit(file);
8291
8292   return chunk_size;
8293 }
8294
8295 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8296 {
8297   char *level_identifier = NULL;
8298   int level_identifier_size;
8299   int i;
8300
8301   tape->no_info_chunk = FALSE;
8302
8303   level_identifier_size = getFile16BitBE(file);
8304
8305   level_identifier = checked_malloc(level_identifier_size);
8306
8307   for (i = 0; i < level_identifier_size; i++)
8308     level_identifier[i] = getFile8Bit(file);
8309
8310   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8311   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8312
8313   checked_free(level_identifier);
8314
8315   tape->level_nr = getFile16BitBE(file);
8316
8317   chunk_size = 2 + level_identifier_size + 2;
8318
8319   return chunk_size;
8320 }
8321
8322 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8323 {
8324   int i, j;
8325   int tape_pos_size = getTapePosSize(tape);
8326   int chunk_size_expected = tape_pos_size * tape->length;
8327
8328   if (chunk_size_expected != chunk_size)
8329   {
8330     ReadUnusedBytesFromFile(file, chunk_size);
8331     return chunk_size_expected;
8332   }
8333
8334   for (i = 0; i < tape->length; i++)
8335   {
8336     if (i >= MAX_TAPE_LEN)
8337     {
8338       Warn("tape truncated -- size exceeds maximum tape size %d",
8339             MAX_TAPE_LEN);
8340
8341       // tape too large; read and ignore remaining tape data from this chunk
8342       for (;i < tape->length; i++)
8343         ReadUnusedBytesFromFile(file, tape_pos_size);
8344
8345       break;
8346     }
8347
8348     if (tape->use_key_actions)
8349     {
8350       for (j = 0; j < MAX_PLAYERS; j++)
8351       {
8352         tape->pos[i].action[j] = MV_NONE;
8353
8354         if (tape->player_participates[j])
8355           tape->pos[i].action[j] = getFile8Bit(file);
8356       }
8357     }
8358
8359     if (tape->use_mouse_actions)
8360     {
8361       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
8362       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
8363       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8364     }
8365
8366     tape->pos[i].delay = getFile8Bit(file);
8367
8368     if (tape->file_version == FILE_VERSION_1_0)
8369     {
8370       // eliminate possible diagonal moves in old tapes
8371       // this is only for backward compatibility
8372
8373       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8374       byte action = tape->pos[i].action[0];
8375       int k, num_moves = 0;
8376
8377       for (k = 0; k < 4; k++)
8378       {
8379         if (action & joy_dir[k])
8380         {
8381           tape->pos[i + num_moves].action[0] = joy_dir[k];
8382           if (num_moves > 0)
8383             tape->pos[i + num_moves].delay = 0;
8384           num_moves++;
8385         }
8386       }
8387
8388       if (num_moves > 1)
8389       {
8390         num_moves--;
8391         i += num_moves;
8392         tape->length += num_moves;
8393       }
8394     }
8395     else if (tape->file_version < FILE_VERSION_2_0)
8396     {
8397       // convert pre-2.0 tapes to new tape format
8398
8399       if (tape->pos[i].delay > 1)
8400       {
8401         // action part
8402         tape->pos[i + 1] = tape->pos[i];
8403         tape->pos[i + 1].delay = 1;
8404
8405         // delay part
8406         for (j = 0; j < MAX_PLAYERS; j++)
8407           tape->pos[i].action[j] = MV_NONE;
8408         tape->pos[i].delay--;
8409
8410         i++;
8411         tape->length++;
8412       }
8413     }
8414
8415     if (checkEndOfFile(file))
8416       break;
8417   }
8418
8419   if (i != tape->length)
8420     chunk_size = tape_pos_size * i;
8421
8422   return chunk_size;
8423 }
8424
8425 static void LoadTape_SokobanSolution(char *filename)
8426 {
8427   File *file;
8428   int move_delay = TILESIZE / level.initial_player_stepsize[0];
8429
8430   if (!(file = openFile(filename, MODE_READ)))
8431   {
8432     tape.no_valid_file = TRUE;
8433
8434     return;
8435   }
8436
8437   while (!checkEndOfFile(file))
8438   {
8439     unsigned char c = getByteFromFile(file);
8440
8441     if (checkEndOfFile(file))
8442       break;
8443
8444     switch (c)
8445     {
8446       case 'u':
8447       case 'U':
8448         tape.pos[tape.length].action[0] = MV_UP;
8449         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8450         tape.length++;
8451         break;
8452
8453       case 'd':
8454       case 'D':
8455         tape.pos[tape.length].action[0] = MV_DOWN;
8456         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8457         tape.length++;
8458         break;
8459
8460       case 'l':
8461       case 'L':
8462         tape.pos[tape.length].action[0] = MV_LEFT;
8463         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8464         tape.length++;
8465         break;
8466
8467       case 'r':
8468       case 'R':
8469         tape.pos[tape.length].action[0] = MV_RIGHT;
8470         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8471         tape.length++;
8472         break;
8473
8474       case '\n':
8475       case '\r':
8476       case '\t':
8477       case ' ':
8478         // ignore white-space characters
8479         break;
8480
8481       default:
8482         tape.no_valid_file = TRUE;
8483
8484         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8485
8486         break;
8487     }
8488   }
8489
8490   closeFile(file);
8491
8492   if (tape.no_valid_file)
8493     return;
8494
8495   tape.length_frames  = GetTapeLengthFrames();
8496   tape.length_seconds = GetTapeLengthSeconds();
8497 }
8498
8499 void LoadTapeFromFilename(char *filename)
8500 {
8501   char cookie[MAX_LINE_LEN];
8502   char chunk_name[CHUNK_ID_LEN + 1];
8503   File *file;
8504   int chunk_size;
8505
8506   // always start with reliable default values
8507   setTapeInfoToDefaults();
8508
8509   if (strSuffix(filename, ".sln"))
8510   {
8511     LoadTape_SokobanSolution(filename);
8512
8513     return;
8514   }
8515
8516   if (!(file = openFile(filename, MODE_READ)))
8517   {
8518     tape.no_valid_file = TRUE;
8519
8520     return;
8521   }
8522
8523   getFileChunkBE(file, chunk_name, NULL);
8524   if (strEqual(chunk_name, "RND1"))
8525   {
8526     getFile32BitBE(file);               // not used
8527
8528     getFileChunkBE(file, chunk_name, NULL);
8529     if (!strEqual(chunk_name, "TAPE"))
8530     {
8531       tape.no_valid_file = TRUE;
8532
8533       Warn("unknown format of tape file '%s'", filename);
8534
8535       closeFile(file);
8536
8537       return;
8538     }
8539   }
8540   else  // check for pre-2.0 file format with cookie string
8541   {
8542     strcpy(cookie, chunk_name);
8543     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8544       cookie[4] = '\0';
8545     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8546       cookie[strlen(cookie) - 1] = '\0';
8547
8548     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8549     {
8550       tape.no_valid_file = TRUE;
8551
8552       Warn("unknown format of tape file '%s'", filename);
8553
8554       closeFile(file);
8555
8556       return;
8557     }
8558
8559     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8560     {
8561       tape.no_valid_file = TRUE;
8562
8563       Warn("unsupported version of tape file '%s'", filename);
8564
8565       closeFile(file);
8566
8567       return;
8568     }
8569
8570     // pre-2.0 tape files have no game version, so use file version here
8571     tape.game_version = tape.file_version;
8572   }
8573
8574   if (tape.file_version < FILE_VERSION_1_2)
8575   {
8576     // tape files from versions before 1.2.0 without chunk structure
8577     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8578     LoadTape_BODY(file, 2 * tape.length,      &tape);
8579   }
8580   else
8581   {
8582     static struct
8583     {
8584       char *name;
8585       int size;
8586       int (*loader)(File *, int, struct TapeInfo *);
8587     }
8588     chunk_info[] =
8589     {
8590       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
8591       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
8592       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
8593       { "INFO", -1,                     LoadTape_INFO },
8594       { "BODY", -1,                     LoadTape_BODY },
8595       {  NULL,  0,                      NULL }
8596     };
8597
8598     while (getFileChunkBE(file, chunk_name, &chunk_size))
8599     {
8600       int i = 0;
8601
8602       while (chunk_info[i].name != NULL &&
8603              !strEqual(chunk_name, chunk_info[i].name))
8604         i++;
8605
8606       if (chunk_info[i].name == NULL)
8607       {
8608         Warn("unknown chunk '%s' in tape file '%s'",
8609               chunk_name, filename);
8610
8611         ReadUnusedBytesFromFile(file, chunk_size);
8612       }
8613       else if (chunk_info[i].size != -1 &&
8614                chunk_info[i].size != chunk_size)
8615       {
8616         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8617               chunk_size, chunk_name, filename);
8618
8619         ReadUnusedBytesFromFile(file, chunk_size);
8620       }
8621       else
8622       {
8623         // call function to load this tape chunk
8624         int chunk_size_expected =
8625           (chunk_info[i].loader)(file, chunk_size, &tape);
8626
8627         // the size of some chunks cannot be checked before reading other
8628         // chunks first (like "HEAD" and "BODY") that contain some header
8629         // information, so check them here
8630         if (chunk_size_expected != chunk_size)
8631         {
8632           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8633                 chunk_size, chunk_name, filename);
8634         }
8635       }
8636     }
8637   }
8638
8639   closeFile(file);
8640
8641   tape.length_frames  = GetTapeLengthFrames();
8642   tape.length_seconds = GetTapeLengthSeconds();
8643
8644 #if 0
8645   Debug("files:LoadTapeFromFilename", "tape file version: %d",
8646         tape.file_version);
8647   Debug("files:LoadTapeFromFilename", "tape game version: %d",
8648         tape.game_version);
8649   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8650         tape.engine_version);
8651 #endif
8652 }
8653
8654 void LoadTape(int nr)
8655 {
8656   char *filename = getTapeFilename(nr);
8657
8658   LoadTapeFromFilename(filename);
8659 }
8660
8661 void LoadSolutionTape(int nr)
8662 {
8663   char *filename = getSolutionTapeFilename(nr);
8664
8665   LoadTapeFromFilename(filename);
8666
8667   if (TAPE_IS_EMPTY(tape))
8668   {
8669     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8670         level.native_bd_level->replay != NULL)
8671       CopyNativeTape_BD_to_RND(&level);
8672     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8673         level.native_sp_level->demo.is_available)
8674       CopyNativeTape_SP_to_RND(&level);
8675   }
8676 }
8677
8678 void LoadScoreTape(char *score_tape_basename, int nr)
8679 {
8680   char *filename = getScoreTapeFilename(score_tape_basename, nr);
8681
8682   LoadTapeFromFilename(filename);
8683 }
8684
8685 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8686 {
8687   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8688
8689   LoadTapeFromFilename(filename);
8690 }
8691
8692 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8693 {
8694   // chunk required for team mode tapes with non-default screen size
8695   return (tape->num_participating_players > 1 &&
8696           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8697            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8698 }
8699
8700 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8701 {
8702   putFileVersion(file, tape->file_version);
8703   putFileVersion(file, tape->game_version);
8704 }
8705
8706 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8707 {
8708   int i;
8709   byte store_participating_players = 0;
8710
8711   // set bits for participating players for compact storage
8712   for (i = 0; i < MAX_PLAYERS; i++)
8713     if (tape->player_participates[i])
8714       store_participating_players |= (1 << i);
8715
8716   putFile32BitBE(file, tape->random_seed);
8717   putFile32BitBE(file, tape->date);
8718   putFile32BitBE(file, tape->length);
8719
8720   putFile8Bit(file, store_participating_players);
8721
8722   putFile8Bit(file, getTapeActionValue(tape));
8723
8724   putFile8Bit(file, tape->property_bits);
8725   putFile8Bit(file, tape->solved);
8726
8727   putFileVersion(file, tape->engine_version);
8728 }
8729
8730 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8731 {
8732   putFile8Bit(file, tape->scr_fieldx);
8733   putFile8Bit(file, tape->scr_fieldy);
8734 }
8735
8736 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8737 {
8738   int level_identifier_size = strlen(tape->level_identifier) + 1;
8739   int i;
8740
8741   putFile16BitBE(file, level_identifier_size);
8742
8743   for (i = 0; i < level_identifier_size; i++)
8744     putFile8Bit(file, tape->level_identifier[i]);
8745
8746   putFile16BitBE(file, tape->level_nr);
8747 }
8748
8749 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8750 {
8751   int i, j;
8752
8753   for (i = 0; i < tape->length; i++)
8754   {
8755     if (tape->use_key_actions)
8756     {
8757       for (j = 0; j < MAX_PLAYERS; j++)
8758         if (tape->player_participates[j])
8759           putFile8Bit(file, tape->pos[i].action[j]);
8760     }
8761
8762     if (tape->use_mouse_actions)
8763     {
8764       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8765       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8766       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8767     }
8768
8769     putFile8Bit(file, tape->pos[i].delay);
8770   }
8771 }
8772
8773 void SaveTapeToFilename(char *filename)
8774 {
8775   FILE *file;
8776   int tape_pos_size;
8777   int info_chunk_size;
8778   int body_chunk_size;
8779
8780   if (!(file = fopen(filename, MODE_WRITE)))
8781   {
8782     Warn("cannot save level recording file '%s'", filename);
8783
8784     return;
8785   }
8786
8787   tape_pos_size = getTapePosSize(&tape);
8788
8789   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8790   body_chunk_size = tape_pos_size * tape.length;
8791
8792   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8793   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8794
8795   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8796   SaveTape_VERS(file, &tape);
8797
8798   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8799   SaveTape_HEAD(file, &tape);
8800
8801   if (checkSaveTape_SCRN(&tape))
8802   {
8803     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8804     SaveTape_SCRN(file, &tape);
8805   }
8806
8807   putFileChunkBE(file, "INFO", info_chunk_size);
8808   SaveTape_INFO(file, &tape);
8809
8810   putFileChunkBE(file, "BODY", body_chunk_size);
8811   SaveTape_BODY(file, &tape);
8812
8813   fclose(file);
8814
8815   SetFilePermissions(filename, PERMS_PRIVATE);
8816 }
8817
8818 static void SaveTapeExt(char *filename)
8819 {
8820   int i;
8821
8822   tape.file_version = FILE_VERSION_ACTUAL;
8823   tape.game_version = GAME_VERSION_ACTUAL;
8824
8825   tape.num_participating_players = 0;
8826
8827   // count number of participating players
8828   for (i = 0; i < MAX_PLAYERS; i++)
8829     if (tape.player_participates[i])
8830       tape.num_participating_players++;
8831
8832   SaveTapeToFilename(filename);
8833
8834   tape.changed = FALSE;
8835 }
8836
8837 void SaveTape(int nr)
8838 {
8839   char *filename = getTapeFilename(nr);
8840
8841   InitTapeDirectory(leveldir_current->subdir);
8842
8843   SaveTapeExt(filename);
8844 }
8845
8846 void SaveScoreTape(int nr)
8847 {
8848   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8849
8850   // used instead of "leveldir_current->subdir" (for network games)
8851   InitScoreTapeDirectory(levelset.identifier, nr);
8852
8853   SaveTapeExt(filename);
8854 }
8855
8856 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8857                                   unsigned int req_state_added)
8858 {
8859   char *filename = getTapeFilename(nr);
8860   boolean new_tape = !fileExists(filename);
8861   boolean tape_saved = FALSE;
8862
8863   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8864   {
8865     SaveTape(nr);
8866
8867     if (new_tape)
8868       Request(msg_saved, REQ_CONFIRM | req_state_added);
8869
8870     tape_saved = TRUE;
8871   }
8872
8873   return tape_saved;
8874 }
8875
8876 boolean SaveTapeChecked(int nr)
8877 {
8878   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8879 }
8880
8881 boolean SaveTapeChecked_LevelSolved(int nr)
8882 {
8883   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8884                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
8885 }
8886
8887 void DumpTape(struct TapeInfo *tape)
8888 {
8889   int tape_frame_counter;
8890   int i, j;
8891
8892   if (tape->no_valid_file)
8893   {
8894     Warn("cannot dump -- no valid tape file found");
8895
8896     return;
8897   }
8898
8899   PrintLine("-", 79);
8900
8901   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8902         tape->level_nr, tape->file_version, tape->game_version);
8903   Print("                  (effective engine version %08d)\n",
8904         tape->engine_version);
8905   Print("Level series identifier: '%s'\n", tape->level_identifier);
8906
8907   Print("Solution tape: %s\n",
8908         tape->solved ? "yes" :
8909         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8910
8911   Print("Special tape properties: ");
8912   if (tape->property_bits == TAPE_PROPERTY_NONE)
8913     Print("[none]");
8914   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8915     Print("[em_random_bug]");
8916   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8917     Print("[game_speed]");
8918   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8919     Print("[pause]");
8920   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8921     Print("[single_step]");
8922   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8923     Print("[snapshot]");
8924   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8925     Print("[replayed]");
8926   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8927     Print("[tas_keys]");
8928   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8929     Print("[small_graphics]");
8930   Print("\n");
8931
8932   int year2 = tape->date / 10000;
8933   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8934   int month_index_raw = (tape->date / 100) % 100;
8935   int month_index = month_index_raw % 12;       // prevent invalid index
8936   int month = month_index + 1;
8937   int day = tape->date % 100;
8938
8939   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8940
8941   PrintLine("-", 79);
8942
8943   tape_frame_counter = 0;
8944
8945   for (i = 0; i < tape->length; i++)
8946   {
8947     if (i >= MAX_TAPE_LEN)
8948       break;
8949
8950     Print("%04d: ", i);
8951
8952     for (j = 0; j < MAX_PLAYERS; j++)
8953     {
8954       if (tape->player_participates[j])
8955       {
8956         int action = tape->pos[i].action[j];
8957
8958         Print("%d:%02x ", j, action);
8959         Print("[%c%c%c%c|%c%c] - ",
8960               (action & JOY_LEFT ? '<' : ' '),
8961               (action & JOY_RIGHT ? '>' : ' '),
8962               (action & JOY_UP ? '^' : ' '),
8963               (action & JOY_DOWN ? 'v' : ' '),
8964               (action & JOY_BUTTON_1 ? '1' : ' '),
8965               (action & JOY_BUTTON_2 ? '2' : ' '));
8966       }
8967     }
8968
8969     Print("(%03d) ", tape->pos[i].delay);
8970     Print("[%05d]\n", tape_frame_counter);
8971
8972     tape_frame_counter += tape->pos[i].delay;
8973   }
8974
8975   PrintLine("-", 79);
8976 }
8977
8978 void DumpTapes(void)
8979 {
8980   static LevelDirTree *dumptape_leveldir = NULL;
8981
8982   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8983                                                 global.dumptape_leveldir);
8984
8985   if (dumptape_leveldir == NULL)
8986     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8987
8988   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8989       global.dumptape_level_nr > dumptape_leveldir->last_level)
8990     Fail("no such level number: %d", global.dumptape_level_nr);
8991
8992   leveldir_current = dumptape_leveldir;
8993
8994   if (options.mytapes)
8995     LoadTape(global.dumptape_level_nr);
8996   else
8997     LoadSolutionTape(global.dumptape_level_nr);
8998
8999   DumpTape(&tape);
9000
9001   CloseAllAndExit(0);
9002 }
9003
9004
9005 // ============================================================================
9006 // score file functions
9007 // ============================================================================
9008
9009 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9010 {
9011   int i;
9012
9013   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9014   {
9015     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9016     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9017     scores->entry[i].score = 0;
9018     scores->entry[i].time = 0;
9019
9020     scores->entry[i].id = -1;
9021     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9022     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9023     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9024     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9025     strcpy(scores->entry[i].country_code, "??");
9026   }
9027
9028   scores->num_entries = 0;
9029   scores->last_added = -1;
9030   scores->last_added_local = -1;
9031
9032   scores->updated = FALSE;
9033   scores->uploaded = FALSE;
9034   scores->tape_downloaded = FALSE;
9035   scores->force_last_added = FALSE;
9036
9037   // The following values are intentionally not reset here:
9038   // - last_level_nr
9039   // - last_entry_nr
9040   // - next_level_nr
9041   // - continue_playing
9042   // - continue_on_return
9043 }
9044
9045 static void setScoreInfoToDefaults(void)
9046 {
9047   setScoreInfoToDefaultsExt(&scores);
9048 }
9049
9050 static void setServerScoreInfoToDefaults(void)
9051 {
9052   setScoreInfoToDefaultsExt(&server_scores);
9053 }
9054
9055 static void LoadScore_OLD(int nr)
9056 {
9057   int i;
9058   char *filename = getScoreFilename(nr);
9059   char cookie[MAX_LINE_LEN];
9060   char line[MAX_LINE_LEN];
9061   char *line_ptr;
9062   FILE *file;
9063
9064   if (!(file = fopen(filename, MODE_READ)))
9065     return;
9066
9067   // check file identifier
9068   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9069     cookie[0] = '\0';
9070   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9071     cookie[strlen(cookie) - 1] = '\0';
9072
9073   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9074   {
9075     Warn("unknown format of score file '%s'", filename);
9076
9077     fclose(file);
9078
9079     return;
9080   }
9081
9082   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9083   {
9084     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9085       Warn("fscanf() failed; %s", strerror(errno));
9086
9087     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9088       line[0] = '\0';
9089
9090     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9091       line[strlen(line) - 1] = '\0';
9092
9093     for (line_ptr = line; *line_ptr; line_ptr++)
9094     {
9095       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9096       {
9097         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9098         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9099         break;
9100       }
9101     }
9102   }
9103
9104   fclose(file);
9105 }
9106
9107 static void ConvertScore_OLD(void)
9108 {
9109   // only convert score to time for levels that rate playing time over score
9110   if (!level.rate_time_over_score)
9111     return;
9112
9113   // convert old score to playing time for score-less levels (like Supaplex)
9114   int time_final_max = 999;
9115   int i;
9116
9117   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9118   {
9119     int score = scores.entry[i].score;
9120
9121     if (score > 0 && score < time_final_max)
9122       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9123   }
9124 }
9125
9126 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9127 {
9128   scores->file_version = getFileVersion(file);
9129   scores->game_version = getFileVersion(file);
9130
9131   return chunk_size;
9132 }
9133
9134 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9135 {
9136   char *level_identifier = NULL;
9137   int level_identifier_size;
9138   int i;
9139
9140   level_identifier_size = getFile16BitBE(file);
9141
9142   level_identifier = checked_malloc(level_identifier_size);
9143
9144   for (i = 0; i < level_identifier_size; i++)
9145     level_identifier[i] = getFile8Bit(file);
9146
9147   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9148   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9149
9150   checked_free(level_identifier);
9151
9152   scores->level_nr = getFile16BitBE(file);
9153   scores->num_entries = getFile16BitBE(file);
9154
9155   chunk_size = 2 + level_identifier_size + 2 + 2;
9156
9157   return chunk_size;
9158 }
9159
9160 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9161 {
9162   int i, j;
9163
9164   for (i = 0; i < scores->num_entries; i++)
9165   {
9166     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9167       scores->entry[i].name[j] = getFile8Bit(file);
9168
9169     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9170   }
9171
9172   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9173
9174   return chunk_size;
9175 }
9176
9177 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9178 {
9179   int i;
9180
9181   for (i = 0; i < scores->num_entries; i++)
9182     scores->entry[i].score = getFile16BitBE(file);
9183
9184   chunk_size = scores->num_entries * 2;
9185
9186   return chunk_size;
9187 }
9188
9189 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9190 {
9191   int i;
9192
9193   for (i = 0; i < scores->num_entries; i++)
9194     scores->entry[i].score = getFile32BitBE(file);
9195
9196   chunk_size = scores->num_entries * 4;
9197
9198   return chunk_size;
9199 }
9200
9201 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9202 {
9203   int i;
9204
9205   for (i = 0; i < scores->num_entries; i++)
9206     scores->entry[i].time = getFile32BitBE(file);
9207
9208   chunk_size = scores->num_entries * 4;
9209
9210   return chunk_size;
9211 }
9212
9213 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9214 {
9215   int i, j;
9216
9217   for (i = 0; i < scores->num_entries; i++)
9218   {
9219     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9220       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9221
9222     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9223   }
9224
9225   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9226
9227   return chunk_size;
9228 }
9229
9230 void LoadScore(int nr)
9231 {
9232   char *filename = getScoreFilename(nr);
9233   char cookie[MAX_LINE_LEN];
9234   char chunk_name[CHUNK_ID_LEN + 1];
9235   int chunk_size;
9236   boolean old_score_file_format = FALSE;
9237   File *file;
9238
9239   // always start with reliable default values
9240   setScoreInfoToDefaults();
9241
9242   if (!(file = openFile(filename, MODE_READ)))
9243     return;
9244
9245   getFileChunkBE(file, chunk_name, NULL);
9246   if (strEqual(chunk_name, "RND1"))
9247   {
9248     getFile32BitBE(file);               // not used
9249
9250     getFileChunkBE(file, chunk_name, NULL);
9251     if (!strEqual(chunk_name, "SCOR"))
9252     {
9253       Warn("unknown format of score file '%s'", filename);
9254
9255       closeFile(file);
9256
9257       return;
9258     }
9259   }
9260   else  // check for old file format with cookie string
9261   {
9262     strcpy(cookie, chunk_name);
9263     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9264       cookie[4] = '\0';
9265     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9266       cookie[strlen(cookie) - 1] = '\0';
9267
9268     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9269     {
9270       Warn("unknown format of score file '%s'", filename);
9271
9272       closeFile(file);
9273
9274       return;
9275     }
9276
9277     old_score_file_format = TRUE;
9278   }
9279
9280   if (old_score_file_format)
9281   {
9282     // score files from versions before 4.2.4.0 without chunk structure
9283     LoadScore_OLD(nr);
9284
9285     // convert score to time, if possible (mainly for Supaplex levels)
9286     ConvertScore_OLD();
9287   }
9288   else
9289   {
9290     static struct
9291     {
9292       char *name;
9293       int size;
9294       int (*loader)(File *, int, struct ScoreInfo *);
9295     }
9296     chunk_info[] =
9297     {
9298       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
9299       { "INFO", -1,                     LoadScore_INFO },
9300       { "NAME", -1,                     LoadScore_NAME },
9301       { "SCOR", -1,                     LoadScore_SCOR },
9302       { "SC4R", -1,                     LoadScore_SC4R },
9303       { "TIME", -1,                     LoadScore_TIME },
9304       { "TAPE", -1,                     LoadScore_TAPE },
9305
9306       {  NULL,  0,                      NULL }
9307     };
9308
9309     while (getFileChunkBE(file, chunk_name, &chunk_size))
9310     {
9311       int i = 0;
9312
9313       while (chunk_info[i].name != NULL &&
9314              !strEqual(chunk_name, chunk_info[i].name))
9315         i++;
9316
9317       if (chunk_info[i].name == NULL)
9318       {
9319         Warn("unknown chunk '%s' in score file '%s'",
9320               chunk_name, filename);
9321
9322         ReadUnusedBytesFromFile(file, chunk_size);
9323       }
9324       else if (chunk_info[i].size != -1 &&
9325                chunk_info[i].size != chunk_size)
9326       {
9327         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9328               chunk_size, chunk_name, filename);
9329
9330         ReadUnusedBytesFromFile(file, chunk_size);
9331       }
9332       else
9333       {
9334         // call function to load this score chunk
9335         int chunk_size_expected =
9336           (chunk_info[i].loader)(file, chunk_size, &scores);
9337
9338         // the size of some chunks cannot be checked before reading other
9339         // chunks first (like "HEAD" and "BODY") that contain some header
9340         // information, so check them here
9341         if (chunk_size_expected != chunk_size)
9342         {
9343           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9344                 chunk_size, chunk_name, filename);
9345         }
9346       }
9347     }
9348   }
9349
9350   closeFile(file);
9351 }
9352
9353 #if ENABLE_HISTORIC_CHUNKS
9354 void SaveScore_OLD(int nr)
9355 {
9356   int i;
9357   char *filename = getScoreFilename(nr);
9358   FILE *file;
9359
9360   // used instead of "leveldir_current->subdir" (for network games)
9361   InitScoreDirectory(levelset.identifier);
9362
9363   if (!(file = fopen(filename, MODE_WRITE)))
9364   {
9365     Warn("cannot save score for level %d", nr);
9366
9367     return;
9368   }
9369
9370   fprintf(file, "%s\n\n", SCORE_COOKIE);
9371
9372   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9373     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9374
9375   fclose(file);
9376
9377   SetFilePermissions(filename, PERMS_PRIVATE);
9378 }
9379 #endif
9380
9381 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9382 {
9383   putFileVersion(file, scores->file_version);
9384   putFileVersion(file, scores->game_version);
9385 }
9386
9387 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9388 {
9389   int level_identifier_size = strlen(scores->level_identifier) + 1;
9390   int i;
9391
9392   putFile16BitBE(file, level_identifier_size);
9393
9394   for (i = 0; i < level_identifier_size; i++)
9395     putFile8Bit(file, scores->level_identifier[i]);
9396
9397   putFile16BitBE(file, scores->level_nr);
9398   putFile16BitBE(file, scores->num_entries);
9399 }
9400
9401 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9402 {
9403   int i, j;
9404
9405   for (i = 0; i < scores->num_entries; i++)
9406   {
9407     int name_size = strlen(scores->entry[i].name);
9408
9409     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9410       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9411   }
9412 }
9413
9414 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9415 {
9416   int i;
9417
9418   for (i = 0; i < scores->num_entries; i++)
9419     putFile16BitBE(file, scores->entry[i].score);
9420 }
9421
9422 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9423 {
9424   int i;
9425
9426   for (i = 0; i < scores->num_entries; i++)
9427     putFile32BitBE(file, scores->entry[i].score);
9428 }
9429
9430 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9431 {
9432   int i;
9433
9434   for (i = 0; i < scores->num_entries; i++)
9435     putFile32BitBE(file, scores->entry[i].time);
9436 }
9437
9438 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9439 {
9440   int i, j;
9441
9442   for (i = 0; i < scores->num_entries; i++)
9443   {
9444     int size = strlen(scores->entry[i].tape_basename);
9445
9446     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9447       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9448   }
9449 }
9450
9451 static void SaveScoreToFilename(char *filename)
9452 {
9453   FILE *file;
9454   int info_chunk_size;
9455   int name_chunk_size;
9456   int scor_chunk_size;
9457   int sc4r_chunk_size;
9458   int time_chunk_size;
9459   int tape_chunk_size;
9460   boolean has_large_score_values;
9461   int i;
9462
9463   if (!(file = fopen(filename, MODE_WRITE)))
9464   {
9465     Warn("cannot save score file '%s'", filename);
9466
9467     return;
9468   }
9469
9470   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9471   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9472   scor_chunk_size = scores.num_entries * 2;
9473   sc4r_chunk_size = scores.num_entries * 4;
9474   time_chunk_size = scores.num_entries * 4;
9475   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9476
9477   has_large_score_values = FALSE;
9478   for (i = 0; i < scores.num_entries; i++)
9479     if (scores.entry[i].score > 0xffff)
9480       has_large_score_values = TRUE;
9481
9482   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9483   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9484
9485   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9486   SaveScore_VERS(file, &scores);
9487
9488   putFileChunkBE(file, "INFO", info_chunk_size);
9489   SaveScore_INFO(file, &scores);
9490
9491   putFileChunkBE(file, "NAME", name_chunk_size);
9492   SaveScore_NAME(file, &scores);
9493
9494   if (has_large_score_values)
9495   {
9496     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9497     SaveScore_SC4R(file, &scores);
9498   }
9499   else
9500   {
9501     putFileChunkBE(file, "SCOR", scor_chunk_size);
9502     SaveScore_SCOR(file, &scores);
9503   }
9504
9505   putFileChunkBE(file, "TIME", time_chunk_size);
9506   SaveScore_TIME(file, &scores);
9507
9508   putFileChunkBE(file, "TAPE", tape_chunk_size);
9509   SaveScore_TAPE(file, &scores);
9510
9511   fclose(file);
9512
9513   SetFilePermissions(filename, PERMS_PRIVATE);
9514 }
9515
9516 void SaveScore(int nr)
9517 {
9518   char *filename = getScoreFilename(nr);
9519   int i;
9520
9521   // used instead of "leveldir_current->subdir" (for network games)
9522   InitScoreDirectory(levelset.identifier);
9523
9524   scores.file_version = FILE_VERSION_ACTUAL;
9525   scores.game_version = GAME_VERSION_ACTUAL;
9526
9527   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9528   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9529   scores.level_nr = level_nr;
9530
9531   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9532     if (scores.entry[i].score == 0 &&
9533         scores.entry[i].time == 0 &&
9534         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9535       break;
9536
9537   scores.num_entries = i;
9538
9539   if (scores.num_entries == 0)
9540     return;
9541
9542   SaveScoreToFilename(filename);
9543 }
9544
9545 static void LoadServerScoreFromCache(int nr)
9546 {
9547   struct ScoreEntry score_entry;
9548   struct
9549   {
9550     void *value;
9551     boolean is_string;
9552     int string_size;
9553   }
9554   score_mapping[] =
9555   {
9556     { &score_entry.score,               FALSE,  0                       },
9557     { &score_entry.time,                FALSE,  0                       },
9558     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
9559     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
9560     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
9561     { &score_entry.id,                  FALSE,  0                       },
9562     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
9563     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
9564     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
9565     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
9566
9567     { NULL,                             FALSE,  0                       }
9568   };
9569   char *filename = getScoreCacheFilename(nr);
9570   SetupFileHash *score_hash = loadSetupFileHash(filename);
9571   int i, j;
9572
9573   server_scores.num_entries = 0;
9574
9575   if (score_hash == NULL)
9576     return;
9577
9578   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9579   {
9580     score_entry = server_scores.entry[i];
9581
9582     for (j = 0; score_mapping[j].value != NULL; j++)
9583     {
9584       char token[10];
9585
9586       sprintf(token, "%02d.%d", i, j);
9587
9588       char *value = getHashEntry(score_hash, token);
9589
9590       if (value == NULL)
9591         continue;
9592
9593       if (score_mapping[j].is_string)
9594       {
9595         char *score_value = (char *)score_mapping[j].value;
9596         int value_size = score_mapping[j].string_size;
9597
9598         strncpy(score_value, value, value_size);
9599         score_value[value_size] = '\0';
9600       }
9601       else
9602       {
9603         int *score_value = (int *)score_mapping[j].value;
9604
9605         *score_value = atoi(value);
9606       }
9607
9608       server_scores.num_entries = i + 1;
9609     }
9610
9611     server_scores.entry[i] = score_entry;
9612   }
9613
9614   freeSetupFileHash(score_hash);
9615 }
9616
9617 void LoadServerScore(int nr, boolean download_score)
9618 {
9619   if (!setup.use_api_server)
9620     return;
9621
9622   // always start with reliable default values
9623   setServerScoreInfoToDefaults();
9624
9625   // 1st step: load server scores from cache file (which may not exist)
9626   // (this should prevent reading it while the thread is writing to it)
9627   LoadServerScoreFromCache(nr);
9628
9629   if (download_score && runtime.use_api_server)
9630   {
9631     // 2nd step: download server scores from score server to cache file
9632     // (as thread, as it might time out if the server is not reachable)
9633     ApiGetScoreAsThread(nr);
9634   }
9635 }
9636
9637 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9638 {
9639   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9640
9641   // if score tape not uploaded, ask for uploading missing tapes later
9642   if (!setup.has_remaining_tapes)
9643     setup.ask_for_remaining_tapes = TRUE;
9644
9645   setup.provide_uploading_tapes = TRUE;
9646   setup.has_remaining_tapes = TRUE;
9647
9648   SaveSetup_ServerSetup();
9649 }
9650
9651 void SaveServerScore(int nr, boolean tape_saved)
9652 {
9653   if (!runtime.use_api_server)
9654   {
9655     PrepareScoreTapesForUpload(leveldir_current->subdir);
9656
9657     return;
9658   }
9659
9660   ApiAddScoreAsThread(nr, tape_saved, NULL);
9661 }
9662
9663 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9664                              char *score_tape_filename)
9665 {
9666   if (!runtime.use_api_server)
9667     return;
9668
9669   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9670 }
9671
9672 void LoadLocalAndServerScore(int nr, boolean download_score)
9673 {
9674   int last_added_local = scores.last_added_local;
9675   boolean force_last_added = scores.force_last_added;
9676
9677   // needed if only showing server scores
9678   setScoreInfoToDefaults();
9679
9680   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9681     LoadScore(nr);
9682
9683   // restore last added local score entry (before merging server scores)
9684   scores.last_added = scores.last_added_local = last_added_local;
9685
9686   if (setup.use_api_server &&
9687       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9688   {
9689     // load server scores from cache file and trigger update from server
9690     LoadServerScore(nr, download_score);
9691
9692     // merge local scores with scores from server
9693     MergeServerScore();
9694   }
9695
9696   if (force_last_added)
9697     scores.force_last_added = force_last_added;
9698 }
9699
9700
9701 // ============================================================================
9702 // setup file functions
9703 // ============================================================================
9704
9705 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
9706
9707
9708 static struct TokenInfo global_setup_tokens[] =
9709 {
9710   {
9711     TYPE_STRING,
9712     &setup.player_name,                         "player_name"
9713   },
9714   {
9715     TYPE_SWITCH,
9716     &setup.multiple_users,                      "multiple_users"
9717   },
9718   {
9719     TYPE_SWITCH,
9720     &setup.sound,                               "sound"
9721   },
9722   {
9723     TYPE_SWITCH,
9724     &setup.sound_loops,                         "repeating_sound_loops"
9725   },
9726   {
9727     TYPE_SWITCH,
9728     &setup.sound_music,                         "background_music"
9729   },
9730   {
9731     TYPE_SWITCH,
9732     &setup.sound_simple,                        "simple_sound_effects"
9733   },
9734   {
9735     TYPE_SWITCH,
9736     &setup.toons,                               "toons"
9737   },
9738   {
9739     TYPE_SWITCH,
9740     &setup.global_animations,                   "global_animations"
9741   },
9742   {
9743     TYPE_SWITCH,
9744     &setup.scroll_delay,                        "scroll_delay"
9745   },
9746   {
9747     TYPE_SWITCH,
9748     &setup.forced_scroll_delay,                 "forced_scroll_delay"
9749   },
9750   {
9751     TYPE_INTEGER,
9752     &setup.scroll_delay_value,                  "scroll_delay_value"
9753   },
9754   {
9755     TYPE_STRING,
9756     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
9757   },
9758   {
9759     TYPE_INTEGER,
9760     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
9761   },
9762   {
9763     TYPE_SWITCH,
9764     &setup.fade_screens,                        "fade_screens"
9765   },
9766   {
9767     TYPE_SWITCH,
9768     &setup.autorecord,                          "automatic_tape_recording"
9769   },
9770   {
9771     TYPE_SWITCH,
9772     &setup.autorecord_after_replay,             "autorecord_after_replay"
9773   },
9774   {
9775     TYPE_SWITCH,
9776     &setup.auto_pause_on_start,                 "auto_pause_on_start"
9777   },
9778   {
9779     TYPE_SWITCH,
9780     &setup.show_titlescreen,                    "show_titlescreen"
9781   },
9782   {
9783     TYPE_SWITCH,
9784     &setup.quick_doors,                         "quick_doors"
9785   },
9786   {
9787     TYPE_SWITCH,
9788     &setup.team_mode,                           "team_mode"
9789   },
9790   {
9791     TYPE_SWITCH,
9792     &setup.handicap,                            "handicap"
9793   },
9794   {
9795     TYPE_SWITCH,
9796     &setup.skip_levels,                         "skip_levels"
9797   },
9798   {
9799     TYPE_SWITCH,
9800     &setup.increment_levels,                    "increment_levels"
9801   },
9802   {
9803     TYPE_SWITCH,
9804     &setup.auto_play_next_level,                "auto_play_next_level"
9805   },
9806   {
9807     TYPE_SWITCH,
9808     &setup.count_score_after_game,              "count_score_after_game"
9809   },
9810   {
9811     TYPE_SWITCH,
9812     &setup.show_scores_after_game,              "show_scores_after_game"
9813   },
9814   {
9815     TYPE_SWITCH,
9816     &setup.time_limit,                          "time_limit"
9817   },
9818   {
9819     TYPE_SWITCH,
9820     &setup.fullscreen,                          "fullscreen"
9821   },
9822   {
9823     TYPE_INTEGER,
9824     &setup.window_scaling_percent,              "window_scaling_percent"
9825   },
9826   {
9827     TYPE_STRING,
9828     &setup.window_scaling_quality,              "window_scaling_quality"
9829   },
9830   {
9831     TYPE_STRING,
9832     &setup.screen_rendering_mode,               "screen_rendering_mode"
9833   },
9834   {
9835     TYPE_STRING,
9836     &setup.vsync_mode,                          "vsync_mode"
9837   },
9838   {
9839     TYPE_SWITCH,
9840     &setup.ask_on_escape,                       "ask_on_escape"
9841   },
9842   {
9843     TYPE_SWITCH,
9844     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
9845   },
9846   {
9847     TYPE_SWITCH,
9848     &setup.ask_on_game_over,                    "ask_on_game_over"
9849   },
9850   {
9851     TYPE_SWITCH,
9852     &setup.ask_on_quit_game,                    "ask_on_quit_game"
9853   },
9854   {
9855     TYPE_SWITCH,
9856     &setup.ask_on_quit_program,                 "ask_on_quit_program"
9857   },
9858   {
9859     TYPE_SWITCH,
9860     &setup.quick_switch,                        "quick_player_switch"
9861   },
9862   {
9863     TYPE_SWITCH,
9864     &setup.input_on_focus,                      "input_on_focus"
9865   },
9866   {
9867     TYPE_SWITCH,
9868     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
9869   },
9870   {
9871     TYPE_SWITCH,
9872     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
9873   },
9874   {
9875     TYPE_SWITCH,
9876     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
9877   },
9878   {
9879     TYPE_SWITCH,
9880     &setup.game_speed_extended,                 "game_speed_extended"
9881   },
9882   {
9883     TYPE_INTEGER,
9884     &setup.game_frame_delay,                    "game_frame_delay"
9885   },
9886   {
9887     TYPE_SWITCH,
9888     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
9889   },
9890   {
9891     TYPE_SWITCH,
9892     &setup.bd_skip_hatching,                    "bd_skip_hatching"
9893   },
9894   {
9895     TYPE_SWITCH,
9896     &setup.bd_scroll_delay,                     "bd_scroll_delay"
9897   },
9898   {
9899     TYPE_SWITCH3,
9900     &setup.bd_smooth_movements,                 "bd_smooth_movements"
9901   },
9902   {
9903     TYPE_SWITCH,
9904     &setup.sp_show_border_elements,             "sp_show_border_elements"
9905   },
9906   {
9907     TYPE_SWITCH,
9908     &setup.small_game_graphics,                 "small_game_graphics"
9909   },
9910   {
9911     TYPE_SWITCH,
9912     &setup.show_load_save_buttons,              "show_load_save_buttons"
9913   },
9914   {
9915     TYPE_SWITCH,
9916     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
9917   },
9918   {
9919     TYPE_STRING,
9920     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
9921   },
9922   {
9923     TYPE_STRING,
9924     &setup.graphics_set,                        "graphics_set"
9925   },
9926   {
9927     TYPE_STRING,
9928     &setup.sounds_set,                          "sounds_set"
9929   },
9930   {
9931     TYPE_STRING,
9932     &setup.music_set,                           "music_set"
9933   },
9934   {
9935     TYPE_SWITCH3,
9936     &setup.override_level_graphics,             "override_level_graphics"
9937   },
9938   {
9939     TYPE_SWITCH3,
9940     &setup.override_level_sounds,               "override_level_sounds"
9941   },
9942   {
9943     TYPE_SWITCH3,
9944     &setup.override_level_music,                "override_level_music"
9945   },
9946   {
9947     TYPE_INTEGER,
9948     &setup.volume_simple,                       "volume_simple"
9949   },
9950   {
9951     TYPE_INTEGER,
9952     &setup.volume_loops,                        "volume_loops"
9953   },
9954   {
9955     TYPE_INTEGER,
9956     &setup.volume_music,                        "volume_music"
9957   },
9958   {
9959     TYPE_SWITCH,
9960     &setup.network_mode,                        "network_mode"
9961   },
9962   {
9963     TYPE_PLAYER,
9964     &setup.network_player_nr,                   "network_player"
9965   },
9966   {
9967     TYPE_STRING,
9968     &setup.network_server_hostname,             "network_server_hostname"
9969   },
9970   {
9971     TYPE_STRING,
9972     &setup.touch.control_type,                  "touch.control_type"
9973   },
9974   {
9975     TYPE_INTEGER,
9976     &setup.touch.move_distance,                 "touch.move_distance"
9977   },
9978   {
9979     TYPE_INTEGER,
9980     &setup.touch.drop_distance,                 "touch.drop_distance"
9981   },
9982   {
9983     TYPE_INTEGER,
9984     &setup.touch.transparency,                  "touch.transparency"
9985   },
9986   {
9987     TYPE_INTEGER,
9988     &setup.touch.draw_outlined,                 "touch.draw_outlined"
9989   },
9990   {
9991     TYPE_INTEGER,
9992     &setup.touch.draw_pressed,                  "touch.draw_pressed"
9993   },
9994   {
9995     TYPE_INTEGER,
9996     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
9997   },
9998   {
9999     TYPE_INTEGER,
10000     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10001   },
10002   {
10003     TYPE_INTEGER,
10004     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10005   },
10006   {
10007     TYPE_INTEGER,
10008     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10009   },
10010   {
10011     TYPE_SWITCH,
10012     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10013   },
10014 };
10015
10016 static struct TokenInfo auto_setup_tokens[] =
10017 {
10018   {
10019     TYPE_INTEGER,
10020     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10021   },
10022 };
10023
10024 static struct TokenInfo server_setup_tokens[] =
10025 {
10026   {
10027     TYPE_STRING,
10028     &setup.player_uuid,                         "player_uuid"
10029   },
10030   {
10031     TYPE_INTEGER,
10032     &setup.player_version,                      "player_version"
10033   },
10034   {
10035     TYPE_SWITCH,
10036     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10037   },
10038   {
10039     TYPE_STRING,
10040     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10041   },
10042   {
10043     TYPE_STRING,
10044     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10045   },
10046   {
10047     TYPE_SWITCH,
10048     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10049   },
10050   {
10051     TYPE_SWITCH,
10052     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10053   },
10054   {
10055     TYPE_SWITCH,
10056     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10057   },
10058   {
10059     TYPE_SWITCH,
10060     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10061   },
10062   {
10063     TYPE_SWITCH,
10064     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10065   },
10066 };
10067
10068 static struct TokenInfo editor_setup_tokens[] =
10069 {
10070   {
10071     TYPE_SWITCH,
10072     &setup.editor.el_classic,                   "editor.el_classic"
10073   },
10074   {
10075     TYPE_SWITCH,
10076     &setup.editor.el_custom,                    "editor.el_custom"
10077   },
10078   {
10079     TYPE_SWITCH,
10080     &setup.editor.el_user_defined,              "editor.el_user_defined"
10081   },
10082   {
10083     TYPE_SWITCH,
10084     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10085   },
10086   {
10087     TYPE_SWITCH,
10088     &setup.editor.el_headlines,                 "editor.el_headlines"
10089   },
10090   {
10091     TYPE_SWITCH,
10092     &setup.editor.show_element_token,           "editor.show_element_token"
10093   },
10094   {
10095     TYPE_SWITCH,
10096     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10097   },
10098 };
10099
10100 static struct TokenInfo editor_cascade_setup_tokens[] =
10101 {
10102   {
10103     TYPE_SWITCH,
10104     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10105   },
10106   {
10107     TYPE_SWITCH,
10108     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10109   },
10110   {
10111     TYPE_SWITCH,
10112     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10113   },
10114   {
10115     TYPE_SWITCH,
10116     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10117   },
10118   {
10119     TYPE_SWITCH,
10120     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10121   },
10122   {
10123     TYPE_SWITCH,
10124     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10125   },
10126   {
10127     TYPE_SWITCH,
10128     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10129   },
10130   {
10131     TYPE_SWITCH,
10132     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10133   },
10134   {
10135     TYPE_SWITCH,
10136     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10137   },
10138   {
10139     TYPE_SWITCH,
10140     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10141   },
10142   {
10143     TYPE_SWITCH,
10144     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10145   },
10146   {
10147     TYPE_SWITCH,
10148     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10149   },
10150   {
10151     TYPE_SWITCH,
10152     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10153   },
10154   {
10155     TYPE_SWITCH,
10156     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10157   },
10158   {
10159     TYPE_SWITCH,
10160     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10161   },
10162   {
10163     TYPE_SWITCH,
10164     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10165   },
10166   {
10167     TYPE_SWITCH,
10168     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10169   },
10170   {
10171     TYPE_SWITCH,
10172     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10173   },
10174   {
10175     TYPE_SWITCH,
10176     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10177   },
10178 };
10179
10180 static struct TokenInfo shortcut_setup_tokens[] =
10181 {
10182   {
10183     TYPE_KEY_X11,
10184     &setup.shortcut.save_game,                  "shortcut.save_game"
10185   },
10186   {
10187     TYPE_KEY_X11,
10188     &setup.shortcut.load_game,                  "shortcut.load_game"
10189   },
10190   {
10191     TYPE_KEY_X11,
10192     &setup.shortcut.restart_game,               "shortcut.restart_game"
10193   },
10194   {
10195     TYPE_KEY_X11,
10196     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10197   },
10198   {
10199     TYPE_KEY_X11,
10200     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10201   },
10202   {
10203     TYPE_KEY_X11,
10204     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10205   },
10206   {
10207     TYPE_KEY_X11,
10208     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10209   },
10210   {
10211     TYPE_KEY_X11,
10212     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10213   },
10214   {
10215     TYPE_KEY_X11,
10216     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10217   },
10218   {
10219     TYPE_KEY_X11,
10220     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10221   },
10222   {
10223     TYPE_KEY_X11,
10224     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10225   },
10226   {
10227     TYPE_KEY_X11,
10228     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10229   },
10230   {
10231     TYPE_KEY_X11,
10232     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10233   },
10234   {
10235     TYPE_KEY_X11,
10236     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10237   },
10238   {
10239     TYPE_KEY_X11,
10240     &setup.shortcut.tape_record,                "shortcut.tape_record"
10241   },
10242   {
10243     TYPE_KEY_X11,
10244     &setup.shortcut.tape_play,                  "shortcut.tape_play"
10245   },
10246   {
10247     TYPE_KEY_X11,
10248     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
10249   },
10250   {
10251     TYPE_KEY_X11,
10252     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
10253   },
10254   {
10255     TYPE_KEY_X11,
10256     &setup.shortcut.sound_music,                "shortcut.sound_music"
10257   },
10258   {
10259     TYPE_KEY_X11,
10260     &setup.shortcut.snap_left,                  "shortcut.snap_left"
10261   },
10262   {
10263     TYPE_KEY_X11,
10264     &setup.shortcut.snap_right,                 "shortcut.snap_right"
10265   },
10266   {
10267     TYPE_KEY_X11,
10268     &setup.shortcut.snap_up,                    "shortcut.snap_up"
10269   },
10270   {
10271     TYPE_KEY_X11,
10272     &setup.shortcut.snap_down,                  "shortcut.snap_down"
10273   },
10274 };
10275
10276 static struct SetupInputInfo setup_input;
10277 static struct TokenInfo player_setup_tokens[] =
10278 {
10279   {
10280     TYPE_BOOLEAN,
10281     &setup_input.use_joystick,                  ".use_joystick"
10282   },
10283   {
10284     TYPE_STRING,
10285     &setup_input.joy.device_name,               ".joy.device_name"
10286   },
10287   {
10288     TYPE_INTEGER,
10289     &setup_input.joy.xleft,                     ".joy.xleft"
10290   },
10291   {
10292     TYPE_INTEGER,
10293     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
10294   },
10295   {
10296     TYPE_INTEGER,
10297     &setup_input.joy.xright,                    ".joy.xright"
10298   },
10299   {
10300     TYPE_INTEGER,
10301     &setup_input.joy.yupper,                    ".joy.yupper"
10302   },
10303   {
10304     TYPE_INTEGER,
10305     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
10306   },
10307   {
10308     TYPE_INTEGER,
10309     &setup_input.joy.ylower,                    ".joy.ylower"
10310   },
10311   {
10312     TYPE_INTEGER,
10313     &setup_input.joy.snap,                      ".joy.snap_field"
10314   },
10315   {
10316     TYPE_INTEGER,
10317     &setup_input.joy.drop,                      ".joy.place_bomb"
10318   },
10319   {
10320     TYPE_KEY_X11,
10321     &setup_input.key.left,                      ".key.move_left"
10322   },
10323   {
10324     TYPE_KEY_X11,
10325     &setup_input.key.right,                     ".key.move_right"
10326   },
10327   {
10328     TYPE_KEY_X11,
10329     &setup_input.key.up,                        ".key.move_up"
10330   },
10331   {
10332     TYPE_KEY_X11,
10333     &setup_input.key.down,                      ".key.move_down"
10334   },
10335   {
10336     TYPE_KEY_X11,
10337     &setup_input.key.snap,                      ".key.snap_field"
10338   },
10339   {
10340     TYPE_KEY_X11,
10341     &setup_input.key.drop,                      ".key.place_bomb"
10342   },
10343 };
10344
10345 static struct TokenInfo system_setup_tokens[] =
10346 {
10347   {
10348     TYPE_STRING,
10349     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10350   },
10351   {
10352     TYPE_STRING,
10353     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10354   },
10355   {
10356     TYPE_STRING,
10357     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10358   },
10359   {
10360     TYPE_INTEGER,
10361     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10362   },
10363 };
10364
10365 static struct TokenInfo internal_setup_tokens[] =
10366 {
10367   {
10368     TYPE_STRING,
10369     &setup.internal.program_title,              "program_title"
10370   },
10371   {
10372     TYPE_STRING,
10373     &setup.internal.program_version,            "program_version"
10374   },
10375   {
10376     TYPE_STRING,
10377     &setup.internal.program_author,             "program_author"
10378   },
10379   {
10380     TYPE_STRING,
10381     &setup.internal.program_email,              "program_email"
10382   },
10383   {
10384     TYPE_STRING,
10385     &setup.internal.program_website,            "program_website"
10386   },
10387   {
10388     TYPE_STRING,
10389     &setup.internal.program_copyright,          "program_copyright"
10390   },
10391   {
10392     TYPE_STRING,
10393     &setup.internal.program_company,            "program_company"
10394   },
10395   {
10396     TYPE_STRING,
10397     &setup.internal.program_icon_file,          "program_icon_file"
10398   },
10399   {
10400     TYPE_STRING,
10401     &setup.internal.default_graphics_set,       "default_graphics_set"
10402   },
10403   {
10404     TYPE_STRING,
10405     &setup.internal.default_sounds_set,         "default_sounds_set"
10406   },
10407   {
10408     TYPE_STRING,
10409     &setup.internal.default_music_set,          "default_music_set"
10410   },
10411   {
10412     TYPE_STRING,
10413     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
10414   },
10415   {
10416     TYPE_STRING,
10417     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
10418   },
10419   {
10420     TYPE_STRING,
10421     &setup.internal.fallback_music_file,        "fallback_music_file"
10422   },
10423   {
10424     TYPE_STRING,
10425     &setup.internal.default_level_series,       "default_level_series"
10426   },
10427   {
10428     TYPE_INTEGER,
10429     &setup.internal.default_window_width,       "default_window_width"
10430   },
10431   {
10432     TYPE_INTEGER,
10433     &setup.internal.default_window_height,      "default_window_height"
10434   },
10435   {
10436     TYPE_BOOLEAN,
10437     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
10438   },
10439   {
10440     TYPE_BOOLEAN,
10441     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
10442   },
10443   {
10444     TYPE_BOOLEAN,
10445     &setup.internal.create_user_levelset,       "create_user_levelset"
10446   },
10447   {
10448     TYPE_BOOLEAN,
10449     &setup.internal.info_screens_from_main,     "info_screens_from_main"
10450   },
10451   {
10452     TYPE_BOOLEAN,
10453     &setup.internal.menu_game,                  "menu_game"
10454   },
10455   {
10456     TYPE_BOOLEAN,
10457     &setup.internal.menu_engines,               "menu_engines"
10458   },
10459   {
10460     TYPE_BOOLEAN,
10461     &setup.internal.menu_editor,                "menu_editor"
10462   },
10463   {
10464     TYPE_BOOLEAN,
10465     &setup.internal.menu_graphics,              "menu_graphics"
10466   },
10467   {
10468     TYPE_BOOLEAN,
10469     &setup.internal.menu_sound,                 "menu_sound"
10470   },
10471   {
10472     TYPE_BOOLEAN,
10473     &setup.internal.menu_artwork,               "menu_artwork"
10474   },
10475   {
10476     TYPE_BOOLEAN,
10477     &setup.internal.menu_input,                 "menu_input"
10478   },
10479   {
10480     TYPE_BOOLEAN,
10481     &setup.internal.menu_touch,                 "menu_touch"
10482   },
10483   {
10484     TYPE_BOOLEAN,
10485     &setup.internal.menu_shortcuts,             "menu_shortcuts"
10486   },
10487   {
10488     TYPE_BOOLEAN,
10489     &setup.internal.menu_exit,                  "menu_exit"
10490   },
10491   {
10492     TYPE_BOOLEAN,
10493     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
10494   },
10495   {
10496     TYPE_BOOLEAN,
10497     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
10498   },
10499   {
10500     TYPE_BOOLEAN,
10501     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
10502   },
10503   {
10504     TYPE_BOOLEAN,
10505     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
10506   },
10507   {
10508     TYPE_BOOLEAN,
10509     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
10510   },
10511   {
10512     TYPE_BOOLEAN,
10513     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
10514   },
10515   {
10516     TYPE_BOOLEAN,
10517     &setup.internal.info_title,                 "info_title"
10518   },
10519   {
10520     TYPE_BOOLEAN,
10521     &setup.internal.info_elements,              "info_elements"
10522   },
10523   {
10524     TYPE_BOOLEAN,
10525     &setup.internal.info_music,                 "info_music"
10526   },
10527   {
10528     TYPE_BOOLEAN,
10529     &setup.internal.info_credits,               "info_credits"
10530   },
10531   {
10532     TYPE_BOOLEAN,
10533     &setup.internal.info_program,               "info_program"
10534   },
10535   {
10536     TYPE_BOOLEAN,
10537     &setup.internal.info_version,               "info_version"
10538   },
10539   {
10540     TYPE_BOOLEAN,
10541     &setup.internal.info_levelset,              "info_levelset"
10542   },
10543   {
10544     TYPE_BOOLEAN,
10545     &setup.internal.info_exit,                  "info_exit"
10546   },
10547 };
10548
10549 static struct TokenInfo debug_setup_tokens[] =
10550 {
10551   {
10552     TYPE_INTEGER,
10553     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
10554   },
10555   {
10556     TYPE_INTEGER,
10557     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
10558   },
10559   {
10560     TYPE_INTEGER,
10561     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
10562   },
10563   {
10564     TYPE_INTEGER,
10565     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
10566   },
10567   {
10568     TYPE_INTEGER,
10569     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
10570   },
10571   {
10572     TYPE_INTEGER,
10573     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
10574   },
10575   {
10576     TYPE_INTEGER,
10577     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
10578   },
10579   {
10580     TYPE_INTEGER,
10581     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
10582   },
10583   {
10584     TYPE_INTEGER,
10585     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
10586   },
10587   {
10588     TYPE_INTEGER,
10589     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
10590   },
10591   {
10592     TYPE_KEY_X11,
10593     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
10594   },
10595   {
10596     TYPE_KEY_X11,
10597     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
10598   },
10599   {
10600     TYPE_KEY_X11,
10601     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
10602   },
10603   {
10604     TYPE_KEY_X11,
10605     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
10606   },
10607   {
10608     TYPE_KEY_X11,
10609     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
10610   },
10611   {
10612     TYPE_KEY_X11,
10613     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
10614   },
10615   {
10616     TYPE_KEY_X11,
10617     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
10618   },
10619   {
10620     TYPE_KEY_X11,
10621     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
10622   },
10623   {
10624     TYPE_KEY_X11,
10625     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
10626   },
10627   {
10628     TYPE_KEY_X11,
10629     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
10630   },
10631   {
10632     TYPE_BOOLEAN,
10633     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
10634   {
10635     TYPE_BOOLEAN,
10636     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
10637   },
10638   {
10639     TYPE_BOOLEAN,
10640     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
10641   },
10642   {
10643     TYPE_SWITCH3,
10644     &setup.debug.xsn_mode,                      "debug.xsn_mode"
10645   },
10646   {
10647     TYPE_INTEGER,
10648     &setup.debug.xsn_percent,                   "debug.xsn_percent"
10649   },
10650 };
10651
10652 static struct TokenInfo options_setup_tokens[] =
10653 {
10654   {
10655     TYPE_BOOLEAN,
10656     &setup.options.verbose,                     "options.verbose"
10657   },
10658   {
10659     TYPE_BOOLEAN,
10660     &setup.options.debug,                       "options.debug"
10661   },
10662   {
10663     TYPE_STRING,
10664     &setup.options.debug_mode,                  "options.debug_mode"
10665   },
10666 };
10667
10668 static void setSetupInfoToDefaults(struct SetupInfo *si)
10669 {
10670   int i;
10671
10672   si->player_name = getStringCopy(getDefaultUserName(user.nr));
10673
10674   si->multiple_users = TRUE;
10675
10676   si->sound = TRUE;
10677   si->sound_loops = TRUE;
10678   si->sound_music = TRUE;
10679   si->sound_simple = TRUE;
10680   si->toons = TRUE;
10681   si->global_animations = TRUE;
10682   si->scroll_delay = TRUE;
10683   si->forced_scroll_delay = FALSE;
10684   si->scroll_delay_value = STD_SCROLL_DELAY;
10685   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10686   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10687   si->fade_screens = TRUE;
10688   si->autorecord = TRUE;
10689   si->autorecord_after_replay = TRUE;
10690   si->auto_pause_on_start = FALSE;
10691   si->show_titlescreen = TRUE;
10692   si->quick_doors = FALSE;
10693   si->team_mode = FALSE;
10694   si->handicap = TRUE;
10695   si->skip_levels = TRUE;
10696   si->increment_levels = TRUE;
10697   si->auto_play_next_level = TRUE;
10698   si->count_score_after_game = TRUE;
10699   si->show_scores_after_game = TRUE;
10700   si->time_limit = TRUE;
10701   si->fullscreen = FALSE;
10702   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10703   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10704   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10705   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10706   si->ask_on_escape = TRUE;
10707   si->ask_on_escape_editor = TRUE;
10708   si->ask_on_game_over = TRUE;
10709   si->ask_on_quit_game = TRUE;
10710   si->ask_on_quit_program = TRUE;
10711   si->quick_switch = FALSE;
10712   si->input_on_focus = FALSE;
10713   si->prefer_aga_graphics = TRUE;
10714   si->prefer_lowpass_sounds = FALSE;
10715   si->prefer_extra_panel_items = TRUE;
10716   si->game_speed_extended = FALSE;
10717   si->game_frame_delay = GAME_FRAME_DELAY;
10718   si->bd_skip_uncovering = FALSE;
10719   si->bd_skip_hatching = FALSE;
10720   si->bd_scroll_delay = TRUE;
10721   si->bd_smooth_movements = AUTO;
10722   si->sp_show_border_elements = FALSE;
10723   si->small_game_graphics = FALSE;
10724   si->show_load_save_buttons = FALSE;
10725   si->show_undo_redo_buttons = FALSE;
10726   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10727
10728   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10729   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
10730   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
10731
10732   si->override_level_graphics = FALSE;
10733   si->override_level_sounds = FALSE;
10734   si->override_level_music = FALSE;
10735
10736   si->volume_simple = 100;              // percent
10737   si->volume_loops = 100;               // percent
10738   si->volume_music = 100;               // percent
10739
10740   si->network_mode = FALSE;
10741   si->network_player_nr = 0;            // first player
10742   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10743
10744   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10745   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
10746   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
10747   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
10748   si->touch.draw_outlined = TRUE;
10749   si->touch.draw_pressed = TRUE;
10750
10751   for (i = 0; i < 2; i++)
10752   {
10753     char *default_grid_button[6][2] =
10754     {
10755       { "      ", "  ^^  " },
10756       { "      ", "  ^^  " },
10757       { "      ", "<<  >>" },
10758       { "      ", "<<  >>" },
10759       { "111222", "  vv  " },
10760       { "111222", "  vv  " }
10761     };
10762     int grid_xsize = DEFAULT_GRID_XSIZE(i);
10763     int grid_ysize = DEFAULT_GRID_YSIZE(i);
10764     int min_xsize = MIN(6, grid_xsize);
10765     int min_ysize = MIN(6, grid_ysize);
10766     int startx = grid_xsize - min_xsize;
10767     int starty = grid_ysize - min_ysize;
10768     int x, y;
10769
10770     // virtual buttons grid can only be set to defaults if video is initialized
10771     // (this will be repeated if virtual buttons are not loaded from setup file)
10772     if (video.initialized)
10773     {
10774       si->touch.grid_xsize[i] = grid_xsize;
10775       si->touch.grid_ysize[i] = grid_ysize;
10776     }
10777     else
10778     {
10779       si->touch.grid_xsize[i] = -1;
10780       si->touch.grid_ysize[i] = -1;
10781     }
10782
10783     for (x = 0; x < MAX_GRID_XSIZE; x++)
10784       for (y = 0; y < MAX_GRID_YSIZE; y++)
10785         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10786
10787     for (x = 0; x < min_xsize; x++)
10788       for (y = 0; y < min_ysize; y++)
10789         si->touch.grid_button[i][x][starty + y] =
10790           default_grid_button[y][0][x];
10791
10792     for (x = 0; x < min_xsize; x++)
10793       for (y = 0; y < min_ysize; y++)
10794         si->touch.grid_button[i][startx + x][starty + y] =
10795           default_grid_button[y][1][x];
10796   }
10797
10798   si->touch.grid_initialized            = video.initialized;
10799
10800   si->touch.overlay_buttons             = FALSE;
10801
10802   si->editor.el_boulderdash             = TRUE;
10803   si->editor.el_boulderdash_native      = TRUE;
10804   si->editor.el_emerald_mine            = TRUE;
10805   si->editor.el_emerald_mine_club       = TRUE;
10806   si->editor.el_more                    = TRUE;
10807   si->editor.el_sokoban                 = TRUE;
10808   si->editor.el_supaplex                = TRUE;
10809   si->editor.el_diamond_caves           = TRUE;
10810   si->editor.el_dx_boulderdash          = TRUE;
10811
10812   si->editor.el_mirror_magic            = TRUE;
10813   si->editor.el_deflektor               = TRUE;
10814
10815   si->editor.el_chars                   = TRUE;
10816   si->editor.el_steel_chars             = TRUE;
10817
10818   si->editor.el_classic                 = TRUE;
10819   si->editor.el_custom                  = TRUE;
10820
10821   si->editor.el_user_defined            = FALSE;
10822   si->editor.el_dynamic                 = TRUE;
10823
10824   si->editor.el_headlines               = TRUE;
10825
10826   si->editor.show_element_token         = FALSE;
10827
10828   si->editor.show_read_only_warning     = TRUE;
10829
10830   si->editor.use_template_for_new_levels = TRUE;
10831
10832   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
10833   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
10834   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
10835   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10836   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
10837
10838   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
10839   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
10840   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
10841   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
10842   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10843
10844   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
10845   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
10846   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
10847   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
10848   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
10849   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
10850
10851   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
10852   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
10853   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
10854
10855   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
10856   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
10857   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
10858   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
10859
10860   for (i = 0; i < MAX_PLAYERS; i++)
10861   {
10862     si->input[i].use_joystick = FALSE;
10863     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10864     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
10865     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10866     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
10867     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
10868     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10869     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
10870     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
10871     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
10872     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
10873     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10874     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
10875     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
10876     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
10877     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
10878   }
10879
10880   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10881   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10882   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10883   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10884
10885   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
10886   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
10887   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
10888   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
10889   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
10890   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10891   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
10892
10893   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10894
10895   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10896   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
10897   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
10898
10899   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10900   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
10901   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
10902
10903   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10904   si->internal.choose_from_top_leveldir = FALSE;
10905   si->internal.show_scaling_in_title = TRUE;
10906   si->internal.create_user_levelset = TRUE;
10907   si->internal.info_screens_from_main = FALSE;
10908
10909   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
10910   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10911
10912   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10913   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10914   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10915   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10916   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10917   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10918   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10919   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10920   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10921   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10922
10923   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10924   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10925   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10926   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10927   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10928   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10929   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10930   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10931   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10932   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10933
10934   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10935   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
10936
10937   si->debug.show_frames_per_second = FALSE;
10938
10939   si->debug.xsn_mode = AUTO;
10940   si->debug.xsn_percent = 0;
10941
10942   si->options.verbose = FALSE;
10943   si->options.debug = FALSE;
10944   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10945
10946 #if defined(PLATFORM_ANDROID)
10947   si->fullscreen = TRUE;
10948   si->touch.overlay_buttons = TRUE;
10949 #endif
10950
10951   setHideSetupEntry(&setup.debug.xsn_mode);
10952 }
10953
10954 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10955 {
10956   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10957 }
10958
10959 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10960 {
10961   si->player_uuid = NULL;       // (will be set later)
10962   si->player_version = 1;       // (will be set later)
10963
10964   si->use_api_server = TRUE;
10965   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10966   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10967   si->ask_for_uploading_tapes = TRUE;
10968   si->ask_for_remaining_tapes = FALSE;
10969   si->provide_uploading_tapes = TRUE;
10970   si->ask_for_using_api_server = TRUE;
10971   si->has_remaining_tapes = FALSE;
10972 }
10973
10974 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10975 {
10976   si->editor_cascade.el_bd              = TRUE;
10977   si->editor_cascade.el_bd_native       = TRUE;
10978   si->editor_cascade.el_em              = TRUE;
10979   si->editor_cascade.el_emc             = TRUE;
10980   si->editor_cascade.el_rnd             = TRUE;
10981   si->editor_cascade.el_sb              = TRUE;
10982   si->editor_cascade.el_sp              = TRUE;
10983   si->editor_cascade.el_dc              = TRUE;
10984   si->editor_cascade.el_dx              = TRUE;
10985
10986   si->editor_cascade.el_mm              = TRUE;
10987   si->editor_cascade.el_df              = TRUE;
10988
10989   si->editor_cascade.el_chars           = FALSE;
10990   si->editor_cascade.el_steel_chars     = FALSE;
10991   si->editor_cascade.el_ce              = FALSE;
10992   si->editor_cascade.el_ge              = FALSE;
10993   si->editor_cascade.el_es              = FALSE;
10994   si->editor_cascade.el_ref             = FALSE;
10995   si->editor_cascade.el_user            = FALSE;
10996   si->editor_cascade.el_dynamic         = FALSE;
10997 }
10998
10999 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11000
11001 static char *getHideSetupToken(void *setup_value)
11002 {
11003   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11004
11005   if (setup_value != NULL)
11006     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11007
11008   return hide_setup_token;
11009 }
11010
11011 void setHideSetupEntry(void *setup_value)
11012 {
11013   char *hide_setup_token = getHideSetupToken(setup_value);
11014
11015   if (hide_setup_hash == NULL)
11016     hide_setup_hash = newSetupFileHash();
11017
11018   if (setup_value != NULL)
11019     setHashEntry(hide_setup_hash, hide_setup_token, "");
11020 }
11021
11022 void removeHideSetupEntry(void *setup_value)
11023 {
11024   char *hide_setup_token = getHideSetupToken(setup_value);
11025
11026   if (setup_value != NULL)
11027     removeHashEntry(hide_setup_hash, hide_setup_token);
11028 }
11029
11030 boolean hideSetupEntry(void *setup_value)
11031 {
11032   char *hide_setup_token = getHideSetupToken(setup_value);
11033
11034   return (setup_value != NULL &&
11035           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11036 }
11037
11038 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11039                                       struct TokenInfo *token_info,
11040                                       int token_nr, char *token_text)
11041 {
11042   char *token_hide_text = getStringCat2(token_text, ".hide");
11043   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11044
11045   // set the value of this setup option in the setup option structure
11046   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11047
11048   // check if this setup option should be hidden in the setup menu
11049   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11050     setHideSetupEntry(token_info[token_nr].value);
11051
11052   free(token_hide_text);
11053 }
11054
11055 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11056                                       struct TokenInfo *token_info,
11057                                       int token_nr)
11058 {
11059   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11060                             token_info[token_nr].text);
11061 }
11062
11063 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11064 {
11065   int i, pnr;
11066
11067   if (!setup_file_hash)
11068     return;
11069
11070   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11071     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11072
11073   setup.touch.grid_initialized = TRUE;
11074   for (i = 0; i < 2; i++)
11075   {
11076     int grid_xsize = setup.touch.grid_xsize[i];
11077     int grid_ysize = setup.touch.grid_ysize[i];
11078     int x, y;
11079
11080     // if virtual buttons are not loaded from setup file, repeat initializing
11081     // virtual buttons grid with default values later when video is initialized
11082     if (grid_xsize == -1 ||
11083         grid_ysize == -1)
11084     {
11085       setup.touch.grid_initialized = FALSE;
11086
11087       continue;
11088     }
11089
11090     for (y = 0; y < grid_ysize; y++)
11091     {
11092       char token_string[MAX_LINE_LEN];
11093
11094       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11095
11096       char *value_string = getHashEntry(setup_file_hash, token_string);
11097
11098       if (value_string == NULL)
11099         continue;
11100
11101       for (x = 0; x < grid_xsize; x++)
11102       {
11103         char c = value_string[x];
11104
11105         setup.touch.grid_button[i][x][y] =
11106           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11107       }
11108     }
11109   }
11110
11111   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11112     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11113
11114   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11115     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11116
11117   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11118   {
11119     char prefix[30];
11120
11121     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11122
11123     setup_input = setup.input[pnr];
11124     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11125     {
11126       char full_token[100];
11127
11128       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11129       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11130                                 full_token);
11131     }
11132     setup.input[pnr] = setup_input;
11133   }
11134
11135   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11136     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11137
11138   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11139     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11140
11141   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11142     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11143
11144   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11145     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11146
11147   setHideRelatedSetupEntries();
11148 }
11149
11150 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11151 {
11152   int i;
11153
11154   if (!setup_file_hash)
11155     return;
11156
11157   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11158     setSetupInfo(auto_setup_tokens, i,
11159                  getHashEntry(setup_file_hash,
11160                               auto_setup_tokens[i].text));
11161 }
11162
11163 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11164 {
11165   int i;
11166
11167   if (!setup_file_hash)
11168     return;
11169
11170   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11171     setSetupInfo(server_setup_tokens, i,
11172                  getHashEntry(setup_file_hash,
11173                               server_setup_tokens[i].text));
11174 }
11175
11176 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11177 {
11178   int i;
11179
11180   if (!setup_file_hash)
11181     return;
11182
11183   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11184     setSetupInfo(editor_cascade_setup_tokens, i,
11185                  getHashEntry(setup_file_hash,
11186                               editor_cascade_setup_tokens[i].text));
11187 }
11188
11189 void LoadUserNames(void)
11190 {
11191   int last_user_nr = user.nr;
11192   int i;
11193
11194   if (global.user_names != NULL)
11195   {
11196     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11197       checked_free(global.user_names[i]);
11198
11199     checked_free(global.user_names);
11200   }
11201
11202   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11203
11204   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11205   {
11206     user.nr = i;
11207
11208     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11209
11210     if (setup_file_hash)
11211     {
11212       char *player_name = getHashEntry(setup_file_hash, "player_name");
11213
11214       global.user_names[i] = getFixedUserName(player_name);
11215
11216       freeSetupFileHash(setup_file_hash);
11217     }
11218
11219     if (global.user_names[i] == NULL)
11220       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11221   }
11222
11223   user.nr = last_user_nr;
11224 }
11225
11226 void LoadSetupFromFilename(char *filename)
11227 {
11228   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11229
11230   if (setup_file_hash)
11231   {
11232     decodeSetupFileHash_Default(setup_file_hash);
11233
11234     freeSetupFileHash(setup_file_hash);
11235   }
11236   else
11237   {
11238     Debug("setup", "using default setup values");
11239   }
11240 }
11241
11242 static void LoadSetup_SpecialPostProcessing(void)
11243 {
11244   char *player_name_new;
11245
11246   // needed to work around problems with fixed length strings
11247   player_name_new = getFixedUserName(setup.player_name);
11248   free(setup.player_name);
11249   setup.player_name = player_name_new;
11250
11251   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11252   if (setup.scroll_delay == FALSE)
11253   {
11254     setup.scroll_delay_value = MIN_SCROLL_DELAY;
11255     setup.scroll_delay = TRUE;                  // now always "on"
11256   }
11257
11258   // make sure that scroll delay value stays inside valid range
11259   setup.scroll_delay_value =
11260     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11261 }
11262
11263 void LoadSetup_Default(void)
11264 {
11265   char *filename;
11266
11267   // always start with reliable default values
11268   setSetupInfoToDefaults(&setup);
11269
11270   // try to load setup values from default setup file
11271   filename = getDefaultSetupFilename();
11272
11273   if (fileExists(filename))
11274     LoadSetupFromFilename(filename);
11275
11276   // try to load setup values from platform setup file
11277   filename = getPlatformSetupFilename();
11278
11279   if (fileExists(filename))
11280     LoadSetupFromFilename(filename);
11281
11282   // try to load setup values from user setup file
11283   filename = getSetupFilename();
11284
11285   LoadSetupFromFilename(filename);
11286
11287   LoadSetup_SpecialPostProcessing();
11288 }
11289
11290 void LoadSetup_AutoSetup(void)
11291 {
11292   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11293   SetupFileHash *setup_file_hash = NULL;
11294
11295   // always start with reliable default values
11296   setSetupInfoToDefaults_AutoSetup(&setup);
11297
11298   setup_file_hash = loadSetupFileHash(filename);
11299
11300   if (setup_file_hash)
11301   {
11302     decodeSetupFileHash_AutoSetup(setup_file_hash);
11303
11304     freeSetupFileHash(setup_file_hash);
11305   }
11306
11307   free(filename);
11308 }
11309
11310 void LoadSetup_ServerSetup(void)
11311 {
11312   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11313   SetupFileHash *setup_file_hash = NULL;
11314
11315   // always start with reliable default values
11316   setSetupInfoToDefaults_ServerSetup(&setup);
11317
11318   setup_file_hash = loadSetupFileHash(filename);
11319
11320   if (setup_file_hash)
11321   {
11322     decodeSetupFileHash_ServerSetup(setup_file_hash);
11323
11324     freeSetupFileHash(setup_file_hash);
11325   }
11326
11327   free(filename);
11328
11329   if (setup.player_uuid == NULL)
11330   {
11331     // player UUID does not yet exist in setup file
11332     setup.player_uuid = getStringCopy(getUUID());
11333     setup.player_version = 2;
11334
11335     SaveSetup_ServerSetup();
11336   }
11337 }
11338
11339 void LoadSetup_EditorCascade(void)
11340 {
11341   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11342   SetupFileHash *setup_file_hash = NULL;
11343
11344   // always start with reliable default values
11345   setSetupInfoToDefaults_EditorCascade(&setup);
11346
11347   setup_file_hash = loadSetupFileHash(filename);
11348
11349   if (setup_file_hash)
11350   {
11351     decodeSetupFileHash_EditorCascade(setup_file_hash);
11352
11353     freeSetupFileHash(setup_file_hash);
11354   }
11355
11356   free(filename);
11357 }
11358
11359 void LoadSetup(void)
11360 {
11361   LoadSetup_Default();
11362   LoadSetup_AutoSetup();
11363   LoadSetup_ServerSetup();
11364   LoadSetup_EditorCascade();
11365 }
11366
11367 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11368                                            char *mapping_line)
11369 {
11370   char mapping_guid[MAX_LINE_LEN];
11371   char *mapping_start, *mapping_end;
11372
11373   // get GUID from game controller mapping line: copy complete line
11374   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11375   mapping_guid[MAX_LINE_LEN - 1] = '\0';
11376
11377   // get GUID from game controller mapping line: cut after GUID part
11378   mapping_start = strchr(mapping_guid, ',');
11379   if (mapping_start != NULL)
11380     *mapping_start = '\0';
11381
11382   // cut newline from game controller mapping line
11383   mapping_end = strchr(mapping_line, '\n');
11384   if (mapping_end != NULL)
11385     *mapping_end = '\0';
11386
11387   // add mapping entry to game controller mappings hash
11388   setHashEntry(mappings_hash, mapping_guid, mapping_line);
11389 }
11390
11391 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11392                                                  char *filename)
11393 {
11394   FILE *file;
11395
11396   if (!(file = fopen(filename, MODE_READ)))
11397   {
11398     Warn("cannot read game controller mappings file '%s'", filename);
11399
11400     return;
11401   }
11402
11403   while (!feof(file))
11404   {
11405     char line[MAX_LINE_LEN];
11406
11407     if (!fgets(line, MAX_LINE_LEN, file))
11408       break;
11409
11410     addGameControllerMappingToHash(mappings_hash, line);
11411   }
11412
11413   fclose(file);
11414 }
11415
11416 void SaveSetup_Default(void)
11417 {
11418   char *filename = getSetupFilename();
11419   FILE *file;
11420   int i, pnr;
11421
11422   InitUserDataDirectory();
11423
11424   if (!(file = fopen(filename, MODE_WRITE)))
11425   {
11426     Warn("cannot write setup file '%s'", filename);
11427
11428     return;
11429   }
11430
11431   fprintFileHeader(file, SETUP_FILENAME);
11432
11433   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11434   {
11435     // just to make things nicer :)
11436     if (global_setup_tokens[i].value == &setup.multiple_users           ||
11437         global_setup_tokens[i].value == &setup.sound                    ||
11438         global_setup_tokens[i].value == &setup.graphics_set             ||
11439         global_setup_tokens[i].value == &setup.volume_simple            ||
11440         global_setup_tokens[i].value == &setup.network_mode             ||
11441         global_setup_tokens[i].value == &setup.touch.control_type       ||
11442         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
11443         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11444       fprintf(file, "\n");
11445
11446     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11447   }
11448
11449   for (i = 0; i < 2; i++)
11450   {
11451     int grid_xsize = setup.touch.grid_xsize[i];
11452     int grid_ysize = setup.touch.grid_ysize[i];
11453     int x, y;
11454
11455     fprintf(file, "\n");
11456
11457     for (y = 0; y < grid_ysize; y++)
11458     {
11459       char token_string[MAX_LINE_LEN];
11460       char value_string[MAX_LINE_LEN];
11461
11462       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11463
11464       for (x = 0; x < grid_xsize; x++)
11465       {
11466         char c = setup.touch.grid_button[i][x][y];
11467
11468         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11469       }
11470
11471       value_string[grid_xsize] = '\0';
11472
11473       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11474     }
11475   }
11476
11477   fprintf(file, "\n");
11478   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11479     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11480
11481   fprintf(file, "\n");
11482   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11483     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11484
11485   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11486   {
11487     char prefix[30];
11488
11489     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11490     fprintf(file, "\n");
11491
11492     setup_input = setup.input[pnr];
11493     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11494       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11495   }
11496
11497   fprintf(file, "\n");
11498   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11499     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11500
11501   // (internal setup values not saved to user setup file)
11502
11503   fprintf(file, "\n");
11504   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11505     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11506         setup.debug.xsn_mode != AUTO)
11507       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11508
11509   fprintf(file, "\n");
11510   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11511     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11512
11513   fclose(file);
11514
11515   SetFilePermissions(filename, PERMS_PRIVATE);
11516 }
11517
11518 void SaveSetup_AutoSetup(void)
11519 {
11520   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11521   FILE *file;
11522   int i;
11523
11524   InitUserDataDirectory();
11525
11526   if (!(file = fopen(filename, MODE_WRITE)))
11527   {
11528     Warn("cannot write auto setup file '%s'", filename);
11529
11530     free(filename);
11531
11532     return;
11533   }
11534
11535   fprintFileHeader(file, AUTOSETUP_FILENAME);
11536
11537   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11538     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11539
11540   fclose(file);
11541
11542   SetFilePermissions(filename, PERMS_PRIVATE);
11543
11544   free(filename);
11545 }
11546
11547 void SaveSetup_ServerSetup(void)
11548 {
11549   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11550   FILE *file;
11551   int i;
11552
11553   InitUserDataDirectory();
11554
11555   if (!(file = fopen(filename, MODE_WRITE)))
11556   {
11557     Warn("cannot write server setup file '%s'", filename);
11558
11559     free(filename);
11560
11561     return;
11562   }
11563
11564   fprintFileHeader(file, SERVERSETUP_FILENAME);
11565
11566   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11567   {
11568     // just to make things nicer :)
11569     if (server_setup_tokens[i].value == &setup.use_api_server)
11570       fprintf(file, "\n");
11571
11572     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11573   }
11574
11575   fclose(file);
11576
11577   SetFilePermissions(filename, PERMS_PRIVATE);
11578
11579   free(filename);
11580 }
11581
11582 void SaveSetup_EditorCascade(void)
11583 {
11584   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11585   FILE *file;
11586   int i;
11587
11588   InitUserDataDirectory();
11589
11590   if (!(file = fopen(filename, MODE_WRITE)))
11591   {
11592     Warn("cannot write editor cascade state file '%s'", filename);
11593
11594     free(filename);
11595
11596     return;
11597   }
11598
11599   fprintFileHeader(file, EDITORCASCADE_FILENAME);
11600
11601   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11602     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11603
11604   fclose(file);
11605
11606   SetFilePermissions(filename, PERMS_PRIVATE);
11607
11608   free(filename);
11609 }
11610
11611 void SaveSetup(void)
11612 {
11613   SaveSetup_Default();
11614   SaveSetup_AutoSetup();
11615   SaveSetup_ServerSetup();
11616   SaveSetup_EditorCascade();
11617 }
11618
11619 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11620                                                   char *filename)
11621 {
11622   FILE *file;
11623
11624   if (!(file = fopen(filename, MODE_WRITE)))
11625   {
11626     Warn("cannot write game controller mappings file '%s'", filename);
11627
11628     return;
11629   }
11630
11631   BEGIN_HASH_ITERATION(mappings_hash, itr)
11632   {
11633     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11634   }
11635   END_HASH_ITERATION(mappings_hash, itr)
11636
11637   fclose(file);
11638 }
11639
11640 void SaveSetup_AddGameControllerMapping(char *mapping)
11641 {
11642   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11643   SetupFileHash *mappings_hash = newSetupFileHash();
11644
11645   InitUserDataDirectory();
11646
11647   // load existing personal game controller mappings
11648   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11649
11650   // add new mapping to personal game controller mappings
11651   addGameControllerMappingToHash(mappings_hash, mapping);
11652
11653   // save updated personal game controller mappings
11654   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11655
11656   freeSetupFileHash(mappings_hash);
11657   free(filename);
11658 }
11659
11660 void LoadCustomElementDescriptions(void)
11661 {
11662   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11663   SetupFileHash *setup_file_hash;
11664   int i;
11665
11666   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11667   {
11668     if (element_info[i].custom_description != NULL)
11669     {
11670       free(element_info[i].custom_description);
11671       element_info[i].custom_description = NULL;
11672     }
11673   }
11674
11675   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11676     return;
11677
11678   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11679   {
11680     char *token = getStringCat2(element_info[i].token_name, ".name");
11681     char *value = getHashEntry(setup_file_hash, token);
11682
11683     if (value != NULL)
11684       element_info[i].custom_description = getStringCopy(value);
11685
11686     free(token);
11687   }
11688
11689   freeSetupFileHash(setup_file_hash);
11690 }
11691
11692 static int getElementFromToken(char *token)
11693 {
11694   char *value = getHashEntry(element_token_hash, token);
11695
11696   if (value != NULL)
11697     return atoi(value);
11698
11699   Warn("unknown element token '%s'", token);
11700
11701   return EL_UNDEFINED;
11702 }
11703
11704 void FreeGlobalAnimEventInfo(void)
11705 {
11706   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11707
11708   if (gaei->event_list == NULL)
11709     return;
11710
11711   int i;
11712
11713   for (i = 0; i < gaei->num_event_lists; i++)
11714   {
11715     checked_free(gaei->event_list[i]->event_value);
11716     checked_free(gaei->event_list[i]);
11717   }
11718
11719   checked_free(gaei->event_list);
11720
11721   gaei->event_list = NULL;
11722   gaei->num_event_lists = 0;
11723 }
11724
11725 static int AddGlobalAnimEventList(void)
11726 {
11727   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11728   int list_pos = gaei->num_event_lists++;
11729
11730   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11731                                      sizeof(struct GlobalAnimEventListInfo *));
11732
11733   gaei->event_list[list_pos] =
11734     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11735
11736   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11737
11738   gaeli->event_value = NULL;
11739   gaeli->num_event_values = 0;
11740
11741   return list_pos;
11742 }
11743
11744 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11745 {
11746   // do not add empty global animation events
11747   if (event_value == ANIM_EVENT_NONE)
11748     return list_pos;
11749
11750   // if list position is undefined, create new list
11751   if (list_pos == ANIM_EVENT_UNDEFINED)
11752     list_pos = AddGlobalAnimEventList();
11753
11754   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11755   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11756   int value_pos = gaeli->num_event_values++;
11757
11758   gaeli->event_value = checked_realloc(gaeli->event_value,
11759                                        gaeli->num_event_values * sizeof(int *));
11760
11761   gaeli->event_value[value_pos] = event_value;
11762
11763   return list_pos;
11764 }
11765
11766 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11767 {
11768   if (list_pos == ANIM_EVENT_UNDEFINED)
11769     return ANIM_EVENT_NONE;
11770
11771   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11772   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11773
11774   return gaeli->event_value[value_pos];
11775 }
11776
11777 int GetGlobalAnimEventValueCount(int list_pos)
11778 {
11779   if (list_pos == ANIM_EVENT_UNDEFINED)
11780     return 0;
11781
11782   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11783   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11784
11785   return gaeli->num_event_values;
11786 }
11787
11788 // This function checks if a string <s> of the format "string1, string2, ..."
11789 // exactly contains a string <s_contained>.
11790
11791 static boolean string_has_parameter(char *s, char *s_contained)
11792 {
11793   char *substring;
11794
11795   if (s == NULL || s_contained == NULL)
11796     return FALSE;
11797
11798   if (strlen(s_contained) > strlen(s))
11799     return FALSE;
11800
11801   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11802   {
11803     char next_char = s[strlen(s_contained)];
11804
11805     // check if next character is delimiter or whitespace
11806     if (next_char == ',' || next_char == '\0' ||
11807         next_char == ' ' || next_char == '\t')
11808       return TRUE;
11809   }
11810
11811   // check if string contains another parameter string after a comma
11812   substring = strchr(s, ',');
11813   if (substring == NULL)        // string does not contain a comma
11814     return FALSE;
11815
11816   // advance string pointer to next character after the comma
11817   substring++;
11818
11819   // skip potential whitespaces after the comma
11820   while (*substring == ' ' || *substring == '\t')
11821     substring++;
11822
11823   return string_has_parameter(substring, s_contained);
11824 }
11825
11826 static int get_anim_parameter_value_ce(char *s)
11827 {
11828   char *s_ptr = s;
11829   char *pattern_1 = "ce_change:custom_";
11830   char *pattern_2 = ".page_";
11831   int pattern_1_len = strlen(pattern_1);
11832   char *matching_char = strstr(s_ptr, pattern_1);
11833   int result = ANIM_EVENT_NONE;
11834
11835   if (matching_char == NULL)
11836     return ANIM_EVENT_NONE;
11837
11838   result = ANIM_EVENT_CE_CHANGE;
11839
11840   s_ptr = matching_char + pattern_1_len;
11841
11842   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11843   if (*s_ptr >= '0' && *s_ptr <= '9')
11844   {
11845     int gic_ce_nr = (*s_ptr++ - '0');
11846
11847     if (*s_ptr >= '0' && *s_ptr <= '9')
11848     {
11849       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11850
11851       if (*s_ptr >= '0' && *s_ptr <= '9')
11852         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11853     }
11854
11855     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11856       return ANIM_EVENT_NONE;
11857
11858     // custom element stored as 0 to 255
11859     gic_ce_nr--;
11860
11861     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11862   }
11863   else
11864   {
11865     // invalid custom element number specified
11866
11867     return ANIM_EVENT_NONE;
11868   }
11869
11870   // check for change page number ("page_X" or "page_XX") (optional)
11871   if (strPrefix(s_ptr, pattern_2))
11872   {
11873     s_ptr += strlen(pattern_2);
11874
11875     if (*s_ptr >= '0' && *s_ptr <= '9')
11876     {
11877       int gic_page_nr = (*s_ptr++ - '0');
11878
11879       if (*s_ptr >= '0' && *s_ptr <= '9')
11880         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11881
11882       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11883         return ANIM_EVENT_NONE;
11884
11885       // change page stored as 1 to 32 (0 means "all change pages")
11886
11887       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11888     }
11889     else
11890     {
11891       // invalid animation part number specified
11892
11893       return ANIM_EVENT_NONE;
11894     }
11895   }
11896
11897   // discard result if next character is neither delimiter nor whitespace
11898   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11899         *s_ptr == ' ' || *s_ptr == '\t'))
11900     return ANIM_EVENT_NONE;
11901
11902   return result;
11903 }
11904
11905 static int get_anim_parameter_value(char *s)
11906 {
11907   int event_value[] =
11908   {
11909     ANIM_EVENT_CLICK,
11910     ANIM_EVENT_INIT,
11911     ANIM_EVENT_START,
11912     ANIM_EVENT_END,
11913     ANIM_EVENT_POST
11914   };
11915   char *pattern_1[] =
11916   {
11917     "click:anim_",
11918     "init:anim_",
11919     "start:anim_",
11920     "end:anim_",
11921     "post:anim_"
11922   };
11923   char *pattern_2 = ".part_";
11924   char *matching_char = NULL;
11925   char *s_ptr = s;
11926   int pattern_1_len = 0;
11927   int result = ANIM_EVENT_NONE;
11928   int i;
11929
11930   result = get_anim_parameter_value_ce(s);
11931
11932   if (result != ANIM_EVENT_NONE)
11933     return result;
11934
11935   for (i = 0; i < ARRAY_SIZE(event_value); i++)
11936   {
11937     matching_char = strstr(s_ptr, pattern_1[i]);
11938     pattern_1_len = strlen(pattern_1[i]);
11939     result = event_value[i];
11940
11941     if (matching_char != NULL)
11942       break;
11943   }
11944
11945   if (matching_char == NULL)
11946     return ANIM_EVENT_NONE;
11947
11948   s_ptr = matching_char + pattern_1_len;
11949
11950   // check for main animation number ("anim_X" or "anim_XX")
11951   if (*s_ptr >= '0' && *s_ptr <= '9')
11952   {
11953     int gic_anim_nr = (*s_ptr++ - '0');
11954
11955     if (*s_ptr >= '0' && *s_ptr <= '9')
11956       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11957
11958     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11959       return ANIM_EVENT_NONE;
11960
11961     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11962   }
11963   else
11964   {
11965     // invalid main animation number specified
11966
11967     return ANIM_EVENT_NONE;
11968   }
11969
11970   // check for animation part number ("part_X" or "part_XX") (optional)
11971   if (strPrefix(s_ptr, pattern_2))
11972   {
11973     s_ptr += strlen(pattern_2);
11974
11975     if (*s_ptr >= '0' && *s_ptr <= '9')
11976     {
11977       int gic_part_nr = (*s_ptr++ - '0');
11978
11979       if (*s_ptr >= '0' && *s_ptr <= '9')
11980         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11981
11982       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11983         return ANIM_EVENT_NONE;
11984
11985       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11986     }
11987     else
11988     {
11989       // invalid animation part number specified
11990
11991       return ANIM_EVENT_NONE;
11992     }
11993   }
11994
11995   // discard result if next character is neither delimiter nor whitespace
11996   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11997         *s_ptr == ' ' || *s_ptr == '\t'))
11998     return ANIM_EVENT_NONE;
11999
12000   return result;
12001 }
12002
12003 static int get_anim_parameter_values(char *s)
12004 {
12005   int list_pos = ANIM_EVENT_UNDEFINED;
12006   int event_value = ANIM_EVENT_DEFAULT;
12007
12008   if (string_has_parameter(s, "any"))
12009     event_value |= ANIM_EVENT_ANY;
12010
12011   if (string_has_parameter(s, "click:self") ||
12012       string_has_parameter(s, "click") ||
12013       string_has_parameter(s, "self"))
12014     event_value |= ANIM_EVENT_SELF;
12015
12016   if (string_has_parameter(s, "unclick:any"))
12017     event_value |= ANIM_EVENT_UNCLICK_ANY;
12018
12019   // if animation event found, add it to global animation event list
12020   if (event_value != ANIM_EVENT_NONE)
12021     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12022
12023   while (s != NULL)
12024   {
12025     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12026     event_value = get_anim_parameter_value(s);
12027
12028     // if animation event found, add it to global animation event list
12029     if (event_value != ANIM_EVENT_NONE)
12030       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12031
12032     // continue with next part of the string, starting with next comma
12033     s = strchr(s + 1, ',');
12034   }
12035
12036   return list_pos;
12037 }
12038
12039 static int get_anim_action_parameter_value(char *token)
12040 {
12041   // check most common default case first to massively speed things up
12042   if (strEqual(token, ARG_UNDEFINED))
12043     return ANIM_EVENT_ACTION_NONE;
12044
12045   int result = getImageIDFromToken(token);
12046
12047   if (result == -1)
12048   {
12049     char *gfx_token = getStringCat2("gfx.", token);
12050
12051     result = getImageIDFromToken(gfx_token);
12052
12053     checked_free(gfx_token);
12054   }
12055
12056   if (result == -1)
12057   {
12058     Key key = getKeyFromX11KeyName(token);
12059
12060     if (key != KSYM_UNDEFINED)
12061       result = -(int)key;
12062   }
12063
12064   if (result == -1)
12065   {
12066     if (isURL(token))
12067     {
12068       result = get_hash_from_string(token);     // unsigned int => int
12069       result = ABS(result);                     // may be negative now
12070       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12071
12072       setHashEntry(anim_url_hash, int2str(result, 0), token);
12073     }
12074   }
12075
12076   if (result == -1)
12077     result = ANIM_EVENT_ACTION_NONE;
12078
12079   return result;
12080 }
12081
12082 int get_parameter_value(char *value_raw, char *suffix, int type)
12083 {
12084   char *value = getStringToLower(value_raw);
12085   int result = 0;       // probably a save default value
12086
12087   if (strEqual(suffix, ".direction"))
12088   {
12089     result = (strEqual(value, "left")  ? MV_LEFT :
12090               strEqual(value, "right") ? MV_RIGHT :
12091               strEqual(value, "up")    ? MV_UP :
12092               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12093   }
12094   else if (strEqual(suffix, ".position"))
12095   {
12096     result = (strEqual(value, "left")   ? POS_LEFT :
12097               strEqual(value, "right")  ? POS_RIGHT :
12098               strEqual(value, "top")    ? POS_TOP :
12099               strEqual(value, "upper")  ? POS_UPPER :
12100               strEqual(value, "middle") ? POS_MIDDLE :
12101               strEqual(value, "lower")  ? POS_LOWER :
12102               strEqual(value, "bottom") ? POS_BOTTOM :
12103               strEqual(value, "any")    ? POS_ANY :
12104               strEqual(value, "ce")     ? POS_CE :
12105               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12106               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12107   }
12108   else if (strEqual(suffix, ".align"))
12109   {
12110     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12111               strEqual(value, "right")  ? ALIGN_RIGHT :
12112               strEqual(value, "center") ? ALIGN_CENTER :
12113               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12114   }
12115   else if (strEqual(suffix, ".valign"))
12116   {
12117     result = (strEqual(value, "top")    ? VALIGN_TOP :
12118               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12119               strEqual(value, "middle") ? VALIGN_MIDDLE :
12120               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12121   }
12122   else if (strEqual(suffix, ".anim_mode"))
12123   {
12124     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12125               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12126               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12127               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12128               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12129               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12130               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12131               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12132               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12133               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12134               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12135               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12136               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12137               string_has_parameter(value, "all")        ? ANIM_ALL :
12138               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12139               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12140               ANIM_DEFAULT);
12141
12142     if (string_has_parameter(value, "once"))
12143       result |= ANIM_ONCE;
12144
12145     if (string_has_parameter(value, "reverse"))
12146       result |= ANIM_REVERSE;
12147
12148     if (string_has_parameter(value, "opaque_player"))
12149       result |= ANIM_OPAQUE_PLAYER;
12150
12151     if (string_has_parameter(value, "static_panel"))
12152       result |= ANIM_STATIC_PANEL;
12153   }
12154   else if (strEqual(suffix, ".init_event") ||
12155            strEqual(suffix, ".anim_event"))
12156   {
12157     result = get_anim_parameter_values(value);
12158   }
12159   else if (strEqual(suffix, ".init_delay_action") ||
12160            strEqual(suffix, ".anim_delay_action") ||
12161            strEqual(suffix, ".post_delay_action") ||
12162            strEqual(suffix, ".init_event_action") ||
12163            strEqual(suffix, ".anim_event_action"))
12164   {
12165     result = get_anim_action_parameter_value(value_raw);
12166   }
12167   else if (strEqual(suffix, ".class"))
12168   {
12169     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12170               get_hash_from_string(value));
12171   }
12172   else if (strEqual(suffix, ".style"))
12173   {
12174     result = STYLE_DEFAULT;
12175
12176     if (string_has_parameter(value, "accurate_borders"))
12177       result |= STYLE_ACCURATE_BORDERS;
12178
12179     if (string_has_parameter(value, "inner_corners"))
12180       result |= STYLE_INNER_CORNERS;
12181
12182     if (string_has_parameter(value, "reverse"))
12183       result |= STYLE_REVERSE;
12184
12185     if (string_has_parameter(value, "leftmost_position"))
12186       result |= STYLE_LEFTMOST_POSITION;
12187
12188     if (string_has_parameter(value, "block_clicks"))
12189       result |= STYLE_BLOCK;
12190
12191     if (string_has_parameter(value, "passthrough_clicks"))
12192       result |= STYLE_PASSTHROUGH;
12193
12194     if (string_has_parameter(value, "multiple_actions"))
12195       result |= STYLE_MULTIPLE_ACTIONS;
12196
12197     if (string_has_parameter(value, "consume_ce_event"))
12198       result |= STYLE_CONSUME_CE_EVENT;
12199   }
12200   else if (strEqual(suffix, ".fade_mode"))
12201   {
12202     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12203               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12204               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12205               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12206               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12207               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12208               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12209               FADE_MODE_DEFAULT);
12210   }
12211   else if (strEqual(suffix, ".auto_delay_unit"))
12212   {
12213     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12214               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12215               AUTO_DELAY_UNIT_DEFAULT);
12216   }
12217   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12218   {
12219     result = gfx.get_font_from_token_function(value);
12220   }
12221   else          // generic parameter of type integer or boolean
12222   {
12223     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12224               type == TYPE_INTEGER ? get_integer_from_string(value) :
12225               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12226               ARG_UNDEFINED_VALUE);
12227   }
12228
12229   free(value);
12230
12231   return result;
12232 }
12233
12234 static int get_token_parameter_value(char *token, char *value_raw)
12235 {
12236   char *suffix;
12237
12238   if (token == NULL || value_raw == NULL)
12239     return ARG_UNDEFINED_VALUE;
12240
12241   suffix = strrchr(token, '.');
12242   if (suffix == NULL)
12243     suffix = token;
12244
12245   if (strEqual(suffix, ".element"))
12246     return getElementFromToken(value_raw);
12247
12248   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12249   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12250 }
12251
12252 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12253                                      boolean ignore_defaults)
12254 {
12255   int i;
12256
12257   for (i = 0; image_config_vars[i].token != NULL; i++)
12258   {
12259     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12260
12261     // (ignore definitions set to "[DEFAULT]" which are already initialized)
12262     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12263       continue;
12264
12265     if (value != NULL)
12266       *image_config_vars[i].value =
12267         get_token_parameter_value(image_config_vars[i].token, value);
12268   }
12269 }
12270
12271 void InitMenuDesignSettings_Static(void)
12272 {
12273   // always start with reliable default values from static default config
12274   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12275 }
12276
12277 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12278 {
12279   int i;
12280
12281   // the following initializes hierarchical values from static configuration
12282
12283   // special case: initialize "ARG_DEFAULT" values in static default config
12284   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12285   titlescreen_initial_first_default.fade_mode  =
12286     title_initial_first_default.fade_mode;
12287   titlescreen_initial_first_default.fade_delay =
12288     title_initial_first_default.fade_delay;
12289   titlescreen_initial_first_default.post_delay =
12290     title_initial_first_default.post_delay;
12291   titlescreen_initial_first_default.auto_delay =
12292     title_initial_first_default.auto_delay;
12293   titlescreen_initial_first_default.auto_delay_unit =
12294     title_initial_first_default.auto_delay_unit;
12295   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
12296   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12297   titlescreen_first_default.post_delay = title_first_default.post_delay;
12298   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12299   titlescreen_first_default.auto_delay_unit =
12300     title_first_default.auto_delay_unit;
12301   titlemessage_initial_first_default.fade_mode  =
12302     title_initial_first_default.fade_mode;
12303   titlemessage_initial_first_default.fade_delay =
12304     title_initial_first_default.fade_delay;
12305   titlemessage_initial_first_default.post_delay =
12306     title_initial_first_default.post_delay;
12307   titlemessage_initial_first_default.auto_delay =
12308     title_initial_first_default.auto_delay;
12309   titlemessage_initial_first_default.auto_delay_unit =
12310     title_initial_first_default.auto_delay_unit;
12311   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
12312   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12313   titlemessage_first_default.post_delay = title_first_default.post_delay;
12314   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12315   titlemessage_first_default.auto_delay_unit =
12316     title_first_default.auto_delay_unit;
12317
12318   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
12319   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12320   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12321   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12322   titlescreen_initial_default.auto_delay_unit =
12323     title_initial_default.auto_delay_unit;
12324   titlescreen_default.fade_mode  = title_default.fade_mode;
12325   titlescreen_default.fade_delay = title_default.fade_delay;
12326   titlescreen_default.post_delay = title_default.post_delay;
12327   titlescreen_default.auto_delay = title_default.auto_delay;
12328   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12329   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
12330   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12331   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12332   titlemessage_initial_default.auto_delay_unit =
12333     title_initial_default.auto_delay_unit;
12334   titlemessage_default.fade_mode  = title_default.fade_mode;
12335   titlemessage_default.fade_delay = title_default.fade_delay;
12336   titlemessage_default.post_delay = title_default.post_delay;
12337   titlemessage_default.auto_delay = title_default.auto_delay;
12338   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12339
12340   // special case: initialize "ARG_DEFAULT" values in static default config
12341   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12342   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12343   {
12344     titlescreen_initial_first[i] = titlescreen_initial_first_default;
12345     titlescreen_first[i] = titlescreen_first_default;
12346     titlemessage_initial_first[i] = titlemessage_initial_first_default;
12347     titlemessage_first[i] = titlemessage_first_default;
12348
12349     titlescreen_initial[i] = titlescreen_initial_default;
12350     titlescreen[i] = titlescreen_default;
12351     titlemessage_initial[i] = titlemessage_initial_default;
12352     titlemessage[i] = titlemessage_default;
12353   }
12354
12355   // special case: initialize "ARG_DEFAULT" values in static default config
12356   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12357   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12358   {
12359     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
12360       continue;
12361
12362     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12363     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12364     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12365   }
12366
12367   // special case: initialize "ARG_DEFAULT" values in static default config
12368   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12369   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12370   {
12371     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12372     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12373     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12374
12375     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
12376       continue;
12377
12378     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12379   }
12380 }
12381
12382 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12383 {
12384   static struct
12385   {
12386     struct XY *dst, *src;
12387   }
12388   game_buttons_xy[] =
12389   {
12390     { &game.button.save,        &game.button.stop       },
12391     { &game.button.pause2,      &game.button.pause      },
12392     { &game.button.load,        &game.button.play       },
12393     { &game.button.undo,        &game.button.stop       },
12394     { &game.button.redo,        &game.button.play       },
12395
12396     { NULL,                     NULL                    }
12397   };
12398   int i, j;
12399
12400   // special case: initialize later added SETUP list size from LEVELS value
12401   if (menu.list_size[GAME_MODE_SETUP] == -1)
12402     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12403
12404   // set default position for snapshot buttons to stop/pause/play buttons
12405   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12406     if ((*game_buttons_xy[i].dst).x == -1 &&
12407         (*game_buttons_xy[i].dst).y == -1)
12408       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12409
12410   // --------------------------------------------------------------------------
12411   // dynamic viewports (including playfield margins, borders and alignments)
12412   // --------------------------------------------------------------------------
12413
12414   // dynamic viewports currently only supported for landscape mode
12415   int display_width  = MAX(video.display_width, video.display_height);
12416   int display_height = MIN(video.display_width, video.display_height);
12417
12418   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12419   {
12420     struct RectWithBorder *vp_window    = &viewport.window[i];
12421     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12422     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
12423     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
12424     boolean dynamic_window_width     = (vp_window->min_width     != -1);
12425     boolean dynamic_window_height    = (vp_window->min_height    != -1);
12426     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
12427     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12428
12429     // adjust window size if min/max width/height is specified
12430
12431     if (vp_window->min_width != -1)
12432     {
12433       int window_width = display_width;
12434
12435       // when using static window height, use aspect ratio of display
12436       if (vp_window->min_height == -1)
12437         window_width = vp_window->height * display_width / display_height;
12438
12439       vp_window->width = MAX(vp_window->min_width, window_width);
12440     }
12441
12442     if (vp_window->min_height != -1)
12443     {
12444       int window_height = display_height;
12445
12446       // when using static window width, use aspect ratio of display
12447       if (vp_window->min_width == -1)
12448         window_height = vp_window->width * display_height / display_width;
12449
12450       vp_window->height = MAX(vp_window->min_height, window_height);
12451     }
12452
12453     if (vp_window->max_width != -1)
12454       vp_window->width = MIN(vp_window->width, vp_window->max_width);
12455
12456     if (vp_window->max_height != -1)
12457       vp_window->height = MIN(vp_window->height, vp_window->max_height);
12458
12459     int playfield_width  = vp_window->width;
12460     int playfield_height = vp_window->height;
12461
12462     // adjust playfield size and position according to specified margins
12463
12464     playfield_width  -= vp_playfield->margin_left;
12465     playfield_width  -= vp_playfield->margin_right;
12466
12467     playfield_height -= vp_playfield->margin_top;
12468     playfield_height -= vp_playfield->margin_bottom;
12469
12470     // adjust playfield size if min/max width/height is specified
12471
12472     if (vp_playfield->min_width != -1)
12473       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12474
12475     if (vp_playfield->min_height != -1)
12476       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12477
12478     if (vp_playfield->max_width != -1)
12479       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12480
12481     if (vp_playfield->max_height != -1)
12482       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12483
12484     // adjust playfield position according to specified alignment
12485
12486     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12487       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12488     else if (vp_playfield->align == ALIGN_CENTER)
12489       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12490     else if (vp_playfield->align == ALIGN_RIGHT)
12491       vp_playfield->x += playfield_width - vp_playfield->width;
12492
12493     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12494       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12495     else if (vp_playfield->valign == VALIGN_MIDDLE)
12496       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12497     else if (vp_playfield->valign == VALIGN_BOTTOM)
12498       vp_playfield->y += playfield_height - vp_playfield->height;
12499
12500     vp_playfield->x += vp_playfield->margin_left;
12501     vp_playfield->y += vp_playfield->margin_top;
12502
12503     // adjust individual playfield borders if only default border is specified
12504
12505     if (vp_playfield->border_left == -1)
12506       vp_playfield->border_left = vp_playfield->border_size;
12507     if (vp_playfield->border_right == -1)
12508       vp_playfield->border_right = vp_playfield->border_size;
12509     if (vp_playfield->border_top == -1)
12510       vp_playfield->border_top = vp_playfield->border_size;
12511     if (vp_playfield->border_bottom == -1)
12512       vp_playfield->border_bottom = vp_playfield->border_size;
12513
12514     // set dynamic playfield borders if borders are specified as undefined
12515     // (but only if window size was dynamic and playfield size was static)
12516
12517     if (dynamic_window_width && !dynamic_playfield_width)
12518     {
12519       if (vp_playfield->border_left == -1)
12520       {
12521         vp_playfield->border_left = (vp_playfield->x -
12522                                      vp_playfield->margin_left);
12523         vp_playfield->x     -= vp_playfield->border_left;
12524         vp_playfield->width += vp_playfield->border_left;
12525       }
12526
12527       if (vp_playfield->border_right == -1)
12528       {
12529         vp_playfield->border_right = (vp_window->width -
12530                                       vp_playfield->x -
12531                                       vp_playfield->width -
12532                                       vp_playfield->margin_right);
12533         vp_playfield->width += vp_playfield->border_right;
12534       }
12535     }
12536
12537     if (dynamic_window_height && !dynamic_playfield_height)
12538     {
12539       if (vp_playfield->border_top == -1)
12540       {
12541         vp_playfield->border_top = (vp_playfield->y -
12542                                     vp_playfield->margin_top);
12543         vp_playfield->y      -= vp_playfield->border_top;
12544         vp_playfield->height += vp_playfield->border_top;
12545       }
12546
12547       if (vp_playfield->border_bottom == -1)
12548       {
12549         vp_playfield->border_bottom = (vp_window->height -
12550                                        vp_playfield->y -
12551                                        vp_playfield->height -
12552                                        vp_playfield->margin_bottom);
12553         vp_playfield->height += vp_playfield->border_bottom;
12554       }
12555     }
12556
12557     // adjust playfield size to be a multiple of a defined alignment tile size
12558
12559     int align_size = vp_playfield->align_size;
12560     int playfield_xtiles = vp_playfield->width  / align_size;
12561     int playfield_ytiles = vp_playfield->height / align_size;
12562     int playfield_width_corrected  = playfield_xtiles * align_size;
12563     int playfield_height_corrected = playfield_ytiles * align_size;
12564     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12565                                  i == GFX_SPECIAL_ARG_EDITOR);
12566
12567     if (is_playfield_mode &&
12568         dynamic_playfield_width &&
12569         vp_playfield->width != playfield_width_corrected)
12570     {
12571       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12572
12573       vp_playfield->width = playfield_width_corrected;
12574
12575       if (vp_playfield->align == ALIGN_LEFT)
12576       {
12577         vp_playfield->border_left += playfield_xdiff;
12578       }
12579       else if (vp_playfield->align == ALIGN_RIGHT)
12580       {
12581         vp_playfield->border_right += playfield_xdiff;
12582       }
12583       else if (vp_playfield->align == ALIGN_CENTER)
12584       {
12585         int border_left_diff  = playfield_xdiff / 2;
12586         int border_right_diff = playfield_xdiff - border_left_diff;
12587
12588         vp_playfield->border_left  += border_left_diff;
12589         vp_playfield->border_right += border_right_diff;
12590       }
12591     }
12592
12593     if (is_playfield_mode &&
12594         dynamic_playfield_height &&
12595         vp_playfield->height != playfield_height_corrected)
12596     {
12597       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12598
12599       vp_playfield->height = playfield_height_corrected;
12600
12601       if (vp_playfield->valign == VALIGN_TOP)
12602       {
12603         vp_playfield->border_top += playfield_ydiff;
12604       }
12605       else if (vp_playfield->align == VALIGN_BOTTOM)
12606       {
12607         vp_playfield->border_right += playfield_ydiff;
12608       }
12609       else if (vp_playfield->align == VALIGN_MIDDLE)
12610       {
12611         int border_top_diff    = playfield_ydiff / 2;
12612         int border_bottom_diff = playfield_ydiff - border_top_diff;
12613
12614         vp_playfield->border_top    += border_top_diff;
12615         vp_playfield->border_bottom += border_bottom_diff;
12616       }
12617     }
12618
12619     // adjust door positions according to specified alignment
12620
12621     for (j = 0; j < 2; j++)
12622     {
12623       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12624
12625       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12626         vp_door->x = ALIGNED_VP_XPOS(vp_door);
12627       else if (vp_door->align == ALIGN_CENTER)
12628         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12629       else if (vp_door->align == ALIGN_RIGHT)
12630         vp_door->x += vp_window->width - vp_door->width;
12631
12632       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12633         vp_door->y = ALIGNED_VP_YPOS(vp_door);
12634       else if (vp_door->valign == VALIGN_MIDDLE)
12635         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12636       else if (vp_door->valign == VALIGN_BOTTOM)
12637         vp_door->y += vp_window->height - vp_door->height;
12638     }
12639   }
12640 }
12641
12642 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12643 {
12644   static struct
12645   {
12646     struct XYTileSize *dst, *src;
12647     int graphic;
12648   }
12649   editor_buttons_xy[] =
12650   {
12651     {
12652       &editor.button.element_left,      &editor.palette.element_left,
12653       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12654     },
12655     {
12656       &editor.button.element_middle,    &editor.palette.element_middle,
12657       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12658     },
12659     {
12660       &editor.button.element_right,     &editor.palette.element_right,
12661       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12662     },
12663
12664     { NULL,                     NULL                    }
12665   };
12666   int i;
12667
12668   // set default position for element buttons to element graphics
12669   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12670   {
12671     if ((*editor_buttons_xy[i].dst).x == -1 &&
12672         (*editor_buttons_xy[i].dst).y == -1)
12673     {
12674       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12675
12676       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12677
12678       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12679     }
12680   }
12681
12682   // adjust editor palette rows and columns if specified to be dynamic
12683
12684   if (editor.palette.cols == -1)
12685   {
12686     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12687     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12688     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12689
12690     editor.palette.cols = (vp_width - sc_width) / bt_width;
12691
12692     if (editor.palette.x == -1)
12693     {
12694       int palette_width = editor.palette.cols * bt_width + sc_width;
12695
12696       editor.palette.x = (vp_width - palette_width) / 2;
12697     }
12698   }
12699
12700   if (editor.palette.rows == -1)
12701   {
12702     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12703     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12704     int tx_height = getFontHeight(FONT_TEXT_2);
12705
12706     editor.palette.rows = (vp_height - tx_height) / bt_height;
12707
12708     if (editor.palette.y == -1)
12709     {
12710       int palette_height = editor.palette.rows * bt_height + tx_height;
12711
12712       editor.palette.y = (vp_height - palette_height) / 2;
12713     }
12714   }
12715 }
12716
12717 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12718                                                       boolean initialize)
12719 {
12720   // special case: check if network and preview player positions are redefined,
12721   // to compare this later against the main menu level preview being redefined
12722   struct TokenIntPtrInfo menu_config_players[] =
12723   {
12724     { "main.network_players.x", &menu.main.network_players.redefined    },
12725     { "main.network_players.y", &menu.main.network_players.redefined    },
12726     { "main.preview_players.x", &menu.main.preview_players.redefined    },
12727     { "main.preview_players.y", &menu.main.preview_players.redefined    },
12728     { "preview.x",              &preview.redefined                      },
12729     { "preview.y",              &preview.redefined                      }
12730   };
12731   int i;
12732
12733   if (initialize)
12734   {
12735     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12736       *menu_config_players[i].value = FALSE;
12737   }
12738   else
12739   {
12740     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12741       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12742         *menu_config_players[i].value = TRUE;
12743   }
12744 }
12745
12746 static void InitMenuDesignSettings_PreviewPlayers(void)
12747 {
12748   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12749 }
12750
12751 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12752 {
12753   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12754 }
12755
12756 static void LoadMenuDesignSettingsFromFilename(char *filename)
12757 {
12758   static struct TitleFadingInfo tfi;
12759   static struct TitleMessageInfo tmi;
12760   static struct TokenInfo title_tokens[] =
12761   {
12762     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
12763     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
12764     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
12765     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
12766     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
12767
12768     { -1,               NULL,                   NULL                    }
12769   };
12770   static struct TokenInfo titlemessage_tokens[] =
12771   {
12772     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
12773     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
12774     { TYPE_INTEGER,     &tmi.width,             ".width"                },
12775     { TYPE_INTEGER,     &tmi.height,            ".height"               },
12776     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
12777     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
12778     { TYPE_INTEGER,     &tmi.align,             ".align"                },
12779     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
12780     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
12781     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
12782     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
12783     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
12784     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
12785     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
12786     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
12787     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
12788     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
12789     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
12790
12791     { -1,               NULL,                   NULL                    }
12792   };
12793   static struct
12794   {
12795     struct TitleFadingInfo *info;
12796     char *text;
12797   }
12798   title_info[] =
12799   {
12800     // initialize first titles from "enter screen" definitions, if defined
12801     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
12802     { &title_first_default,             "menu.enter_screen.TITLE"       },
12803
12804     // initialize title screens from "next screen" definitions, if defined
12805     { &title_initial_default,           "menu.next_screen.TITLE"        },
12806     { &title_default,                   "menu.next_screen.TITLE"        },
12807
12808     { NULL,                             NULL                            }
12809   };
12810   static struct
12811   {
12812     struct TitleMessageInfo *array;
12813     char *text;
12814   }
12815   titlemessage_arrays[] =
12816   {
12817     // initialize first titles from "enter screen" definitions, if defined
12818     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
12819     { titlescreen_first,                "menu.enter_screen.TITLE"       },
12820     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
12821     { titlemessage_first,               "menu.enter_screen.TITLE"       },
12822
12823     // initialize titles from "next screen" definitions, if defined
12824     { titlescreen_initial,              "menu.next_screen.TITLE"        },
12825     { titlescreen,                      "menu.next_screen.TITLE"        },
12826     { titlemessage_initial,             "menu.next_screen.TITLE"        },
12827     { titlemessage,                     "menu.next_screen.TITLE"        },
12828
12829     // overwrite titles with title definitions, if defined
12830     { titlescreen_initial_first,        "[title_initial]"               },
12831     { titlescreen_first,                "[title]"                       },
12832     { titlemessage_initial_first,       "[title_initial]"               },
12833     { titlemessage_first,               "[title]"                       },
12834
12835     { titlescreen_initial,              "[title_initial]"               },
12836     { titlescreen,                      "[title]"                       },
12837     { titlemessage_initial,             "[title_initial]"               },
12838     { titlemessage,                     "[title]"                       },
12839
12840     // overwrite titles with title screen/message definitions, if defined
12841     { titlescreen_initial_first,        "[titlescreen_initial]"         },
12842     { titlescreen_first,                "[titlescreen]"                 },
12843     { titlemessage_initial_first,       "[titlemessage_initial]"        },
12844     { titlemessage_first,               "[titlemessage]"                },
12845
12846     { titlescreen_initial,              "[titlescreen_initial]"         },
12847     { titlescreen,                      "[titlescreen]"                 },
12848     { titlemessage_initial,             "[titlemessage_initial]"        },
12849     { titlemessage,                     "[titlemessage]"                },
12850
12851     { NULL,                             NULL                            }
12852   };
12853   SetupFileHash *setup_file_hash;
12854   int i, j, k;
12855
12856   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12857     return;
12858
12859   // the following initializes hierarchical values from dynamic configuration
12860
12861   // special case: initialize with default values that may be overwritten
12862   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12863   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12864   {
12865     struct TokenIntPtrInfo menu_config[] =
12866     {
12867       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
12868       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
12869       { "menu.list_size",       &menu.list_size[i]      }
12870     };
12871
12872     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12873     {
12874       char *token = menu_config[j].token;
12875       char *value = getHashEntry(setup_file_hash, token);
12876
12877       if (value != NULL)
12878         *menu_config[j].value = get_integer_from_string(value);
12879     }
12880   }
12881
12882   // special case: initialize with default values that may be overwritten
12883   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12884   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12885   {
12886     struct TokenIntPtrInfo menu_config[] =
12887     {
12888       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
12889       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
12890       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
12891       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
12892       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
12893     };
12894
12895     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12896     {
12897       char *token = menu_config[j].token;
12898       char *value = getHashEntry(setup_file_hash, token);
12899
12900       if (value != NULL)
12901         *menu_config[j].value = get_integer_from_string(value);
12902     }
12903   }
12904
12905   // special case: initialize with default values that may be overwritten
12906   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12907   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12908   {
12909     struct TokenIntPtrInfo menu_config[] =
12910     {
12911       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
12912       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
12913     };
12914
12915     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12916     {
12917       char *token = menu_config[j].token;
12918       char *value = getHashEntry(setup_file_hash, token);
12919
12920       if (value != NULL)
12921         *menu_config[j].value = get_integer_from_string(value);
12922     }
12923   }
12924
12925   // special case: initialize with default values that may be overwritten
12926   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12927   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12928   {
12929     struct TokenIntPtrInfo menu_config[] =
12930     {
12931       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
12932       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
12933       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
12934       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
12935       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
12936       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
12937       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
12938       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
12939       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
12940       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
12941     };
12942
12943     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12944     {
12945       char *token = menu_config[j].token;
12946       char *value = getHashEntry(setup_file_hash, token);
12947
12948       if (value != NULL)
12949         *menu_config[j].value = get_integer_from_string(value);
12950     }
12951   }
12952
12953   // special case: initialize with default values that may be overwritten
12954   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12955   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12956   {
12957     struct TokenIntPtrInfo menu_config[] =
12958     {
12959       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
12960       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12961       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12962       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
12963       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12964       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12965       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
12966       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
12967       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
12968     };
12969
12970     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12971     {
12972       char *token = menu_config[j].token;
12973       char *value = getHashEntry(setup_file_hash, token);
12974
12975       if (value != NULL)
12976         *menu_config[j].value = get_token_parameter_value(token, value);
12977     }
12978   }
12979
12980   // special case: initialize with default values that may be overwritten
12981   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12982   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12983   {
12984     struct
12985     {
12986       char *token_prefix;
12987       struct RectWithBorder *struct_ptr;
12988     }
12989     vp_struct[] =
12990     {
12991       { "viewport.window",      &viewport.window[i]     },
12992       { "viewport.playfield",   &viewport.playfield[i]  },
12993       { "viewport.door_1",      &viewport.door_1[i]     },
12994       { "viewport.door_2",      &viewport.door_2[i]     }
12995     };
12996
12997     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12998     {
12999       struct TokenIntPtrInfo vp_config[] =
13000       {
13001         { ".x",                 &vp_struct[j].struct_ptr->x             },
13002         { ".y",                 &vp_struct[j].struct_ptr->y             },
13003         { ".width",             &vp_struct[j].struct_ptr->width         },
13004         { ".height",            &vp_struct[j].struct_ptr->height        },
13005         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13006         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13007         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13008         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13009         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13010         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13011         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13012         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13013         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13014         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13015         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13016         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13017         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13018         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13019         { ".align",             &vp_struct[j].struct_ptr->align         },
13020         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13021       };
13022
13023       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13024       {
13025         char *token = getStringCat2(vp_struct[j].token_prefix,
13026                                     vp_config[k].token);
13027         char *value = getHashEntry(setup_file_hash, token);
13028
13029         if (value != NULL)
13030           *vp_config[k].value = get_token_parameter_value(token, value);
13031
13032         free(token);
13033       }
13034     }
13035   }
13036
13037   // special case: initialize with default values that may be overwritten
13038   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13039   for (i = 0; title_info[i].info != NULL; i++)
13040   {
13041     struct TitleFadingInfo *info = title_info[i].info;
13042     char *base_token = title_info[i].text;
13043
13044     for (j = 0; title_tokens[j].type != -1; j++)
13045     {
13046       char *token = getStringCat2(base_token, title_tokens[j].text);
13047       char *value = getHashEntry(setup_file_hash, token);
13048
13049       if (value != NULL)
13050       {
13051         int parameter_value = get_token_parameter_value(token, value);
13052
13053         tfi = *info;
13054
13055         *(int *)title_tokens[j].value = (int)parameter_value;
13056
13057         *info = tfi;
13058       }
13059
13060       free(token);
13061     }
13062   }
13063
13064   // special case: initialize with default values that may be overwritten
13065   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13066   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13067   {
13068     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13069     char *base_token = titlemessage_arrays[i].text;
13070
13071     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13072     {
13073       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13074       char *value = getHashEntry(setup_file_hash, token);
13075
13076       if (value != NULL)
13077       {
13078         int parameter_value = get_token_parameter_value(token, value);
13079
13080         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13081         {
13082           tmi = array[k];
13083
13084           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13085             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13086           else
13087             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13088
13089           array[k] = tmi;
13090         }
13091       }
13092
13093       free(token);
13094     }
13095   }
13096
13097   // read (and overwrite with) values that may be specified in config file
13098   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13099
13100   // special case: check if network and preview player positions are redefined
13101   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13102
13103   freeSetupFileHash(setup_file_hash);
13104 }
13105
13106 void LoadMenuDesignSettings(void)
13107 {
13108   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13109
13110   InitMenuDesignSettings_Static();
13111   InitMenuDesignSettings_SpecialPreProcessing();
13112   InitMenuDesignSettings_PreviewPlayers();
13113
13114   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13115   {
13116     // first look for special settings configured in level series config
13117     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13118
13119     if (fileExists(filename_base))
13120       LoadMenuDesignSettingsFromFilename(filename_base);
13121   }
13122
13123   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13124
13125   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13126     LoadMenuDesignSettingsFromFilename(filename_local);
13127
13128   InitMenuDesignSettings_SpecialPostProcessing();
13129 }
13130
13131 void LoadMenuDesignSettings_AfterGraphics(void)
13132 {
13133   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13134 }
13135
13136 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13137                                 boolean ignore_defaults)
13138 {
13139   int i;
13140
13141   for (i = 0; sound_config_vars[i].token != NULL; i++)
13142   {
13143     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13144
13145     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13146     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13147       continue;
13148
13149     if (value != NULL)
13150       *sound_config_vars[i].value =
13151         get_token_parameter_value(sound_config_vars[i].token, value);
13152   }
13153 }
13154
13155 void InitSoundSettings_Static(void)
13156 {
13157   // always start with reliable default values from static default config
13158   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13159 }
13160
13161 static void LoadSoundSettingsFromFilename(char *filename)
13162 {
13163   SetupFileHash *setup_file_hash;
13164
13165   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13166     return;
13167
13168   // read (and overwrite with) values that may be specified in config file
13169   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13170
13171   freeSetupFileHash(setup_file_hash);
13172 }
13173
13174 void LoadSoundSettings(void)
13175 {
13176   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13177
13178   InitSoundSettings_Static();
13179
13180   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13181   {
13182     // first look for special settings configured in level series config
13183     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13184
13185     if (fileExists(filename_base))
13186       LoadSoundSettingsFromFilename(filename_base);
13187   }
13188
13189   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13190
13191   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13192     LoadSoundSettingsFromFilename(filename_local);
13193 }
13194
13195 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13196 {
13197   char *filename = getEditorSetupFilename();
13198   SetupFileList *setup_file_list, *list;
13199   SetupFileHash *element_hash;
13200   int num_unknown_tokens = 0;
13201   int i;
13202
13203   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13204     return;
13205
13206   element_hash = newSetupFileHash();
13207
13208   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13209     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13210
13211   // determined size may be larger than needed (due to unknown elements)
13212   *num_elements = 0;
13213   for (list = setup_file_list; list != NULL; list = list->next)
13214     (*num_elements)++;
13215
13216   // add space for up to 3 more elements for padding that may be needed
13217   *num_elements += 3;
13218
13219   // free memory for old list of elements, if needed
13220   checked_free(*elements);
13221
13222   // allocate memory for new list of elements
13223   *elements = checked_malloc(*num_elements * sizeof(int));
13224
13225   *num_elements = 0;
13226   for (list = setup_file_list; list != NULL; list = list->next)
13227   {
13228     char *value = getHashEntry(element_hash, list->token);
13229
13230     if (value == NULL)          // try to find obsolete token mapping
13231     {
13232       char *mapped_token = get_mapped_token(list->token);
13233
13234       if (mapped_token != NULL)
13235       {
13236         value = getHashEntry(element_hash, mapped_token);
13237
13238         free(mapped_token);
13239       }
13240     }
13241
13242     if (value != NULL)
13243     {
13244       (*elements)[(*num_elements)++] = atoi(value);
13245     }
13246     else
13247     {
13248       if (num_unknown_tokens == 0)
13249       {
13250         Warn("---");
13251         Warn("unknown token(s) found in config file:");
13252         Warn("- config file: '%s'", filename);
13253
13254         num_unknown_tokens++;
13255       }
13256
13257       Warn("- token: '%s'", list->token);
13258     }
13259   }
13260
13261   if (num_unknown_tokens > 0)
13262     Warn("---");
13263
13264   while (*num_elements % 4)     // pad with empty elements, if needed
13265     (*elements)[(*num_elements)++] = EL_EMPTY;
13266
13267   freeSetupFileList(setup_file_list);
13268   freeSetupFileHash(element_hash);
13269
13270 #if 0
13271   for (i = 0; i < *num_elements; i++)
13272     Debug("editor", "element '%s' [%d]\n",
13273           element_info[(*elements)[i]].token_name, (*elements)[i]);
13274 #endif
13275 }
13276
13277 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13278                                                      boolean is_sound)
13279 {
13280   SetupFileHash *setup_file_hash = NULL;
13281   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13282   char *filename_music, *filename_prefix, *filename_info;
13283   struct
13284   {
13285     char *token;
13286     char **value_ptr;
13287   }
13288   token_to_value_ptr[] =
13289   {
13290     { "title_header",   &tmp_music_file_info.title_header       },
13291     { "artist_header",  &tmp_music_file_info.artist_header      },
13292     { "album_header",   &tmp_music_file_info.album_header       },
13293     { "year_header",    &tmp_music_file_info.year_header        },
13294     { "played_header",  &tmp_music_file_info.played_header      },
13295
13296     { "title",          &tmp_music_file_info.title              },
13297     { "artist",         &tmp_music_file_info.artist             },
13298     { "album",          &tmp_music_file_info.album              },
13299     { "year",           &tmp_music_file_info.year               },
13300     { "played",         &tmp_music_file_info.played             },
13301
13302     { NULL,             NULL                                    },
13303   };
13304   int i;
13305
13306   filename_music = (is_sound ? getCustomSoundFilename(basename) :
13307                     getCustomMusicFilename(basename));
13308
13309   if (filename_music == NULL)
13310     return NULL;
13311
13312   // ---------- try to replace file extension ----------
13313
13314   filename_prefix = getStringCopy(filename_music);
13315   if (strrchr(filename_prefix, '.') != NULL)
13316     *strrchr(filename_prefix, '.') = '\0';
13317   filename_info = getStringCat2(filename_prefix, ".txt");
13318
13319   if (fileExists(filename_info))
13320     setup_file_hash = loadSetupFileHash(filename_info);
13321
13322   free(filename_prefix);
13323   free(filename_info);
13324
13325   if (setup_file_hash == NULL)
13326   {
13327     // ---------- try to add file extension ----------
13328
13329     filename_prefix = getStringCopy(filename_music);
13330     filename_info = getStringCat2(filename_prefix, ".txt");
13331
13332     if (fileExists(filename_info))
13333       setup_file_hash = loadSetupFileHash(filename_info);
13334
13335     free(filename_prefix);
13336     free(filename_info);
13337   }
13338
13339   if (setup_file_hash == NULL)
13340     return NULL;
13341
13342   // ---------- music file info found ----------
13343
13344   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13345
13346   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13347   {
13348     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13349
13350     *token_to_value_ptr[i].value_ptr =
13351       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13352   }
13353
13354   tmp_music_file_info.basename = getStringCopy(basename);
13355   tmp_music_file_info.music = music;
13356   tmp_music_file_info.is_sound = is_sound;
13357
13358   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13359   *new_music_file_info = tmp_music_file_info;
13360
13361   return new_music_file_info;
13362 }
13363
13364 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13365 {
13366   return get_music_file_info_ext(basename, music, FALSE);
13367 }
13368
13369 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13370 {
13371   return get_music_file_info_ext(basename, sound, TRUE);
13372 }
13373
13374 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13375                                      char *basename, boolean is_sound)
13376 {
13377   for (; list != NULL; list = list->next)
13378     if (list->is_sound == is_sound && strEqual(list->basename, basename))
13379       return TRUE;
13380
13381   return FALSE;
13382 }
13383
13384 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13385 {
13386   return music_info_listed_ext(list, basename, FALSE);
13387 }
13388
13389 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13390 {
13391   return music_info_listed_ext(list, basename, TRUE);
13392 }
13393
13394 void LoadMusicInfo(void)
13395 {
13396   int num_music_noconf = getMusicListSize_NoConf();
13397   int num_music = getMusicListSize();
13398   int num_sounds = getSoundListSize();
13399   struct FileInfo *music, *sound;
13400   struct MusicFileInfo *next, **new;
13401
13402   int i;
13403
13404   while (music_file_info != NULL)
13405   {
13406     next = music_file_info->next;
13407
13408     checked_free(music_file_info->basename);
13409
13410     checked_free(music_file_info->title_header);
13411     checked_free(music_file_info->artist_header);
13412     checked_free(music_file_info->album_header);
13413     checked_free(music_file_info->year_header);
13414     checked_free(music_file_info->played_header);
13415
13416     checked_free(music_file_info->title);
13417     checked_free(music_file_info->artist);
13418     checked_free(music_file_info->album);
13419     checked_free(music_file_info->year);
13420     checked_free(music_file_info->played);
13421
13422     free(music_file_info);
13423
13424     music_file_info = next;
13425   }
13426
13427   new = &music_file_info;
13428
13429   // get (configured or unconfigured) music file info for all levels
13430   for (i = leveldir_current->first_level;
13431        i <= leveldir_current->last_level; i++)
13432   {
13433     int music_nr;
13434
13435     if (levelset.music[i] != MUS_UNDEFINED)
13436     {
13437       // get music file info for configured level music
13438       music_nr = levelset.music[i];
13439     }
13440     else if (num_music_noconf > 0)
13441     {
13442       // get music file info for unconfigured level music
13443       int level_pos = i - leveldir_current->first_level;
13444
13445       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13446     }
13447     else
13448     {
13449       continue;
13450     }
13451
13452     char *basename = getMusicInfoEntryFilename(music_nr);
13453
13454     if (basename == NULL)
13455       continue;
13456
13457     if (!music_info_listed(music_file_info, basename))
13458     {
13459       *new = get_music_file_info(basename, music_nr);
13460
13461       if (*new != NULL)
13462         new = &(*new)->next;
13463     }
13464   }
13465
13466   // get music file info for all remaining configured music files
13467   for (i = 0; i < num_music; i++)
13468   {
13469     music = getMusicListEntry(i);
13470
13471     if (music->filename == NULL)
13472       continue;
13473
13474     if (strEqual(music->filename, UNDEFINED_FILENAME))
13475       continue;
13476
13477     // a configured file may be not recognized as music
13478     if (!FileIsMusic(music->filename))
13479       continue;
13480
13481     if (!music_info_listed(music_file_info, music->filename))
13482     {
13483       *new = get_music_file_info(music->filename, i);
13484
13485       if (*new != NULL)
13486         new = &(*new)->next;
13487     }
13488   }
13489
13490   // get sound file info for all configured sound files
13491   for (i = 0; i < num_sounds; i++)
13492   {
13493     sound = getSoundListEntry(i);
13494
13495     if (sound->filename == NULL)
13496       continue;
13497
13498     if (strEqual(sound->filename, UNDEFINED_FILENAME))
13499       continue;
13500
13501     // a configured file may be not recognized as sound
13502     if (!FileIsSound(sound->filename))
13503       continue;
13504
13505     if (!sound_info_listed(music_file_info, sound->filename))
13506     {
13507       *new = get_sound_file_info(sound->filename, i);
13508       if (*new != NULL)
13509         new = &(*new)->next;
13510     }
13511   }
13512
13513   // add pointers to previous list nodes
13514
13515   struct MusicFileInfo *node = music_file_info;
13516
13517   while (node != NULL)
13518   {
13519     if (node->next)
13520       node->next->prev = node;
13521
13522     node = node->next;
13523   }
13524 }
13525
13526 static void add_helpanim_entry(int element, int action, int direction,
13527                                int delay, int *num_list_entries)
13528 {
13529   struct HelpAnimInfo *new_list_entry;
13530   (*num_list_entries)++;
13531
13532   helpanim_info =
13533     checked_realloc(helpanim_info,
13534                     *num_list_entries * sizeof(struct HelpAnimInfo));
13535   new_list_entry = &helpanim_info[*num_list_entries - 1];
13536
13537   new_list_entry->element = element;
13538   new_list_entry->action = action;
13539   new_list_entry->direction = direction;
13540   new_list_entry->delay = delay;
13541 }
13542
13543 static void print_unknown_token(char *filename, char *token, int token_nr)
13544 {
13545   if (token_nr == 0)
13546   {
13547     Warn("---");
13548     Warn("unknown token(s) found in config file:");
13549     Warn("- config file: '%s'", filename);
13550   }
13551
13552   Warn("- token: '%s'", token);
13553 }
13554
13555 static void print_unknown_token_end(int token_nr)
13556 {
13557   if (token_nr > 0)
13558     Warn("---");
13559 }
13560
13561 void LoadHelpAnimInfo(void)
13562 {
13563   char *filename = getHelpAnimFilename();
13564   SetupFileList *setup_file_list = NULL, *list;
13565   SetupFileHash *element_hash, *action_hash, *direction_hash;
13566   int num_list_entries = 0;
13567   int num_unknown_tokens = 0;
13568   int i;
13569
13570   if (fileExists(filename))
13571     setup_file_list = loadSetupFileList(filename);
13572
13573   if (setup_file_list == NULL)
13574   {
13575     // use reliable default values from static configuration
13576     SetupFileList *insert_ptr;
13577
13578     insert_ptr = setup_file_list =
13579       newSetupFileList(helpanim_config[0].token,
13580                        helpanim_config[0].value);
13581
13582     for (i = 1; helpanim_config[i].token; i++)
13583       insert_ptr = addListEntry(insert_ptr,
13584                                 helpanim_config[i].token,
13585                                 helpanim_config[i].value);
13586   }
13587
13588   element_hash   = newSetupFileHash();
13589   action_hash    = newSetupFileHash();
13590   direction_hash = newSetupFileHash();
13591
13592   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13593     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13594
13595   for (i = 0; i < NUM_ACTIONS; i++)
13596     setHashEntry(action_hash, element_action_info[i].suffix,
13597                  i_to_a(element_action_info[i].value));
13598
13599   // do not store direction index (bit) here, but direction value!
13600   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13601     setHashEntry(direction_hash, element_direction_info[i].suffix,
13602                  i_to_a(1 << element_direction_info[i].value));
13603
13604   for (list = setup_file_list; list != NULL; list = list->next)
13605   {
13606     char *element_token, *action_token, *direction_token;
13607     char *element_value, *action_value, *direction_value;
13608     int delay = atoi(list->value);
13609
13610     if (strEqual(list->token, "end"))
13611     {
13612       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13613
13614       continue;
13615     }
13616
13617     /* first try to break element into element/action/direction parts;
13618        if this does not work, also accept combined "element[.act][.dir]"
13619        elements (like "dynamite.active"), which are unique elements */
13620
13621     if (strchr(list->token, '.') == NULL)       // token contains no '.'
13622     {
13623       element_value = getHashEntry(element_hash, list->token);
13624       if (element_value != NULL)        // element found
13625         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13626                            &num_list_entries);
13627       else
13628       {
13629         // no further suffixes found -- this is not an element
13630         print_unknown_token(filename, list->token, num_unknown_tokens++);
13631       }
13632
13633       continue;
13634     }
13635
13636     // token has format "<prefix>.<something>"
13637
13638     action_token = strchr(list->token, '.');    // suffix may be action ...
13639     direction_token = action_token;             // ... or direction
13640
13641     element_token = getStringCopy(list->token);
13642     *strchr(element_token, '.') = '\0';
13643
13644     element_value = getHashEntry(element_hash, element_token);
13645
13646     if (element_value == NULL)          // this is no element
13647     {
13648       element_value = getHashEntry(element_hash, list->token);
13649       if (element_value != NULL)        // combined element found
13650         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13651                            &num_list_entries);
13652       else
13653         print_unknown_token(filename, list->token, num_unknown_tokens++);
13654
13655       free(element_token);
13656
13657       continue;
13658     }
13659
13660     action_value = getHashEntry(action_hash, action_token);
13661
13662     if (action_value != NULL)           // action found
13663     {
13664       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13665                     &num_list_entries);
13666
13667       free(element_token);
13668
13669       continue;
13670     }
13671
13672     direction_value = getHashEntry(direction_hash, direction_token);
13673
13674     if (direction_value != NULL)        // direction found
13675     {
13676       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13677                          &num_list_entries);
13678
13679       free(element_token);
13680
13681       continue;
13682     }
13683
13684     if (strchr(action_token + 1, '.') == NULL)
13685     {
13686       // no further suffixes found -- this is not an action nor direction
13687
13688       element_value = getHashEntry(element_hash, list->token);
13689       if (element_value != NULL)        // combined element found
13690         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13691                            &num_list_entries);
13692       else
13693         print_unknown_token(filename, list->token, num_unknown_tokens++);
13694
13695       free(element_token);
13696
13697       continue;
13698     }
13699
13700     // token has format "<prefix>.<suffix>.<something>"
13701
13702     direction_token = strchr(action_token + 1, '.');
13703
13704     action_token = getStringCopy(action_token);
13705     *strchr(action_token + 1, '.') = '\0';
13706
13707     action_value = getHashEntry(action_hash, action_token);
13708
13709     if (action_value == NULL)           // this is no action
13710     {
13711       element_value = getHashEntry(element_hash, list->token);
13712       if (element_value != NULL)        // combined element found
13713         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13714                            &num_list_entries);
13715       else
13716         print_unknown_token(filename, list->token, num_unknown_tokens++);
13717
13718       free(element_token);
13719       free(action_token);
13720
13721       continue;
13722     }
13723
13724     direction_value = getHashEntry(direction_hash, direction_token);
13725
13726     if (direction_value != NULL)        // direction found
13727     {
13728       add_helpanim_entry(atoi(element_value), atoi(action_value),
13729                          atoi(direction_value), delay, &num_list_entries);
13730
13731       free(element_token);
13732       free(action_token);
13733
13734       continue;
13735     }
13736
13737     // this is no direction
13738
13739     element_value = getHashEntry(element_hash, list->token);
13740     if (element_value != NULL)          // combined element found
13741       add_helpanim_entry(atoi(element_value), -1, -1, delay,
13742                          &num_list_entries);
13743     else
13744       print_unknown_token(filename, list->token, num_unknown_tokens++);
13745
13746     free(element_token);
13747     free(action_token);
13748   }
13749
13750   print_unknown_token_end(num_unknown_tokens);
13751
13752   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13753   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
13754
13755   freeSetupFileList(setup_file_list);
13756   freeSetupFileHash(element_hash);
13757   freeSetupFileHash(action_hash);
13758   freeSetupFileHash(direction_hash);
13759
13760 #if 0
13761   for (i = 0; i < num_list_entries; i++)
13762     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13763           EL_NAME(helpanim_info[i].element),
13764           helpanim_info[i].element,
13765           helpanim_info[i].action,
13766           helpanim_info[i].direction,
13767           helpanim_info[i].delay);
13768 #endif
13769 }
13770
13771 void LoadHelpTextInfo(void)
13772 {
13773   char *filename = getHelpTextFilename();
13774   int i;
13775
13776   if (helptext_info != NULL)
13777   {
13778     freeSetupFileHash(helptext_info);
13779     helptext_info = NULL;
13780   }
13781
13782   if (fileExists(filename))
13783     helptext_info = loadSetupFileHash(filename);
13784
13785   if (helptext_info == NULL)
13786   {
13787     // use reliable default values from static configuration
13788     helptext_info = newSetupFileHash();
13789
13790     for (i = 0; helptext_config[i].token; i++)
13791       setHashEntry(helptext_info,
13792                    helptext_config[i].token,
13793                    helptext_config[i].value);
13794   }
13795
13796 #if 0
13797   BEGIN_HASH_ITERATION(helptext_info, itr)
13798   {
13799     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13800           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13801   }
13802   END_HASH_ITERATION(hash, itr)
13803 #endif
13804 }
13805
13806
13807 // ----------------------------------------------------------------------------
13808 // convert levels
13809 // ----------------------------------------------------------------------------
13810
13811 #define MAX_NUM_CONVERT_LEVELS          1000
13812
13813 void ConvertLevels(void)
13814 {
13815   static LevelDirTree *convert_leveldir = NULL;
13816   static int convert_level_nr = -1;
13817   static int num_levels_handled = 0;
13818   static int num_levels_converted = 0;
13819   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13820   int i;
13821
13822   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13823                                                global.convert_leveldir);
13824
13825   if (convert_leveldir == NULL)
13826     Fail("no such level identifier: '%s'", global.convert_leveldir);
13827
13828   leveldir_current = convert_leveldir;
13829
13830   if (global.convert_level_nr != -1)
13831   {
13832     convert_leveldir->first_level = global.convert_level_nr;
13833     convert_leveldir->last_level  = global.convert_level_nr;
13834   }
13835
13836   convert_level_nr = convert_leveldir->first_level;
13837
13838   PrintLine("=", 79);
13839   Print("Converting levels\n");
13840   PrintLine("-", 79);
13841   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13842   Print("Level series name:       '%s'\n", convert_leveldir->name);
13843   Print("Level series author:     '%s'\n", convert_leveldir->author);
13844   Print("Number of levels:        %d\n",   convert_leveldir->levels);
13845   PrintLine("=", 79);
13846   Print("\n");
13847
13848   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13849     levels_failed[i] = FALSE;
13850
13851   while (convert_level_nr <= convert_leveldir->last_level)
13852   {
13853     char *level_filename;
13854     boolean new_level;
13855
13856     level_nr = convert_level_nr++;
13857
13858     Print("Level %03d: ", level_nr);
13859
13860     LoadLevel(level_nr);
13861     if (level.no_level_file || level.no_valid_file)
13862     {
13863       Print("(no level)\n");
13864       continue;
13865     }
13866
13867     Print("converting level ... ");
13868
13869 #if 0
13870     // special case: conversion of some EMC levels as requested by ACME
13871     level.game_engine_type = GAME_ENGINE_TYPE_RND;
13872 #endif
13873
13874     level_filename = getDefaultLevelFilename(level_nr);
13875     new_level = !fileExists(level_filename);
13876
13877     if (new_level)
13878     {
13879       SaveLevel(level_nr);
13880
13881       num_levels_converted++;
13882
13883       Print("converted.\n");
13884     }
13885     else
13886     {
13887       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13888         levels_failed[level_nr] = TRUE;
13889
13890       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13891     }
13892
13893     num_levels_handled++;
13894   }
13895
13896   Print("\n");
13897   PrintLine("=", 79);
13898   Print("Number of levels handled: %d\n", num_levels_handled);
13899   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13900          (num_levels_handled ?
13901           num_levels_converted * 100 / num_levels_handled : 0));
13902   PrintLine("-", 79);
13903   Print("Summary (for automatic parsing by scripts):\n");
13904   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13905          convert_leveldir->identifier, num_levels_converted,
13906          num_levels_handled,
13907          (num_levels_handled ?
13908           num_levels_converted * 100 / num_levels_handled : 0));
13909
13910   if (num_levels_handled != num_levels_converted)
13911   {
13912     Print(", FAILED:");
13913     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13914       if (levels_failed[i])
13915         Print(" %03d", i);
13916   }
13917
13918   Print("\n");
13919   PrintLine("=", 79);
13920
13921   CloseAllAndExit(0);
13922 }
13923
13924
13925 // ----------------------------------------------------------------------------
13926 // create and save images for use in level sketches (raw BMP format)
13927 // ----------------------------------------------------------------------------
13928
13929 void CreateLevelSketchImages(void)
13930 {
13931   Bitmap *bitmap1;
13932   Bitmap *bitmap2;
13933   int i;
13934
13935   InitElementPropertiesGfxElement();
13936
13937   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13938   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13939
13940   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13941   {
13942     int element = getMappedElement(i);
13943     char basename1[16];
13944     char basename2[16];
13945     char *filename1;
13946     char *filename2;
13947
13948     sprintf(basename1, "%04d.bmp", i);
13949     sprintf(basename2, "%04ds.bmp", i);
13950
13951     filename1 = getPath2(global.create_sketch_images_dir, basename1);
13952     filename2 = getPath2(global.create_sketch_images_dir, basename2);
13953
13954     DrawSizedElement(0, 0, element, TILESIZE);
13955     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13956
13957     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13958       Fail("cannot save level sketch image file '%s'", filename1);
13959
13960     DrawSizedElement(0, 0, element, MINI_TILESIZE);
13961     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13962
13963     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13964       Fail("cannot save level sketch image file '%s'", filename2);
13965
13966     free(filename1);
13967     free(filename2);
13968
13969     // create corresponding SQL statements (for normal and small images)
13970     if (i < 1000)
13971     {
13972       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13973       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13974     }
13975
13976     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13977     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13978
13979     // optional: create content for forum level sketch demonstration post
13980     if (options.debug)
13981       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13982   }
13983
13984   FreeBitmap(bitmap1);
13985   FreeBitmap(bitmap2);
13986
13987   if (options.debug)
13988     fprintf(stderr, "\n");
13989
13990   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13991
13992   CloseAllAndExit(0);
13993 }
13994
13995
13996 // ----------------------------------------------------------------------------
13997 // create and save images for element collecting animations (raw BMP format)
13998 // ----------------------------------------------------------------------------
13999
14000 static boolean createCollectImage(int element)
14001 {
14002   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14003 }
14004
14005 void CreateCollectElementImages(void)
14006 {
14007   int i, j;
14008   int num_steps = 8;
14009   int anim_frames = num_steps - 1;
14010   int tile_size = TILESIZE;
14011   int anim_width  = tile_size * anim_frames;
14012   int anim_height = tile_size;
14013   int num_collect_images = 0;
14014   int pos_collect_images = 0;
14015
14016   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14017     if (createCollectImage(i))
14018       num_collect_images++;
14019
14020   Info("Creating %d element collecting animation images ...",
14021        num_collect_images);
14022
14023   int dst_width  = anim_width * 2;
14024   int dst_height = anim_height * num_collect_images / 2;
14025   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14026   char *basename_bmp = "RocksCollect.bmp";
14027   char *basename_png = "RocksCollect.png";
14028   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14029   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14030   int len_filename_bmp = strlen(filename_bmp);
14031   int len_filename_png = strlen(filename_png);
14032   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14033   char cmd_convert[max_command_len];
14034
14035   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14036            filename_bmp,
14037            filename_png);
14038
14039   // force using RGBA surface for destination bitmap
14040   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14041                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14042
14043   dst_bitmap->surface =
14044     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14045
14046   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14047   {
14048     if (!createCollectImage(i))
14049       continue;
14050
14051     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14052     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14053     int graphic = el2img(i);
14054     char *token_name = element_info[i].token_name;
14055     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14056     Bitmap *src_bitmap;
14057     int src_x, src_y;
14058
14059     Info("- creating collecting image for '%s' ...", token_name);
14060
14061     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14062
14063     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14064                tile_size, tile_size, 0, 0);
14065
14066     // force using RGBA surface for temporary bitmap (using transparent black)
14067     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14068                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14069
14070     tmp_bitmap->surface =
14071       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14072
14073     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14074
14075     for (j = 0; j < anim_frames; j++)
14076     {
14077       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14078       int frame_size = frame_size_final * num_steps;
14079       int offset = (tile_size - frame_size_final) / 2;
14080       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14081
14082       while (frame_size > frame_size_final)
14083       {
14084         frame_size /= 2;
14085
14086         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14087
14088         FreeBitmap(frame_bitmap);
14089
14090         frame_bitmap = half_bitmap;
14091       }
14092
14093       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14094                        frame_size_final, frame_size_final,
14095                        dst_x + j * tile_size + offset, dst_y + offset);
14096
14097       FreeBitmap(frame_bitmap);
14098     }
14099
14100     tmp_bitmap->surface_masked = NULL;
14101
14102     FreeBitmap(tmp_bitmap);
14103
14104     pos_collect_images++;
14105   }
14106
14107   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14108     Fail("cannot save element collecting image file '%s'", filename_bmp);
14109
14110   FreeBitmap(dst_bitmap);
14111
14112   Info("Converting image file from BMP to PNG ...");
14113
14114   if (system(cmd_convert) != 0)
14115     Fail("converting image file failed");
14116
14117   unlink(filename_bmp);
14118
14119   Info("Done.");
14120
14121   CloseAllAndExit(0);
14122 }
14123
14124
14125 // ----------------------------------------------------------------------------
14126 // create and save images for custom and group elements (raw BMP format)
14127 // ----------------------------------------------------------------------------
14128
14129 void CreateCustomElementImages(char *directory)
14130 {
14131   char *src_basename = "RocksCE-template.ilbm";
14132   char *dst_basename = "RocksCE.bmp";
14133   char *src_filename = getPath2(directory, src_basename);
14134   char *dst_filename = getPath2(directory, dst_basename);
14135   Bitmap *src_bitmap;
14136   Bitmap *bitmap;
14137   int yoffset_ce = 0;
14138   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14139   int i;
14140
14141   InitVideoDefaults();
14142
14143   ReCreateBitmap(&backbuffer, video.width, video.height);
14144
14145   src_bitmap = LoadImage(src_filename);
14146
14147   bitmap = CreateBitmap(TILEX * 16 * 2,
14148                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14149                         DEFAULT_DEPTH);
14150
14151   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14152   {
14153     int x = i % 16;
14154     int y = i / 16;
14155     int ii = i + 1;
14156     int j;
14157
14158     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14159                TILEX * x, TILEY * y + yoffset_ce);
14160
14161     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14162                TILEX, TILEY,
14163                TILEX * x + TILEX * 16,
14164                TILEY * y + yoffset_ce);
14165
14166     for (j = 2; j >= 0; j--)
14167     {
14168       int c = ii % 10;
14169
14170       BlitBitmap(src_bitmap, bitmap,
14171                  TILEX + c * 7, 0, 6, 10,
14172                  TILEX * x + 6 + j * 7,
14173                  TILEY * y + 11 + yoffset_ce);
14174
14175       BlitBitmap(src_bitmap, bitmap,
14176                  TILEX + c * 8, TILEY, 6, 10,
14177                  TILEX * 16 + TILEX * x + 6 + j * 8,
14178                  TILEY * y + 10 + yoffset_ce);
14179
14180       ii /= 10;
14181     }
14182   }
14183
14184   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14185   {
14186     int x = i % 16;
14187     int y = i / 16;
14188     int ii = i + 1;
14189     int j;
14190
14191     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14192                TILEX * x, TILEY * y + yoffset_ge);
14193
14194     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14195                TILEX, TILEY,
14196                TILEX * x + TILEX * 16,
14197                TILEY * y + yoffset_ge);
14198
14199     for (j = 1; j >= 0; j--)
14200     {
14201       int c = ii % 10;
14202
14203       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14204                  TILEX * x + 6 + j * 10,
14205                  TILEY * y + 11 + yoffset_ge);
14206
14207       BlitBitmap(src_bitmap, bitmap,
14208                  TILEX + c * 8, TILEY + 12, 6, 10,
14209                  TILEX * 16 + TILEX * x + 10 + j * 8,
14210                  TILEY * y + 10 + yoffset_ge);
14211
14212       ii /= 10;
14213     }
14214   }
14215
14216   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14217     Fail("cannot save CE graphics file '%s'", dst_filename);
14218
14219   FreeBitmap(bitmap);
14220
14221   CloseAllAndExit(0);
14222 }