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