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