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