added support for slime settings in BD engine to level editor
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "tools.h"
23 #include "tape.h"
24 #include "config.h"
25 #include "api.h"
26
27 #define ENABLE_UNUSED_CODE      0       // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
29 #define ENABLE_RESERVED_CODE    0       // reserved for later use
30
31 #define CHUNK_ID_LEN            4       // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
34
35 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
37
38 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
49
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
52
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
57
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
60
61 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
64
65 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
66
67 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
70
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER                 0
78 #define SAVE_CONF_ALWAYS                1
79 #define SAVE_CONF_WHEN_CHANGED          -1
80
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE                0x00
83 #define CONF_MASK_2_BYTE                0x40
84 #define CONF_MASK_4_BYTE                0x80
85 #define CONF_MASK_MULTI_BYTES           0xc0
86
87 #define CONF_MASK_BYTES                 0xc0
88 #define CONF_MASK_TOKEN                 0x3f
89
90 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
91 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
92 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
93 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
94
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
100
101 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
102                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
103                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
104
105 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES          (2)
108
109 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
110                                          (t) == TYPE_ELEMENT_LIST ?     \
111                                          CONF_ELEMENT_NUM_BYTES :       \
112                                          (t) == TYPE_CONTENT ||         \
113                                          (t) == TYPE_CONTENT_LIST ?     \
114                                          CONF_CONTENT_NUM_BYTES : 1)
115
116 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
121                                          (y) * 3 + (x))
122 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
123                                          CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
139
140 struct LevelFileConfigInfo
141 {
142   int element;                  // element for which data is to be stored
143   int save_type;                // save data always, never or when changed
144   int data_type;                // data type (used internally, not stored)
145   int conf_type;                // micro chunk identifier (stored in file)
146
147   // (mandatory)
148   void *value;                  // variable that holds the data to be stored
149   int default_value;            // initial default value for this variable
150
151   // (optional)
152   void *value_copy;             // variable that holds the data to be copied
153   void *num_entities;           // number of entities for multi-byte data
154   int default_num_entities;     // default number of entities for this data
155   int max_num_entities;         // maximal number of entities for this data
156   char *default_string;         // optional default string for string data
157 };
158
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 {
161   // ---------- values not related to single elements -------------------------
162
163   {
164     -1,                                 SAVE_CONF_ALWAYS,
165     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
166     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
167   },
168   {
169     -1,                                 SAVE_CONF_ALWAYS,
170     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
171     &li.fieldx,                         STD_LEV_FIELDX
172   },
173   {
174     -1,                                 SAVE_CONF_ALWAYS,
175     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
176     &li.fieldy,                         STD_LEV_FIELDY
177   },
178   {
179     -1,                                 SAVE_CONF_ALWAYS,
180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
181     &li.time,                           100
182   },
183   {
184     -1,                                 SAVE_CONF_ALWAYS,
185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
186     &li.gems_needed,                    0
187   },
188   {
189     -1,                                 -1,
190     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
191     &li.random_seed,                    0
192   },
193   {
194     -1,                                 -1,
195     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
196     &li.use_step_counter,               FALSE
197   },
198   {
199     -1,                                 -1,
200     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
201     &li.wind_direction_initial,         MV_NONE
202   },
203   {
204     -1,                                 -1,
205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
206     &li.em_slippery_gems,               FALSE
207   },
208   {
209     -1,                                 -1,
210     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
211     &li.use_custom_template,            FALSE
212   },
213   {
214     -1,                                 -1,
215     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
216     &li.can_move_into_acid_bits,        ~0      // default: everything can
217   },
218   {
219     -1,                                 -1,
220     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
221     &li.dont_collide_with_bits,         ~0      // default: always deadly
222   },
223   {
224     -1,                                 -1,
225     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
226     &li.em_explodes_by_fire,            FALSE
227   },
228   {
229     -1,                                 -1,
230     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
231     &li.score[SC_TIME_BONUS],           1
232   },
233   {
234     -1,                                 -1,
235     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
236     &li.auto_exit_sokoban,              FALSE
237   },
238   {
239     -1,                                 -1,
240     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
241     &li.auto_count_gems,                FALSE
242   },
243   {
244     -1,                                 -1,
245     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
246     &li.solved_by_one_player,           FALSE
247   },
248   {
249     -1,                                 -1,
250     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
251     &li.time_score_base,                1
252   },
253   {
254     -1,                                 -1,
255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
256     &li.rate_time_over_score,           FALSE
257   },
258   {
259     -1,                                 -1,
260     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
261     &li.bd_intermission,                FALSE
262   },
263   {
264     -1,                                 -1,
265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
266     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
267   },
268   {
269     -1,                                 -1,
270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
271     &li.bd_pal_timing,                  FALSE
272   },
273   {
274     -1,                                 -1,
275     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
276     &li.bd_cycle_delay_ms,              200
277   },
278   {
279     -1,                                 -1,
280     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
281     &li.bd_cycle_delay_c64,             0
282   },
283   {
284     -1,                                 -1,
285     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
286     &li.bd_hatching_delay_cycles,       21
287   },
288   {
289     -1,                                 -1,
290     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
291     &li.bd_hatching_delay_seconds,      2
292   },
293   {
294     -1,                                 -1,
295     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
296     &li.bd_line_shifting_borders,       FALSE
297   },
298   {
299     -1,                                 -1,
300     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
301     &li.bd_scan_first_and_last_row,     TRUE
302   },
303   {
304     -1,                                 -1,
305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
306     &li.bd_short_explosions,            TRUE
307   },
308   {
309     -1,                                 -1,
310     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(23),
311     &li.bd_gravity_affects_all,         TRUE
312   },
313
314   {
315     -1,                                 -1,
316     -1,                                 -1,
317     NULL,                               -1
318   }
319 };
320
321 static struct LevelFileConfigInfo chunk_config_ELEM[] =
322 {
323   // (these values are the same for each player)
324   {
325     EL_PLAYER_1,                        -1,
326     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
327     &li.block_last_field,               FALSE   // default case for EM levels
328   },
329   {
330     EL_PLAYER_1,                        -1,
331     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
332     &li.sp_block_last_field,            TRUE    // default case for SP levels
333   },
334   {
335     EL_PLAYER_1,                        -1,
336     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
337     &li.instant_relocation,             FALSE
338   },
339   {
340     EL_PLAYER_1,                        -1,
341     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
342     &li.can_pass_to_walkable,           FALSE
343   },
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
347     &li.block_snap_field,               TRUE
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
352     &li.continuous_snapping,            TRUE
353   },
354   {
355     EL_PLAYER_1,                        -1,
356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
357     &li.shifted_relocation,             FALSE
358   },
359   {
360     EL_PLAYER_1,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
362     &li.lazy_relocation,                FALSE
363   },
364   {
365     EL_PLAYER_1,                        -1,
366     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
367     &li.finish_dig_collect,             TRUE
368   },
369   {
370     EL_PLAYER_1,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
372     &li.keep_walkable_ce,               FALSE
373   },
374
375   // (these values are different for each player)
376   {
377     EL_PLAYER_1,                        -1,
378     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
379     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
380   },
381   {
382     EL_PLAYER_1,                        -1,
383     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
384     &li.initial_player_gravity[0],      FALSE
385   },
386   {
387     EL_PLAYER_1,                        -1,
388     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
389     &li.use_start_element[0],           FALSE
390   },
391   {
392     EL_PLAYER_1,                        -1,
393     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
394     &li.start_element[0],               EL_PLAYER_1
395   },
396   {
397     EL_PLAYER_1,                        -1,
398     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
399     &li.use_artwork_element[0],         FALSE
400   },
401   {
402     EL_PLAYER_1,                        -1,
403     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
404     &li.artwork_element[0],             EL_PLAYER_1
405   },
406   {
407     EL_PLAYER_1,                        -1,
408     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
409     &li.use_explosion_element[0],       FALSE
410   },
411   {
412     EL_PLAYER_1,                        -1,
413     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
414     &li.explosion_element[0],           EL_PLAYER_1
415   },
416   {
417     EL_PLAYER_1,                        -1,
418     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
419     &li.use_initial_inventory[0],       FALSE
420   },
421   {
422     EL_PLAYER_1,                        -1,
423     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
424     &li.initial_inventory_size[0],      1
425   },
426   {
427     EL_PLAYER_1,                        -1,
428     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
429     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
430     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
431   },
432
433   {
434     EL_PLAYER_2,                        -1,
435     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
436     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
437   },
438   {
439     EL_PLAYER_2,                        -1,
440     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
441     &li.initial_player_gravity[1],      FALSE
442   },
443   {
444     EL_PLAYER_2,                        -1,
445     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
446     &li.use_start_element[1],           FALSE
447   },
448   {
449     EL_PLAYER_2,                        -1,
450     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
451     &li.start_element[1],               EL_PLAYER_2
452   },
453   {
454     EL_PLAYER_2,                        -1,
455     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
456     &li.use_artwork_element[1],         FALSE
457   },
458   {
459     EL_PLAYER_2,                        -1,
460     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
461     &li.artwork_element[1],             EL_PLAYER_2
462   },
463   {
464     EL_PLAYER_2,                        -1,
465     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
466     &li.use_explosion_element[1],       FALSE
467   },
468   {
469     EL_PLAYER_2,                        -1,
470     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
471     &li.explosion_element[1],           EL_PLAYER_2
472   },
473   {
474     EL_PLAYER_2,                        -1,
475     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
476     &li.use_initial_inventory[1],       FALSE
477   },
478   {
479     EL_PLAYER_2,                        -1,
480     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
481     &li.initial_inventory_size[1],      1
482   },
483   {
484     EL_PLAYER_2,                        -1,
485     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
486     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
487     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
488   },
489
490   {
491     EL_PLAYER_3,                        -1,
492     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
493     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
494   },
495   {
496     EL_PLAYER_3,                        -1,
497     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
498     &li.initial_player_gravity[2],      FALSE
499   },
500   {
501     EL_PLAYER_3,                        -1,
502     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
503     &li.use_start_element[2],           FALSE
504   },
505   {
506     EL_PLAYER_3,                        -1,
507     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
508     &li.start_element[2],               EL_PLAYER_3
509   },
510   {
511     EL_PLAYER_3,                        -1,
512     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
513     &li.use_artwork_element[2],         FALSE
514   },
515   {
516     EL_PLAYER_3,                        -1,
517     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
518     &li.artwork_element[2],             EL_PLAYER_3
519   },
520   {
521     EL_PLAYER_3,                        -1,
522     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
523     &li.use_explosion_element[2],       FALSE
524   },
525   {
526     EL_PLAYER_3,                        -1,
527     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
528     &li.explosion_element[2],           EL_PLAYER_3
529   },
530   {
531     EL_PLAYER_3,                        -1,
532     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
533     &li.use_initial_inventory[2],       FALSE
534   },
535   {
536     EL_PLAYER_3,                        -1,
537     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
538     &li.initial_inventory_size[2],      1
539   },
540   {
541     EL_PLAYER_3,                        -1,
542     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
543     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
544     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
545   },
546
547   {
548     EL_PLAYER_4,                        -1,
549     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
550     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
551   },
552   {
553     EL_PLAYER_4,                        -1,
554     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
555     &li.initial_player_gravity[3],      FALSE
556   },
557   {
558     EL_PLAYER_4,                        -1,
559     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
560     &li.use_start_element[3],           FALSE
561   },
562   {
563     EL_PLAYER_4,                        -1,
564     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
565     &li.start_element[3],               EL_PLAYER_4
566   },
567   {
568     EL_PLAYER_4,                        -1,
569     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
570     &li.use_artwork_element[3],         FALSE
571   },
572   {
573     EL_PLAYER_4,                        -1,
574     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
575     &li.artwork_element[3],             EL_PLAYER_4
576   },
577   {
578     EL_PLAYER_4,                        -1,
579     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
580     &li.use_explosion_element[3],       FALSE
581   },
582   {
583     EL_PLAYER_4,                        -1,
584     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
585     &li.explosion_element[3],           EL_PLAYER_4
586   },
587   {
588     EL_PLAYER_4,                        -1,
589     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
590     &li.use_initial_inventory[3],       FALSE
591   },
592   {
593     EL_PLAYER_4,                        -1,
594     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
595     &li.initial_inventory_size[3],      1
596   },
597   {
598     EL_PLAYER_4,                        -1,
599     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
600     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
601     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
602   },
603
604   // (these values are only valid for BD style levels)
605   // (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     EL_BD_SLIME,                        -1,
688     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
689     &li.bd_slime_is_predictable,        TRUE
690   },
691   {
692     EL_BD_SLIME,                        -1,
693     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
694     &li.bd_slime_permeability_rate,     100
695   },
696   {
697     EL_BD_SLIME,                        -1,
698     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
699     &li.bd_slime_permeability_bits_c64, 0
700   },
701   {
702     EL_BD_SLIME,                        -1,
703     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
704     &li.bd_slime_random_seed_c64,       -1
705   },
706
707   // (the following values are related to various game elements)
708
709   {
710     EL_EMERALD,                         -1,
711     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
712     &li.score[SC_EMERALD],              10
713   },
714
715   {
716     EL_DIAMOND,                         -1,
717     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
718     &li.score[SC_DIAMOND],              10
719   },
720
721   {
722     EL_BUG,                             -1,
723     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
724     &li.score[SC_BUG],                  10
725   },
726
727   {
728     EL_SPACESHIP,                       -1,
729     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
730     &li.score[SC_SPACESHIP],            10
731   },
732
733   {
734     EL_PACMAN,                          -1,
735     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
736     &li.score[SC_PACMAN],               10
737   },
738
739   {
740     EL_NUT,                             -1,
741     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
742     &li.score[SC_NUT],                  10
743   },
744
745   {
746     EL_DYNAMITE,                        -1,
747     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
748     &li.score[SC_DYNAMITE],             10
749   },
750
751   {
752     EL_KEY_1,                           -1,
753     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
754     &li.score[SC_KEY],                  10
755   },
756
757   {
758     EL_PEARL,                           -1,
759     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
760     &li.score[SC_PEARL],                10
761   },
762
763   {
764     EL_CRYSTAL,                         -1,
765     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
766     &li.score[SC_CRYSTAL],              10
767   },
768
769   // (amoeba values used by R'n'D game engine only)
770   {
771     EL_BD_AMOEBA,                       -1,
772     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
773     &li.amoeba_content,                 EL_DIAMOND
774   },
775   {
776     EL_BD_AMOEBA,                       -1,
777     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
778     &li.amoeba_speed,                   10
779   },
780   {
781     EL_BD_AMOEBA,                       -1,
782     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
783     &li.grow_into_diggable,             TRUE
784   },
785   // (amoeba values used by BD game engine only)
786   {
787     EL_BD_AMOEBA,                       -1,
788     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
789     &li.bd_amoeba_wait_for_hatching,    FALSE
790   },
791   {
792     EL_BD_AMOEBA,                       -1,
793     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
794     &li.bd_amoeba_start_immediately,    TRUE
795   },
796   {
797     EL_BD_AMOEBA,                       -1,
798     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
799     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
800   },
801   {
802     EL_BD_AMOEBA,                       -1,
803     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
804     &li.bd_amoeba_threshold_too_big,    200
805   },
806   {
807     EL_BD_AMOEBA,                       -1,
808     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
809     &li.bd_amoeba_slow_growth_time,     200
810   },
811   {
812     EL_BD_AMOEBA,                       -1,
813     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
814     &li.bd_amoeba_slow_growth_rate,     3
815   },
816   {
817     EL_BD_AMOEBA,                       -1,
818     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
819     &li.bd_amoeba_fast_growth_rate,     25
820   },
821   {
822     EL_BD_AMOEBA,                       -1,
823     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
824     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
825   },
826   {
827     EL_BD_AMOEBA,                       -1,
828     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
829     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
830   },
831
832   {
833     EL_BD_AMOEBA_2,                     -1,
834     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
835     &li.bd_amoeba_2_threshold_too_big,  200
836   },
837   {
838     EL_BD_AMOEBA_2,                     -1,
839     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
840     &li.bd_amoeba_2_slow_growth_time,   200
841   },
842   {
843     EL_BD_AMOEBA_2,                     -1,
844     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
845     &li.bd_amoeba_2_slow_growth_rate,   3
846   },
847   {
848     EL_BD_AMOEBA_2,                     -1,
849     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
850     &li.bd_amoeba_2_fast_growth_rate,   25
851   },
852   {
853     EL_BD_AMOEBA_2,                     -1,
854     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
855     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
856   },
857   {
858     EL_BD_AMOEBA_2,                     -1,
859     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
860     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
861   },
862   {
863     EL_BD_AMOEBA_2,                     -1,
864     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
865     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
866   },
867   {
868     EL_BD_AMOEBA_2,                     -1,
869     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
870     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
871   },
872
873   {
874     EL_YAMYAM,                          -1,
875     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
876     &li.yamyam_content,                 EL_ROCK, NULL,
877     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
878   },
879   {
880     EL_YAMYAM,                          -1,
881     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
882     &li.score[SC_YAMYAM],               10
883   },
884
885   {
886     EL_ROBOT,                           -1,
887     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
888     &li.score[SC_ROBOT],                10
889   },
890   {
891     EL_ROBOT,                           -1,
892     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
893     &li.slurp_score,                    10
894   },
895
896   {
897     EL_ROBOT_WHEEL,                     -1,
898     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
899     &li.time_wheel,                     10
900   },
901
902   {
903     EL_MAGIC_WALL,                      -1,
904     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
905     &li.time_magic_wall,                10
906   },
907
908   {
909     EL_GAME_OF_LIFE,                    -1,
910     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
911     &li.game_of_life[0],                2
912   },
913   {
914     EL_GAME_OF_LIFE,                    -1,
915     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
916     &li.game_of_life[1],                3
917   },
918   {
919     EL_GAME_OF_LIFE,                    -1,
920     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
921     &li.game_of_life[2],                3
922   },
923   {
924     EL_GAME_OF_LIFE,                    -1,
925     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
926     &li.game_of_life[3],                3
927   },
928   {
929     EL_GAME_OF_LIFE,                    -1,
930     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
931     &li.use_life_bugs,                  FALSE
932   },
933
934   {
935     EL_BIOMAZE,                         -1,
936     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
937     &li.biomaze[0],                     2
938   },
939   {
940     EL_BIOMAZE,                         -1,
941     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
942     &li.biomaze[1],                     3
943   },
944   {
945     EL_BIOMAZE,                         -1,
946     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
947     &li.biomaze[2],                     3
948   },
949   {
950     EL_BIOMAZE,                         -1,
951     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
952     &li.biomaze[3],                     3
953   },
954
955   {
956     EL_TIMEGATE_SWITCH,                 -1,
957     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
958     &li.time_timegate,                  10
959   },
960
961   {
962     EL_LIGHT_SWITCH_ACTIVE,             -1,
963     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
964     &li.time_light,                     10
965   },
966
967   {
968     EL_SHIELD_NORMAL,                   -1,
969     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
970     &li.shield_normal_time,             10
971   },
972   {
973     EL_SHIELD_NORMAL,                   -1,
974     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
975     &li.score[SC_SHIELD],               10
976   },
977
978   {
979     EL_SHIELD_DEADLY,                   -1,
980     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
981     &li.shield_deadly_time,             10
982   },
983   {
984     EL_SHIELD_DEADLY,                   -1,
985     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
986     &li.score[SC_SHIELD],               10
987   },
988
989   {
990     EL_EXTRA_TIME,                      -1,
991     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
992     &li.extra_time,                     10
993   },
994   {
995     EL_EXTRA_TIME,                      -1,
996     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
997     &li.extra_time_score,               10
998   },
999
1000   {
1001     EL_TIME_ORB_FULL,                   -1,
1002     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1003     &li.time_orb_time,                  10
1004   },
1005   {
1006     EL_TIME_ORB_FULL,                   -1,
1007     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1008     &li.use_time_orb_bug,               FALSE
1009   },
1010
1011   {
1012     EL_SPRING,                          -1,
1013     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1014     &li.use_spring_bug,                 FALSE
1015   },
1016
1017   {
1018     EL_EMC_ANDROID,                     -1,
1019     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1020     &li.android_move_time,              10
1021   },
1022   {
1023     EL_EMC_ANDROID,                     -1,
1024     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1025     &li.android_clone_time,             10
1026   },
1027   {
1028     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1029     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1030     &li.android_clone_element[0],       EL_EMPTY, NULL,
1031     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1032   },
1033   {
1034     EL_EMC_ANDROID,                     -1,
1035     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1036     &li.android_clone_element[0],       EL_EMPTY, NULL,
1037     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1038   },
1039
1040   {
1041     EL_EMC_LENSES,                      -1,
1042     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1043     &li.lenses_score,                   10
1044   },
1045   {
1046     EL_EMC_LENSES,                      -1,
1047     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1048     &li.lenses_time,                    10
1049   },
1050
1051   {
1052     EL_EMC_MAGNIFIER,                   -1,
1053     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1054     &li.magnify_score,                  10
1055   },
1056   {
1057     EL_EMC_MAGNIFIER,                   -1,
1058     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1059     &li.magnify_time,                   10
1060   },
1061
1062   {
1063     EL_EMC_MAGIC_BALL,                  -1,
1064     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1065     &li.ball_time,                      10
1066   },
1067   {
1068     EL_EMC_MAGIC_BALL,                  -1,
1069     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1070     &li.ball_random,                    FALSE
1071   },
1072   {
1073     EL_EMC_MAGIC_BALL,                  -1,
1074     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1075     &li.ball_active_initial,            FALSE
1076   },
1077   {
1078     EL_EMC_MAGIC_BALL,                  -1,
1079     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1080     &li.ball_content,                   EL_EMPTY, NULL,
1081     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1082   },
1083
1084   {
1085     EL_SOKOBAN_FIELD_EMPTY,             -1,
1086     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1087     &li.sb_fields_needed,               TRUE
1088   },
1089
1090   {
1091     EL_SOKOBAN_OBJECT,                  -1,
1092     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1093     &li.sb_objects_needed,              TRUE
1094   },
1095
1096   {
1097     EL_MM_MCDUFFIN,                     -1,
1098     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1099     &li.mm_laser_red,                   FALSE
1100   },
1101   {
1102     EL_MM_MCDUFFIN,                     -1,
1103     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1104     &li.mm_laser_green,                 FALSE
1105   },
1106   {
1107     EL_MM_MCDUFFIN,                     -1,
1108     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1109     &li.mm_laser_blue,                  TRUE
1110   },
1111
1112   {
1113     EL_DF_LASER,                        -1,
1114     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1115     &li.df_laser_red,                   TRUE
1116   },
1117   {
1118     EL_DF_LASER,                        -1,
1119     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1120     &li.df_laser_green,                 TRUE
1121   },
1122   {
1123     EL_DF_LASER,                        -1,
1124     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1125     &li.df_laser_blue,                  FALSE
1126   },
1127
1128   {
1129     EL_MM_FUSE_ACTIVE,                  -1,
1130     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1131     &li.mm_time_fuse,                   25
1132   },
1133   {
1134     EL_MM_BOMB,                         -1,
1135     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1136     &li.mm_time_bomb,                   75
1137   },
1138
1139   {
1140     EL_MM_GRAY_BALL,                    -1,
1141     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1142     &li.mm_time_ball,                   75
1143   },
1144   {
1145     EL_MM_GRAY_BALL,                    -1,
1146     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1147     &li.mm_ball_choice_mode,            ANIM_RANDOM
1148   },
1149   {
1150     EL_MM_GRAY_BALL,                    -1,
1151     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1152     &li.mm_ball_content,                EL_EMPTY, NULL,
1153     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1154   },
1155   {
1156     EL_MM_GRAY_BALL,                    -1,
1157     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1158     &li.rotate_mm_ball_content,         TRUE
1159   },
1160   {
1161     EL_MM_GRAY_BALL,                    -1,
1162     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1163     &li.explode_mm_ball,                FALSE
1164   },
1165
1166   {
1167     EL_MM_STEEL_BLOCK,                  -1,
1168     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1169     &li.mm_time_block,                  75
1170   },
1171   {
1172     EL_MM_LIGHTBALL,                    -1,
1173     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1174     &li.score[SC_ELEM_BONUS],           10
1175   },
1176
1177   {
1178     -1,                                 -1,
1179     -1,                                 -1,
1180     NULL,                               -1
1181   }
1182 };
1183
1184 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1185 {
1186   {
1187     -1,                                 -1,
1188     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1189     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1190   },
1191   {
1192     -1,                                 -1,
1193     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1194     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1195   },
1196
1197   {
1198     -1,                                 -1,
1199     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1200     &xx_envelope.autowrap,              FALSE
1201   },
1202   {
1203     -1,                                 -1,
1204     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1205     &xx_envelope.centered,              FALSE
1206   },
1207
1208   {
1209     -1,                                 -1,
1210     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1211     &xx_envelope.text,                  -1, NULL,
1212     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1213     &xx_default_string_empty[0]
1214   },
1215
1216   {
1217     -1,                                 -1,
1218     -1,                                 -1,
1219     NULL,                               -1
1220   }
1221 };
1222
1223 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1224 {
1225   {
1226     -1,                                 -1,
1227     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1228     &xx_ei.description[0],              -1,
1229     &yy_ei.description[0],
1230     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1231     &xx_default_description[0]
1232   },
1233
1234   {
1235     -1,                                 -1,
1236     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1237     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1238     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1239   },
1240 #if ENABLE_RESERVED_CODE
1241   // (reserved for later use)
1242   {
1243     -1,                                 -1,
1244     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1245     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1246     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1247   },
1248 #endif
1249
1250   {
1251     -1,                                 -1,
1252     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1253     &xx_ei.use_gfx_element,             FALSE,
1254     &yy_ei.use_gfx_element
1255   },
1256   {
1257     -1,                                 -1,
1258     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1259     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1260     &yy_ei.gfx_element_initial
1261   },
1262
1263   {
1264     -1,                                 -1,
1265     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1266     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1267     &yy_ei.access_direction
1268   },
1269
1270   {
1271     -1,                                 -1,
1272     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1273     &xx_ei.collect_score_initial,       10,
1274     &yy_ei.collect_score_initial
1275   },
1276   {
1277     -1,                                 -1,
1278     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1279     &xx_ei.collect_count_initial,       1,
1280     &yy_ei.collect_count_initial
1281   },
1282
1283   {
1284     -1,                                 -1,
1285     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1286     &xx_ei.ce_value_fixed_initial,      0,
1287     &yy_ei.ce_value_fixed_initial
1288   },
1289   {
1290     -1,                                 -1,
1291     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1292     &xx_ei.ce_value_random_initial,     0,
1293     &yy_ei.ce_value_random_initial
1294   },
1295   {
1296     -1,                                 -1,
1297     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1298     &xx_ei.use_last_ce_value,           FALSE,
1299     &yy_ei.use_last_ce_value
1300   },
1301
1302   {
1303     -1,                                 -1,
1304     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1305     &xx_ei.push_delay_fixed,            8,
1306     &yy_ei.push_delay_fixed
1307   },
1308   {
1309     -1,                                 -1,
1310     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1311     &xx_ei.push_delay_random,           8,
1312     &yy_ei.push_delay_random
1313   },
1314   {
1315     -1,                                 -1,
1316     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1317     &xx_ei.drop_delay_fixed,            0,
1318     &yy_ei.drop_delay_fixed
1319   },
1320   {
1321     -1,                                 -1,
1322     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1323     &xx_ei.drop_delay_random,           0,
1324     &yy_ei.drop_delay_random
1325   },
1326   {
1327     -1,                                 -1,
1328     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1329     &xx_ei.move_delay_fixed,            0,
1330     &yy_ei.move_delay_fixed
1331   },
1332   {
1333     -1,                                 -1,
1334     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1335     &xx_ei.move_delay_random,           0,
1336     &yy_ei.move_delay_random
1337   },
1338   {
1339     -1,                                 -1,
1340     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1341     &xx_ei.step_delay_fixed,            0,
1342     &yy_ei.step_delay_fixed
1343   },
1344   {
1345     -1,                                 -1,
1346     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1347     &xx_ei.step_delay_random,           0,
1348     &yy_ei.step_delay_random
1349   },
1350
1351   {
1352     -1,                                 -1,
1353     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1354     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1355     &yy_ei.move_pattern
1356   },
1357   {
1358     -1,                                 -1,
1359     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1360     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1361     &yy_ei.move_direction_initial
1362   },
1363   {
1364     -1,                                 -1,
1365     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1366     &xx_ei.move_stepsize,               TILEX / 8,
1367     &yy_ei.move_stepsize
1368   },
1369
1370   {
1371     -1,                                 -1,
1372     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1373     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1374     &yy_ei.move_enter_element
1375   },
1376   {
1377     -1,                                 -1,
1378     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1379     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1380     &yy_ei.move_leave_element
1381   },
1382   {
1383     -1,                                 -1,
1384     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1385     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1386     &yy_ei.move_leave_type
1387   },
1388
1389   {
1390     -1,                                 -1,
1391     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1392     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1393     &yy_ei.slippery_type
1394   },
1395
1396   {
1397     -1,                                 -1,
1398     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1399     &xx_ei.explosion_type,              EXPLODES_3X3,
1400     &yy_ei.explosion_type
1401   },
1402   {
1403     -1,                                 -1,
1404     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1405     &xx_ei.explosion_delay,             16,
1406     &yy_ei.explosion_delay
1407   },
1408   {
1409     -1,                                 -1,
1410     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1411     &xx_ei.ignition_delay,              8,
1412     &yy_ei.ignition_delay
1413   },
1414
1415   {
1416     -1,                                 -1,
1417     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1418     &xx_ei.content,                     EL_EMPTY_SPACE,
1419     &yy_ei.content,
1420     &xx_num_contents,                   1, 1
1421   },
1422
1423   // ---------- "num_change_pages" must be the last entry ---------------------
1424
1425   {
1426     -1,                                 SAVE_CONF_ALWAYS,
1427     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1428     &xx_ei.num_change_pages,            1,
1429     &yy_ei.num_change_pages
1430   },
1431
1432   {
1433     -1,                                 -1,
1434     -1,                                 -1,
1435     NULL,                               -1,
1436     NULL
1437   }
1438 };
1439
1440 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1441 {
1442   // ---------- "current_change_page" must be the first entry -----------------
1443
1444   {
1445     -1,                                 SAVE_CONF_ALWAYS,
1446     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1447     &xx_current_change_page,            -1
1448   },
1449
1450   // ---------- (the remaining entries can be in any order) -------------------
1451
1452   {
1453     -1,                                 -1,
1454     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1455     &xx_change.can_change,              FALSE
1456   },
1457
1458   {
1459     -1,                                 -1,
1460     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1461     &xx_event_bits[0],                  0
1462   },
1463   {
1464     -1,                                 -1,
1465     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1466     &xx_event_bits[1],                  0
1467   },
1468
1469   {
1470     -1,                                 -1,
1471     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1472     &xx_change.trigger_player,          CH_PLAYER_ANY
1473   },
1474   {
1475     -1,                                 -1,
1476     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1477     &xx_change.trigger_side,            CH_SIDE_ANY
1478   },
1479   {
1480     -1,                                 -1,
1481     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1482     &xx_change.trigger_page,            CH_PAGE_ANY
1483   },
1484
1485   {
1486     -1,                                 -1,
1487     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1488     &xx_change.target_element,          EL_EMPTY_SPACE
1489   },
1490
1491   {
1492     -1,                                 -1,
1493     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1494     &xx_change.delay_fixed,             0
1495   },
1496   {
1497     -1,                                 -1,
1498     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1499     &xx_change.delay_random,            0
1500   },
1501   {
1502     -1,                                 -1,
1503     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1504     &xx_change.delay_frames,            FRAMES_PER_SECOND
1505   },
1506
1507   {
1508     -1,                                 -1,
1509     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1510     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1511   },
1512
1513   {
1514     -1,                                 -1,
1515     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1516     &xx_change.explode,                 FALSE
1517   },
1518   {
1519     -1,                                 -1,
1520     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1521     &xx_change.use_target_content,      FALSE
1522   },
1523   {
1524     -1,                                 -1,
1525     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1526     &xx_change.only_if_complete,        FALSE
1527   },
1528   {
1529     -1,                                 -1,
1530     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1531     &xx_change.use_random_replace,      FALSE
1532   },
1533   {
1534     -1,                                 -1,
1535     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1536     &xx_change.random_percentage,       100
1537   },
1538   {
1539     -1,                                 -1,
1540     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1541     &xx_change.replace_when,            CP_WHEN_EMPTY
1542   },
1543
1544   {
1545     -1,                                 -1,
1546     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1547     &xx_change.has_action,              FALSE
1548   },
1549   {
1550     -1,                                 -1,
1551     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1552     &xx_change.action_type,             CA_NO_ACTION
1553   },
1554   {
1555     -1,                                 -1,
1556     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1557     &xx_change.action_mode,             CA_MODE_UNDEFINED
1558   },
1559   {
1560     -1,                                 -1,
1561     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1562     &xx_change.action_arg,              CA_ARG_UNDEFINED
1563   },
1564
1565   {
1566     -1,                                 -1,
1567     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1568     &xx_change.action_element,          EL_EMPTY_SPACE
1569   },
1570
1571   {
1572     -1,                                 -1,
1573     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1574     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1575     &xx_num_contents,                   1, 1
1576   },
1577
1578   {
1579     -1,                                 -1,
1580     -1,                                 -1,
1581     NULL,                               -1
1582   }
1583 };
1584
1585 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1586 {
1587   {
1588     -1,                                 -1,
1589     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1590     &xx_ei.description[0],              -1, NULL,
1591     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1592     &xx_default_description[0]
1593   },
1594
1595   {
1596     -1,                                 -1,
1597     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1598     &xx_ei.use_gfx_element,             FALSE
1599   },
1600   {
1601     -1,                                 -1,
1602     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1603     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1604   },
1605
1606   {
1607     -1,                                 -1,
1608     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1609     &xx_group.choice_mode,              ANIM_RANDOM
1610   },
1611
1612   {
1613     -1,                                 -1,
1614     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1615     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1616     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1617   },
1618
1619   {
1620     -1,                                 -1,
1621     -1,                                 -1,
1622     NULL,                               -1
1623   }
1624 };
1625
1626 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1627 {
1628   {
1629     -1,                                 -1,
1630     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1631     &xx_ei.use_gfx_element,             FALSE
1632   },
1633   {
1634     -1,                                 -1,
1635     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1636     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1637   },
1638
1639   {
1640     -1,                                 -1,
1641     -1,                                 -1,
1642     NULL,                               -1
1643   }
1644 };
1645
1646 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1647 {
1648   {
1649     EL_PLAYER_1,                        -1,
1650     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1651     &li.block_snap_field,               TRUE
1652   },
1653   {
1654     EL_PLAYER_1,                        -1,
1655     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1656     &li.continuous_snapping,            TRUE
1657   },
1658   {
1659     EL_PLAYER_1,                        -1,
1660     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1661     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1662   },
1663   {
1664     EL_PLAYER_1,                        -1,
1665     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1666     &li.use_start_element[0],           FALSE
1667   },
1668   {
1669     EL_PLAYER_1,                        -1,
1670     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1671     &li.start_element[0],               EL_PLAYER_1
1672   },
1673   {
1674     EL_PLAYER_1,                        -1,
1675     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1676     &li.use_artwork_element[0],         FALSE
1677   },
1678   {
1679     EL_PLAYER_1,                        -1,
1680     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1681     &li.artwork_element[0],             EL_PLAYER_1
1682   },
1683   {
1684     EL_PLAYER_1,                        -1,
1685     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1686     &li.use_explosion_element[0],       FALSE
1687   },
1688   {
1689     EL_PLAYER_1,                        -1,
1690     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1691     &li.explosion_element[0],           EL_PLAYER_1
1692   },
1693
1694   {
1695     -1,                                 -1,
1696     -1,                                 -1,
1697     NULL,                               -1
1698   }
1699 };
1700
1701 static struct
1702 {
1703   int filetype;
1704   char *id;
1705 }
1706 filetype_id_list[] =
1707 {
1708   { LEVEL_FILE_TYPE_RND,        "RND"   },
1709   { LEVEL_FILE_TYPE_BD,         "BD"    },
1710   { LEVEL_FILE_TYPE_EM,         "EM"    },
1711   { LEVEL_FILE_TYPE_SP,         "SP"    },
1712   { LEVEL_FILE_TYPE_DX,         "DX"    },
1713   { LEVEL_FILE_TYPE_SB,         "SB"    },
1714   { LEVEL_FILE_TYPE_DC,         "DC"    },
1715   { LEVEL_FILE_TYPE_MM,         "MM"    },
1716   { LEVEL_FILE_TYPE_MM,         "DF"    },
1717   { -1,                         NULL    },
1718 };
1719
1720
1721 // ============================================================================
1722 // level file functions
1723 // ============================================================================
1724
1725 static boolean check_special_flags(char *flag)
1726 {
1727   if (strEqual(options.special_flags, flag) ||
1728       strEqual(leveldir_current->special_flags, flag))
1729     return TRUE;
1730
1731   return FALSE;
1732 }
1733
1734 static struct DateInfo getCurrentDate(void)
1735 {
1736   time_t epoch_seconds = time(NULL);
1737   struct tm *now = localtime(&epoch_seconds);
1738   struct DateInfo date;
1739
1740   date.year  = now->tm_year + 1900;
1741   date.month = now->tm_mon  + 1;
1742   date.day   = now->tm_mday;
1743
1744   date.src   = DATE_SRC_CLOCK;
1745
1746   return date;
1747 }
1748
1749 static void resetEventFlags(struct ElementChangeInfo *change)
1750 {
1751   int i;
1752
1753   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1754     change->has_event[i] = FALSE;
1755 }
1756
1757 static void resetEventBits(void)
1758 {
1759   int i;
1760
1761   for (i = 0; i < NUM_CE_BITFIELDS; i++)
1762     xx_event_bits[i] = 0;
1763 }
1764
1765 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1766 {
1767   int i;
1768
1769   /* important: only change event flag if corresponding event bit is set
1770      (this is because all xx_event_bits[] values are loaded separately,
1771      and all xx_event_bits[] values are set back to zero before loading
1772      another value xx_event_bits[x] (each value representing 32 flags)) */
1773
1774   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1775     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1776       change->has_event[i] = TRUE;
1777 }
1778
1779 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1780 {
1781   int i;
1782
1783   /* in contrast to the above function setEventFlagsFromEventBits(), it
1784      would also be possible to set all bits in xx_event_bits[] to 0 or 1
1785      depending on the corresponding change->has_event[i] values here, as
1786      all xx_event_bits[] values are reset in resetEventBits() before */
1787
1788   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1789     if (change->has_event[i])
1790       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1791 }
1792
1793 static char *getDefaultElementDescription(struct ElementInfo *ei)
1794 {
1795   static char description[MAX_ELEMENT_NAME_LEN + 1];
1796   char *default_description = (ei->custom_description != NULL ?
1797                                ei->custom_description :
1798                                ei->editor_description);
1799   int i;
1800
1801   // always start with reliable default values
1802   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1803     description[i] = '\0';
1804
1805   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1806   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1807
1808   return &description[0];
1809 }
1810
1811 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1812 {
1813   char *default_description = getDefaultElementDescription(ei);
1814   int i;
1815
1816   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1817     ei->description[i] = default_description[i];
1818 }
1819
1820 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1821 {
1822   int i;
1823
1824   for (i = 0; conf[i].data_type != -1; i++)
1825   {
1826     int default_value = conf[i].default_value;
1827     int data_type = conf[i].data_type;
1828     int conf_type = conf[i].conf_type;
1829     int byte_mask = conf_type & CONF_MASK_BYTES;
1830
1831     if (byte_mask == CONF_MASK_MULTI_BYTES)
1832     {
1833       int default_num_entities = conf[i].default_num_entities;
1834       int max_num_entities = conf[i].max_num_entities;
1835
1836       *(int *)(conf[i].num_entities) = default_num_entities;
1837
1838       if (data_type == TYPE_STRING)
1839       {
1840         char *default_string = conf[i].default_string;
1841         char *string = (char *)(conf[i].value);
1842
1843         strncpy(string, default_string, max_num_entities);
1844       }
1845       else if (data_type == TYPE_ELEMENT_LIST)
1846       {
1847         int *element_array = (int *)(conf[i].value);
1848         int j;
1849
1850         for (j = 0; j < max_num_entities; j++)
1851           element_array[j] = default_value;
1852       }
1853       else if (data_type == TYPE_CONTENT_LIST)
1854       {
1855         struct Content *content = (struct Content *)(conf[i].value);
1856         int c, x, y;
1857
1858         for (c = 0; c < max_num_entities; c++)
1859           for (y = 0; y < 3; y++)
1860             for (x = 0; x < 3; x++)
1861               content[c].e[x][y] = default_value;
1862       }
1863     }
1864     else        // constant size configuration data (1, 2 or 4 bytes)
1865     {
1866       if (data_type == TYPE_BOOLEAN)
1867         *(boolean *)(conf[i].value) = default_value;
1868       else
1869         *(int *)    (conf[i].value) = default_value;
1870     }
1871   }
1872 }
1873
1874 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1875 {
1876   int i;
1877
1878   for (i = 0; conf[i].data_type != -1; i++)
1879   {
1880     int data_type = conf[i].data_type;
1881     int conf_type = conf[i].conf_type;
1882     int byte_mask = conf_type & CONF_MASK_BYTES;
1883
1884     if (byte_mask == CONF_MASK_MULTI_BYTES)
1885     {
1886       int max_num_entities = conf[i].max_num_entities;
1887
1888       if (data_type == TYPE_STRING)
1889       {
1890         char *string      = (char *)(conf[i].value);
1891         char *string_copy = (char *)(conf[i].value_copy);
1892
1893         strncpy(string_copy, string, max_num_entities);
1894       }
1895       else if (data_type == TYPE_ELEMENT_LIST)
1896       {
1897         int *element_array      = (int *)(conf[i].value);
1898         int *element_array_copy = (int *)(conf[i].value_copy);
1899         int j;
1900
1901         for (j = 0; j < max_num_entities; j++)
1902           element_array_copy[j] = element_array[j];
1903       }
1904       else if (data_type == TYPE_CONTENT_LIST)
1905       {
1906         struct Content *content      = (struct Content *)(conf[i].value);
1907         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1908         int c, x, y;
1909
1910         for (c = 0; c < max_num_entities; c++)
1911           for (y = 0; y < 3; y++)
1912             for (x = 0; x < 3; x++)
1913               content_copy[c].e[x][y] = content[c].e[x][y];
1914       }
1915     }
1916     else        // constant size configuration data (1, 2 or 4 bytes)
1917     {
1918       if (data_type == TYPE_BOOLEAN)
1919         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1920       else
1921         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
1922     }
1923   }
1924 }
1925
1926 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1927 {
1928   int i;
1929
1930   xx_ei = *ei_from;     // copy element data into temporary buffer
1931   yy_ei = *ei_to;       // copy element data into temporary buffer
1932
1933   copyConfigFromConfigList(chunk_config_CUSX_base);
1934
1935   *ei_from = xx_ei;
1936   *ei_to   = yy_ei;
1937
1938   // ---------- reinitialize and copy change pages ----------
1939
1940   ei_to->num_change_pages = ei_from->num_change_pages;
1941   ei_to->current_change_page = ei_from->current_change_page;
1942
1943   setElementChangePages(ei_to, ei_to->num_change_pages);
1944
1945   for (i = 0; i < ei_to->num_change_pages; i++)
1946     ei_to->change_page[i] = ei_from->change_page[i];
1947
1948   // ---------- copy group element info ----------
1949   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
1950     *ei_to->group = *ei_from->group;
1951
1952   // mark this custom element as modified
1953   ei_to->modified_settings = TRUE;
1954 }
1955
1956 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1957 {
1958   int change_page_size = sizeof(struct ElementChangeInfo);
1959
1960   ei->num_change_pages = MAX(1, change_pages);
1961
1962   ei->change_page =
1963     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1964
1965   if (ei->current_change_page >= ei->num_change_pages)
1966     ei->current_change_page = ei->num_change_pages - 1;
1967
1968   ei->change = &ei->change_page[ei->current_change_page];
1969 }
1970
1971 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1972 {
1973   xx_change = *change;          // copy change data into temporary buffer
1974
1975   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1976
1977   *change = xx_change;
1978
1979   resetEventFlags(change);
1980
1981   change->direct_action = 0;
1982   change->other_action = 0;
1983
1984   change->pre_change_function = NULL;
1985   change->change_function = NULL;
1986   change->post_change_function = NULL;
1987 }
1988
1989 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1990 {
1991   int i, x, y;
1992
1993   li = *level;          // copy level data into temporary buffer
1994   setConfigToDefaultsFromConfigList(chunk_config_INFO);
1995   *level = li;          // copy temporary buffer back to level data
1996
1997   setLevelInfoToDefaults_BD();
1998   setLevelInfoToDefaults_EM();
1999   setLevelInfoToDefaults_SP();
2000   setLevelInfoToDefaults_MM();
2001
2002   level->native_bd_level = &native_bd_level;
2003   level->native_em_level = &native_em_level;
2004   level->native_sp_level = &native_sp_level;
2005   level->native_mm_level = &native_mm_level;
2006
2007   level->file_version = FILE_VERSION_ACTUAL;
2008   level->game_version = GAME_VERSION_ACTUAL;
2009
2010   level->creation_date = getCurrentDate();
2011
2012   level->encoding_16bit_field  = TRUE;
2013   level->encoding_16bit_yamyam = TRUE;
2014   level->encoding_16bit_amoeba = TRUE;
2015
2016   // clear level name and level author string buffers
2017   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2018     level->name[i] = '\0';
2019   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2020     level->author[i] = '\0';
2021
2022   // set level name and level author to default values
2023   strcpy(level->name, NAMELESS_LEVEL_NAME);
2024   strcpy(level->author, ANONYMOUS_NAME);
2025
2026   // set level playfield to playable default level with player and exit
2027   for (x = 0; x < MAX_LEV_FIELDX; x++)
2028     for (y = 0; y < MAX_LEV_FIELDY; y++)
2029       level->field[x][y] = EL_SAND;
2030
2031   level->field[0][0] = EL_PLAYER_1;
2032   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2033
2034   BorderElement = EL_STEELWALL;
2035
2036   // detect custom elements when loading them
2037   level->file_has_custom_elements = FALSE;
2038
2039   // set all bug compatibility flags to "false" => do not emulate this bug
2040   level->use_action_after_change_bug = FALSE;
2041
2042   if (leveldir_current)
2043   {
2044     // try to determine better author name than 'anonymous'
2045     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2046     {
2047       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2048       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2049     }
2050     else
2051     {
2052       switch (LEVELCLASS(leveldir_current))
2053       {
2054         case LEVELCLASS_TUTORIAL:
2055           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2056           break;
2057
2058         case LEVELCLASS_CONTRIB:
2059           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2060           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2061           break;
2062
2063         case LEVELCLASS_PRIVATE:
2064           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2065           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2066           break;
2067
2068         default:
2069           // keep default value
2070           break;
2071       }
2072     }
2073   }
2074 }
2075
2076 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2077 {
2078   static boolean clipboard_elements_initialized = FALSE;
2079   int i;
2080
2081   InitElementPropertiesStatic();
2082
2083   li = *level;          // copy level data into temporary buffer
2084   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2085   *level = li;          // copy temporary buffer back to level data
2086
2087   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2088   {
2089     int element = i;
2090     struct ElementInfo *ei = &element_info[element];
2091
2092     if (element == EL_MM_GRAY_BALL)
2093     {
2094       struct LevelInfo_MM *level_mm = level->native_mm_level;
2095       int j;
2096
2097       for (j = 0; j < level->num_mm_ball_contents; j++)
2098         level->mm_ball_content[j] =
2099           map_element_MM_to_RND(level_mm->ball_content[j]);
2100     }
2101
2102     // never initialize clipboard elements after the very first time
2103     // (to be able to use clipboard elements between several levels)
2104     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2105       continue;
2106
2107     if (IS_ENVELOPE(element))
2108     {
2109       int envelope_nr = element - EL_ENVELOPE_1;
2110
2111       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2112
2113       level->envelope[envelope_nr] = xx_envelope;
2114     }
2115
2116     if (IS_CUSTOM_ELEMENT(element) ||
2117         IS_GROUP_ELEMENT(element) ||
2118         IS_INTERNAL_ELEMENT(element))
2119     {
2120       xx_ei = *ei;      // copy element data into temporary buffer
2121
2122       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2123
2124       *ei = xx_ei;
2125     }
2126
2127     setElementChangePages(ei, 1);
2128     setElementChangeInfoToDefaults(ei->change);
2129
2130     if (IS_CUSTOM_ELEMENT(element) ||
2131         IS_GROUP_ELEMENT(element))
2132     {
2133       setElementDescriptionToDefault(ei);
2134
2135       ei->modified_settings = FALSE;
2136     }
2137
2138     if (IS_CUSTOM_ELEMENT(element) ||
2139         IS_INTERNAL_ELEMENT(element))
2140     {
2141       // internal values used in level editor
2142
2143       ei->access_type = 0;
2144       ei->access_layer = 0;
2145       ei->access_protected = 0;
2146       ei->walk_to_action = 0;
2147       ei->smash_targets = 0;
2148       ei->deadliness = 0;
2149
2150       ei->can_explode_by_fire = FALSE;
2151       ei->can_explode_smashed = FALSE;
2152       ei->can_explode_impact = FALSE;
2153
2154       ei->current_change_page = 0;
2155     }
2156
2157     if (IS_GROUP_ELEMENT(element) ||
2158         IS_INTERNAL_ELEMENT(element))
2159     {
2160       struct ElementGroupInfo *group;
2161
2162       // initialize memory for list of elements in group
2163       if (ei->group == NULL)
2164         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2165
2166       group = ei->group;
2167
2168       xx_group = *group;        // copy group data into temporary buffer
2169
2170       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2171
2172       *group = xx_group;
2173     }
2174
2175     if (IS_EMPTY_ELEMENT(element) ||
2176         IS_INTERNAL_ELEMENT(element))
2177     {
2178       xx_ei = *ei;              // copy element data into temporary buffer
2179
2180       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2181
2182       *ei = xx_ei;
2183     }
2184   }
2185
2186   clipboard_elements_initialized = TRUE;
2187 }
2188
2189 static void setLevelInfoToDefaults(struct LevelInfo *level,
2190                                    boolean level_info_only,
2191                                    boolean reset_file_status)
2192 {
2193   setLevelInfoToDefaults_Level(level);
2194
2195   if (!level_info_only)
2196     setLevelInfoToDefaults_Elements(level);
2197
2198   if (reset_file_status)
2199   {
2200     level->no_valid_file = FALSE;
2201     level->no_level_file = FALSE;
2202   }
2203
2204   level->changed = FALSE;
2205 }
2206
2207 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2208 {
2209   level_file_info->nr = 0;
2210   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2211   level_file_info->packed = FALSE;
2212
2213   setString(&level_file_info->basename, NULL);
2214   setString(&level_file_info->filename, NULL);
2215 }
2216
2217 int getMappedElement_SB(int, boolean);
2218
2219 static void ActivateLevelTemplate(void)
2220 {
2221   int x, y;
2222
2223   if (check_special_flags("load_xsb_to_ces"))
2224   {
2225     // fill smaller playfields with padding "beyond border wall" elements
2226     if (level.fieldx < level_template.fieldx ||
2227         level.fieldy < level_template.fieldy)
2228     {
2229       short field[level.fieldx][level.fieldy];
2230       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2231       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2232       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2233       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2234
2235       // copy old playfield (which is smaller than the visible area)
2236       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2237         field[x][y] = level.field[x][y];
2238
2239       // fill new, larger playfield with "beyond border wall" elements
2240       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2241         level.field[x][y] = getMappedElement_SB('_', TRUE);
2242
2243       // copy the old playfield to the middle of the new playfield
2244       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2245         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2246
2247       level.fieldx = new_fieldx;
2248       level.fieldy = new_fieldy;
2249     }
2250   }
2251
2252   // Currently there is no special action needed to activate the template
2253   // data, because 'element_info' property settings overwrite the original
2254   // level data, while all other variables do not change.
2255
2256   // Exception: 'from_level_template' elements in the original level playfield
2257   // are overwritten with the corresponding elements at the same position in
2258   // playfield from the level template.
2259
2260   for (x = 0; x < level.fieldx; x++)
2261     for (y = 0; y < level.fieldy; y++)
2262       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2263         level.field[x][y] = level_template.field[x][y];
2264
2265   if (check_special_flags("load_xsb_to_ces"))
2266   {
2267     struct LevelInfo level_backup = level;
2268
2269     // overwrite all individual level settings from template level settings
2270     level = level_template;
2271
2272     // restore level file info
2273     level.file_info = level_backup.file_info;
2274
2275     // restore playfield size
2276     level.fieldx = level_backup.fieldx;
2277     level.fieldy = level_backup.fieldy;
2278
2279     // restore playfield content
2280     for (x = 0; x < level.fieldx; x++)
2281       for (y = 0; y < level.fieldy; y++)
2282         level.field[x][y] = level_backup.field[x][y];
2283
2284     // restore name and author from individual level
2285     strcpy(level.name,   level_backup.name);
2286     strcpy(level.author, level_backup.author);
2287
2288     // restore flag "use_custom_template"
2289     level.use_custom_template = level_backup.use_custom_template;
2290   }
2291 }
2292
2293 static boolean checkForPackageFromBasename_BD(char *basename)
2294 {
2295   // check for native BD level file extensions
2296   if (!strSuffixLower(basename, ".bd") &&
2297       !strSuffixLower(basename, ".bdr") &&
2298       !strSuffixLower(basename, ".brc") &&
2299       !strSuffixLower(basename, ".gds"))
2300     return FALSE;
2301
2302   // check for standard single-level BD files (like "001.bd")
2303   if (strSuffixLower(basename, ".bd") &&
2304       strlen(basename) == 6 &&
2305       basename[0] >= '0' && basename[0] <= '9' &&
2306       basename[1] >= '0' && basename[1] <= '9' &&
2307       basename[2] >= '0' && basename[2] <= '9')
2308     return FALSE;
2309
2310   // this is a level package in native BD file format
2311   return TRUE;
2312 }
2313
2314 static char *getLevelFilenameFromBasename(char *basename)
2315 {
2316   static char *filename = NULL;
2317
2318   checked_free(filename);
2319
2320   filename = getPath2(getCurrentLevelDir(), basename);
2321
2322   return filename;
2323 }
2324
2325 static int getFileTypeFromBasename(char *basename)
2326 {
2327   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2328
2329   static char *filename = NULL;
2330   struct stat file_status;
2331
2332   // ---------- try to determine file type from filename ----------
2333
2334   // check for typical filename of a Supaplex level package file
2335   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2336     return LEVEL_FILE_TYPE_SP;
2337
2338   // check for typical filename of a Diamond Caves II level package file
2339   if (strSuffixLower(basename, ".dc") ||
2340       strSuffixLower(basename, ".dc2"))
2341     return LEVEL_FILE_TYPE_DC;
2342
2343   // check for typical filename of a Sokoban level package file
2344   if (strSuffixLower(basename, ".xsb") &&
2345       strchr(basename, '%') == NULL)
2346     return LEVEL_FILE_TYPE_SB;
2347
2348   // check for typical filename of a Boulder Dash (GDash) level package file
2349   if (checkForPackageFromBasename_BD(basename))
2350     return LEVEL_FILE_TYPE_BD;
2351
2352   // ---------- try to determine file type from filesize ----------
2353
2354   checked_free(filename);
2355   filename = getPath2(getCurrentLevelDir(), basename);
2356
2357   if (stat(filename, &file_status) == 0)
2358   {
2359     // check for typical filesize of a Supaplex level package file
2360     if (file_status.st_size == 170496)
2361       return LEVEL_FILE_TYPE_SP;
2362   }
2363
2364   return LEVEL_FILE_TYPE_UNKNOWN;
2365 }
2366
2367 static int getFileTypeFromMagicBytes(char *filename, int type)
2368 {
2369   File *file;
2370
2371   if ((file = openFile(filename, MODE_READ)))
2372   {
2373     char chunk_name[CHUNK_ID_LEN + 1];
2374
2375     getFileChunkBE(file, chunk_name, NULL);
2376
2377     if (strEqual(chunk_name, "MMII") ||
2378         strEqual(chunk_name, "MIRR"))
2379       type = LEVEL_FILE_TYPE_MM;
2380
2381     closeFile(file);
2382   }
2383
2384   return type;
2385 }
2386
2387 static boolean checkForPackageFromBasename(char *basename)
2388 {
2389   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2390   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2391
2392   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2393 }
2394
2395 static char *getSingleLevelBasenameExt(int nr, char *extension)
2396 {
2397   static char basename[MAX_FILENAME_LEN];
2398
2399   if (nr < 0)
2400     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2401   else
2402     sprintf(basename, "%03d.%s", nr, extension);
2403
2404   return basename;
2405 }
2406
2407 static char *getSingleLevelBasename(int nr)
2408 {
2409   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2410 }
2411
2412 static char *getPackedLevelBasename(int type)
2413 {
2414   static char basename[MAX_FILENAME_LEN];
2415   char *directory = getCurrentLevelDir();
2416   Directory *dir;
2417   DirectoryEntry *dir_entry;
2418
2419   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2420
2421   if ((dir = openDirectory(directory)) == NULL)
2422   {
2423     Warn("cannot read current level directory '%s'", directory);
2424
2425     return basename;
2426   }
2427
2428   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2429   {
2430     char *entry_basename = dir_entry->basename;
2431     int entry_type = getFileTypeFromBasename(entry_basename);
2432
2433     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2434     {
2435       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2436           type == entry_type)
2437       {
2438         strcpy(basename, entry_basename);
2439
2440         break;
2441       }
2442     }
2443   }
2444
2445   closeDirectory(dir);
2446
2447   return basename;
2448 }
2449
2450 static char *getSingleLevelFilename(int nr)
2451 {
2452   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2453 }
2454
2455 #if ENABLE_UNUSED_CODE
2456 static char *getPackedLevelFilename(int type)
2457 {
2458   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2459 }
2460 #endif
2461
2462 char *getDefaultLevelFilename(int nr)
2463 {
2464   return getSingleLevelFilename(nr);
2465 }
2466
2467 #if ENABLE_UNUSED_CODE
2468 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2469                                                  int type)
2470 {
2471   lfi->type = type;
2472   lfi->packed = FALSE;
2473
2474   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2475   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2476 }
2477 #endif
2478
2479 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2480                                                  int type, char *format, ...)
2481 {
2482   static char basename[MAX_FILENAME_LEN];
2483   va_list ap;
2484
2485   va_start(ap, format);
2486   vsprintf(basename, format, ap);
2487   va_end(ap);
2488
2489   lfi->type = type;
2490   lfi->packed = FALSE;
2491
2492   setString(&lfi->basename, basename);
2493   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2494 }
2495
2496 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2497                                                  int type)
2498 {
2499   lfi->type = type;
2500   lfi->packed = TRUE;
2501
2502   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2503   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2504 }
2505
2506 static int getFiletypeFromID(char *filetype_id)
2507 {
2508   char *filetype_id_lower;
2509   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2510   int i;
2511
2512   if (filetype_id == NULL)
2513     return LEVEL_FILE_TYPE_UNKNOWN;
2514
2515   filetype_id_lower = getStringToLower(filetype_id);
2516
2517   for (i = 0; filetype_id_list[i].id != NULL; i++)
2518   {
2519     char *id_lower = getStringToLower(filetype_id_list[i].id);
2520     
2521     if (strEqual(filetype_id_lower, id_lower))
2522       filetype = filetype_id_list[i].filetype;
2523
2524     free(id_lower);
2525
2526     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2527       break;
2528   }
2529
2530   free(filetype_id_lower);
2531
2532   return filetype;
2533 }
2534
2535 char *getLocalLevelTemplateFilename(void)
2536 {
2537   return getDefaultLevelFilename(-1);
2538 }
2539
2540 char *getGlobalLevelTemplateFilename(void)
2541 {
2542   // global variable "leveldir_current" must be modified in the loop below
2543   LevelDirTree *leveldir_current_last = leveldir_current;
2544   char *filename = NULL;
2545
2546   // check for template level in path from current to topmost tree node
2547
2548   while (leveldir_current != NULL)
2549   {
2550     filename = getDefaultLevelFilename(-1);
2551
2552     if (fileExists(filename))
2553       break;
2554
2555     leveldir_current = leveldir_current->node_parent;
2556   }
2557
2558   // restore global variable "leveldir_current" modified in above loop
2559   leveldir_current = leveldir_current_last;
2560
2561   return filename;
2562 }
2563
2564 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2565 {
2566   int nr = lfi->nr;
2567
2568   // special case: level number is negative => check for level template file
2569   if (nr < 0)
2570   {
2571     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2572                                          getSingleLevelBasename(-1));
2573
2574     // replace local level template filename with global template filename
2575     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2576
2577     // no fallback if template file not existing
2578     return;
2579   }
2580
2581   // special case: check for file name/pattern specified in "levelinfo.conf"
2582   if (leveldir_current->level_filename != NULL)
2583   {
2584     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2585
2586     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2587                                          leveldir_current->level_filename, nr);
2588
2589     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2590
2591     if (fileExists(lfi->filename))
2592       return;
2593   }
2594   else if (leveldir_current->level_filetype != NULL)
2595   {
2596     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2597
2598     // check for specified native level file with standard file name
2599     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2600                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2601     if (fileExists(lfi->filename))
2602       return;
2603   }
2604
2605   // check for native Rocks'n'Diamonds level file
2606   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2607                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2608   if (fileExists(lfi->filename))
2609     return;
2610
2611   // check for native Boulder Dash level file
2612   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2613   if (fileExists(lfi->filename))
2614     return;
2615
2616   // check for Emerald Mine level file (V1)
2617   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2618                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2619   if (fileExists(lfi->filename))
2620     return;
2621   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2622                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2623   if (fileExists(lfi->filename))
2624     return;
2625
2626   // check for Emerald Mine level file (V2 to V5)
2627   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2628   if (fileExists(lfi->filename))
2629     return;
2630
2631   // check for Emerald Mine level file (V6 / single mode)
2632   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2633   if (fileExists(lfi->filename))
2634     return;
2635   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2636   if (fileExists(lfi->filename))
2637     return;
2638
2639   // check for Emerald Mine level file (V6 / teamwork mode)
2640   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2641   if (fileExists(lfi->filename))
2642     return;
2643   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2644   if (fileExists(lfi->filename))
2645     return;
2646
2647   // check for various packed level file formats
2648   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2649   if (fileExists(lfi->filename))
2650     return;
2651
2652   // no known level file found -- use default values (and fail later)
2653   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2654                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2655 }
2656
2657 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2658 {
2659   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2660     lfi->type = getFileTypeFromBasename(lfi->basename);
2661
2662   if (lfi->type == LEVEL_FILE_TYPE_RND)
2663     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2664 }
2665
2666 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2667 {
2668   // always start with reliable default values
2669   setFileInfoToDefaults(level_file_info);
2670
2671   level_file_info->nr = nr;     // set requested level number
2672
2673   determineLevelFileInfo_Filename(level_file_info);
2674   determineLevelFileInfo_Filetype(level_file_info);
2675 }
2676
2677 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2678                               struct LevelFileInfo *lfi_to)
2679 {
2680   lfi_to->nr     = lfi_from->nr;
2681   lfi_to->type   = lfi_from->type;
2682   lfi_to->packed = lfi_from->packed;
2683
2684   setString(&lfi_to->basename, lfi_from->basename);
2685   setString(&lfi_to->filename, lfi_from->filename);
2686 }
2687
2688 // ----------------------------------------------------------------------------
2689 // functions for loading R'n'D level
2690 // ----------------------------------------------------------------------------
2691
2692 int getMappedElement(int element)
2693 {
2694   // remap some (historic, now obsolete) elements
2695
2696   switch (element)
2697   {
2698     case EL_PLAYER_OBSOLETE:
2699       element = EL_PLAYER_1;
2700       break;
2701
2702     case EL_KEY_OBSOLETE:
2703       element = EL_KEY_1;
2704       break;
2705
2706     case EL_EM_KEY_1_FILE_OBSOLETE:
2707       element = EL_EM_KEY_1;
2708       break;
2709
2710     case EL_EM_KEY_2_FILE_OBSOLETE:
2711       element = EL_EM_KEY_2;
2712       break;
2713
2714     case EL_EM_KEY_3_FILE_OBSOLETE:
2715       element = EL_EM_KEY_3;
2716       break;
2717
2718     case EL_EM_KEY_4_FILE_OBSOLETE:
2719       element = EL_EM_KEY_4;
2720       break;
2721
2722     case EL_ENVELOPE_OBSOLETE:
2723       element = EL_ENVELOPE_1;
2724       break;
2725
2726     case EL_SP_EMPTY:
2727       element = EL_EMPTY;
2728       break;
2729
2730     default:
2731       if (element >= NUM_FILE_ELEMENTS)
2732       {
2733         Warn("invalid level element %d", element);
2734
2735         element = EL_UNKNOWN;
2736       }
2737       break;
2738   }
2739
2740   return element;
2741 }
2742
2743 static int getMappedElementByVersion(int element, int game_version)
2744 {
2745   // remap some elements due to certain game version
2746
2747   if (game_version <= VERSION_IDENT(2,2,0,0))
2748   {
2749     // map game font elements
2750     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2751                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2752                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2753                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2754   }
2755
2756   if (game_version < VERSION_IDENT(3,0,0,0))
2757   {
2758     // map Supaplex gravity tube elements
2759     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2760                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2761                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2762                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2763                element);
2764   }
2765
2766   return element;
2767 }
2768
2769 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2770 {
2771   level->file_version = getFileVersion(file);
2772   level->game_version = getFileVersion(file);
2773
2774   return chunk_size;
2775 }
2776
2777 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2778 {
2779   level->creation_date.year  = getFile16BitBE(file);
2780   level->creation_date.month = getFile8Bit(file);
2781   level->creation_date.day   = getFile8Bit(file);
2782
2783   level->creation_date.src   = DATE_SRC_LEVELFILE;
2784
2785   return chunk_size;
2786 }
2787
2788 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2789 {
2790   int initial_player_stepsize;
2791   int initial_player_gravity;
2792   int i, x, y;
2793
2794   level->fieldx = getFile8Bit(file);
2795   level->fieldy = getFile8Bit(file);
2796
2797   level->time           = getFile16BitBE(file);
2798   level->gems_needed    = getFile16BitBE(file);
2799
2800   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2801     level->name[i] = getFile8Bit(file);
2802   level->name[MAX_LEVEL_NAME_LEN] = 0;
2803
2804   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2805     level->score[i] = getFile8Bit(file);
2806
2807   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2808   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2809     for (y = 0; y < 3; y++)
2810       for (x = 0; x < 3; x++)
2811         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2812
2813   level->amoeba_speed           = getFile8Bit(file);
2814   level->time_magic_wall        = getFile8Bit(file);
2815   level->time_wheel             = getFile8Bit(file);
2816   level->amoeba_content         = getMappedElement(getFile8Bit(file));
2817
2818   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2819                                    STEPSIZE_NORMAL);
2820
2821   for (i = 0; i < MAX_PLAYERS; i++)
2822     level->initial_player_stepsize[i] = initial_player_stepsize;
2823
2824   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2825
2826   for (i = 0; i < MAX_PLAYERS; i++)
2827     level->initial_player_gravity[i] = initial_player_gravity;
2828
2829   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2830   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2831
2832   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2833
2834   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2835   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2836   level->can_move_into_acid_bits = getFile32BitBE(file);
2837   level->dont_collide_with_bits = getFile8Bit(file);
2838
2839   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2840   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2841
2842   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2843   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2844   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2845
2846   level->game_engine_type       = getFile8Bit(file);
2847
2848   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2849
2850   return chunk_size;
2851 }
2852
2853 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2854 {
2855   int i;
2856
2857   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2858     level->name[i] = getFile8Bit(file);
2859   level->name[MAX_LEVEL_NAME_LEN] = 0;
2860
2861   return chunk_size;
2862 }
2863
2864 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2865 {
2866   int i;
2867
2868   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2869     level->author[i] = getFile8Bit(file);
2870   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2871
2872   return chunk_size;
2873 }
2874
2875 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2876 {
2877   int x, y;
2878   int chunk_size_expected = level->fieldx * level->fieldy;
2879
2880   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2881      stored with 16-bit encoding (and should be twice as big then).
2882      Even worse, playfield data was stored 16-bit when only yamyam content
2883      contained 16-bit elements and vice versa. */
2884
2885   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2886     chunk_size_expected *= 2;
2887
2888   if (chunk_size_expected != chunk_size)
2889   {
2890     ReadUnusedBytesFromFile(file, chunk_size);
2891     return chunk_size_expected;
2892   }
2893
2894   for (y = 0; y < level->fieldy; y++)
2895     for (x = 0; x < level->fieldx; x++)
2896       level->field[x][y] =
2897         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2898                          getFile8Bit(file));
2899   return chunk_size;
2900 }
2901
2902 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2903 {
2904   int i, x, y;
2905   int header_size = 4;
2906   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2907   int chunk_size_expected = header_size + content_size;
2908
2909   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2910      stored with 16-bit encoding (and should be twice as big then).
2911      Even worse, playfield data was stored 16-bit when only yamyam content
2912      contained 16-bit elements and vice versa. */
2913
2914   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2915     chunk_size_expected += content_size;
2916
2917   if (chunk_size_expected != chunk_size)
2918   {
2919     ReadUnusedBytesFromFile(file, chunk_size);
2920     return chunk_size_expected;
2921   }
2922
2923   getFile8Bit(file);
2924   level->num_yamyam_contents = getFile8Bit(file);
2925   getFile8Bit(file);
2926   getFile8Bit(file);
2927
2928   // correct invalid number of content fields -- should never happen
2929   if (level->num_yamyam_contents < 1 ||
2930       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2931     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2932
2933   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2934     for (y = 0; y < 3; y++)
2935       for (x = 0; x < 3; x++)
2936         level->yamyam_content[i].e[x][y] =
2937           getMappedElement(level->encoding_16bit_field ?
2938                            getFile16BitBE(file) : getFile8Bit(file));
2939   return chunk_size;
2940 }
2941
2942 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2943 {
2944   int i, x, y;
2945   int element;
2946   int num_contents;
2947   int content_array[MAX_ELEMENT_CONTENTS][3][3];
2948
2949   element = getMappedElement(getFile16BitBE(file));
2950   num_contents = getFile8Bit(file);
2951
2952   getFile8Bit(file);    // content x size (unused)
2953   getFile8Bit(file);    // content y size (unused)
2954
2955   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2956
2957   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2958     for (y = 0; y < 3; y++)
2959       for (x = 0; x < 3; x++)
2960         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2961
2962   // correct invalid number of content fields -- should never happen
2963   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2964     num_contents = STD_ELEMENT_CONTENTS;
2965
2966   if (element == EL_YAMYAM)
2967   {
2968     level->num_yamyam_contents = num_contents;
2969
2970     for (i = 0; i < num_contents; i++)
2971       for (y = 0; y < 3; y++)
2972         for (x = 0; x < 3; x++)
2973           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2974   }
2975   else if (element == EL_BD_AMOEBA)
2976   {
2977     level->amoeba_content = content_array[0][0][0];
2978   }
2979   else
2980   {
2981     Warn("cannot load content for element '%d'", element);
2982   }
2983
2984   return chunk_size;
2985 }
2986
2987 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2988 {
2989   int i;
2990   int element;
2991   int envelope_nr;
2992   int envelope_len;
2993   int chunk_size_expected;
2994
2995   element = getMappedElement(getFile16BitBE(file));
2996   if (!IS_ENVELOPE(element))
2997     element = EL_ENVELOPE_1;
2998
2999   envelope_nr = element - EL_ENVELOPE_1;
3000
3001   envelope_len = getFile16BitBE(file);
3002
3003   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3004   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3005
3006   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3007
3008   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3009   if (chunk_size_expected != chunk_size)
3010   {
3011     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3012     return chunk_size_expected;
3013   }
3014
3015   for (i = 0; i < envelope_len; i++)
3016     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3017
3018   return chunk_size;
3019 }
3020
3021 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3022 {
3023   int num_changed_custom_elements = getFile16BitBE(file);
3024   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3025   int i;
3026
3027   if (chunk_size_expected != chunk_size)
3028   {
3029     ReadUnusedBytesFromFile(file, chunk_size - 2);
3030     return chunk_size_expected;
3031   }
3032
3033   for (i = 0; i < num_changed_custom_elements; i++)
3034   {
3035     int element = getMappedElement(getFile16BitBE(file));
3036     int properties = getFile32BitBE(file);
3037
3038     if (IS_CUSTOM_ELEMENT(element))
3039       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3040     else
3041       Warn("invalid custom element number %d", element);
3042
3043     // older game versions that wrote level files with CUS1 chunks used
3044     // different default push delay values (not yet stored in level file)
3045     element_info[element].push_delay_fixed = 2;
3046     element_info[element].push_delay_random = 8;
3047   }
3048
3049   level->file_has_custom_elements = TRUE;
3050
3051   return chunk_size;
3052 }
3053
3054 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3055 {
3056   int num_changed_custom_elements = getFile16BitBE(file);
3057   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3058   int i;
3059
3060   if (chunk_size_expected != chunk_size)
3061   {
3062     ReadUnusedBytesFromFile(file, chunk_size - 2);
3063     return chunk_size_expected;
3064   }
3065
3066   for (i = 0; i < num_changed_custom_elements; i++)
3067   {
3068     int element = getMappedElement(getFile16BitBE(file));
3069     int custom_target_element = getMappedElement(getFile16BitBE(file));
3070
3071     if (IS_CUSTOM_ELEMENT(element))
3072       element_info[element].change->target_element = custom_target_element;
3073     else
3074       Warn("invalid custom element number %d", element);
3075   }
3076
3077   level->file_has_custom_elements = TRUE;
3078
3079   return chunk_size;
3080 }
3081
3082 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3083 {
3084   int num_changed_custom_elements = getFile16BitBE(file);
3085   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3086   int i, j, x, y;
3087
3088   if (chunk_size_expected != chunk_size)
3089   {
3090     ReadUnusedBytesFromFile(file, chunk_size - 2);
3091     return chunk_size_expected;
3092   }
3093
3094   for (i = 0; i < num_changed_custom_elements; i++)
3095   {
3096     int element = getMappedElement(getFile16BitBE(file));
3097     struct ElementInfo *ei = &element_info[element];
3098     unsigned int event_bits;
3099
3100     if (!IS_CUSTOM_ELEMENT(element))
3101     {
3102       Warn("invalid custom element number %d", element);
3103
3104       element = EL_INTERNAL_DUMMY;
3105     }
3106
3107     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3108       ei->description[j] = getFile8Bit(file);
3109     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3110
3111     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3112
3113     // some free bytes for future properties and padding
3114     ReadUnusedBytesFromFile(file, 7);
3115
3116     ei->use_gfx_element = getFile8Bit(file);
3117     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3118
3119     ei->collect_score_initial = getFile8Bit(file);
3120     ei->collect_count_initial = getFile8Bit(file);
3121
3122     ei->push_delay_fixed = getFile16BitBE(file);
3123     ei->push_delay_random = getFile16BitBE(file);
3124     ei->move_delay_fixed = getFile16BitBE(file);
3125     ei->move_delay_random = getFile16BitBE(file);
3126
3127     ei->move_pattern = getFile16BitBE(file);
3128     ei->move_direction_initial = getFile8Bit(file);
3129     ei->move_stepsize = getFile8Bit(file);
3130
3131     for (y = 0; y < 3; y++)
3132       for (x = 0; x < 3; x++)
3133         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3134
3135     // bits 0 - 31 of "has_event[]"
3136     event_bits = getFile32BitBE(file);
3137     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3138       if (event_bits & (1u << j))
3139         ei->change->has_event[j] = TRUE;
3140
3141     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3142
3143     ei->change->delay_fixed = getFile16BitBE(file);
3144     ei->change->delay_random = getFile16BitBE(file);
3145     ei->change->delay_frames = getFile16BitBE(file);
3146
3147     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3148
3149     ei->change->explode = getFile8Bit(file);
3150     ei->change->use_target_content = getFile8Bit(file);
3151     ei->change->only_if_complete = getFile8Bit(file);
3152     ei->change->use_random_replace = getFile8Bit(file);
3153
3154     ei->change->random_percentage = getFile8Bit(file);
3155     ei->change->replace_when = getFile8Bit(file);
3156
3157     for (y = 0; y < 3; y++)
3158       for (x = 0; x < 3; x++)
3159         ei->change->target_content.e[x][y] =
3160           getMappedElement(getFile16BitBE(file));
3161
3162     ei->slippery_type = getFile8Bit(file);
3163
3164     // some free bytes for future properties and padding
3165     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3166
3167     // mark that this custom element has been modified
3168     ei->modified_settings = TRUE;
3169   }
3170
3171   level->file_has_custom_elements = TRUE;
3172
3173   return chunk_size;
3174 }
3175
3176 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3177 {
3178   struct ElementInfo *ei;
3179   int chunk_size_expected;
3180   int element;
3181   int i, j, x, y;
3182
3183   // ---------- custom element base property values (96 bytes) ----------------
3184
3185   element = getMappedElement(getFile16BitBE(file));
3186
3187   if (!IS_CUSTOM_ELEMENT(element))
3188   {
3189     Warn("invalid custom element number %d", element);
3190
3191     ReadUnusedBytesFromFile(file, chunk_size - 2);
3192
3193     return chunk_size;
3194   }
3195
3196   ei = &element_info[element];
3197
3198   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3199     ei->description[i] = getFile8Bit(file);
3200   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3201
3202   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3203
3204   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3205
3206   ei->num_change_pages = getFile8Bit(file);
3207
3208   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3209   if (chunk_size_expected != chunk_size)
3210   {
3211     ReadUnusedBytesFromFile(file, chunk_size - 43);
3212     return chunk_size_expected;
3213   }
3214
3215   ei->ce_value_fixed_initial = getFile16BitBE(file);
3216   ei->ce_value_random_initial = getFile16BitBE(file);
3217   ei->use_last_ce_value = getFile8Bit(file);
3218
3219   ei->use_gfx_element = getFile8Bit(file);
3220   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3221
3222   ei->collect_score_initial = getFile8Bit(file);
3223   ei->collect_count_initial = getFile8Bit(file);
3224
3225   ei->drop_delay_fixed = getFile8Bit(file);
3226   ei->push_delay_fixed = getFile8Bit(file);
3227   ei->drop_delay_random = getFile8Bit(file);
3228   ei->push_delay_random = getFile8Bit(file);
3229   ei->move_delay_fixed = getFile16BitBE(file);
3230   ei->move_delay_random = getFile16BitBE(file);
3231
3232   // bits 0 - 15 of "move_pattern" ...
3233   ei->move_pattern = getFile16BitBE(file);
3234   ei->move_direction_initial = getFile8Bit(file);
3235   ei->move_stepsize = getFile8Bit(file);
3236
3237   ei->slippery_type = getFile8Bit(file);
3238
3239   for (y = 0; y < 3; y++)
3240     for (x = 0; x < 3; x++)
3241       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3242
3243   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3244   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3245   ei->move_leave_type = getFile8Bit(file);
3246
3247   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3248   ei->move_pattern |= (getFile16BitBE(file) << 16);
3249
3250   ei->access_direction = getFile8Bit(file);
3251
3252   ei->explosion_delay = getFile8Bit(file);
3253   ei->ignition_delay = getFile8Bit(file);
3254   ei->explosion_type = getFile8Bit(file);
3255
3256   // some free bytes for future custom property values and padding
3257   ReadUnusedBytesFromFile(file, 1);
3258
3259   // ---------- change page property values (48 bytes) ------------------------
3260
3261   setElementChangePages(ei, ei->num_change_pages);
3262
3263   for (i = 0; i < ei->num_change_pages; i++)
3264   {
3265     struct ElementChangeInfo *change = &ei->change_page[i];
3266     unsigned int event_bits;
3267
3268     // always start with reliable default values
3269     setElementChangeInfoToDefaults(change);
3270
3271     // bits 0 - 31 of "has_event[]" ...
3272     event_bits = getFile32BitBE(file);
3273     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3274       if (event_bits & (1u << j))
3275         change->has_event[j] = TRUE;
3276
3277     change->target_element = getMappedElement(getFile16BitBE(file));
3278
3279     change->delay_fixed = getFile16BitBE(file);
3280     change->delay_random = getFile16BitBE(file);
3281     change->delay_frames = getFile16BitBE(file);
3282
3283     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3284
3285     change->explode = getFile8Bit(file);
3286     change->use_target_content = getFile8Bit(file);
3287     change->only_if_complete = getFile8Bit(file);
3288     change->use_random_replace = getFile8Bit(file);
3289
3290     change->random_percentage = getFile8Bit(file);
3291     change->replace_when = getFile8Bit(file);
3292
3293     for (y = 0; y < 3; y++)
3294       for (x = 0; x < 3; x++)
3295         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3296
3297     change->can_change = getFile8Bit(file);
3298
3299     change->trigger_side = getFile8Bit(file);
3300
3301     change->trigger_player = getFile8Bit(file);
3302     change->trigger_page = getFile8Bit(file);
3303
3304     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3305                             CH_PAGE_ANY : (1 << change->trigger_page));
3306
3307     change->has_action = getFile8Bit(file);
3308     change->action_type = getFile8Bit(file);
3309     change->action_mode = getFile8Bit(file);
3310     change->action_arg = getFile16BitBE(file);
3311
3312     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3313     event_bits = getFile8Bit(file);
3314     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3315       if (event_bits & (1u << (j - 32)))
3316         change->has_event[j] = TRUE;
3317   }
3318
3319   // mark this custom element as modified
3320   ei->modified_settings = TRUE;
3321
3322   level->file_has_custom_elements = TRUE;
3323
3324   return chunk_size;
3325 }
3326
3327 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3328 {
3329   struct ElementInfo *ei;
3330   struct ElementGroupInfo *group;
3331   int element;
3332   int i;
3333
3334   element = getMappedElement(getFile16BitBE(file));
3335
3336   if (!IS_GROUP_ELEMENT(element))
3337   {
3338     Warn("invalid group element number %d", element);
3339
3340     ReadUnusedBytesFromFile(file, chunk_size - 2);
3341
3342     return chunk_size;
3343   }
3344
3345   ei = &element_info[element];
3346
3347   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3348     ei->description[i] = getFile8Bit(file);
3349   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3350
3351   group = element_info[element].group;
3352
3353   group->num_elements = getFile8Bit(file);
3354
3355   ei->use_gfx_element = getFile8Bit(file);
3356   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3357
3358   group->choice_mode = getFile8Bit(file);
3359
3360   // some free bytes for future values and padding
3361   ReadUnusedBytesFromFile(file, 3);
3362
3363   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3364     group->element[i] = getMappedElement(getFile16BitBE(file));
3365
3366   // mark this group element as modified
3367   element_info[element].modified_settings = TRUE;
3368
3369   level->file_has_custom_elements = TRUE;
3370
3371   return chunk_size;
3372 }
3373
3374 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3375                                 int element, int real_element)
3376 {
3377   int micro_chunk_size = 0;
3378   int conf_type = getFile8Bit(file);
3379   int byte_mask = conf_type & CONF_MASK_BYTES;
3380   boolean element_found = FALSE;
3381   int i;
3382
3383   micro_chunk_size += 1;
3384
3385   if (byte_mask == CONF_MASK_MULTI_BYTES)
3386   {
3387     int num_bytes = getFile16BitBE(file);
3388     byte *buffer = checked_malloc(num_bytes);
3389
3390     ReadBytesFromFile(file, buffer, num_bytes);
3391
3392     for (i = 0; conf[i].data_type != -1; i++)
3393     {
3394       if (conf[i].element == element &&
3395           conf[i].conf_type == conf_type)
3396       {
3397         int data_type = conf[i].data_type;
3398         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3399         int max_num_entities = conf[i].max_num_entities;
3400
3401         if (num_entities > max_num_entities)
3402         {
3403           Warn("truncating number of entities for element %d from %d to %d",
3404                element, num_entities, max_num_entities);
3405
3406           num_entities = max_num_entities;
3407         }
3408
3409         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3410                                   data_type == TYPE_CONTENT_LIST))
3411         {
3412           // for element and content lists, zero entities are not allowed
3413           Warn("found empty list of entities for element %d", element);
3414
3415           // do not set "num_entities" here to prevent reading behind buffer
3416
3417           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3418         }
3419         else
3420         {
3421           *(int *)(conf[i].num_entities) = num_entities;
3422         }
3423
3424         element_found = TRUE;
3425
3426         if (data_type == TYPE_STRING)
3427         {
3428           char *string = (char *)(conf[i].value);
3429           int j;
3430
3431           for (j = 0; j < max_num_entities; j++)
3432             string[j] = (j < num_entities ? buffer[j] : '\0');
3433         }
3434         else if (data_type == TYPE_ELEMENT_LIST)
3435         {
3436           int *element_array = (int *)(conf[i].value);
3437           int j;
3438
3439           for (j = 0; j < num_entities; j++)
3440             element_array[j] =
3441               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3442         }
3443         else if (data_type == TYPE_CONTENT_LIST)
3444         {
3445           struct Content *content= (struct Content *)(conf[i].value);
3446           int c, x, y;
3447
3448           for (c = 0; c < num_entities; c++)
3449             for (y = 0; y < 3; y++)
3450               for (x = 0; x < 3; x++)
3451                 content[c].e[x][y] =
3452                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3453         }
3454         else
3455           element_found = FALSE;
3456
3457         break;
3458       }
3459     }
3460
3461     checked_free(buffer);
3462
3463     micro_chunk_size += 2 + num_bytes;
3464   }
3465   else          // constant size configuration data (1, 2 or 4 bytes)
3466   {
3467     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3468                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3469                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3470
3471     for (i = 0; conf[i].data_type != -1; i++)
3472     {
3473       if (conf[i].element == element &&
3474           conf[i].conf_type == conf_type)
3475       {
3476         int data_type = conf[i].data_type;
3477
3478         if (data_type == TYPE_ELEMENT)
3479           value = getMappedElement(value);
3480
3481         if (data_type == TYPE_BOOLEAN)
3482           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3483         else
3484           *(int *)    (conf[i].value) = value;
3485
3486         element_found = TRUE;
3487
3488         break;
3489       }
3490     }
3491
3492     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3493   }
3494
3495   if (!element_found)
3496   {
3497     char *error_conf_chunk_bytes =
3498       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3499        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3500        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3501     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3502     int error_element = real_element;
3503
3504     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3505          error_conf_chunk_bytes, error_conf_chunk_token,
3506          error_element, EL_NAME(error_element));
3507   }
3508
3509   return micro_chunk_size;
3510 }
3511
3512 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3513 {
3514   int real_chunk_size = 0;
3515
3516   li = *level;          // copy level data into temporary buffer
3517
3518   while (!checkEndOfFile(file))
3519   {
3520     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3521
3522     if (real_chunk_size >= chunk_size)
3523       break;
3524   }
3525
3526   *level = li;          // copy temporary buffer back to level data
3527
3528   return real_chunk_size;
3529 }
3530
3531 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3532 {
3533   int real_chunk_size = 0;
3534
3535   li = *level;          // copy level data into temporary buffer
3536
3537   while (!checkEndOfFile(file))
3538   {
3539     int element = getMappedElement(getFile16BitBE(file));
3540
3541     real_chunk_size += 2;
3542     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3543                                             element, element);
3544     if (real_chunk_size >= chunk_size)
3545       break;
3546   }
3547
3548   *level = li;          // copy temporary buffer back to level data
3549
3550   return real_chunk_size;
3551 }
3552
3553 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3554 {
3555   int real_chunk_size = 0;
3556
3557   li = *level;          // copy level data into temporary buffer
3558
3559   while (!checkEndOfFile(file))
3560   {
3561     int element = getMappedElement(getFile16BitBE(file));
3562
3563     real_chunk_size += 2;
3564     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3565                                             element, element);
3566     if (real_chunk_size >= chunk_size)
3567       break;
3568   }
3569
3570   *level = li;          // copy temporary buffer back to level data
3571
3572   return real_chunk_size;
3573 }
3574
3575 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3576 {
3577   int element = getMappedElement(getFile16BitBE(file));
3578   int envelope_nr = element - EL_ENVELOPE_1;
3579   int real_chunk_size = 2;
3580
3581   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3582
3583   while (!checkEndOfFile(file))
3584   {
3585     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3586                                             -1, element);
3587
3588     if (real_chunk_size >= chunk_size)
3589       break;
3590   }
3591
3592   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3593
3594   return real_chunk_size;
3595 }
3596
3597 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3598 {
3599   int element = getMappedElement(getFile16BitBE(file));
3600   int real_chunk_size = 2;
3601   struct ElementInfo *ei = &element_info[element];
3602   int i;
3603
3604   xx_ei = *ei;          // copy element data into temporary buffer
3605
3606   xx_ei.num_change_pages = -1;
3607
3608   while (!checkEndOfFile(file))
3609   {
3610     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3611                                             -1, element);
3612     if (xx_ei.num_change_pages != -1)
3613       break;
3614
3615     if (real_chunk_size >= chunk_size)
3616       break;
3617   }
3618
3619   *ei = xx_ei;
3620
3621   if (ei->num_change_pages == -1)
3622   {
3623     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3624          EL_NAME(element));
3625
3626     ei->num_change_pages = 1;
3627
3628     setElementChangePages(ei, 1);
3629     setElementChangeInfoToDefaults(ei->change);
3630
3631     return real_chunk_size;
3632   }
3633
3634   // initialize number of change pages stored for this custom element
3635   setElementChangePages(ei, ei->num_change_pages);
3636   for (i = 0; i < ei->num_change_pages; i++)
3637     setElementChangeInfoToDefaults(&ei->change_page[i]);
3638
3639   // start with reading properties for the first change page
3640   xx_current_change_page = 0;
3641
3642   while (!checkEndOfFile(file))
3643   {
3644     // level file might contain invalid change page number
3645     if (xx_current_change_page >= ei->num_change_pages)
3646       break;
3647
3648     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3649
3650     xx_change = *change;        // copy change data into temporary buffer
3651
3652     resetEventBits();           // reset bits; change page might have changed
3653
3654     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3655                                             -1, element);
3656
3657     *change = xx_change;
3658
3659     setEventFlagsFromEventBits(change);
3660
3661     if (real_chunk_size >= chunk_size)
3662       break;
3663   }
3664
3665   level->file_has_custom_elements = TRUE;
3666
3667   return real_chunk_size;
3668 }
3669
3670 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3671 {
3672   int element = getMappedElement(getFile16BitBE(file));
3673   int real_chunk_size = 2;
3674   struct ElementInfo *ei = &element_info[element];
3675   struct ElementGroupInfo *group = ei->group;
3676
3677   if (group == NULL)
3678     return -1;
3679
3680   xx_ei = *ei;          // copy element data into temporary buffer
3681   xx_group = *group;    // copy group data into temporary buffer
3682
3683   while (!checkEndOfFile(file))
3684   {
3685     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3686                                             -1, element);
3687
3688     if (real_chunk_size >= chunk_size)
3689       break;
3690   }
3691
3692   *ei = xx_ei;
3693   *group = xx_group;
3694
3695   level->file_has_custom_elements = TRUE;
3696
3697   return real_chunk_size;
3698 }
3699
3700 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3701 {
3702   int element = getMappedElement(getFile16BitBE(file));
3703   int real_chunk_size = 2;
3704   struct ElementInfo *ei = &element_info[element];
3705
3706   xx_ei = *ei;          // copy element data into temporary buffer
3707
3708   while (!checkEndOfFile(file))
3709   {
3710     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3711                                             -1, element);
3712
3713     if (real_chunk_size >= chunk_size)
3714       break;
3715   }
3716
3717   *ei = xx_ei;
3718
3719   level->file_has_custom_elements = TRUE;
3720
3721   return real_chunk_size;
3722 }
3723
3724 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3725                                       struct LevelFileInfo *level_file_info,
3726                                       boolean level_info_only)
3727 {
3728   char *filename = level_file_info->filename;
3729   char cookie[MAX_LINE_LEN];
3730   char chunk_name[CHUNK_ID_LEN + 1];
3731   int chunk_size;
3732   File *file;
3733
3734   if (!(file = openFile(filename, MODE_READ)))
3735   {
3736     level->no_valid_file = TRUE;
3737     level->no_level_file = TRUE;
3738
3739     if (level_info_only)
3740       return;
3741
3742     Warn("cannot read level '%s' -- using empty level", filename);
3743
3744     if (!setup.editor.use_template_for_new_levels)
3745       return;
3746
3747     // if level file not found, try to initialize level data from template
3748     filename = getGlobalLevelTemplateFilename();
3749
3750     if (!(file = openFile(filename, MODE_READ)))
3751       return;
3752
3753     // default: for empty levels, use level template for custom elements
3754     level->use_custom_template = TRUE;
3755
3756     level->no_valid_file = FALSE;
3757   }
3758
3759   getFileChunkBE(file, chunk_name, NULL);
3760   if (strEqual(chunk_name, "RND1"))
3761   {
3762     getFile32BitBE(file);               // not used
3763
3764     getFileChunkBE(file, chunk_name, NULL);
3765     if (!strEqual(chunk_name, "CAVE"))
3766     {
3767       level->no_valid_file = TRUE;
3768
3769       Warn("unknown format of level file '%s'", filename);
3770
3771       closeFile(file);
3772
3773       return;
3774     }
3775   }
3776   else  // check for pre-2.0 file format with cookie string
3777   {
3778     strcpy(cookie, chunk_name);
3779     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3780       cookie[4] = '\0';
3781     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3782       cookie[strlen(cookie) - 1] = '\0';
3783
3784     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3785     {
3786       level->no_valid_file = TRUE;
3787
3788       Warn("unknown format of level file '%s'", filename);
3789
3790       closeFile(file);
3791
3792       return;
3793     }
3794
3795     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3796     {
3797       level->no_valid_file = TRUE;
3798
3799       Warn("unsupported version of level file '%s'", filename);
3800
3801       closeFile(file);
3802
3803       return;
3804     }
3805
3806     // pre-2.0 level files have no game version, so use file version here
3807     level->game_version = level->file_version;
3808   }
3809
3810   if (level->file_version < FILE_VERSION_1_2)
3811   {
3812     // level files from versions before 1.2.0 without chunk structure
3813     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
3814     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3815   }
3816   else
3817   {
3818     static struct
3819     {
3820       char *name;
3821       int size;
3822       int (*loader)(File *, int, struct LevelInfo *);
3823     }
3824     chunk_info[] =
3825     {
3826       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
3827       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
3828       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
3829       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
3830       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
3831       { "INFO", -1,                     LoadLevel_INFO },
3832       { "BODY", -1,                     LoadLevel_BODY },
3833       { "CONT", -1,                     LoadLevel_CONT },
3834       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
3835       { "CNT3", -1,                     LoadLevel_CNT3 },
3836       { "CUS1", -1,                     LoadLevel_CUS1 },
3837       { "CUS2", -1,                     LoadLevel_CUS2 },
3838       { "CUS3", -1,                     LoadLevel_CUS3 },
3839       { "CUS4", -1,                     LoadLevel_CUS4 },
3840       { "GRP1", -1,                     LoadLevel_GRP1 },
3841       { "CONF", -1,                     LoadLevel_CONF },
3842       { "ELEM", -1,                     LoadLevel_ELEM },
3843       { "NOTE", -1,                     LoadLevel_NOTE },
3844       { "CUSX", -1,                     LoadLevel_CUSX },
3845       { "GRPX", -1,                     LoadLevel_GRPX },
3846       { "EMPX", -1,                     LoadLevel_EMPX },
3847
3848       {  NULL,  0,                      NULL }
3849     };
3850
3851     while (getFileChunkBE(file, chunk_name, &chunk_size))
3852     {
3853       int i = 0;
3854
3855       while (chunk_info[i].name != NULL &&
3856              !strEqual(chunk_name, chunk_info[i].name))
3857         i++;
3858
3859       if (chunk_info[i].name == NULL)
3860       {
3861         Warn("unknown chunk '%s' in level file '%s'",
3862              chunk_name, filename);
3863
3864         ReadUnusedBytesFromFile(file, chunk_size);
3865       }
3866       else if (chunk_info[i].size != -1 &&
3867                chunk_info[i].size != chunk_size)
3868       {
3869         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3870              chunk_size, chunk_name, filename);
3871
3872         ReadUnusedBytesFromFile(file, chunk_size);
3873       }
3874       else
3875       {
3876         // call function to load this level chunk
3877         int chunk_size_expected =
3878           (chunk_info[i].loader)(file, chunk_size, level);
3879
3880         if (chunk_size_expected < 0)
3881         {
3882           Warn("error reading chunk '%s' in level file '%s'",
3883                chunk_name, filename);
3884
3885           break;
3886         }
3887
3888         // the size of some chunks cannot be checked before reading other
3889         // chunks first (like "HEAD" and "BODY") that contain some header
3890         // information, so check them here
3891         if (chunk_size_expected != chunk_size)
3892         {
3893           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3894                chunk_size, chunk_name, filename);
3895
3896           break;
3897         }
3898       }
3899     }
3900   }
3901
3902   closeFile(file);
3903 }
3904
3905
3906 // ----------------------------------------------------------------------------
3907 // functions for loading BD level
3908 // ----------------------------------------------------------------------------
3909
3910 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3911 {
3912   struct LevelInfo_BD *level_bd = level->native_bd_level;
3913   GdCave *cave = NULL;  // will be changed below
3914   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3915   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3916   int x, y;
3917
3918   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3919
3920   // cave and map newly allocated when set to defaults above
3921   cave = level_bd->cave;
3922
3923   // level type
3924   cave->intermission                    = level->bd_intermission;
3925
3926   // level settings
3927   cave->level_time[0]                   = level->time;
3928   cave->level_diamonds[0]               = level->gems_needed;
3929
3930   // game timing
3931   cave->scheduling                      = level->bd_scheduling_type;
3932   cave->pal_timing                      = level->bd_pal_timing;
3933   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
3934   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
3935   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
3936   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
3937
3938   // scores
3939   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
3940   cave->diamond_value                   = level->score[SC_EMERALD];
3941   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
3942
3943   // compatibility settings
3944   cave->lineshift                       = level->bd_line_shifting_borders;
3945   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
3946   cave->short_explosions                = level->bd_short_explosions;
3947   cave->gravity_affects_all             = level->bd_gravity_affects_all;
3948
3949   // player properties
3950   cave->diagonal_movements              = level->bd_diagonal_movements;
3951   cave->active_is_first_found           = level->bd_topmost_player_active;
3952   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
3953   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
3954   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
3955   cave->snap_element                    = map_element_RND_to_BD(level->bd_snap_element);
3956
3957   // element properties
3958   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
3959   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
3960   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
3961   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
3962   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
3963   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
3964   cave->level_magic_wall_time[0]        = level->time_magic_wall;
3965   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
3966   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
3967   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
3968   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
3969   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
3970   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
3971   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
3972   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
3973   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
3974   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
3975   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
3976   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
3977   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
3978
3979   cave->amoeba_too_big_effect       = map_element_RND_to_BD(level->bd_amoeba_content_too_big);
3980   cave->amoeba_enclosed_effect      = map_element_RND_to_BD(level->bd_amoeba_content_enclosed);
3981   cave->amoeba_2_too_big_effect     = map_element_RND_to_BD(level->bd_amoeba_2_content_too_big);
3982   cave->amoeba_2_enclosed_effect    = map_element_RND_to_BD(level->bd_amoeba_2_content_enclosed);
3983   cave->amoeba_2_explosion_effect   = map_element_RND_to_BD(level->bd_amoeba_2_content_exploding);
3984   cave->amoeba_2_looks_like         = map_element_RND_to_BD(level->bd_amoeba_2_content_looks_like);
3985
3986   cave->slime_predictable               = level->bd_slime_is_predictable;
3987   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
3988   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
3989   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
3990
3991   // level name
3992   strncpy(cave->name, level->name, sizeof(GdString));
3993   cave->name[sizeof(GdString) - 1] = '\0';
3994
3995   // playfield elements
3996   for (x = 0; x < cave->w; x++)
3997     for (y = 0; y < cave->h; y++)
3998       cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3999 }
4000
4001 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4002 {
4003   struct LevelInfo_BD *level_bd = level->native_bd_level;
4004   GdCave *cave = level_bd->cave;
4005   int bd_level_nr = level_bd->level_nr;
4006   int x, y;
4007
4008   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4009   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4010
4011   // level type
4012   level->bd_intermission                = cave->intermission;
4013
4014   // level settings
4015   level->time                           = cave->level_time[bd_level_nr];
4016   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4017
4018   // game timing
4019   level->bd_scheduling_type             = cave->scheduling;
4020   level->bd_pal_timing                  = cave->pal_timing;
4021   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4022   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4023   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4024   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4025
4026   // scores
4027   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4028   level->score[SC_EMERALD]              = cave->diamond_value;
4029   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4030
4031   // compatibility settings
4032   level->bd_line_shifting_borders       = cave->lineshift;
4033   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4034   level->bd_short_explosions            = cave->short_explosions;
4035   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4036
4037   // player properties
4038   level->bd_diagonal_movements          = cave->diagonal_movements;
4039   level->bd_topmost_player_active       = cave->active_is_first_found;
4040   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4041   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4042   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4043   level->bd_snap_element                = map_element_BD_to_RND(cave->snap_element);
4044
4045   // element properties
4046   level->bd_clock_extra_time            = cave->level_bonus_time[0];
4047   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4048   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4049   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4050   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4051   level->bd_voodoo_penalty_time         = cave->level_penalty_time[0];
4052   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4053   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4054   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4055   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4056   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4057   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4058   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[0];
4059   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[0];
4060   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4061   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4062   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[0];
4063   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[0];
4064   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4065   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4066
4067   level->bd_amoeba_content_too_big      = map_element_BD_to_RND(cave->amoeba_too_big_effect);
4068   level->bd_amoeba_content_enclosed     = map_element_BD_to_RND(cave->amoeba_enclosed_effect);
4069   level->bd_amoeba_2_content_too_big    = map_element_BD_to_RND(cave->amoeba_2_too_big_effect);
4070   level->bd_amoeba_2_content_enclosed   = map_element_BD_to_RND(cave->amoeba_2_enclosed_effect);
4071   level->bd_amoeba_2_content_exploding  = map_element_BD_to_RND(cave->amoeba_2_explosion_effect);
4072   level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND(cave->amoeba_2_looks_like);
4073
4074   level->bd_slime_is_predictable        = cave->slime_predictable;
4075   level->bd_slime_permeability_rate     = cave->level_slime_permeability[0] / 10000;
4076   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[0];
4077   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[0];
4078
4079   // level name
4080   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4081
4082   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4083   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4084
4085   // playfield elements
4086   for (x = 0; x < level->fieldx; x++)
4087     for (y = 0; y < level->fieldy; y++)
4088       level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
4089
4090   checked_free(cave_name);
4091 }
4092
4093 static void setTapeInfoToDefaults(void);
4094
4095 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4096 {
4097   struct LevelInfo_BD *level_bd = level->native_bd_level;
4098   GdCave *cave = level_bd->cave;
4099   GdReplay *replay = level_bd->replay;
4100   int i;
4101
4102   if (replay == NULL)
4103     return;
4104
4105   // always start with reliable default values
4106   setTapeInfoToDefaults();
4107
4108   tape.level_nr = level_nr;             // (currently not used)
4109   tape.random_seed = replay->seed;
4110
4111   TapeSetDateFromIsoDateString(replay->date);
4112
4113   tape.counter = 0;
4114   tape.pos[tape.counter].delay = 0;
4115
4116   tape.bd_replay = TRUE;
4117
4118   // all time calculations only used to display approximate tape time
4119   int cave_speed = cave->speed;
4120   int milliseconds_game = 0;
4121   int milliseconds_elapsed = 20;
4122
4123   for (i = 0; i < replay->movements->len; i++)
4124   {
4125     int replay_action = replay->movements->data[i];
4126     int tape_action = map_action_BD_to_RND(replay_action);
4127     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4128     boolean success = 0;
4129
4130     while (1)
4131     {
4132       success = TapeAddAction(action);
4133
4134       milliseconds_game += milliseconds_elapsed;
4135
4136       if (milliseconds_game >= cave_speed)
4137       {
4138         milliseconds_game -= cave_speed;
4139
4140         break;
4141       }
4142     }
4143
4144     tape.counter++;
4145     tape.pos[tape.counter].delay = 0;
4146     tape.pos[tape.counter].action[0] = 0;
4147
4148     if (!success)
4149     {
4150       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4151
4152       break;
4153     }
4154   }
4155
4156   TapeHaltRecording();
4157 }
4158
4159
4160 // ----------------------------------------------------------------------------
4161 // functions for loading EM level
4162 // ----------------------------------------------------------------------------
4163
4164 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4165 {
4166   static int ball_xy[8][2] =
4167   {
4168     { 0, 0 },
4169     { 1, 0 },
4170     { 2, 0 },
4171     { 0, 1 },
4172     { 2, 1 },
4173     { 0, 2 },
4174     { 1, 2 },
4175     { 2, 2 },
4176   };
4177   struct LevelInfo_EM *level_em = level->native_em_level;
4178   struct CAVE *cav = level_em->cav;
4179   int i, j, x, y;
4180
4181   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4182   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4183
4184   cav->time_seconds     = level->time;
4185   cav->gems_needed      = level->gems_needed;
4186
4187   cav->emerald_score    = level->score[SC_EMERALD];
4188   cav->diamond_score    = level->score[SC_DIAMOND];
4189   cav->alien_score      = level->score[SC_ROBOT];
4190   cav->tank_score       = level->score[SC_SPACESHIP];
4191   cav->bug_score        = level->score[SC_BUG];
4192   cav->eater_score      = level->score[SC_YAMYAM];
4193   cav->nut_score        = level->score[SC_NUT];
4194   cav->dynamite_score   = level->score[SC_DYNAMITE];
4195   cav->key_score        = level->score[SC_KEY];
4196   cav->exit_score       = level->score[SC_TIME_BONUS];
4197
4198   cav->num_eater_arrays = level->num_yamyam_contents;
4199
4200   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4201     for (y = 0; y < 3; y++)
4202       for (x = 0; x < 3; x++)
4203         cav->eater_array[i][y * 3 + x] =
4204           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4205
4206   cav->amoeba_time              = level->amoeba_speed;
4207   cav->wonderwall_time          = level->time_magic_wall;
4208   cav->wheel_time               = level->time_wheel;
4209
4210   cav->android_move_time        = level->android_move_time;
4211   cav->android_clone_time       = level->android_clone_time;
4212   cav->ball_random              = level->ball_random;
4213   cav->ball_active              = level->ball_active_initial;
4214   cav->ball_time                = level->ball_time;
4215   cav->num_ball_arrays          = level->num_ball_contents;
4216
4217   cav->lenses_score             = level->lenses_score;
4218   cav->magnify_score            = level->magnify_score;
4219   cav->slurp_score              = level->slurp_score;
4220
4221   cav->lenses_time              = level->lenses_time;
4222   cav->magnify_time             = level->magnify_time;
4223
4224   cav->wind_time = 9999;
4225   cav->wind_direction =
4226     map_direction_RND_to_EM(level->wind_direction_initial);
4227
4228   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4229     for (j = 0; j < 8; j++)
4230       cav->ball_array[i][j] =
4231         map_element_RND_to_EM_cave(level->ball_content[i].
4232                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4233
4234   map_android_clone_elements_RND_to_EM(level);
4235
4236   // first fill the complete playfield with the empty space element
4237   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4238     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4239       cav->cave[x][y] = Cblank;
4240
4241   // then copy the real level contents from level file into the playfield
4242   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4243   {
4244     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4245
4246     if (level->field[x][y] == EL_AMOEBA_DEAD)
4247       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4248
4249     cav->cave[x][y] = new_element;
4250   }
4251
4252   for (i = 0; i < MAX_PLAYERS; i++)
4253   {
4254     cav->player_x[i] = -1;
4255     cav->player_y[i] = -1;
4256   }
4257
4258   // initialize player positions and delete players from the playfield
4259   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4260   {
4261     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4262     {
4263       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4264
4265       cav->player_x[player_nr] = x;
4266       cav->player_y[player_nr] = y;
4267
4268       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4269     }
4270   }
4271 }
4272
4273 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4274 {
4275   static int ball_xy[8][2] =
4276   {
4277     { 0, 0 },
4278     { 1, 0 },
4279     { 2, 0 },
4280     { 0, 1 },
4281     { 2, 1 },
4282     { 0, 2 },
4283     { 1, 2 },
4284     { 2, 2 },
4285   };
4286   struct LevelInfo_EM *level_em = level->native_em_level;
4287   struct CAVE *cav = level_em->cav;
4288   int i, j, x, y;
4289
4290   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4291   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4292
4293   level->time        = cav->time_seconds;
4294   level->gems_needed = cav->gems_needed;
4295
4296   sprintf(level->name, "Level %d", level->file_info.nr);
4297
4298   level->score[SC_EMERALD]      = cav->emerald_score;
4299   level->score[SC_DIAMOND]      = cav->diamond_score;
4300   level->score[SC_ROBOT]        = cav->alien_score;
4301   level->score[SC_SPACESHIP]    = cav->tank_score;
4302   level->score[SC_BUG]          = cav->bug_score;
4303   level->score[SC_YAMYAM]       = cav->eater_score;
4304   level->score[SC_NUT]          = cav->nut_score;
4305   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4306   level->score[SC_KEY]          = cav->key_score;
4307   level->score[SC_TIME_BONUS]   = cav->exit_score;
4308
4309   level->num_yamyam_contents    = cav->num_eater_arrays;
4310
4311   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4312     for (y = 0; y < 3; y++)
4313       for (x = 0; x < 3; x++)
4314         level->yamyam_content[i].e[x][y] =
4315           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4316
4317   level->amoeba_speed           = cav->amoeba_time;
4318   level->time_magic_wall        = cav->wonderwall_time;
4319   level->time_wheel             = cav->wheel_time;
4320
4321   level->android_move_time      = cav->android_move_time;
4322   level->android_clone_time     = cav->android_clone_time;
4323   level->ball_random            = cav->ball_random;
4324   level->ball_active_initial    = cav->ball_active;
4325   level->ball_time              = cav->ball_time;
4326   level->num_ball_contents      = cav->num_ball_arrays;
4327
4328   level->lenses_score           = cav->lenses_score;
4329   level->magnify_score          = cav->magnify_score;
4330   level->slurp_score            = cav->slurp_score;
4331
4332   level->lenses_time            = cav->lenses_time;
4333   level->magnify_time           = cav->magnify_time;
4334
4335   level->wind_direction_initial =
4336     map_direction_EM_to_RND(cav->wind_direction);
4337
4338   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4339     for (j = 0; j < 8; j++)
4340       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4341         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4342
4343   map_android_clone_elements_EM_to_RND(level);
4344
4345   // convert the playfield (some elements need special treatment)
4346   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4347   {
4348     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4349
4350     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4351       new_element = EL_AMOEBA_DEAD;
4352
4353     level->field[x][y] = new_element;
4354   }
4355
4356   for (i = 0; i < MAX_PLAYERS; i++)
4357   {
4358     // in case of all players set to the same field, use the first player
4359     int nr = MAX_PLAYERS - i - 1;
4360     int jx = cav->player_x[nr];
4361     int jy = cav->player_y[nr];
4362
4363     if (jx != -1 && jy != -1)
4364       level->field[jx][jy] = EL_PLAYER_1 + nr;
4365   }
4366
4367   // time score is counted for each 10 seconds left in Emerald Mine levels
4368   level->time_score_base = 10;
4369 }
4370
4371
4372 // ----------------------------------------------------------------------------
4373 // functions for loading SP level
4374 // ----------------------------------------------------------------------------
4375
4376 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4377 {
4378   struct LevelInfo_SP *level_sp = level->native_sp_level;
4379   LevelInfoType *header = &level_sp->header;
4380   int i, x, y;
4381
4382   level_sp->width  = level->fieldx;
4383   level_sp->height = level->fieldy;
4384
4385   for (x = 0; x < level->fieldx; x++)
4386     for (y = 0; y < level->fieldy; y++)
4387       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4388
4389   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4390
4391   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4392     header->LevelTitle[i] = level->name[i];
4393   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4394
4395   header->InfotronsNeeded = level->gems_needed;
4396
4397   header->SpecialPortCount = 0;
4398
4399   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4400   {
4401     boolean gravity_port_found = FALSE;
4402     boolean gravity_port_valid = FALSE;
4403     int gravity_port_flag;
4404     int gravity_port_base_element;
4405     int element = level->field[x][y];
4406
4407     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4408         element <= EL_SP_GRAVITY_ON_PORT_UP)
4409     {
4410       gravity_port_found = TRUE;
4411       gravity_port_valid = TRUE;
4412       gravity_port_flag = 1;
4413       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4414     }
4415     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4416              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4417     {
4418       gravity_port_found = TRUE;
4419       gravity_port_valid = TRUE;
4420       gravity_port_flag = 0;
4421       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4422     }
4423     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4424              element <= EL_SP_GRAVITY_PORT_UP)
4425     {
4426       // change R'n'D style gravity inverting special port to normal port
4427       // (there are no gravity inverting ports in native Supaplex engine)
4428
4429       gravity_port_found = TRUE;
4430       gravity_port_valid = FALSE;
4431       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4432     }
4433
4434     if (gravity_port_found)
4435     {
4436       if (gravity_port_valid &&
4437           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4438       {
4439         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4440
4441         port->PortLocation = (y * level->fieldx + x) * 2;
4442         port->Gravity = gravity_port_flag;
4443
4444         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4445
4446         header->SpecialPortCount++;
4447       }
4448       else
4449       {
4450         // change special gravity port to normal port
4451
4452         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4453       }
4454
4455       level_sp->playfield[x][y] = element - EL_SP_START;
4456     }
4457   }
4458 }
4459
4460 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4461 {
4462   struct LevelInfo_SP *level_sp = level->native_sp_level;
4463   LevelInfoType *header = &level_sp->header;
4464   boolean num_invalid_elements = 0;
4465   int i, j, x, y;
4466
4467   level->fieldx = level_sp->width;
4468   level->fieldy = level_sp->height;
4469
4470   for (x = 0; x < level->fieldx; x++)
4471   {
4472     for (y = 0; y < level->fieldy; y++)
4473     {
4474       int element_old = level_sp->playfield[x][y];
4475       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4476
4477       if (element_new == EL_UNKNOWN)
4478       {
4479         num_invalid_elements++;
4480
4481         Debug("level:native:SP", "invalid element %d at position %d, %d",
4482               element_old, x, y);
4483       }
4484
4485       level->field[x][y] = element_new;
4486     }
4487   }
4488
4489   if (num_invalid_elements > 0)
4490     Warn("found %d invalid elements%s", num_invalid_elements,
4491          (!options.debug ? " (use '--debug' for more details)" : ""));
4492
4493   for (i = 0; i < MAX_PLAYERS; i++)
4494     level->initial_player_gravity[i] =
4495       (header->InitialGravity == 1 ? TRUE : FALSE);
4496
4497   // skip leading spaces
4498   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4499     if (header->LevelTitle[i] != ' ')
4500       break;
4501
4502   // copy level title
4503   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4504     level->name[j] = header->LevelTitle[i];
4505   level->name[j] = '\0';
4506
4507   // cut trailing spaces
4508   for (; j > 0; j--)
4509     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4510       level->name[j - 1] = '\0';
4511
4512   level->gems_needed = header->InfotronsNeeded;
4513
4514   for (i = 0; i < header->SpecialPortCount; i++)
4515   {
4516     SpecialPortType *port = &header->SpecialPort[i];
4517     int port_location = port->PortLocation;
4518     int gravity = port->Gravity;
4519     int port_x, port_y, port_element;
4520
4521     port_x = (port_location / 2) % level->fieldx;
4522     port_y = (port_location / 2) / level->fieldx;
4523
4524     if (port_x < 0 || port_x >= level->fieldx ||
4525         port_y < 0 || port_y >= level->fieldy)
4526     {
4527       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4528
4529       continue;
4530     }
4531
4532     port_element = level->field[port_x][port_y];
4533
4534     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4535         port_element > EL_SP_GRAVITY_PORT_UP)
4536     {
4537       Warn("no special port at position (%d, %d)", port_x, port_y);
4538
4539       continue;
4540     }
4541
4542     // change previous (wrong) gravity inverting special port to either
4543     // gravity enabling special port or gravity disabling special port
4544     level->field[port_x][port_y] +=
4545       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4546        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4547   }
4548
4549   // change special gravity ports without database entries to normal ports
4550   for (x = 0; x < level->fieldx; x++)
4551     for (y = 0; y < level->fieldy; y++)
4552       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4553           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4554         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4555
4556   level->time = 0;                      // no time limit
4557   level->amoeba_speed = 0;
4558   level->time_magic_wall = 0;
4559   level->time_wheel = 0;
4560   level->amoeba_content = EL_EMPTY;
4561
4562   // original Supaplex does not use score values -- rate by playing time
4563   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4564     level->score[i] = 0;
4565
4566   level->rate_time_over_score = TRUE;
4567
4568   // there are no yamyams in supaplex levels
4569   for (i = 0; i < level->num_yamyam_contents; i++)
4570     for (x = 0; x < 3; x++)
4571       for (y = 0; y < 3; y++)
4572         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4573 }
4574
4575 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4576 {
4577   struct LevelInfo_SP *level_sp = level->native_sp_level;
4578   struct DemoInfo_SP *demo = &level_sp->demo;
4579   int i, j;
4580
4581   // always start with reliable default values
4582   demo->is_available = FALSE;
4583   demo->length = 0;
4584
4585   if (TAPE_IS_EMPTY(tape))
4586     return;
4587
4588   demo->level_nr = tape.level_nr;       // (currently not used)
4589
4590   level_sp->header.DemoRandomSeed = tape.random_seed;
4591
4592   demo->length = 0;
4593
4594   for (i = 0; i < tape.length; i++)
4595   {
4596     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4597     int demo_repeat = tape.pos[i].delay;
4598     int demo_entries = (demo_repeat + 15) / 16;
4599
4600     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4601     {
4602       Warn("tape truncated: size exceeds maximum SP demo size %d",
4603            SP_MAX_TAPE_LEN);
4604
4605       break;
4606     }
4607
4608     for (j = 0; j < demo_repeat / 16; j++)
4609       demo->data[demo->length++] = 0xf0 | demo_action;
4610
4611     if (demo_repeat % 16)
4612       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4613   }
4614
4615   demo->is_available = TRUE;
4616 }
4617
4618 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4619 {
4620   struct LevelInfo_SP *level_sp = level->native_sp_level;
4621   struct DemoInfo_SP *demo = &level_sp->demo;
4622   char *filename = level->file_info.filename;
4623   int i;
4624
4625   // always start with reliable default values
4626   setTapeInfoToDefaults();
4627
4628   if (!demo->is_available)
4629     return;
4630
4631   tape.level_nr = demo->level_nr;       // (currently not used)
4632   tape.random_seed = level_sp->header.DemoRandomSeed;
4633
4634   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4635
4636   tape.counter = 0;
4637   tape.pos[tape.counter].delay = 0;
4638
4639   for (i = 0; i < demo->length; i++)
4640   {
4641     int demo_action = demo->data[i] & 0x0f;
4642     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4643     int tape_action = map_key_SP_to_RND(demo_action);
4644     int tape_repeat = demo_repeat + 1;
4645     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4646     boolean success = 0;
4647     int j;
4648
4649     for (j = 0; j < tape_repeat; j++)
4650       success = TapeAddAction(action);
4651
4652     if (!success)
4653     {
4654       Warn("SP demo truncated: size exceeds maximum tape size %d",
4655            MAX_TAPE_LEN);
4656
4657       break;
4658     }
4659   }
4660
4661   TapeHaltRecording();
4662 }
4663
4664
4665 // ----------------------------------------------------------------------------
4666 // functions for loading MM level
4667 // ----------------------------------------------------------------------------
4668
4669 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4670 {
4671   struct LevelInfo_MM *level_mm = level->native_mm_level;
4672   int i, x, y;
4673
4674   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4675   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4676
4677   level_mm->time = level->time;
4678   level_mm->kettles_needed = level->gems_needed;
4679   level_mm->auto_count_kettles = level->auto_count_gems;
4680
4681   level_mm->mm_laser_red   = level->mm_laser_red;
4682   level_mm->mm_laser_green = level->mm_laser_green;
4683   level_mm->mm_laser_blue  = level->mm_laser_blue;
4684
4685   level_mm->df_laser_red   = level->df_laser_red;
4686   level_mm->df_laser_green = level->df_laser_green;
4687   level_mm->df_laser_blue  = level->df_laser_blue;
4688
4689   strcpy(level_mm->name, level->name);
4690   strcpy(level_mm->author, level->author);
4691
4692   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
4693   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
4694   level_mm->score[SC_KEY]        = level->score[SC_KEY];
4695   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4696   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4697
4698   level_mm->amoeba_speed = level->amoeba_speed;
4699   level_mm->time_fuse    = level->mm_time_fuse;
4700   level_mm->time_bomb    = level->mm_time_bomb;
4701   level_mm->time_ball    = level->mm_time_ball;
4702   level_mm->time_block   = level->mm_time_block;
4703
4704   level_mm->num_ball_contents = level->num_mm_ball_contents;
4705   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4706   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4707   level_mm->explode_ball = level->explode_mm_ball;
4708
4709   for (i = 0; i < level->num_mm_ball_contents; i++)
4710     level_mm->ball_content[i] =
4711       map_element_RND_to_MM(level->mm_ball_content[i]);
4712
4713   for (x = 0; x < level->fieldx; x++)
4714     for (y = 0; y < level->fieldy; y++)
4715       Ur[x][y] =
4716         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4717 }
4718
4719 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4720 {
4721   struct LevelInfo_MM *level_mm = level->native_mm_level;
4722   int i, x, y;
4723
4724   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4725   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4726
4727   level->time = level_mm->time;
4728   level->gems_needed = level_mm->kettles_needed;
4729   level->auto_count_gems = level_mm->auto_count_kettles;
4730
4731   level->mm_laser_red   = level_mm->mm_laser_red;
4732   level->mm_laser_green = level_mm->mm_laser_green;
4733   level->mm_laser_blue  = level_mm->mm_laser_blue;
4734
4735   level->df_laser_red   = level_mm->df_laser_red;
4736   level->df_laser_green = level_mm->df_laser_green;
4737   level->df_laser_blue  = level_mm->df_laser_blue;
4738
4739   strcpy(level->name, level_mm->name);
4740
4741   // only overwrite author from 'levelinfo.conf' if author defined in level
4742   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4743     strcpy(level->author, level_mm->author);
4744
4745   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
4746   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
4747   level->score[SC_KEY]        = level_mm->score[SC_KEY];
4748   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4749   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4750
4751   level->amoeba_speed  = level_mm->amoeba_speed;
4752   level->mm_time_fuse  = level_mm->time_fuse;
4753   level->mm_time_bomb  = level_mm->time_bomb;
4754   level->mm_time_ball  = level_mm->time_ball;
4755   level->mm_time_block = level_mm->time_block;
4756
4757   level->num_mm_ball_contents = level_mm->num_ball_contents;
4758   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4759   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4760   level->explode_mm_ball = level_mm->explode_ball;
4761
4762   for (i = 0; i < level->num_mm_ball_contents; i++)
4763     level->mm_ball_content[i] =
4764       map_element_MM_to_RND(level_mm->ball_content[i]);
4765
4766   for (x = 0; x < level->fieldx; x++)
4767     for (y = 0; y < level->fieldy; y++)
4768       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4769 }
4770
4771
4772 // ----------------------------------------------------------------------------
4773 // functions for loading DC level
4774 // ----------------------------------------------------------------------------
4775
4776 #define DC_LEVEL_HEADER_SIZE            344
4777
4778 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4779                                         boolean init)
4780 {
4781   static int last_data_encoded;
4782   static int offset1;
4783   static int offset2;
4784   int diff;
4785   int diff_hi, diff_lo;
4786   int data_hi, data_lo;
4787   unsigned short data_decoded;
4788
4789   if (init)
4790   {
4791     last_data_encoded = 0;
4792     offset1 = -1;
4793     offset2 = 0;
4794
4795     return 0;
4796   }
4797
4798   diff = data_encoded - last_data_encoded;
4799   diff_hi = diff & ~0xff;
4800   diff_lo = diff &  0xff;
4801
4802   offset2 += diff_lo;
4803
4804   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4805   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4806   data_hi = data_hi & 0xff00;
4807
4808   data_decoded = data_hi | data_lo;
4809
4810   last_data_encoded = data_encoded;
4811
4812   offset1 = (offset1 + 1) % 31;
4813   offset2 = offset2 & 0xff;
4814
4815   return data_decoded;
4816 }
4817
4818 static int getMappedElement_DC(int element)
4819 {
4820   switch (element)
4821   {
4822     case 0x0000:
4823       element = EL_ROCK;
4824       break;
4825
4826       // 0x0117 - 0x036e: (?)
4827       // EL_DIAMOND
4828
4829       // 0x042d - 0x0684: (?)
4830       // EL_EMERALD
4831
4832     case 0x06f1:
4833       element = EL_NUT;
4834       break;
4835
4836     case 0x074c:
4837       element = EL_BOMB;
4838       break;
4839
4840     case 0x07a4:
4841       element = EL_PEARL;
4842       break;
4843
4844     case 0x0823:
4845       element = EL_CRYSTAL;
4846       break;
4847
4848     case 0x0e77:        // quicksand (boulder)
4849       element = EL_QUICKSAND_FAST_FULL;
4850       break;
4851
4852     case 0x0e99:        // slow quicksand (boulder)
4853       element = EL_QUICKSAND_FULL;
4854       break;
4855
4856     case 0x0ed2:
4857       element = EL_EM_EXIT_OPEN;
4858       break;
4859
4860     case 0x0ee3:
4861       element = EL_EM_EXIT_CLOSED;
4862       break;
4863
4864     case 0x0eeb:
4865       element = EL_EM_STEEL_EXIT_OPEN;
4866       break;
4867
4868     case 0x0efc:
4869       element = EL_EM_STEEL_EXIT_CLOSED;
4870       break;
4871
4872     case 0x0f4f:        // dynamite (lit 1)
4873       element = EL_EM_DYNAMITE_ACTIVE;
4874       break;
4875
4876     case 0x0f57:        // dynamite (lit 2)
4877       element = EL_EM_DYNAMITE_ACTIVE;
4878       break;
4879
4880     case 0x0f5f:        // dynamite (lit 3)
4881       element = EL_EM_DYNAMITE_ACTIVE;
4882       break;
4883
4884     case 0x0f67:        // dynamite (lit 4)
4885       element = EL_EM_DYNAMITE_ACTIVE;
4886       break;
4887
4888     case 0x0f81:
4889     case 0x0f82:
4890     case 0x0f83:
4891     case 0x0f84:
4892       element = EL_AMOEBA_WET;
4893       break;
4894
4895     case 0x0f85:
4896       element = EL_AMOEBA_DROP;
4897       break;
4898
4899     case 0x0fb9:
4900       element = EL_DC_MAGIC_WALL;
4901       break;
4902
4903     case 0x0fd0:
4904       element = EL_SPACESHIP_UP;
4905       break;
4906
4907     case 0x0fd9:
4908       element = EL_SPACESHIP_DOWN;
4909       break;
4910
4911     case 0x0ff1:
4912       element = EL_SPACESHIP_LEFT;
4913       break;
4914
4915     case 0x0ff9:
4916       element = EL_SPACESHIP_RIGHT;
4917       break;
4918
4919     case 0x1057:
4920       element = EL_BUG_UP;
4921       break;
4922
4923     case 0x1060:
4924       element = EL_BUG_DOWN;
4925       break;
4926
4927     case 0x1078:
4928       element = EL_BUG_LEFT;
4929       break;
4930
4931     case 0x1080:
4932       element = EL_BUG_RIGHT;
4933       break;
4934
4935     case 0x10de:
4936       element = EL_MOLE_UP;
4937       break;
4938
4939     case 0x10e7:
4940       element = EL_MOLE_DOWN;
4941       break;
4942
4943     case 0x10ff:
4944       element = EL_MOLE_LEFT;
4945       break;
4946
4947     case 0x1107:
4948       element = EL_MOLE_RIGHT;
4949       break;
4950
4951     case 0x11c0:
4952       element = EL_ROBOT;
4953       break;
4954
4955     case 0x13f5:
4956       element = EL_YAMYAM_UP;
4957       break;
4958
4959     case 0x1425:
4960       element = EL_SWITCHGATE_OPEN;
4961       break;
4962
4963     case 0x1426:
4964       element = EL_SWITCHGATE_CLOSED;
4965       break;
4966
4967     case 0x1437:
4968       element = EL_DC_SWITCHGATE_SWITCH_UP;
4969       break;
4970
4971     case 0x143a:
4972       element = EL_TIMEGATE_CLOSED;
4973       break;
4974
4975     case 0x144c:        // conveyor belt switch (green)
4976       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4977       break;
4978
4979     case 0x144f:        // conveyor belt switch (red)
4980       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4981       break;
4982
4983     case 0x1452:        // conveyor belt switch (blue)
4984       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4985       break;
4986
4987     case 0x145b:
4988       element = EL_CONVEYOR_BELT_3_MIDDLE;
4989       break;
4990
4991     case 0x1463:
4992       element = EL_CONVEYOR_BELT_3_LEFT;
4993       break;
4994
4995     case 0x146b:
4996       element = EL_CONVEYOR_BELT_3_RIGHT;
4997       break;
4998
4999     case 0x1473:
5000       element = EL_CONVEYOR_BELT_1_MIDDLE;
5001       break;
5002
5003     case 0x147b:
5004       element = EL_CONVEYOR_BELT_1_LEFT;
5005       break;
5006
5007     case 0x1483:
5008       element = EL_CONVEYOR_BELT_1_RIGHT;
5009       break;
5010
5011     case 0x148b:
5012       element = EL_CONVEYOR_BELT_4_MIDDLE;
5013       break;
5014
5015     case 0x1493:
5016       element = EL_CONVEYOR_BELT_4_LEFT;
5017       break;
5018
5019     case 0x149b:
5020       element = EL_CONVEYOR_BELT_4_RIGHT;
5021       break;
5022
5023     case 0x14ac:
5024       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5025       break;
5026
5027     case 0x14bd:
5028       element = EL_EXPANDABLE_WALL_VERTICAL;
5029       break;
5030
5031     case 0x14c6:
5032       element = EL_EXPANDABLE_WALL_ANY;
5033       break;
5034
5035     case 0x14ce:        // growing steel wall (left/right)
5036       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5037       break;
5038
5039     case 0x14df:        // growing steel wall (up/down)
5040       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5041       break;
5042
5043     case 0x14e8:        // growing steel wall (up/down/left/right)
5044       element = EL_EXPANDABLE_STEELWALL_ANY;
5045       break;
5046
5047     case 0x14e9:
5048       element = EL_SHIELD_DEADLY;
5049       break;
5050
5051     case 0x1501:
5052       element = EL_EXTRA_TIME;
5053       break;
5054
5055     case 0x154f:
5056       element = EL_ACID;
5057       break;
5058
5059     case 0x1577:
5060       element = EL_EMPTY_SPACE;
5061       break;
5062
5063     case 0x1578:        // quicksand (empty)
5064       element = EL_QUICKSAND_FAST_EMPTY;
5065       break;
5066
5067     case 0x1579:        // slow quicksand (empty)
5068       element = EL_QUICKSAND_EMPTY;
5069       break;
5070
5071       // 0x157c - 0x158b:
5072       // EL_SAND
5073
5074       // 0x1590 - 0x159f:
5075       // EL_DC_LANDMINE
5076
5077     case 0x15a0:
5078       element = EL_EM_DYNAMITE;
5079       break;
5080
5081     case 0x15a1:        // key (red)
5082       element = EL_EM_KEY_1;
5083       break;
5084
5085     case 0x15a2:        // key (yellow)
5086       element = EL_EM_KEY_2;
5087       break;
5088
5089     case 0x15a3:        // key (blue)
5090       element = EL_EM_KEY_4;
5091       break;
5092
5093     case 0x15a4:        // key (green)
5094       element = EL_EM_KEY_3;
5095       break;
5096
5097     case 0x15a5:        // key (white)
5098       element = EL_DC_KEY_WHITE;
5099       break;
5100
5101     case 0x15a6:
5102       element = EL_WALL_SLIPPERY;
5103       break;
5104
5105     case 0x15a7:
5106       element = EL_WALL;
5107       break;
5108
5109     case 0x15a8:        // wall (not round)
5110       element = EL_WALL;
5111       break;
5112
5113     case 0x15a9:        // (blue)
5114       element = EL_CHAR_A;
5115       break;
5116
5117     case 0x15aa:        // (blue)
5118       element = EL_CHAR_B;
5119       break;
5120
5121     case 0x15ab:        // (blue)
5122       element = EL_CHAR_C;
5123       break;
5124
5125     case 0x15ac:        // (blue)
5126       element = EL_CHAR_D;
5127       break;
5128
5129     case 0x15ad:        // (blue)
5130       element = EL_CHAR_E;
5131       break;
5132
5133     case 0x15ae:        // (blue)
5134       element = EL_CHAR_F;
5135       break;
5136
5137     case 0x15af:        // (blue)
5138       element = EL_CHAR_G;
5139       break;
5140
5141     case 0x15b0:        // (blue)
5142       element = EL_CHAR_H;
5143       break;
5144
5145     case 0x15b1:        // (blue)
5146       element = EL_CHAR_I;
5147       break;
5148
5149     case 0x15b2:        // (blue)
5150       element = EL_CHAR_J;
5151       break;
5152
5153     case 0x15b3:        // (blue)
5154       element = EL_CHAR_K;
5155       break;
5156
5157     case 0x15b4:        // (blue)
5158       element = EL_CHAR_L;
5159       break;
5160
5161     case 0x15b5:        // (blue)
5162       element = EL_CHAR_M;
5163       break;
5164
5165     case 0x15b6:        // (blue)
5166       element = EL_CHAR_N;
5167       break;
5168
5169     case 0x15b7:        // (blue)
5170       element = EL_CHAR_O;
5171       break;
5172
5173     case 0x15b8:        // (blue)
5174       element = EL_CHAR_P;
5175       break;
5176
5177     case 0x15b9:        // (blue)
5178       element = EL_CHAR_Q;
5179       break;
5180
5181     case 0x15ba:        // (blue)
5182       element = EL_CHAR_R;
5183       break;
5184
5185     case 0x15bb:        // (blue)
5186       element = EL_CHAR_S;
5187       break;
5188
5189     case 0x15bc:        // (blue)
5190       element = EL_CHAR_T;
5191       break;
5192
5193     case 0x15bd:        // (blue)
5194       element = EL_CHAR_U;
5195       break;
5196
5197     case 0x15be:        // (blue)
5198       element = EL_CHAR_V;
5199       break;
5200
5201     case 0x15bf:        // (blue)
5202       element = EL_CHAR_W;
5203       break;
5204
5205     case 0x15c0:        // (blue)
5206       element = EL_CHAR_X;
5207       break;
5208
5209     case 0x15c1:        // (blue)
5210       element = EL_CHAR_Y;
5211       break;
5212
5213     case 0x15c2:        // (blue)
5214       element = EL_CHAR_Z;
5215       break;
5216
5217     case 0x15c3:        // (blue)
5218       element = EL_CHAR_AUMLAUT;
5219       break;
5220
5221     case 0x15c4:        // (blue)
5222       element = EL_CHAR_OUMLAUT;
5223       break;
5224
5225     case 0x15c5:        // (blue)
5226       element = EL_CHAR_UUMLAUT;
5227       break;
5228
5229     case 0x15c6:        // (blue)
5230       element = EL_CHAR_0;
5231       break;
5232
5233     case 0x15c7:        // (blue)
5234       element = EL_CHAR_1;
5235       break;
5236
5237     case 0x15c8:        // (blue)
5238       element = EL_CHAR_2;
5239       break;
5240
5241     case 0x15c9:        // (blue)
5242       element = EL_CHAR_3;
5243       break;
5244
5245     case 0x15ca:        // (blue)
5246       element = EL_CHAR_4;
5247       break;
5248
5249     case 0x15cb:        // (blue)
5250       element = EL_CHAR_5;
5251       break;
5252
5253     case 0x15cc:        // (blue)
5254       element = EL_CHAR_6;
5255       break;
5256
5257     case 0x15cd:        // (blue)
5258       element = EL_CHAR_7;
5259       break;
5260
5261     case 0x15ce:        // (blue)
5262       element = EL_CHAR_8;
5263       break;
5264
5265     case 0x15cf:        // (blue)
5266       element = EL_CHAR_9;
5267       break;
5268
5269     case 0x15d0:        // (blue)
5270       element = EL_CHAR_PERIOD;
5271       break;
5272
5273     case 0x15d1:        // (blue)
5274       element = EL_CHAR_EXCLAM;
5275       break;
5276
5277     case 0x15d2:        // (blue)
5278       element = EL_CHAR_COLON;
5279       break;
5280
5281     case 0x15d3:        // (blue)
5282       element = EL_CHAR_LESS;
5283       break;
5284
5285     case 0x15d4:        // (blue)
5286       element = EL_CHAR_GREATER;
5287       break;
5288
5289     case 0x15d5:        // (blue)
5290       element = EL_CHAR_QUESTION;
5291       break;
5292
5293     case 0x15d6:        // (blue)
5294       element = EL_CHAR_COPYRIGHT;
5295       break;
5296
5297     case 0x15d7:        // (blue)
5298       element = EL_CHAR_UP;
5299       break;
5300
5301     case 0x15d8:        // (blue)
5302       element = EL_CHAR_DOWN;
5303       break;
5304
5305     case 0x15d9:        // (blue)
5306       element = EL_CHAR_BUTTON;
5307       break;
5308
5309     case 0x15da:        // (blue)
5310       element = EL_CHAR_PLUS;
5311       break;
5312
5313     case 0x15db:        // (blue)
5314       element = EL_CHAR_MINUS;
5315       break;
5316
5317     case 0x15dc:        // (blue)
5318       element = EL_CHAR_APOSTROPHE;
5319       break;
5320
5321     case 0x15dd:        // (blue)
5322       element = EL_CHAR_PARENLEFT;
5323       break;
5324
5325     case 0x15de:        // (blue)
5326       element = EL_CHAR_PARENRIGHT;
5327       break;
5328
5329     case 0x15df:        // (green)
5330       element = EL_CHAR_A;
5331       break;
5332
5333     case 0x15e0:        // (green)
5334       element = EL_CHAR_B;
5335       break;
5336
5337     case 0x15e1:        // (green)
5338       element = EL_CHAR_C;
5339       break;
5340
5341     case 0x15e2:        // (green)
5342       element = EL_CHAR_D;
5343       break;
5344
5345     case 0x15e3:        // (green)
5346       element = EL_CHAR_E;
5347       break;
5348
5349     case 0x15e4:        // (green)
5350       element = EL_CHAR_F;
5351       break;
5352
5353     case 0x15e5:        // (green)
5354       element = EL_CHAR_G;
5355       break;
5356
5357     case 0x15e6:        // (green)
5358       element = EL_CHAR_H;
5359       break;
5360
5361     case 0x15e7:        // (green)
5362       element = EL_CHAR_I;
5363       break;
5364
5365     case 0x15e8:        // (green)
5366       element = EL_CHAR_J;
5367       break;
5368
5369     case 0x15e9:        // (green)
5370       element = EL_CHAR_K;
5371       break;
5372
5373     case 0x15ea:        // (green)
5374       element = EL_CHAR_L;
5375       break;
5376
5377     case 0x15eb:        // (green)
5378       element = EL_CHAR_M;
5379       break;
5380
5381     case 0x15ec:        // (green)
5382       element = EL_CHAR_N;
5383       break;
5384
5385     case 0x15ed:        // (green)
5386       element = EL_CHAR_O;
5387       break;
5388
5389     case 0x15ee:        // (green)
5390       element = EL_CHAR_P;
5391       break;
5392
5393     case 0x15ef:        // (green)
5394       element = EL_CHAR_Q;
5395       break;
5396
5397     case 0x15f0:        // (green)
5398       element = EL_CHAR_R;
5399       break;
5400
5401     case 0x15f1:        // (green)
5402       element = EL_CHAR_S;
5403       break;
5404
5405     case 0x15f2:        // (green)
5406       element = EL_CHAR_T;
5407       break;
5408
5409     case 0x15f3:        // (green)
5410       element = EL_CHAR_U;
5411       break;
5412
5413     case 0x15f4:        // (green)
5414       element = EL_CHAR_V;
5415       break;
5416
5417     case 0x15f5:        // (green)
5418       element = EL_CHAR_W;
5419       break;
5420
5421     case 0x15f6:        // (green)
5422       element = EL_CHAR_X;
5423       break;
5424
5425     case 0x15f7:        // (green)
5426       element = EL_CHAR_Y;
5427       break;
5428
5429     case 0x15f8:        // (green)
5430       element = EL_CHAR_Z;
5431       break;
5432
5433     case 0x15f9:        // (green)
5434       element = EL_CHAR_AUMLAUT;
5435       break;
5436
5437     case 0x15fa:        // (green)
5438       element = EL_CHAR_OUMLAUT;
5439       break;
5440
5441     case 0x15fb:        // (green)
5442       element = EL_CHAR_UUMLAUT;
5443       break;
5444
5445     case 0x15fc:        // (green)
5446       element = EL_CHAR_0;
5447       break;
5448
5449     case 0x15fd:        // (green)
5450       element = EL_CHAR_1;
5451       break;
5452
5453     case 0x15fe:        // (green)
5454       element = EL_CHAR_2;
5455       break;
5456
5457     case 0x15ff:        // (green)
5458       element = EL_CHAR_3;
5459       break;
5460
5461     case 0x1600:        // (green)
5462       element = EL_CHAR_4;
5463       break;
5464
5465     case 0x1601:        // (green)
5466       element = EL_CHAR_5;
5467       break;
5468
5469     case 0x1602:        // (green)
5470       element = EL_CHAR_6;
5471       break;
5472
5473     case 0x1603:        // (green)
5474       element = EL_CHAR_7;
5475       break;
5476
5477     case 0x1604:        // (green)
5478       element = EL_CHAR_8;
5479       break;
5480
5481     case 0x1605:        // (green)
5482       element = EL_CHAR_9;
5483       break;
5484
5485     case 0x1606:        // (green)
5486       element = EL_CHAR_PERIOD;
5487       break;
5488
5489     case 0x1607:        // (green)
5490       element = EL_CHAR_EXCLAM;
5491       break;
5492
5493     case 0x1608:        // (green)
5494       element = EL_CHAR_COLON;
5495       break;
5496
5497     case 0x1609:        // (green)
5498       element = EL_CHAR_LESS;
5499       break;
5500
5501     case 0x160a:        // (green)
5502       element = EL_CHAR_GREATER;
5503       break;
5504
5505     case 0x160b:        // (green)
5506       element = EL_CHAR_QUESTION;
5507       break;
5508
5509     case 0x160c:        // (green)
5510       element = EL_CHAR_COPYRIGHT;
5511       break;
5512
5513     case 0x160d:        // (green)
5514       element = EL_CHAR_UP;
5515       break;
5516
5517     case 0x160e:        // (green)
5518       element = EL_CHAR_DOWN;
5519       break;
5520
5521     case 0x160f:        // (green)
5522       element = EL_CHAR_BUTTON;
5523       break;
5524
5525     case 0x1610:        // (green)
5526       element = EL_CHAR_PLUS;
5527       break;
5528
5529     case 0x1611:        // (green)
5530       element = EL_CHAR_MINUS;
5531       break;
5532
5533     case 0x1612:        // (green)
5534       element = EL_CHAR_APOSTROPHE;
5535       break;
5536
5537     case 0x1613:        // (green)
5538       element = EL_CHAR_PARENLEFT;
5539       break;
5540
5541     case 0x1614:        // (green)
5542       element = EL_CHAR_PARENRIGHT;
5543       break;
5544
5545     case 0x1615:        // (blue steel)
5546       element = EL_STEEL_CHAR_A;
5547       break;
5548
5549     case 0x1616:        // (blue steel)
5550       element = EL_STEEL_CHAR_B;
5551       break;
5552
5553     case 0x1617:        // (blue steel)
5554       element = EL_STEEL_CHAR_C;
5555       break;
5556
5557     case 0x1618:        // (blue steel)
5558       element = EL_STEEL_CHAR_D;
5559       break;
5560
5561     case 0x1619:        // (blue steel)
5562       element = EL_STEEL_CHAR_E;
5563       break;
5564
5565     case 0x161a:        // (blue steel)
5566       element = EL_STEEL_CHAR_F;
5567       break;
5568
5569     case 0x161b:        // (blue steel)
5570       element = EL_STEEL_CHAR_G;
5571       break;
5572
5573     case 0x161c:        // (blue steel)
5574       element = EL_STEEL_CHAR_H;
5575       break;
5576
5577     case 0x161d:        // (blue steel)
5578       element = EL_STEEL_CHAR_I;
5579       break;
5580
5581     case 0x161e:        // (blue steel)
5582       element = EL_STEEL_CHAR_J;
5583       break;
5584
5585     case 0x161f:        // (blue steel)
5586       element = EL_STEEL_CHAR_K;
5587       break;
5588
5589     case 0x1620:        // (blue steel)
5590       element = EL_STEEL_CHAR_L;
5591       break;
5592
5593     case 0x1621:        // (blue steel)
5594       element = EL_STEEL_CHAR_M;
5595       break;
5596
5597     case 0x1622:        // (blue steel)
5598       element = EL_STEEL_CHAR_N;
5599       break;
5600
5601     case 0x1623:        // (blue steel)
5602       element = EL_STEEL_CHAR_O;
5603       break;
5604
5605     case 0x1624:        // (blue steel)
5606       element = EL_STEEL_CHAR_P;
5607       break;
5608
5609     case 0x1625:        // (blue steel)
5610       element = EL_STEEL_CHAR_Q;
5611       break;
5612
5613     case 0x1626:        // (blue steel)
5614       element = EL_STEEL_CHAR_R;
5615       break;
5616
5617     case 0x1627:        // (blue steel)
5618       element = EL_STEEL_CHAR_S;
5619       break;
5620
5621     case 0x1628:        // (blue steel)
5622       element = EL_STEEL_CHAR_T;
5623       break;
5624
5625     case 0x1629:        // (blue steel)
5626       element = EL_STEEL_CHAR_U;
5627       break;
5628
5629     case 0x162a:        // (blue steel)
5630       element = EL_STEEL_CHAR_V;
5631       break;
5632
5633     case 0x162b:        // (blue steel)
5634       element = EL_STEEL_CHAR_W;
5635       break;
5636
5637     case 0x162c:        // (blue steel)
5638       element = EL_STEEL_CHAR_X;
5639       break;
5640
5641     case 0x162d:        // (blue steel)
5642       element = EL_STEEL_CHAR_Y;
5643       break;
5644
5645     case 0x162e:        // (blue steel)
5646       element = EL_STEEL_CHAR_Z;
5647       break;
5648
5649     case 0x162f:        // (blue steel)
5650       element = EL_STEEL_CHAR_AUMLAUT;
5651       break;
5652
5653     case 0x1630:        // (blue steel)
5654       element = EL_STEEL_CHAR_OUMLAUT;
5655       break;
5656
5657     case 0x1631:        // (blue steel)
5658       element = EL_STEEL_CHAR_UUMLAUT;
5659       break;
5660
5661     case 0x1632:        // (blue steel)
5662       element = EL_STEEL_CHAR_0;
5663       break;
5664
5665     case 0x1633:        // (blue steel)
5666       element = EL_STEEL_CHAR_1;
5667       break;
5668
5669     case 0x1634:        // (blue steel)
5670       element = EL_STEEL_CHAR_2;
5671       break;
5672
5673     case 0x1635:        // (blue steel)
5674       element = EL_STEEL_CHAR_3;
5675       break;
5676
5677     case 0x1636:        // (blue steel)
5678       element = EL_STEEL_CHAR_4;
5679       break;
5680
5681     case 0x1637:        // (blue steel)
5682       element = EL_STEEL_CHAR_5;
5683       break;
5684
5685     case 0x1638:        // (blue steel)
5686       element = EL_STEEL_CHAR_6;
5687       break;
5688
5689     case 0x1639:        // (blue steel)
5690       element = EL_STEEL_CHAR_7;
5691       break;
5692
5693     case 0x163a:        // (blue steel)
5694       element = EL_STEEL_CHAR_8;
5695       break;
5696
5697     case 0x163b:        // (blue steel)
5698       element = EL_STEEL_CHAR_9;
5699       break;
5700
5701     case 0x163c:        // (blue steel)
5702       element = EL_STEEL_CHAR_PERIOD;
5703       break;
5704
5705     case 0x163d:        // (blue steel)
5706       element = EL_STEEL_CHAR_EXCLAM;
5707       break;
5708
5709     case 0x163e:        // (blue steel)
5710       element = EL_STEEL_CHAR_COLON;
5711       break;
5712
5713     case 0x163f:        // (blue steel)
5714       element = EL_STEEL_CHAR_LESS;
5715       break;
5716
5717     case 0x1640:        // (blue steel)
5718       element = EL_STEEL_CHAR_GREATER;
5719       break;
5720
5721     case 0x1641:        // (blue steel)
5722       element = EL_STEEL_CHAR_QUESTION;
5723       break;
5724
5725     case 0x1642:        // (blue steel)
5726       element = EL_STEEL_CHAR_COPYRIGHT;
5727       break;
5728
5729     case 0x1643:        // (blue steel)
5730       element = EL_STEEL_CHAR_UP;
5731       break;
5732
5733     case 0x1644:        // (blue steel)
5734       element = EL_STEEL_CHAR_DOWN;
5735       break;
5736
5737     case 0x1645:        // (blue steel)
5738       element = EL_STEEL_CHAR_BUTTON;
5739       break;
5740
5741     case 0x1646:        // (blue steel)
5742       element = EL_STEEL_CHAR_PLUS;
5743       break;
5744
5745     case 0x1647:        // (blue steel)
5746       element = EL_STEEL_CHAR_MINUS;
5747       break;
5748
5749     case 0x1648:        // (blue steel)
5750       element = EL_STEEL_CHAR_APOSTROPHE;
5751       break;
5752
5753     case 0x1649:        // (blue steel)
5754       element = EL_STEEL_CHAR_PARENLEFT;
5755       break;
5756
5757     case 0x164a:        // (blue steel)
5758       element = EL_STEEL_CHAR_PARENRIGHT;
5759       break;
5760
5761     case 0x164b:        // (green steel)
5762       element = EL_STEEL_CHAR_A;
5763       break;
5764
5765     case 0x164c:        // (green steel)
5766       element = EL_STEEL_CHAR_B;
5767       break;
5768
5769     case 0x164d:        // (green steel)
5770       element = EL_STEEL_CHAR_C;
5771       break;
5772
5773     case 0x164e:        // (green steel)
5774       element = EL_STEEL_CHAR_D;
5775       break;
5776
5777     case 0x164f:        // (green steel)
5778       element = EL_STEEL_CHAR_E;
5779       break;
5780
5781     case 0x1650:        // (green steel)
5782       element = EL_STEEL_CHAR_F;
5783       break;
5784
5785     case 0x1651:        // (green steel)
5786       element = EL_STEEL_CHAR_G;
5787       break;
5788
5789     case 0x1652:        // (green steel)
5790       element = EL_STEEL_CHAR_H;
5791       break;
5792
5793     case 0x1653:        // (green steel)
5794       element = EL_STEEL_CHAR_I;
5795       break;
5796
5797     case 0x1654:        // (green steel)
5798       element = EL_STEEL_CHAR_J;
5799       break;
5800
5801     case 0x1655:        // (green steel)
5802       element = EL_STEEL_CHAR_K;
5803       break;
5804
5805     case 0x1656:        // (green steel)
5806       element = EL_STEEL_CHAR_L;
5807       break;
5808
5809     case 0x1657:        // (green steel)
5810       element = EL_STEEL_CHAR_M;
5811       break;
5812
5813     case 0x1658:        // (green steel)
5814       element = EL_STEEL_CHAR_N;
5815       break;
5816
5817     case 0x1659:        // (green steel)
5818       element = EL_STEEL_CHAR_O;
5819       break;
5820
5821     case 0x165a:        // (green steel)
5822       element = EL_STEEL_CHAR_P;
5823       break;
5824
5825     case 0x165b:        // (green steel)
5826       element = EL_STEEL_CHAR_Q;
5827       break;
5828
5829     case 0x165c:        // (green steel)
5830       element = EL_STEEL_CHAR_R;
5831       break;
5832
5833     case 0x165d:        // (green steel)
5834       element = EL_STEEL_CHAR_S;
5835       break;
5836
5837     case 0x165e:        // (green steel)
5838       element = EL_STEEL_CHAR_T;
5839       break;
5840
5841     case 0x165f:        // (green steel)
5842       element = EL_STEEL_CHAR_U;
5843       break;
5844
5845     case 0x1660:        // (green steel)
5846       element = EL_STEEL_CHAR_V;
5847       break;
5848
5849     case 0x1661:        // (green steel)
5850       element = EL_STEEL_CHAR_W;
5851       break;
5852
5853     case 0x1662:        // (green steel)
5854       element = EL_STEEL_CHAR_X;
5855       break;
5856
5857     case 0x1663:        // (green steel)
5858       element = EL_STEEL_CHAR_Y;
5859       break;
5860
5861     case 0x1664:        // (green steel)
5862       element = EL_STEEL_CHAR_Z;
5863       break;
5864
5865     case 0x1665:        // (green steel)
5866       element = EL_STEEL_CHAR_AUMLAUT;
5867       break;
5868
5869     case 0x1666:        // (green steel)
5870       element = EL_STEEL_CHAR_OUMLAUT;
5871       break;
5872
5873     case 0x1667:        // (green steel)
5874       element = EL_STEEL_CHAR_UUMLAUT;
5875       break;
5876
5877     case 0x1668:        // (green steel)
5878       element = EL_STEEL_CHAR_0;
5879       break;
5880
5881     case 0x1669:        // (green steel)
5882       element = EL_STEEL_CHAR_1;
5883       break;
5884
5885     case 0x166a:        // (green steel)
5886       element = EL_STEEL_CHAR_2;
5887       break;
5888
5889     case 0x166b:        // (green steel)
5890       element = EL_STEEL_CHAR_3;
5891       break;
5892
5893     case 0x166c:        // (green steel)
5894       element = EL_STEEL_CHAR_4;
5895       break;
5896
5897     case 0x166d:        // (green steel)
5898       element = EL_STEEL_CHAR_5;
5899       break;
5900
5901     case 0x166e:        // (green steel)
5902       element = EL_STEEL_CHAR_6;
5903       break;
5904
5905     case 0x166f:        // (green steel)
5906       element = EL_STEEL_CHAR_7;
5907       break;
5908
5909     case 0x1670:        // (green steel)
5910       element = EL_STEEL_CHAR_8;
5911       break;
5912
5913     case 0x1671:        // (green steel)
5914       element = EL_STEEL_CHAR_9;
5915       break;
5916
5917     case 0x1672:        // (green steel)
5918       element = EL_STEEL_CHAR_PERIOD;
5919       break;
5920
5921     case 0x1673:        // (green steel)
5922       element = EL_STEEL_CHAR_EXCLAM;
5923       break;
5924
5925     case 0x1674:        // (green steel)
5926       element = EL_STEEL_CHAR_COLON;
5927       break;
5928
5929     case 0x1675:        // (green steel)
5930       element = EL_STEEL_CHAR_LESS;
5931       break;
5932
5933     case 0x1676:        // (green steel)
5934       element = EL_STEEL_CHAR_GREATER;
5935       break;
5936
5937     case 0x1677:        // (green steel)
5938       element = EL_STEEL_CHAR_QUESTION;
5939       break;
5940
5941     case 0x1678:        // (green steel)
5942       element = EL_STEEL_CHAR_COPYRIGHT;
5943       break;
5944
5945     case 0x1679:        // (green steel)
5946       element = EL_STEEL_CHAR_UP;
5947       break;
5948
5949     case 0x167a:        // (green steel)
5950       element = EL_STEEL_CHAR_DOWN;
5951       break;
5952
5953     case 0x167b:        // (green steel)
5954       element = EL_STEEL_CHAR_BUTTON;
5955       break;
5956
5957     case 0x167c:        // (green steel)
5958       element = EL_STEEL_CHAR_PLUS;
5959       break;
5960
5961     case 0x167d:        // (green steel)
5962       element = EL_STEEL_CHAR_MINUS;
5963       break;
5964
5965     case 0x167e:        // (green steel)
5966       element = EL_STEEL_CHAR_APOSTROPHE;
5967       break;
5968
5969     case 0x167f:        // (green steel)
5970       element = EL_STEEL_CHAR_PARENLEFT;
5971       break;
5972
5973     case 0x1680:        // (green steel)
5974       element = EL_STEEL_CHAR_PARENRIGHT;
5975       break;
5976
5977     case 0x1681:        // gate (red)
5978       element = EL_EM_GATE_1;
5979       break;
5980
5981     case 0x1682:        // secret gate (red)
5982       element = EL_EM_GATE_1_GRAY;
5983       break;
5984
5985     case 0x1683:        // gate (yellow)
5986       element = EL_EM_GATE_2;
5987       break;
5988
5989     case 0x1684:        // secret gate (yellow)
5990       element = EL_EM_GATE_2_GRAY;
5991       break;
5992
5993     case 0x1685:        // gate (blue)
5994       element = EL_EM_GATE_4;
5995       break;
5996
5997     case 0x1686:        // secret gate (blue)
5998       element = EL_EM_GATE_4_GRAY;
5999       break;
6000
6001     case 0x1687:        // gate (green)
6002       element = EL_EM_GATE_3;
6003       break;
6004
6005     case 0x1688:        // secret gate (green)
6006       element = EL_EM_GATE_3_GRAY;
6007       break;
6008
6009     case 0x1689:        // gate (white)
6010       element = EL_DC_GATE_WHITE;
6011       break;
6012
6013     case 0x168a:        // secret gate (white)
6014       element = EL_DC_GATE_WHITE_GRAY;
6015       break;
6016
6017     case 0x168b:        // secret gate (no key)
6018       element = EL_DC_GATE_FAKE_GRAY;
6019       break;
6020
6021     case 0x168c:
6022       element = EL_ROBOT_WHEEL;
6023       break;
6024
6025     case 0x168d:
6026       element = EL_DC_TIMEGATE_SWITCH;
6027       break;
6028
6029     case 0x168e:
6030       element = EL_ACID_POOL_BOTTOM;
6031       break;
6032
6033     case 0x168f:
6034       element = EL_ACID_POOL_TOPLEFT;
6035       break;
6036
6037     case 0x1690:
6038       element = EL_ACID_POOL_TOPRIGHT;
6039       break;
6040
6041     case 0x1691:
6042       element = EL_ACID_POOL_BOTTOMLEFT;
6043       break;
6044
6045     case 0x1692:
6046       element = EL_ACID_POOL_BOTTOMRIGHT;
6047       break;
6048
6049     case 0x1693:
6050       element = EL_STEELWALL;
6051       break;
6052
6053     case 0x1694:
6054       element = EL_STEELWALL_SLIPPERY;
6055       break;
6056
6057     case 0x1695:        // steel wall (not round)
6058       element = EL_STEELWALL;
6059       break;
6060
6061     case 0x1696:        // steel wall (left)
6062       element = EL_DC_STEELWALL_1_LEFT;
6063       break;
6064
6065     case 0x1697:        // steel wall (bottom)
6066       element = EL_DC_STEELWALL_1_BOTTOM;
6067       break;
6068
6069     case 0x1698:        // steel wall (right)
6070       element = EL_DC_STEELWALL_1_RIGHT;
6071       break;
6072
6073     case 0x1699:        // steel wall (top)
6074       element = EL_DC_STEELWALL_1_TOP;
6075       break;
6076
6077     case 0x169a:        // steel wall (left/bottom)
6078       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6079       break;
6080
6081     case 0x169b:        // steel wall (right/bottom)
6082       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6083       break;
6084
6085     case 0x169c:        // steel wall (right/top)
6086       element = EL_DC_STEELWALL_1_TOPRIGHT;
6087       break;
6088
6089     case 0x169d:        // steel wall (left/top)
6090       element = EL_DC_STEELWALL_1_TOPLEFT;
6091       break;
6092
6093     case 0x169e:        // steel wall (right/bottom small)
6094       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6095       break;
6096
6097     case 0x169f:        // steel wall (left/bottom small)
6098       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6099       break;
6100
6101     case 0x16a0:        // steel wall (right/top small)
6102       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6103       break;
6104
6105     case 0x16a1:        // steel wall (left/top small)
6106       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6107       break;
6108
6109     case 0x16a2:        // steel wall (left/right)
6110       element = EL_DC_STEELWALL_1_VERTICAL;
6111       break;
6112
6113     case 0x16a3:        // steel wall (top/bottom)
6114       element = EL_DC_STEELWALL_1_HORIZONTAL;
6115       break;
6116
6117     case 0x16a4:        // steel wall 2 (left end)
6118       element = EL_DC_STEELWALL_2_LEFT;
6119       break;
6120
6121     case 0x16a5:        // steel wall 2 (right end)
6122       element = EL_DC_STEELWALL_2_RIGHT;
6123       break;
6124
6125     case 0x16a6:        // steel wall 2 (top end)
6126       element = EL_DC_STEELWALL_2_TOP;
6127       break;
6128
6129     case 0x16a7:        // steel wall 2 (bottom end)
6130       element = EL_DC_STEELWALL_2_BOTTOM;
6131       break;
6132
6133     case 0x16a8:        // steel wall 2 (left/right)
6134       element = EL_DC_STEELWALL_2_HORIZONTAL;
6135       break;
6136
6137     case 0x16a9:        // steel wall 2 (up/down)
6138       element = EL_DC_STEELWALL_2_VERTICAL;
6139       break;
6140
6141     case 0x16aa:        // steel wall 2 (mid)
6142       element = EL_DC_STEELWALL_2_MIDDLE;
6143       break;
6144
6145     case 0x16ab:
6146       element = EL_SIGN_EXCLAMATION;
6147       break;
6148
6149     case 0x16ac:
6150       element = EL_SIGN_RADIOACTIVITY;
6151       break;
6152
6153     case 0x16ad:
6154       element = EL_SIGN_STOP;
6155       break;
6156
6157     case 0x16ae:
6158       element = EL_SIGN_WHEELCHAIR;
6159       break;
6160
6161     case 0x16af:
6162       element = EL_SIGN_PARKING;
6163       break;
6164
6165     case 0x16b0:
6166       element = EL_SIGN_NO_ENTRY;
6167       break;
6168
6169     case 0x16b1:
6170       element = EL_SIGN_HEART;
6171       break;
6172
6173     case 0x16b2:
6174       element = EL_SIGN_GIVE_WAY;
6175       break;
6176
6177     case 0x16b3:
6178       element = EL_SIGN_ENTRY_FORBIDDEN;
6179       break;
6180
6181     case 0x16b4:
6182       element = EL_SIGN_EMERGENCY_EXIT;
6183       break;
6184
6185     case 0x16b5:
6186       element = EL_SIGN_YIN_YANG;
6187       break;
6188
6189     case 0x16b6:
6190       element = EL_WALL_EMERALD;
6191       break;
6192
6193     case 0x16b7:
6194       element = EL_WALL_DIAMOND;
6195       break;
6196
6197     case 0x16b8:
6198       element = EL_WALL_PEARL;
6199       break;
6200
6201     case 0x16b9:
6202       element = EL_WALL_CRYSTAL;
6203       break;
6204
6205     case 0x16ba:
6206       element = EL_INVISIBLE_WALL;
6207       break;
6208
6209     case 0x16bb:
6210       element = EL_INVISIBLE_STEELWALL;
6211       break;
6212
6213       // 0x16bc - 0x16cb:
6214       // EL_INVISIBLE_SAND
6215
6216     case 0x16cc:
6217       element = EL_LIGHT_SWITCH;
6218       break;
6219
6220     case 0x16cd:
6221       element = EL_ENVELOPE_1;
6222       break;
6223
6224     default:
6225       if (element >= 0x0117 && element <= 0x036e)       // (?)
6226         element = EL_DIAMOND;
6227       else if (element >= 0x042d && element <= 0x0684)  // (?)
6228         element = EL_EMERALD;
6229       else if (element >= 0x157c && element <= 0x158b)
6230         element = EL_SAND;
6231       else if (element >= 0x1590 && element <= 0x159f)
6232         element = EL_DC_LANDMINE;
6233       else if (element >= 0x16bc && element <= 0x16cb)
6234         element = EL_INVISIBLE_SAND;
6235       else
6236       {
6237         Warn("unknown Diamond Caves element 0x%04x", element);
6238
6239         element = EL_UNKNOWN;
6240       }
6241       break;
6242   }
6243
6244   return getMappedElement(element);
6245 }
6246
6247 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6248 {
6249   byte header[DC_LEVEL_HEADER_SIZE];
6250   int envelope_size;
6251   int envelope_header_pos = 62;
6252   int envelope_content_pos = 94;
6253   int level_name_pos = 251;
6254   int level_author_pos = 292;
6255   int envelope_header_len;
6256   int envelope_content_len;
6257   int level_name_len;
6258   int level_author_len;
6259   int fieldx, fieldy;
6260   int num_yamyam_contents;
6261   int i, x, y;
6262
6263   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6264
6265   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6266   {
6267     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6268
6269     header[i * 2 + 0] = header_word >> 8;
6270     header[i * 2 + 1] = header_word & 0xff;
6271   }
6272
6273   // read some values from level header to check level decoding integrity
6274   fieldx = header[6] | (header[7] << 8);
6275   fieldy = header[8] | (header[9] << 8);
6276   num_yamyam_contents = header[60] | (header[61] << 8);
6277
6278   // do some simple sanity checks to ensure that level was correctly decoded
6279   if (fieldx < 1 || fieldx > 256 ||
6280       fieldy < 1 || fieldy > 256 ||
6281       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6282   {
6283     level->no_valid_file = TRUE;
6284
6285     Warn("cannot decode level from stream -- using empty level");
6286
6287     return;
6288   }
6289
6290   // maximum envelope header size is 31 bytes
6291   envelope_header_len   = header[envelope_header_pos];
6292   // maximum envelope content size is 110 (156?) bytes
6293   envelope_content_len  = header[envelope_content_pos];
6294
6295   // maximum level title size is 40 bytes
6296   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6297   // maximum level author size is 30 (51?) bytes
6298   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6299
6300   envelope_size = 0;
6301
6302   for (i = 0; i < envelope_header_len; i++)
6303     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6304       level->envelope[0].text[envelope_size++] =
6305         header[envelope_header_pos + 1 + i];
6306
6307   if (envelope_header_len > 0 && envelope_content_len > 0)
6308   {
6309     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6310       level->envelope[0].text[envelope_size++] = '\n';
6311     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6312       level->envelope[0].text[envelope_size++] = '\n';
6313   }
6314
6315   for (i = 0; i < envelope_content_len; i++)
6316     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6317       level->envelope[0].text[envelope_size++] =
6318         header[envelope_content_pos + 1 + i];
6319
6320   level->envelope[0].text[envelope_size] = '\0';
6321
6322   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6323   level->envelope[0].ysize = 10;
6324   level->envelope[0].autowrap = TRUE;
6325   level->envelope[0].centered = TRUE;
6326
6327   for (i = 0; i < level_name_len; i++)
6328     level->name[i] = header[level_name_pos + 1 + i];
6329   level->name[level_name_len] = '\0';
6330
6331   for (i = 0; i < level_author_len; i++)
6332     level->author[i] = header[level_author_pos + 1 + i];
6333   level->author[level_author_len] = '\0';
6334
6335   num_yamyam_contents = header[60] | (header[61] << 8);
6336   level->num_yamyam_contents =
6337     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6338
6339   for (i = 0; i < num_yamyam_contents; i++)
6340   {
6341     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6342     {
6343       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6344       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6345
6346       if (i < MAX_ELEMENT_CONTENTS)
6347         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6348     }
6349   }
6350
6351   fieldx = header[6] | (header[7] << 8);
6352   fieldy = header[8] | (header[9] << 8);
6353   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6354   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6355
6356   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6357   {
6358     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6359     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6360
6361     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6362       level->field[x][y] = getMappedElement_DC(element_dc);
6363   }
6364
6365   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6366   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6367   level->field[x][y] = EL_PLAYER_1;
6368
6369   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6370   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6371   level->field[x][y] = EL_PLAYER_2;
6372
6373   level->gems_needed            = header[18] | (header[19] << 8);
6374
6375   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6376   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6377   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6378   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6379   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6380   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6381   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6382   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6383   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6384   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6385   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6386   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6387
6388   level->time                   = header[44] | (header[45] << 8);
6389
6390   level->amoeba_speed           = header[46] | (header[47] << 8);
6391   level->time_light             = header[48] | (header[49] << 8);
6392   level->time_timegate          = header[50] | (header[51] << 8);
6393   level->time_wheel             = header[52] | (header[53] << 8);
6394   level->time_magic_wall        = header[54] | (header[55] << 8);
6395   level->extra_time             = header[56] | (header[57] << 8);
6396   level->shield_normal_time     = header[58] | (header[59] << 8);
6397
6398   // shield and extra time elements do not have a score
6399   level->score[SC_SHIELD]       = 0;
6400   level->extra_time_score       = 0;
6401
6402   // set time for normal and deadly shields to the same value
6403   level->shield_deadly_time     = level->shield_normal_time;
6404
6405   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6406   // can slip down from flat walls, like normal walls and steel walls
6407   level->em_slippery_gems = TRUE;
6408
6409   // time score is counted for each 10 seconds left in Diamond Caves levels
6410   level->time_score_base = 10;
6411 }
6412
6413 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6414                                      struct LevelFileInfo *level_file_info,
6415                                      boolean level_info_only)
6416 {
6417   char *filename = level_file_info->filename;
6418   File *file;
6419   int num_magic_bytes = 8;
6420   char magic_bytes[num_magic_bytes + 1];
6421   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6422
6423   if (!(file = openFile(filename, MODE_READ)))
6424   {
6425     level->no_valid_file = TRUE;
6426
6427     if (!level_info_only)
6428       Warn("cannot read level '%s' -- using empty level", filename);
6429
6430     return;
6431   }
6432
6433   // fseek(file, 0x0000, SEEK_SET);
6434
6435   if (level_file_info->packed)
6436   {
6437     // read "magic bytes" from start of file
6438     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6439       magic_bytes[0] = '\0';
6440
6441     // check "magic bytes" for correct file format
6442     if (!strPrefix(magic_bytes, "DC2"))
6443     {
6444       level->no_valid_file = TRUE;
6445
6446       Warn("unknown DC level file '%s' -- using empty level", filename);
6447
6448       return;
6449     }
6450
6451     if (strPrefix(magic_bytes, "DC2Win95") ||
6452         strPrefix(magic_bytes, "DC2Win98"))
6453     {
6454       int position_first_level = 0x00fa;
6455       int extra_bytes = 4;
6456       int skip_bytes;
6457
6458       // advance file stream to first level inside the level package
6459       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6460
6461       // each block of level data is followed by block of non-level data
6462       num_levels_to_skip *= 2;
6463
6464       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6465       while (num_levels_to_skip >= 0)
6466       {
6467         // advance file stream to next level inside the level package
6468         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6469         {
6470           level->no_valid_file = TRUE;
6471
6472           Warn("cannot fseek in file '%s' -- using empty level", filename);
6473
6474           return;
6475         }
6476
6477         // skip apparently unused extra bytes following each level
6478         ReadUnusedBytesFromFile(file, extra_bytes);
6479
6480         // read size of next level in level package
6481         skip_bytes = getFile32BitLE(file);
6482
6483         num_levels_to_skip--;
6484       }
6485     }
6486     else
6487     {
6488       level->no_valid_file = TRUE;
6489
6490       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6491
6492       return;
6493     }
6494   }
6495
6496   LoadLevelFromFileStream_DC(file, level);
6497
6498   closeFile(file);
6499 }
6500
6501
6502 // ----------------------------------------------------------------------------
6503 // functions for loading SB level
6504 // ----------------------------------------------------------------------------
6505
6506 int getMappedElement_SB(int element_ascii, boolean use_ces)
6507 {
6508   static struct
6509   {
6510     int ascii;
6511     int sb;
6512     int ce;
6513   }
6514   sb_element_mapping[] =
6515   {
6516     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6517     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6518     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6519     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6520     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6521     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6522     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6523     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6524
6525     { 0,   -1,                      -1          },
6526   };
6527
6528   int i;
6529
6530   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6531     if (element_ascii == sb_element_mapping[i].ascii)
6532       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6533
6534   return EL_UNDEFINED;
6535 }
6536
6537 static void SetLevelSettings_SB(struct LevelInfo *level)
6538 {
6539   // time settings
6540   level->time = 0;
6541   level->use_step_counter = TRUE;
6542
6543   // score settings
6544   level->score[SC_TIME_BONUS] = 0;
6545   level->time_score_base = 1;
6546   level->rate_time_over_score = TRUE;
6547
6548   // game settings
6549   level->auto_exit_sokoban = TRUE;
6550 }
6551
6552 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6553                                      struct LevelFileInfo *level_file_info,
6554                                      boolean level_info_only)
6555 {
6556   char *filename = level_file_info->filename;
6557   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6558   char last_comment[MAX_LINE_LEN];
6559   char level_name[MAX_LINE_LEN];
6560   char *line_ptr;
6561   File *file;
6562   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6563   boolean read_continued_line = FALSE;
6564   boolean reading_playfield = FALSE;
6565   boolean got_valid_playfield_line = FALSE;
6566   boolean invalid_playfield_char = FALSE;
6567   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6568   int file_level_nr = 0;
6569   int x = 0, y = 0;             // initialized to make compilers happy
6570
6571   last_comment[0] = '\0';
6572   level_name[0] = '\0';
6573
6574   if (!(file = openFile(filename, MODE_READ)))
6575   {
6576     level->no_valid_file = TRUE;
6577
6578     if (!level_info_only)
6579       Warn("cannot read level '%s' -- using empty level", filename);
6580
6581     return;
6582   }
6583
6584   while (!checkEndOfFile(file))
6585   {
6586     // level successfully read, but next level may follow here
6587     if (!got_valid_playfield_line && reading_playfield)
6588     {
6589       // read playfield from single level file -- skip remaining file
6590       if (!level_file_info->packed)
6591         break;
6592
6593       if (file_level_nr >= num_levels_to_skip)
6594         break;
6595
6596       file_level_nr++;
6597
6598       last_comment[0] = '\0';
6599       level_name[0] = '\0';
6600
6601       reading_playfield = FALSE;
6602     }
6603
6604     got_valid_playfield_line = FALSE;
6605
6606     // read next line of input file
6607     if (!getStringFromFile(file, line, MAX_LINE_LEN))
6608       break;
6609
6610     // cut trailing line break (this can be newline and/or carriage return)
6611     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6612       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6613         *line_ptr = '\0';
6614
6615     // copy raw input line for later use (mainly debugging output)
6616     strcpy(line_raw, line);
6617
6618     if (read_continued_line)
6619     {
6620       // append new line to existing line, if there is enough space
6621       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6622         strcat(previous_line, line_ptr);
6623
6624       strcpy(line, previous_line);      // copy storage buffer to line
6625
6626       read_continued_line = FALSE;
6627     }
6628
6629     // if the last character is '\', continue at next line
6630     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6631     {
6632       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
6633       strcpy(previous_line, line);      // copy line to storage buffer
6634
6635       read_continued_line = TRUE;
6636
6637       continue;
6638     }
6639
6640     // skip empty lines
6641     if (line[0] == '\0')
6642       continue;
6643
6644     // extract comment text from comment line
6645     if (line[0] == ';')
6646     {
6647       for (line_ptr = line; *line_ptr; line_ptr++)
6648         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6649           break;
6650
6651       strcpy(last_comment, line_ptr);
6652
6653       continue;
6654     }
6655
6656     // extract level title text from line containing level title
6657     if (line[0] == '\'')
6658     {
6659       strcpy(level_name, &line[1]);
6660
6661       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6662         level_name[strlen(level_name) - 1] = '\0';
6663
6664       continue;
6665     }
6666
6667     // skip lines containing only spaces (or empty lines)
6668     for (line_ptr = line; *line_ptr; line_ptr++)
6669       if (*line_ptr != ' ')
6670         break;
6671     if (*line_ptr == '\0')
6672       continue;
6673
6674     // at this point, we have found a line containing part of a playfield
6675
6676     got_valid_playfield_line = TRUE;
6677
6678     if (!reading_playfield)
6679     {
6680       reading_playfield = TRUE;
6681       invalid_playfield_char = FALSE;
6682
6683       for (x = 0; x < MAX_LEV_FIELDX; x++)
6684         for (y = 0; y < MAX_LEV_FIELDY; y++)
6685           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6686
6687       level->fieldx = 0;
6688       level->fieldy = 0;
6689
6690       // start with topmost tile row
6691       y = 0;
6692     }
6693
6694     // skip playfield line if larger row than allowed
6695     if (y >= MAX_LEV_FIELDY)
6696       continue;
6697
6698     // start with leftmost tile column
6699     x = 0;
6700
6701     // read playfield elements from line
6702     for (line_ptr = line; *line_ptr; line_ptr++)
6703     {
6704       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6705
6706       // stop parsing playfield line if larger column than allowed
6707       if (x >= MAX_LEV_FIELDX)
6708         break;
6709
6710       if (mapped_sb_element == EL_UNDEFINED)
6711       {
6712         invalid_playfield_char = TRUE;
6713
6714         break;
6715       }
6716
6717       level->field[x][y] = mapped_sb_element;
6718
6719       // continue with next tile column
6720       x++;
6721
6722       level->fieldx = MAX(x, level->fieldx);
6723     }
6724
6725     if (invalid_playfield_char)
6726     {
6727       // if first playfield line, treat invalid lines as comment lines
6728       if (y == 0)
6729         reading_playfield = FALSE;
6730
6731       continue;
6732     }
6733
6734     // continue with next tile row
6735     y++;
6736   }
6737
6738   closeFile(file);
6739
6740   level->fieldy = y;
6741
6742   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6743   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6744
6745   if (!reading_playfield)
6746   {
6747     level->no_valid_file = TRUE;
6748
6749     Warn("cannot read level '%s' -- using empty level", filename);
6750
6751     return;
6752   }
6753
6754   if (*level_name != '\0')
6755   {
6756     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6757     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6758   }
6759   else if (*last_comment != '\0')
6760   {
6761     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6762     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6763   }
6764   else
6765   {
6766     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6767   }
6768
6769   // set all empty fields beyond the border walls to invisible steel wall
6770   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6771   {
6772     if ((x == 0 || x == level->fieldx - 1 ||
6773          y == 0 || y == level->fieldy - 1) &&
6774         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6775       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6776                      level->field, level->fieldx, level->fieldy);
6777   }
6778
6779   // set special level settings for Sokoban levels
6780   SetLevelSettings_SB(level);
6781
6782   if (load_xsb_to_ces)
6783   {
6784     // special global settings can now be set in level template
6785     level->use_custom_template = TRUE;
6786   }
6787 }
6788
6789
6790 // -------------------------------------------------------------------------
6791 // functions for handling native levels
6792 // -------------------------------------------------------------------------
6793
6794 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6795                                      struct LevelFileInfo *level_file_info,
6796                                      boolean level_info_only)
6797 {
6798   int pos = 0;
6799
6800   // determine position of requested level inside level package
6801   if (level_file_info->packed)
6802     pos = level_file_info->nr - leveldir_current->first_level;
6803
6804   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6805     level->no_valid_file = TRUE;
6806 }
6807
6808 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6809                                      struct LevelFileInfo *level_file_info,
6810                                      boolean level_info_only)
6811 {
6812   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6813     level->no_valid_file = TRUE;
6814 }
6815
6816 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6817                                      struct LevelFileInfo *level_file_info,
6818                                      boolean level_info_only)
6819 {
6820   int pos = 0;
6821
6822   // determine position of requested level inside level package
6823   if (level_file_info->packed)
6824     pos = level_file_info->nr - leveldir_current->first_level;
6825
6826   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6827     level->no_valid_file = TRUE;
6828 }
6829
6830 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6831                                      struct LevelFileInfo *level_file_info,
6832                                      boolean level_info_only)
6833 {
6834   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6835     level->no_valid_file = TRUE;
6836 }
6837
6838 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6839 {
6840   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6841     CopyNativeLevel_RND_to_BD(level);
6842   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6843     CopyNativeLevel_RND_to_EM(level);
6844   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6845     CopyNativeLevel_RND_to_SP(level);
6846   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6847     CopyNativeLevel_RND_to_MM(level);
6848 }
6849
6850 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6851 {
6852   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6853     CopyNativeLevel_BD_to_RND(level);
6854   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6855     CopyNativeLevel_EM_to_RND(level);
6856   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6857     CopyNativeLevel_SP_to_RND(level);
6858   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6859     CopyNativeLevel_MM_to_RND(level);
6860 }
6861
6862 void SaveNativeLevel(struct LevelInfo *level)
6863 {
6864   // saving native level files only supported for some game engines
6865   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6866       level->game_engine_type != GAME_ENGINE_TYPE_SP)
6867     return;
6868
6869   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6870                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6871   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6872   char *filename = getLevelFilenameFromBasename(basename);
6873
6874   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6875     return;
6876
6877   boolean success = FALSE;
6878
6879   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6880   {
6881     CopyNativeLevel_RND_to_BD(level);
6882     // CopyNativeTape_RND_to_BD(level);
6883
6884     success = SaveNativeLevel_BD(filename);
6885   }
6886   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6887   {
6888     CopyNativeLevel_RND_to_SP(level);
6889     CopyNativeTape_RND_to_SP(level);
6890
6891     success = SaveNativeLevel_SP(filename);
6892   }
6893
6894   if (success)
6895     Request("Native level file saved!", REQ_CONFIRM);
6896   else
6897     Request("Failed to save native level file!", REQ_CONFIRM);
6898 }
6899
6900
6901 // ----------------------------------------------------------------------------
6902 // functions for loading generic level
6903 // ----------------------------------------------------------------------------
6904
6905 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6906                                   struct LevelFileInfo *level_file_info,
6907                                   boolean level_info_only)
6908 {
6909   // always start with reliable default values
6910   setLevelInfoToDefaults(level, level_info_only, TRUE);
6911
6912   switch (level_file_info->type)
6913   {
6914     case LEVEL_FILE_TYPE_RND:
6915       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6916       break;
6917
6918     case LEVEL_FILE_TYPE_BD:
6919       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6920       level->game_engine_type = GAME_ENGINE_TYPE_BD;
6921       break;
6922
6923     case LEVEL_FILE_TYPE_EM:
6924       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6925       level->game_engine_type = GAME_ENGINE_TYPE_EM;
6926       break;
6927
6928     case LEVEL_FILE_TYPE_SP:
6929       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6930       level->game_engine_type = GAME_ENGINE_TYPE_SP;
6931       break;
6932
6933     case LEVEL_FILE_TYPE_MM:
6934       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6935       level->game_engine_type = GAME_ENGINE_TYPE_MM;
6936       break;
6937
6938     case LEVEL_FILE_TYPE_DC:
6939       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6940       break;
6941
6942     case LEVEL_FILE_TYPE_SB:
6943       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6944       break;
6945
6946     default:
6947       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6948       break;
6949   }
6950
6951   // if level file is invalid, restore level structure to default values
6952   if (level->no_valid_file)
6953     setLevelInfoToDefaults(level, level_info_only, FALSE);
6954
6955   if (check_special_flags("use_native_bd_game_engine"))
6956     level->game_engine_type = GAME_ENGINE_TYPE_BD;
6957
6958   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6959     level->game_engine_type = GAME_ENGINE_TYPE_RND;
6960
6961   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6962     CopyNativeLevel_Native_to_RND(level);
6963 }
6964
6965 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6966 {
6967   static struct LevelFileInfo level_file_info;
6968
6969   // always start with reliable default values
6970   setFileInfoToDefaults(&level_file_info);
6971
6972   level_file_info.nr = 0;                       // unknown level number
6973   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
6974
6975   setString(&level_file_info.filename, filename);
6976
6977   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6978 }
6979
6980 static void LoadLevel_InitVersion(struct LevelInfo *level)
6981 {
6982   int i, j;
6983
6984   if (leveldir_current == NULL)         // only when dumping level
6985     return;
6986
6987   // all engine modifications also valid for levels which use latest engine
6988   if (level->game_version < VERSION_IDENT(3,2,0,5))
6989   {
6990     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6991     level->time_score_base = 10;
6992   }
6993
6994   if (leveldir_current->latest_engine)
6995   {
6996     // ---------- use latest game engine --------------------------------------
6997
6998     /* For all levels which are forced to use the latest game engine version
6999        (normally all but user contributed, private and undefined levels), set
7000        the game engine version to the actual version; this allows for actual
7001        corrections in the game engine to take effect for existing, converted
7002        levels (from "classic" or other existing games) to make the emulation
7003        of the corresponding game more accurate, while (hopefully) not breaking
7004        existing levels created from other players. */
7005
7006     level->game_version = GAME_VERSION_ACTUAL;
7007
7008     /* Set special EM style gems behaviour: EM style gems slip down from
7009        normal, steel and growing wall. As this is a more fundamental change,
7010        it seems better to set the default behaviour to "off" (as it is more
7011        natural) and make it configurable in the level editor (as a property
7012        of gem style elements). Already existing converted levels (neither
7013        private nor contributed levels) are changed to the new behaviour. */
7014
7015     if (level->file_version < FILE_VERSION_2_0)
7016       level->em_slippery_gems = TRUE;
7017
7018     return;
7019   }
7020
7021   // ---------- use game engine the level was created with --------------------
7022
7023   /* For all levels which are not forced to use the latest game engine
7024      version (normally user contributed, private and undefined levels),
7025      use the version of the game engine the levels were created for.
7026
7027      Since 2.0.1, the game engine version is now directly stored
7028      in the level file (chunk "VERS"), so there is no need anymore
7029      to set the game version from the file version (except for old,
7030      pre-2.0 levels, where the game version is still taken from the
7031      file format version used to store the level -- see above). */
7032
7033   // player was faster than enemies in 1.0.0 and before
7034   if (level->file_version == FILE_VERSION_1_0)
7035     for (i = 0; i < MAX_PLAYERS; i++)
7036       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7037
7038   // default behaviour for EM style gems was "slippery" only in 2.0.1
7039   if (level->game_version == VERSION_IDENT(2,0,1,0))
7040     level->em_slippery_gems = TRUE;
7041
7042   // springs could be pushed over pits before (pre-release version) 2.2.0
7043   if (level->game_version < VERSION_IDENT(2,2,0,0))
7044     level->use_spring_bug = TRUE;
7045
7046   if (level->game_version < VERSION_IDENT(3,2,0,5))
7047   {
7048     // time orb caused limited time in endless time levels before 3.2.0-5
7049     level->use_time_orb_bug = TRUE;
7050
7051     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7052     level->block_snap_field = FALSE;
7053
7054     // extra time score was same value as time left score before 3.2.0-5
7055     level->extra_time_score = level->score[SC_TIME_BONUS];
7056   }
7057
7058   if (level->game_version < VERSION_IDENT(3,2,0,7))
7059   {
7060     // default behaviour for snapping was "not continuous" before 3.2.0-7
7061     level->continuous_snapping = FALSE;
7062   }
7063
7064   // only few elements were able to actively move into acid before 3.1.0
7065   // trigger settings did not exist before 3.1.0; set to default "any"
7066   if (level->game_version < VERSION_IDENT(3,1,0,0))
7067   {
7068     // correct "can move into acid" settings (all zero in old levels)
7069
7070     level->can_move_into_acid_bits = 0; // nothing can move into acid
7071     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7072
7073     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7074     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7075     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7076     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7077
7078     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7079       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7080
7081     // correct trigger settings (stored as zero == "none" in old levels)
7082
7083     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7084     {
7085       int element = EL_CUSTOM_START + i;
7086       struct ElementInfo *ei = &element_info[element];
7087
7088       for (j = 0; j < ei->num_change_pages; j++)
7089       {
7090         struct ElementChangeInfo *change = &ei->change_page[j];
7091
7092         change->trigger_player = CH_PLAYER_ANY;
7093         change->trigger_page = CH_PAGE_ANY;
7094       }
7095     }
7096   }
7097
7098   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7099   {
7100     int element = EL_CUSTOM_256;
7101     struct ElementInfo *ei = &element_info[element];
7102     struct ElementChangeInfo *change = &ei->change_page[0];
7103
7104     /* This is needed to fix a problem that was caused by a bugfix in function
7105        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7106        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7107        not replace walkable elements, but instead just placed the player on it,
7108        without placing the Sokoban field under the player). Unfortunately, this
7109        breaks "Snake Bite" style levels when the snake is halfway through a door
7110        that just closes (the snake head is still alive and can be moved in this
7111        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7112        player (without Sokoban element) which then gets killed as designed). */
7113
7114     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7115          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7116         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7117       change->target_element = EL_PLAYER_1;
7118   }
7119
7120   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7121   if (level->game_version < VERSION_IDENT(3,2,5,0))
7122   {
7123     /* This is needed to fix a problem that was caused by a bugfix in function
7124        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7125        corrects the behaviour when a custom element changes to another custom
7126        element with a higher element number that has change actions defined.
7127        Normally, only one change per frame is allowed for custom elements.
7128        Therefore, it is checked if a custom element already changed in the
7129        current frame; if it did, subsequent changes are suppressed.
7130        Unfortunately, this is only checked for element changes, but not for
7131        change actions, which are still executed. As the function above loops
7132        through all custom elements from lower to higher, an element change
7133        resulting in a lower CE number won't be checked again, while a target
7134        element with a higher number will also be checked, and potential change
7135        actions will get executed for this CE, too (which is wrong), while
7136        further changes are ignored (which is correct). As this bugfix breaks
7137        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7138        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7139        behaviour for existing levels and tapes that make use of this bug */
7140
7141     level->use_action_after_change_bug = TRUE;
7142   }
7143
7144   // not centering level after relocating player was default only in 3.2.3
7145   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7146     level->shifted_relocation = TRUE;
7147
7148   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7149   if (level->game_version < VERSION_IDENT(3,2,6,0))
7150     level->em_explodes_by_fire = TRUE;
7151
7152   // levels were solved by the first player entering an exit up to 4.1.0.0
7153   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7154     level->solved_by_one_player = TRUE;
7155
7156   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7157   if (level->game_version < VERSION_IDENT(4,1,1,1))
7158     level->use_life_bugs = TRUE;
7159
7160   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7161   if (level->game_version < VERSION_IDENT(4,1,1,1))
7162     level->sb_objects_needed = FALSE;
7163
7164   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7165   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7166     level->finish_dig_collect = FALSE;
7167
7168   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7169   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7170     level->keep_walkable_ce = TRUE;
7171 }
7172
7173 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7174 {
7175   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7176   int x, y;
7177
7178   // check if this level is (not) a Sokoban level
7179   for (y = 0; y < level->fieldy; y++)
7180     for (x = 0; x < level->fieldx; x++)
7181       if (!IS_SB_ELEMENT(Tile[x][y]))
7182         is_sokoban_level = FALSE;
7183
7184   if (is_sokoban_level)
7185   {
7186     // set special level settings for Sokoban levels
7187     SetLevelSettings_SB(level);
7188   }
7189 }
7190
7191 static void LoadLevel_InitSettings(struct LevelInfo *level)
7192 {
7193   // adjust level settings for (non-native) Sokoban-style levels
7194   LoadLevel_InitSettings_SB(level);
7195
7196   // rename levels with title "nameless level" or if renaming is forced
7197   if (leveldir_current->empty_level_name != NULL &&
7198       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7199        leveldir_current->force_level_name))
7200     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7201              leveldir_current->empty_level_name, level_nr);
7202 }
7203
7204 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7205 {
7206   int i, x, y;
7207
7208   // map elements that have changed in newer versions
7209   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7210                                                     level->game_version);
7211   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7212     for (x = 0; x < 3; x++)
7213       for (y = 0; y < 3; y++)
7214         level->yamyam_content[i].e[x][y] =
7215           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7216                                     level->game_version);
7217
7218 }
7219
7220 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7221 {
7222   int i, j;
7223
7224   // map custom element change events that have changed in newer versions
7225   // (these following values were accidentally changed in version 3.0.1)
7226   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7227   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7228   {
7229     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7230     {
7231       int element = EL_CUSTOM_START + i;
7232
7233       // order of checking and copying events to be mapped is important
7234       // (do not change the start and end value -- they are constant)
7235       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7236       {
7237         if (HAS_CHANGE_EVENT(element, j - 2))
7238         {
7239           SET_CHANGE_EVENT(element, j - 2, FALSE);
7240           SET_CHANGE_EVENT(element, j, TRUE);
7241         }
7242       }
7243
7244       // order of checking and copying events to be mapped is important
7245       // (do not change the start and end value -- they are constant)
7246       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7247       {
7248         if (HAS_CHANGE_EVENT(element, j - 1))
7249         {
7250           SET_CHANGE_EVENT(element, j - 1, FALSE);
7251           SET_CHANGE_EVENT(element, j, TRUE);
7252         }
7253       }
7254     }
7255   }
7256
7257   // initialize "can_change" field for old levels with only one change page
7258   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7259   {
7260     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7261     {
7262       int element = EL_CUSTOM_START + i;
7263
7264       if (CAN_CHANGE(element))
7265         element_info[element].change->can_change = TRUE;
7266     }
7267   }
7268
7269   // correct custom element values (for old levels without these options)
7270   if (level->game_version < VERSION_IDENT(3,1,1,0))
7271   {
7272     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7273     {
7274       int element = EL_CUSTOM_START + i;
7275       struct ElementInfo *ei = &element_info[element];
7276
7277       if (ei->access_direction == MV_NO_DIRECTION)
7278         ei->access_direction = MV_ALL_DIRECTIONS;
7279     }
7280   }
7281
7282   // correct custom element values (fix invalid values for all versions)
7283   if (1)
7284   {
7285     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7286     {
7287       int element = EL_CUSTOM_START + i;
7288       struct ElementInfo *ei = &element_info[element];
7289
7290       for (j = 0; j < ei->num_change_pages; j++)
7291       {
7292         struct ElementChangeInfo *change = &ei->change_page[j];
7293
7294         if (change->trigger_player == CH_PLAYER_NONE)
7295           change->trigger_player = CH_PLAYER_ANY;
7296
7297         if (change->trigger_side == CH_SIDE_NONE)
7298           change->trigger_side = CH_SIDE_ANY;
7299       }
7300     }
7301   }
7302
7303   // initialize "can_explode" field for old levels which did not store this
7304   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7305   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7306   {
7307     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7308     {
7309       int element = EL_CUSTOM_START + i;
7310
7311       if (EXPLODES_1X1_OLD(element))
7312         element_info[element].explosion_type = EXPLODES_1X1;
7313
7314       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7315                                              EXPLODES_SMASHED(element) ||
7316                                              EXPLODES_IMPACT(element)));
7317     }
7318   }
7319
7320   // correct previously hard-coded move delay values for maze runner style
7321   if (level->game_version < VERSION_IDENT(3,1,1,0))
7322   {
7323     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7324     {
7325       int element = EL_CUSTOM_START + i;
7326
7327       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7328       {
7329         // previously hard-coded and therefore ignored
7330         element_info[element].move_delay_fixed = 9;
7331         element_info[element].move_delay_random = 0;
7332       }
7333     }
7334   }
7335
7336   // set some other uninitialized values of custom elements in older levels
7337   if (level->game_version < VERSION_IDENT(3,1,0,0))
7338   {
7339     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7340     {
7341       int element = EL_CUSTOM_START + i;
7342
7343       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7344
7345       element_info[element].explosion_delay = 17;
7346       element_info[element].ignition_delay = 8;
7347     }
7348   }
7349
7350   // set mouse click change events to work for left/middle/right mouse button
7351   if (level->game_version < VERSION_IDENT(4,2,3,0))
7352   {
7353     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7354     {
7355       int element = EL_CUSTOM_START + i;
7356       struct ElementInfo *ei = &element_info[element];
7357
7358       for (j = 0; j < ei->num_change_pages; j++)
7359       {
7360         struct ElementChangeInfo *change = &ei->change_page[j];
7361
7362         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7363             change->has_event[CE_PRESSED_BY_MOUSE] ||
7364             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7365             change->has_event[CE_MOUSE_PRESSED_ON_X])
7366           change->trigger_side = CH_SIDE_ANY;
7367       }
7368     }
7369   }
7370 }
7371
7372 static void LoadLevel_InitElements(struct LevelInfo *level)
7373 {
7374   LoadLevel_InitStandardElements(level);
7375
7376   if (level->file_has_custom_elements)
7377     LoadLevel_InitCustomElements(level);
7378
7379   // initialize element properties for level editor etc.
7380   InitElementPropertiesEngine(level->game_version);
7381   InitElementPropertiesGfxElement();
7382 }
7383
7384 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7385 {
7386   int x, y;
7387
7388   // map elements that have changed in newer versions
7389   for (y = 0; y < level->fieldy; y++)
7390     for (x = 0; x < level->fieldx; x++)
7391       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7392                                                      level->game_version);
7393
7394   // clear unused playfield data (nicer if level gets resized in editor)
7395   for (x = 0; x < MAX_LEV_FIELDX; x++)
7396     for (y = 0; y < MAX_LEV_FIELDY; y++)
7397       if (x >= level->fieldx || y >= level->fieldy)
7398         level->field[x][y] = EL_EMPTY;
7399
7400   // copy elements to runtime playfield array
7401   for (x = 0; x < MAX_LEV_FIELDX; x++)
7402     for (y = 0; y < MAX_LEV_FIELDY; y++)
7403       Tile[x][y] = level->field[x][y];
7404
7405   // initialize level size variables for faster access
7406   lev_fieldx = level->fieldx;
7407   lev_fieldy = level->fieldy;
7408
7409   // determine border element for this level
7410   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7411     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7412   else
7413     SetBorderElement();
7414 }
7415
7416 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7417 {
7418   struct LevelFileInfo *level_file_info = &level->file_info;
7419
7420   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7421     CopyNativeLevel_RND_to_Native(level);
7422 }
7423
7424 static void LoadLevelTemplate_LoadAndInit(void)
7425 {
7426   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7427
7428   LoadLevel_InitVersion(&level_template);
7429   LoadLevel_InitElements(&level_template);
7430   LoadLevel_InitSettings(&level_template);
7431
7432   ActivateLevelTemplate();
7433 }
7434
7435 void LoadLevelTemplate(int nr)
7436 {
7437   if (!fileExists(getGlobalLevelTemplateFilename()))
7438   {
7439     Warn("no level template found for this level");
7440
7441     return;
7442   }
7443
7444   setLevelFileInfo(&level_template.file_info, nr);
7445
7446   LoadLevelTemplate_LoadAndInit();
7447 }
7448
7449 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7450 {
7451   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7452
7453   LoadLevelTemplate_LoadAndInit();
7454 }
7455
7456 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7457 {
7458   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7459
7460   if (level.use_custom_template)
7461   {
7462     if (network_level != NULL)
7463       LoadNetworkLevelTemplate(network_level);
7464     else
7465       LoadLevelTemplate(-1);
7466   }
7467
7468   LoadLevel_InitVersion(&level);
7469   LoadLevel_InitElements(&level);
7470   LoadLevel_InitPlayfield(&level);
7471   LoadLevel_InitSettings(&level);
7472
7473   LoadLevel_InitNativeEngines(&level);
7474 }
7475
7476 void LoadLevel(int nr)
7477 {
7478   SetLevelSetInfo(leveldir_current->identifier, nr);
7479
7480   setLevelFileInfo(&level.file_info, nr);
7481
7482   LoadLevel_LoadAndInit(NULL);
7483 }
7484
7485 void LoadLevelInfoOnly(int nr)
7486 {
7487   setLevelFileInfo(&level.file_info, nr);
7488
7489   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7490 }
7491
7492 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7493 {
7494   SetLevelSetInfo(network_level->leveldir_identifier,
7495                   network_level->file_info.nr);
7496
7497   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7498
7499   LoadLevel_LoadAndInit(network_level);
7500 }
7501
7502 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7503 {
7504   int chunk_size = 0;
7505
7506   chunk_size += putFileVersion(file, level->file_version);
7507   chunk_size += putFileVersion(file, level->game_version);
7508
7509   return chunk_size;
7510 }
7511
7512 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7513 {
7514   int chunk_size = 0;
7515
7516   chunk_size += putFile16BitBE(file, level->creation_date.year);
7517   chunk_size += putFile8Bit(file,    level->creation_date.month);
7518   chunk_size += putFile8Bit(file,    level->creation_date.day);
7519
7520   return chunk_size;
7521 }
7522
7523 #if ENABLE_HISTORIC_CHUNKS
7524 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7525 {
7526   int i, x, y;
7527
7528   putFile8Bit(file, level->fieldx);
7529   putFile8Bit(file, level->fieldy);
7530
7531   putFile16BitBE(file, level->time);
7532   putFile16BitBE(file, level->gems_needed);
7533
7534   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7535     putFile8Bit(file, level->name[i]);
7536
7537   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7538     putFile8Bit(file, level->score[i]);
7539
7540   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7541     for (y = 0; y < 3; y++)
7542       for (x = 0; x < 3; x++)
7543         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7544                            level->yamyam_content[i].e[x][y]));
7545   putFile8Bit(file, level->amoeba_speed);
7546   putFile8Bit(file, level->time_magic_wall);
7547   putFile8Bit(file, level->time_wheel);
7548   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7549                      level->amoeba_content));
7550   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7551   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7552   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7553   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7554
7555   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7556
7557   putFile8Bit(file, (level->block_last_field ? 1 : 0));
7558   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7559   putFile32BitBE(file, level->can_move_into_acid_bits);
7560   putFile8Bit(file, level->dont_collide_with_bits);
7561
7562   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7563   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7564
7565   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7566   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7567   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7568
7569   putFile8Bit(file, level->game_engine_type);
7570
7571   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7572 }
7573 #endif
7574
7575 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7576 {
7577   int chunk_size = 0;
7578   int i;
7579
7580   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7581     chunk_size += putFile8Bit(file, level->name[i]);
7582
7583   return chunk_size;
7584 }
7585
7586 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7587 {
7588   int chunk_size = 0;
7589   int i;
7590
7591   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7592     chunk_size += putFile8Bit(file, level->author[i]);
7593
7594   return chunk_size;
7595 }
7596
7597 #if ENABLE_HISTORIC_CHUNKS
7598 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7599 {
7600   int chunk_size = 0;
7601   int x, y;
7602
7603   for (y = 0; y < level->fieldy; y++)
7604     for (x = 0; x < level->fieldx; x++)
7605       if (level->encoding_16bit_field)
7606         chunk_size += putFile16BitBE(file, level->field[x][y]);
7607       else
7608         chunk_size += putFile8Bit(file, level->field[x][y]);
7609
7610   return chunk_size;
7611 }
7612 #endif
7613
7614 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7615 {
7616   int chunk_size = 0;
7617   int x, y;
7618
7619   for (y = 0; y < level->fieldy; y++) 
7620     for (x = 0; x < level->fieldx; x++) 
7621       chunk_size += putFile16BitBE(file, level->field[x][y]);
7622
7623   return chunk_size;
7624 }
7625
7626 #if ENABLE_HISTORIC_CHUNKS
7627 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7628 {
7629   int i, x, y;
7630
7631   putFile8Bit(file, EL_YAMYAM);
7632   putFile8Bit(file, level->num_yamyam_contents);
7633   putFile8Bit(file, 0);
7634   putFile8Bit(file, 0);
7635
7636   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7637     for (y = 0; y < 3; y++)
7638       for (x = 0; x < 3; x++)
7639         if (level->encoding_16bit_field)
7640           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7641         else
7642           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7643 }
7644 #endif
7645
7646 #if ENABLE_HISTORIC_CHUNKS
7647 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7648 {
7649   int i, x, y;
7650   int num_contents, content_xsize, content_ysize;
7651   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7652
7653   if (element == EL_YAMYAM)
7654   {
7655     num_contents = level->num_yamyam_contents;
7656     content_xsize = 3;
7657     content_ysize = 3;
7658
7659     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7660       for (y = 0; y < 3; y++)
7661         for (x = 0; x < 3; x++)
7662           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7663   }
7664   else if (element == EL_BD_AMOEBA)
7665   {
7666     num_contents = 1;
7667     content_xsize = 1;
7668     content_ysize = 1;
7669
7670     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7671       for (y = 0; y < 3; y++)
7672         for (x = 0; x < 3; x++)
7673           content_array[i][x][y] = EL_EMPTY;
7674     content_array[0][0][0] = level->amoeba_content;
7675   }
7676   else
7677   {
7678     // chunk header already written -- write empty chunk data
7679     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7680
7681     Warn("cannot save content for element '%d'", element);
7682
7683     return;
7684   }
7685
7686   putFile16BitBE(file, element);
7687   putFile8Bit(file, num_contents);
7688   putFile8Bit(file, content_xsize);
7689   putFile8Bit(file, content_ysize);
7690
7691   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7692
7693   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7694     for (y = 0; y < 3; y++)
7695       for (x = 0; x < 3; x++)
7696         putFile16BitBE(file, content_array[i][x][y]);
7697 }
7698 #endif
7699
7700 #if ENABLE_HISTORIC_CHUNKS
7701 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7702 {
7703   int envelope_nr = element - EL_ENVELOPE_1;
7704   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7705   int chunk_size = 0;
7706   int i;
7707
7708   chunk_size += putFile16BitBE(file, element);
7709   chunk_size += putFile16BitBE(file, envelope_len);
7710   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7711   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7712
7713   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7714   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7715
7716   for (i = 0; i < envelope_len; i++)
7717     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7718
7719   return chunk_size;
7720 }
7721 #endif
7722
7723 #if ENABLE_HISTORIC_CHUNKS
7724 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7725                            int num_changed_custom_elements)
7726 {
7727   int i, check = 0;
7728
7729   putFile16BitBE(file, num_changed_custom_elements);
7730
7731   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7732   {
7733     int element = EL_CUSTOM_START + i;
7734
7735     struct ElementInfo *ei = &element_info[element];
7736
7737     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7738     {
7739       if (check < num_changed_custom_elements)
7740       {
7741         putFile16BitBE(file, element);
7742         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7743       }
7744
7745       check++;
7746     }
7747   }
7748
7749   if (check != num_changed_custom_elements)     // should not happen
7750     Warn("inconsistent number of custom element properties");
7751 }
7752 #endif
7753
7754 #if ENABLE_HISTORIC_CHUNKS
7755 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7756                            int num_changed_custom_elements)
7757 {
7758   int i, check = 0;
7759
7760   putFile16BitBE(file, num_changed_custom_elements);
7761
7762   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7763   {
7764     int element = EL_CUSTOM_START + i;
7765
7766     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7767     {
7768       if (check < num_changed_custom_elements)
7769       {
7770         putFile16BitBE(file, element);
7771         putFile16BitBE(file, element_info[element].change->target_element);
7772       }
7773
7774       check++;
7775     }
7776   }
7777
7778   if (check != num_changed_custom_elements)     // should not happen
7779     Warn("inconsistent number of custom target elements");
7780 }
7781 #endif
7782
7783 #if ENABLE_HISTORIC_CHUNKS
7784 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7785                            int num_changed_custom_elements)
7786 {
7787   int i, j, x, y, check = 0;
7788
7789   putFile16BitBE(file, num_changed_custom_elements);
7790
7791   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7792   {
7793     int element = EL_CUSTOM_START + i;
7794     struct ElementInfo *ei = &element_info[element];
7795
7796     if (ei->modified_settings)
7797     {
7798       if (check < num_changed_custom_elements)
7799       {
7800         putFile16BitBE(file, element);
7801
7802         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7803           putFile8Bit(file, ei->description[j]);
7804
7805         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7806
7807         // some free bytes for future properties and padding
7808         WriteUnusedBytesToFile(file, 7);
7809
7810         putFile8Bit(file, ei->use_gfx_element);
7811         putFile16BitBE(file, ei->gfx_element_initial);
7812
7813         putFile8Bit(file, ei->collect_score_initial);
7814         putFile8Bit(file, ei->collect_count_initial);
7815
7816         putFile16BitBE(file, ei->push_delay_fixed);
7817         putFile16BitBE(file, ei->push_delay_random);
7818         putFile16BitBE(file, ei->move_delay_fixed);
7819         putFile16BitBE(file, ei->move_delay_random);
7820
7821         putFile16BitBE(file, ei->move_pattern);
7822         putFile8Bit(file, ei->move_direction_initial);
7823         putFile8Bit(file, ei->move_stepsize);
7824
7825         for (y = 0; y < 3; y++)
7826           for (x = 0; x < 3; x++)
7827             putFile16BitBE(file, ei->content.e[x][y]);
7828
7829         putFile32BitBE(file, ei->change->events);
7830
7831         putFile16BitBE(file, ei->change->target_element);
7832
7833         putFile16BitBE(file, ei->change->delay_fixed);
7834         putFile16BitBE(file, ei->change->delay_random);
7835         putFile16BitBE(file, ei->change->delay_frames);
7836
7837         putFile16BitBE(file, ei->change->initial_trigger_element);
7838
7839         putFile8Bit(file, ei->change->explode);
7840         putFile8Bit(file, ei->change->use_target_content);
7841         putFile8Bit(file, ei->change->only_if_complete);
7842         putFile8Bit(file, ei->change->use_random_replace);
7843
7844         putFile8Bit(file, ei->change->random_percentage);
7845         putFile8Bit(file, ei->change->replace_when);
7846
7847         for (y = 0; y < 3; y++)
7848           for (x = 0; x < 3; x++)
7849             putFile16BitBE(file, ei->change->content.e[x][y]);
7850
7851         putFile8Bit(file, ei->slippery_type);
7852
7853         // some free bytes for future properties and padding
7854         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7855       }
7856
7857       check++;
7858     }
7859   }
7860
7861   if (check != num_changed_custom_elements)     // should not happen
7862     Warn("inconsistent number of custom element properties");
7863 }
7864 #endif
7865
7866 #if ENABLE_HISTORIC_CHUNKS
7867 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7868 {
7869   struct ElementInfo *ei = &element_info[element];
7870   int i, j, x, y;
7871
7872   // ---------- custom element base property values (96 bytes) ----------------
7873
7874   putFile16BitBE(file, element);
7875
7876   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7877     putFile8Bit(file, ei->description[i]);
7878
7879   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7880
7881   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
7882
7883   putFile8Bit(file, ei->num_change_pages);
7884
7885   putFile16BitBE(file, ei->ce_value_fixed_initial);
7886   putFile16BitBE(file, ei->ce_value_random_initial);
7887   putFile8Bit(file, ei->use_last_ce_value);
7888
7889   putFile8Bit(file, ei->use_gfx_element);
7890   putFile16BitBE(file, ei->gfx_element_initial);
7891
7892   putFile8Bit(file, ei->collect_score_initial);
7893   putFile8Bit(file, ei->collect_count_initial);
7894
7895   putFile8Bit(file, ei->drop_delay_fixed);
7896   putFile8Bit(file, ei->push_delay_fixed);
7897   putFile8Bit(file, ei->drop_delay_random);
7898   putFile8Bit(file, ei->push_delay_random);
7899   putFile16BitBE(file, ei->move_delay_fixed);
7900   putFile16BitBE(file, ei->move_delay_random);
7901
7902   // bits 0 - 15 of "move_pattern" ...
7903   putFile16BitBE(file, ei->move_pattern & 0xffff);
7904   putFile8Bit(file, ei->move_direction_initial);
7905   putFile8Bit(file, ei->move_stepsize);
7906
7907   putFile8Bit(file, ei->slippery_type);
7908
7909   for (y = 0; y < 3; y++)
7910     for (x = 0; x < 3; x++)
7911       putFile16BitBE(file, ei->content.e[x][y]);
7912
7913   putFile16BitBE(file, ei->move_enter_element);
7914   putFile16BitBE(file, ei->move_leave_element);
7915   putFile8Bit(file, ei->move_leave_type);
7916
7917   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7918   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7919
7920   putFile8Bit(file, ei->access_direction);
7921
7922   putFile8Bit(file, ei->explosion_delay);
7923   putFile8Bit(file, ei->ignition_delay);
7924   putFile8Bit(file, ei->explosion_type);
7925
7926   // some free bytes for future custom property values and padding
7927   WriteUnusedBytesToFile(file, 1);
7928
7929   // ---------- change page property values (48 bytes) ------------------------
7930
7931   for (i = 0; i < ei->num_change_pages; i++)
7932   {
7933     struct ElementChangeInfo *change = &ei->change_page[i];
7934     unsigned int event_bits;
7935
7936     // bits 0 - 31 of "has_event[]" ...
7937     event_bits = 0;
7938     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7939       if (change->has_event[j])
7940         event_bits |= (1u << j);
7941     putFile32BitBE(file, event_bits);
7942
7943     putFile16BitBE(file, change->target_element);
7944
7945     putFile16BitBE(file, change->delay_fixed);
7946     putFile16BitBE(file, change->delay_random);
7947     putFile16BitBE(file, change->delay_frames);
7948
7949     putFile16BitBE(file, change->initial_trigger_element);
7950
7951     putFile8Bit(file, change->explode);
7952     putFile8Bit(file, change->use_target_content);
7953     putFile8Bit(file, change->only_if_complete);
7954     putFile8Bit(file, change->use_random_replace);
7955
7956     putFile8Bit(file, change->random_percentage);
7957     putFile8Bit(file, change->replace_when);
7958
7959     for (y = 0; y < 3; y++)
7960       for (x = 0; x < 3; x++)
7961         putFile16BitBE(file, change->target_content.e[x][y]);
7962
7963     putFile8Bit(file, change->can_change);
7964
7965     putFile8Bit(file, change->trigger_side);
7966
7967     putFile8Bit(file, change->trigger_player);
7968     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7969                        log_2(change->trigger_page)));
7970
7971     putFile8Bit(file, change->has_action);
7972     putFile8Bit(file, change->action_type);
7973     putFile8Bit(file, change->action_mode);
7974     putFile16BitBE(file, change->action_arg);
7975
7976     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7977     event_bits = 0;
7978     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7979       if (change->has_event[j])
7980         event_bits |= (1u << (j - 32));
7981     putFile8Bit(file, event_bits);
7982   }
7983 }
7984 #endif
7985
7986 #if ENABLE_HISTORIC_CHUNKS
7987 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7988 {
7989   struct ElementInfo *ei = &element_info[element];
7990   struct ElementGroupInfo *group = ei->group;
7991   int i;
7992
7993   putFile16BitBE(file, element);
7994
7995   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7996     putFile8Bit(file, ei->description[i]);
7997
7998   putFile8Bit(file, group->num_elements);
7999
8000   putFile8Bit(file, ei->use_gfx_element);
8001   putFile16BitBE(file, ei->gfx_element_initial);
8002
8003   putFile8Bit(file, group->choice_mode);
8004
8005   // some free bytes for future values and padding
8006   WriteUnusedBytesToFile(file, 3);
8007
8008   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8009     putFile16BitBE(file, group->element[i]);
8010 }
8011 #endif
8012
8013 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8014                                 boolean write_element)
8015 {
8016   int save_type = entry->save_type;
8017   int data_type = entry->data_type;
8018   int conf_type = entry->conf_type;
8019   int byte_mask = conf_type & CONF_MASK_BYTES;
8020   int element = entry->element;
8021   int default_value = entry->default_value;
8022   int num_bytes = 0;
8023   boolean modified = FALSE;
8024
8025   if (byte_mask != CONF_MASK_MULTI_BYTES)
8026   {
8027     void *value_ptr = entry->value;
8028     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8029                  *(int *)value_ptr);
8030
8031     // check if any settings have been modified before saving them
8032     if (value != default_value)
8033       modified = TRUE;
8034
8035     // do not save if explicitly told or if unmodified default settings
8036     if ((save_type == SAVE_CONF_NEVER) ||
8037         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8038       return 0;
8039
8040     if (write_element)
8041       num_bytes += putFile16BitBE(file, element);
8042
8043     num_bytes += putFile8Bit(file, conf_type);
8044     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8045                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8046                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8047                   0);
8048   }
8049   else if (data_type == TYPE_STRING)
8050   {
8051     char *default_string = entry->default_string;
8052     char *string = (char *)(entry->value);
8053     int string_length = strlen(string);
8054     int i;
8055
8056     // check if any settings have been modified before saving them
8057     if (!strEqual(string, default_string))
8058       modified = TRUE;
8059
8060     // do not save if explicitly told or if unmodified default settings
8061     if ((save_type == SAVE_CONF_NEVER) ||
8062         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8063       return 0;
8064
8065     if (write_element)
8066       num_bytes += putFile16BitBE(file, element);
8067
8068     num_bytes += putFile8Bit(file, conf_type);
8069     num_bytes += putFile16BitBE(file, string_length);
8070
8071     for (i = 0; i < string_length; i++)
8072       num_bytes += putFile8Bit(file, string[i]);
8073   }
8074   else if (data_type == TYPE_ELEMENT_LIST)
8075   {
8076     int *element_array = (int *)(entry->value);
8077     int num_elements = *(int *)(entry->num_entities);
8078     int i;
8079
8080     // check if any settings have been modified before saving them
8081     for (i = 0; i < num_elements; i++)
8082       if (element_array[i] != default_value)
8083         modified = TRUE;
8084
8085     // do not save if explicitly told or if unmodified default settings
8086     if ((save_type == SAVE_CONF_NEVER) ||
8087         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8088       return 0;
8089
8090     if (write_element)
8091       num_bytes += putFile16BitBE(file, element);
8092
8093     num_bytes += putFile8Bit(file, conf_type);
8094     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8095
8096     for (i = 0; i < num_elements; i++)
8097       num_bytes += putFile16BitBE(file, element_array[i]);
8098   }
8099   else if (data_type == TYPE_CONTENT_LIST)
8100   {
8101     struct Content *content = (struct Content *)(entry->value);
8102     int num_contents = *(int *)(entry->num_entities);
8103     int i, x, y;
8104
8105     // check if any settings have been modified before saving them
8106     for (i = 0; i < num_contents; i++)
8107       for (y = 0; y < 3; y++)
8108         for (x = 0; x < 3; x++)
8109           if (content[i].e[x][y] != default_value)
8110             modified = TRUE;
8111
8112     // do not save if explicitly told or if unmodified default settings
8113     if ((save_type == SAVE_CONF_NEVER) ||
8114         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8115       return 0;
8116
8117     if (write_element)
8118       num_bytes += putFile16BitBE(file, element);
8119
8120     num_bytes += putFile8Bit(file, conf_type);
8121     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8122
8123     for (i = 0; i < num_contents; i++)
8124       for (y = 0; y < 3; y++)
8125         for (x = 0; x < 3; x++)
8126           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8127   }
8128
8129   return num_bytes;
8130 }
8131
8132 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8133 {
8134   int chunk_size = 0;
8135   int i;
8136
8137   li = *level;          // copy level data into temporary buffer
8138
8139   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8140     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8141
8142   return chunk_size;
8143 }
8144
8145 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8146 {
8147   int chunk_size = 0;
8148   int i;
8149
8150   li = *level;          // copy level data into temporary buffer
8151
8152   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8153     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8154
8155   return chunk_size;
8156 }
8157
8158 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8159 {
8160   int envelope_nr = element - EL_ENVELOPE_1;
8161   int chunk_size = 0;
8162   int i;
8163
8164   chunk_size += putFile16BitBE(file, element);
8165
8166   // copy envelope data into temporary buffer
8167   xx_envelope = level->envelope[envelope_nr];
8168
8169   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8170     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8171
8172   return chunk_size;
8173 }
8174
8175 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8176 {
8177   struct ElementInfo *ei = &element_info[element];
8178   int chunk_size = 0;
8179   int i, j;
8180
8181   chunk_size += putFile16BitBE(file, element);
8182
8183   xx_ei = *ei;          // copy element data into temporary buffer
8184
8185   // set default description string for this specific element
8186   strcpy(xx_default_description, getDefaultElementDescription(ei));
8187
8188   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8189     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8190
8191   for (i = 0; i < ei->num_change_pages; i++)
8192   {
8193     struct ElementChangeInfo *change = &ei->change_page[i];
8194
8195     xx_current_change_page = i;
8196
8197     xx_change = *change;        // copy change data into temporary buffer
8198
8199     resetEventBits();
8200     setEventBitsFromEventFlags(change);
8201
8202     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8203       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8204                                          FALSE);
8205   }
8206
8207   return chunk_size;
8208 }
8209
8210 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8211 {
8212   struct ElementInfo *ei = &element_info[element];
8213   struct ElementGroupInfo *group = ei->group;
8214   int chunk_size = 0;
8215   int i;
8216
8217   chunk_size += putFile16BitBE(file, element);
8218
8219   xx_ei = *ei;          // copy element data into temporary buffer
8220   xx_group = *group;    // copy group data into temporary buffer
8221
8222   // set default description string for this specific element
8223   strcpy(xx_default_description, getDefaultElementDescription(ei));
8224
8225   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8226     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8227
8228   return chunk_size;
8229 }
8230
8231 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8232 {
8233   struct ElementInfo *ei = &element_info[element];
8234   int chunk_size = 0;
8235   int i;
8236
8237   chunk_size += putFile16BitBE(file, element);
8238
8239   xx_ei = *ei;          // copy element data into temporary buffer
8240
8241   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8242     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8243
8244   return chunk_size;
8245 }
8246
8247 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8248                                   boolean save_as_template)
8249 {
8250   int chunk_size;
8251   int i;
8252   FILE *file;
8253
8254   if (!(file = fopen(filename, MODE_WRITE)))
8255   {
8256     Warn("cannot save level file '%s'", filename);
8257
8258     return;
8259   }
8260
8261   level->file_version = FILE_VERSION_ACTUAL;
8262   level->game_version = GAME_VERSION_ACTUAL;
8263
8264   level->creation_date = getCurrentDate();
8265
8266   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8267   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8268
8269   chunk_size = SaveLevel_VERS(NULL, level);
8270   putFileChunkBE(file, "VERS", chunk_size);
8271   SaveLevel_VERS(file, level);
8272
8273   chunk_size = SaveLevel_DATE(NULL, level);
8274   putFileChunkBE(file, "DATE", chunk_size);
8275   SaveLevel_DATE(file, level);
8276
8277   chunk_size = SaveLevel_NAME(NULL, level);
8278   putFileChunkBE(file, "NAME", chunk_size);
8279   SaveLevel_NAME(file, level);
8280
8281   chunk_size = SaveLevel_AUTH(NULL, level);
8282   putFileChunkBE(file, "AUTH", chunk_size);
8283   SaveLevel_AUTH(file, level);
8284
8285   chunk_size = SaveLevel_INFO(NULL, level);
8286   putFileChunkBE(file, "INFO", chunk_size);
8287   SaveLevel_INFO(file, level);
8288
8289   chunk_size = SaveLevel_BODY(NULL, level);
8290   putFileChunkBE(file, "BODY", chunk_size);
8291   SaveLevel_BODY(file, level);
8292
8293   chunk_size = SaveLevel_ELEM(NULL, level);
8294   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8295   {
8296     putFileChunkBE(file, "ELEM", chunk_size);
8297     SaveLevel_ELEM(file, level);
8298   }
8299
8300   for (i = 0; i < NUM_ENVELOPES; i++)
8301   {
8302     int element = EL_ENVELOPE_1 + i;
8303
8304     chunk_size = SaveLevel_NOTE(NULL, level, element);
8305     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8306     {
8307       putFileChunkBE(file, "NOTE", chunk_size);
8308       SaveLevel_NOTE(file, level, element);
8309     }
8310   }
8311
8312   // if not using template level, check for non-default custom/group elements
8313   if (!level->use_custom_template || save_as_template)
8314   {
8315     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8316     {
8317       int element = EL_CUSTOM_START + i;
8318
8319       chunk_size = SaveLevel_CUSX(NULL, level, element);
8320       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8321       {
8322         putFileChunkBE(file, "CUSX", chunk_size);
8323         SaveLevel_CUSX(file, level, element);
8324       }
8325     }
8326
8327     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8328     {
8329       int element = EL_GROUP_START + i;
8330
8331       chunk_size = SaveLevel_GRPX(NULL, level, element);
8332       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8333       {
8334         putFileChunkBE(file, "GRPX", chunk_size);
8335         SaveLevel_GRPX(file, level, element);
8336       }
8337     }
8338
8339     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8340     {
8341       int element = GET_EMPTY_ELEMENT(i);
8342
8343       chunk_size = SaveLevel_EMPX(NULL, level, element);
8344       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8345       {
8346         putFileChunkBE(file, "EMPX", chunk_size);
8347         SaveLevel_EMPX(file, level, element);
8348       }
8349     }
8350   }
8351
8352   fclose(file);
8353
8354   SetFilePermissions(filename, PERMS_PRIVATE);
8355 }
8356
8357 void SaveLevel(int nr)
8358 {
8359   char *filename = getDefaultLevelFilename(nr);
8360
8361   SaveLevelFromFilename(&level, filename, FALSE);
8362 }
8363
8364 void SaveLevelTemplate(void)
8365 {
8366   char *filename = getLocalLevelTemplateFilename();
8367
8368   SaveLevelFromFilename(&level, filename, TRUE);
8369 }
8370
8371 boolean SaveLevelChecked(int nr)
8372 {
8373   char *filename = getDefaultLevelFilename(nr);
8374   boolean new_level = !fileExists(filename);
8375   boolean level_saved = FALSE;
8376
8377   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8378   {
8379     SaveLevel(nr);
8380
8381     if (new_level)
8382       Request("Level saved!", REQ_CONFIRM);
8383
8384     level_saved = TRUE;
8385   }
8386
8387   return level_saved;
8388 }
8389
8390 void DumpLevel(struct LevelInfo *level)
8391 {
8392   if (level->no_level_file || level->no_valid_file)
8393   {
8394     Warn("cannot dump -- no valid level file found");
8395
8396     return;
8397   }
8398
8399   PrintLine("-", 79);
8400   Print("Level xxx (file version %08d, game version %08d)\n",
8401         level->file_version, level->game_version);
8402   PrintLine("-", 79);
8403
8404   Print("Level author: '%s'\n", level->author);
8405   Print("Level title:  '%s'\n", level->name);
8406   Print("\n");
8407   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8408   Print("\n");
8409   Print("Level time:  %d seconds\n", level->time);
8410   Print("Gems needed: %d\n", level->gems_needed);
8411   Print("\n");
8412   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8413   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8414   Print("Time for light:      %d seconds\n", level->time_light);
8415   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8416   Print("\n");
8417   Print("Amoeba speed: %d\n", level->amoeba_speed);
8418   Print("\n");
8419
8420   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8421   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8422   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8423   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8424   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8425   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8426
8427   if (options.debug)
8428   {
8429     int i, j;
8430
8431     for (i = 0; i < NUM_ENVELOPES; i++)
8432     {
8433       char *text = level->envelope[i].text;
8434       int text_len = strlen(text);
8435       boolean has_text = FALSE;
8436
8437       for (j = 0; j < text_len; j++)
8438         if (text[j] != ' ' && text[j] != '\n')
8439           has_text = TRUE;
8440
8441       if (has_text)
8442       {
8443         Print("\n");
8444         Print("Envelope %d:\n'%s'\n", i + 1, text);
8445       }
8446     }
8447   }
8448
8449   PrintLine("-", 79);
8450 }
8451
8452 void DumpLevels(void)
8453 {
8454   static LevelDirTree *dumplevel_leveldir = NULL;
8455
8456   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8457                                                  global.dumplevel_leveldir);
8458
8459   if (dumplevel_leveldir == NULL)
8460     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8461
8462   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8463       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8464     Fail("no such level number: %d", global.dumplevel_level_nr);
8465
8466   leveldir_current = dumplevel_leveldir;
8467
8468   LoadLevel(global.dumplevel_level_nr);
8469   DumpLevel(&level);
8470
8471   CloseAllAndExit(0);
8472 }
8473
8474
8475 // ============================================================================
8476 // tape file functions
8477 // ============================================================================
8478
8479 static void setTapeInfoToDefaults(void)
8480 {
8481   int i;
8482
8483   // always start with reliable default values (empty tape)
8484   TapeErase();
8485
8486   // default values (also for pre-1.2 tapes) with only the first player
8487   tape.player_participates[0] = TRUE;
8488   for (i = 1; i < MAX_PLAYERS; i++)
8489     tape.player_participates[i] = FALSE;
8490
8491   // at least one (default: the first) player participates in every tape
8492   tape.num_participating_players = 1;
8493
8494   tape.property_bits = TAPE_PROPERTY_NONE;
8495
8496   tape.level_nr = level_nr;
8497   tape.counter = 0;
8498   tape.changed = FALSE;
8499   tape.solved = FALSE;
8500
8501   tape.recording = FALSE;
8502   tape.playing = FALSE;
8503   tape.pausing = FALSE;
8504
8505   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8506   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8507
8508   tape.no_info_chunk = TRUE;
8509   tape.no_valid_file = FALSE;
8510 }
8511
8512 static int getTapePosSize(struct TapeInfo *tape)
8513 {
8514   int tape_pos_size = 0;
8515
8516   if (tape->use_key_actions)
8517     tape_pos_size += tape->num_participating_players;
8518
8519   if (tape->use_mouse_actions)
8520     tape_pos_size += 3;         // x and y position and mouse button mask
8521
8522   tape_pos_size += 1;           // tape action delay value
8523
8524   return tape_pos_size;
8525 }
8526
8527 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8528 {
8529   tape->use_key_actions = FALSE;
8530   tape->use_mouse_actions = FALSE;
8531
8532   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8533     tape->use_key_actions = TRUE;
8534
8535   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8536     tape->use_mouse_actions = TRUE;
8537 }
8538
8539 static int getTapeActionValue(struct TapeInfo *tape)
8540 {
8541   return (tape->use_key_actions &&
8542           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8543           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
8544           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8545           TAPE_ACTIONS_DEFAULT);
8546 }
8547
8548 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8549 {
8550   tape->file_version = getFileVersion(file);
8551   tape->game_version = getFileVersion(file);
8552
8553   return chunk_size;
8554 }
8555
8556 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8557 {
8558   int i;
8559
8560   tape->random_seed = getFile32BitBE(file);
8561   tape->date        = getFile32BitBE(file);
8562   tape->length      = getFile32BitBE(file);
8563
8564   // read header fields that are new since version 1.2
8565   if (tape->file_version >= FILE_VERSION_1_2)
8566   {
8567     byte store_participating_players = getFile8Bit(file);
8568     int engine_version;
8569
8570     // since version 1.2, tapes store which players participate in the tape
8571     tape->num_participating_players = 0;
8572     for (i = 0; i < MAX_PLAYERS; i++)
8573     {
8574       tape->player_participates[i] = FALSE;
8575
8576       if (store_participating_players & (1 << i))
8577       {
8578         tape->player_participates[i] = TRUE;
8579         tape->num_participating_players++;
8580       }
8581     }
8582
8583     setTapeActionFlags(tape, getFile8Bit(file));
8584
8585     tape->property_bits = getFile8Bit(file);
8586     tape->solved = getFile8Bit(file);
8587
8588     engine_version = getFileVersion(file);
8589     if (engine_version > 0)
8590       tape->engine_version = engine_version;
8591     else
8592       tape->engine_version = tape->game_version;
8593   }
8594
8595   return chunk_size;
8596 }
8597
8598 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8599 {
8600   tape->scr_fieldx = getFile8Bit(file);
8601   tape->scr_fieldy = getFile8Bit(file);
8602
8603   return chunk_size;
8604 }
8605
8606 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8607 {
8608   char *level_identifier = NULL;
8609   int level_identifier_size;
8610   int i;
8611
8612   tape->no_info_chunk = FALSE;
8613
8614   level_identifier_size = getFile16BitBE(file);
8615
8616   level_identifier = checked_malloc(level_identifier_size);
8617
8618   for (i = 0; i < level_identifier_size; i++)
8619     level_identifier[i] = getFile8Bit(file);
8620
8621   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8622   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8623
8624   checked_free(level_identifier);
8625
8626   tape->level_nr = getFile16BitBE(file);
8627
8628   chunk_size = 2 + level_identifier_size + 2;
8629
8630   return chunk_size;
8631 }
8632
8633 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8634 {
8635   int i, j;
8636   int tape_pos_size = getTapePosSize(tape);
8637   int chunk_size_expected = tape_pos_size * tape->length;
8638
8639   if (chunk_size_expected != chunk_size)
8640   {
8641     ReadUnusedBytesFromFile(file, chunk_size);
8642     return chunk_size_expected;
8643   }
8644
8645   for (i = 0; i < tape->length; i++)
8646   {
8647     if (i >= MAX_TAPE_LEN)
8648     {
8649       Warn("tape truncated -- size exceeds maximum tape size %d",
8650             MAX_TAPE_LEN);
8651
8652       // tape too large; read and ignore remaining tape data from this chunk
8653       for (;i < tape->length; i++)
8654         ReadUnusedBytesFromFile(file, tape_pos_size);
8655
8656       break;
8657     }
8658
8659     if (tape->use_key_actions)
8660     {
8661       for (j = 0; j < MAX_PLAYERS; j++)
8662       {
8663         tape->pos[i].action[j] = MV_NONE;
8664
8665         if (tape->player_participates[j])
8666           tape->pos[i].action[j] = getFile8Bit(file);
8667       }
8668     }
8669
8670     if (tape->use_mouse_actions)
8671     {
8672       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
8673       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
8674       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8675     }
8676
8677     tape->pos[i].delay = getFile8Bit(file);
8678
8679     if (tape->file_version == FILE_VERSION_1_0)
8680     {
8681       // eliminate possible diagonal moves in old tapes
8682       // this is only for backward compatibility
8683
8684       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8685       byte action = tape->pos[i].action[0];
8686       int k, num_moves = 0;
8687
8688       for (k = 0; k < 4; k++)
8689       {
8690         if (action & joy_dir[k])
8691         {
8692           tape->pos[i + num_moves].action[0] = joy_dir[k];
8693           if (num_moves > 0)
8694             tape->pos[i + num_moves].delay = 0;
8695           num_moves++;
8696         }
8697       }
8698
8699       if (num_moves > 1)
8700       {
8701         num_moves--;
8702         i += num_moves;
8703         tape->length += num_moves;
8704       }
8705     }
8706     else if (tape->file_version < FILE_VERSION_2_0)
8707     {
8708       // convert pre-2.0 tapes to new tape format
8709
8710       if (tape->pos[i].delay > 1)
8711       {
8712         // action part
8713         tape->pos[i + 1] = tape->pos[i];
8714         tape->pos[i + 1].delay = 1;
8715
8716         // delay part
8717         for (j = 0; j < MAX_PLAYERS; j++)
8718           tape->pos[i].action[j] = MV_NONE;
8719         tape->pos[i].delay--;
8720
8721         i++;
8722         tape->length++;
8723       }
8724     }
8725
8726     if (checkEndOfFile(file))
8727       break;
8728   }
8729
8730   if (i != tape->length)
8731     chunk_size = tape_pos_size * i;
8732
8733   return chunk_size;
8734 }
8735
8736 static void LoadTape_SokobanSolution(char *filename)
8737 {
8738   File *file;
8739   int move_delay = TILESIZE / level.initial_player_stepsize[0];
8740
8741   if (!(file = openFile(filename, MODE_READ)))
8742   {
8743     tape.no_valid_file = TRUE;
8744
8745     return;
8746   }
8747
8748   while (!checkEndOfFile(file))
8749   {
8750     unsigned char c = getByteFromFile(file);
8751
8752     if (checkEndOfFile(file))
8753       break;
8754
8755     switch (c)
8756     {
8757       case 'u':
8758       case 'U':
8759         tape.pos[tape.length].action[0] = MV_UP;
8760         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8761         tape.length++;
8762         break;
8763
8764       case 'd':
8765       case 'D':
8766         tape.pos[tape.length].action[0] = MV_DOWN;
8767         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8768         tape.length++;
8769         break;
8770
8771       case 'l':
8772       case 'L':
8773         tape.pos[tape.length].action[0] = MV_LEFT;
8774         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8775         tape.length++;
8776         break;
8777
8778       case 'r':
8779       case 'R':
8780         tape.pos[tape.length].action[0] = MV_RIGHT;
8781         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8782         tape.length++;
8783         break;
8784
8785       case '\n':
8786       case '\r':
8787       case '\t':
8788       case ' ':
8789         // ignore white-space characters
8790         break;
8791
8792       default:
8793         tape.no_valid_file = TRUE;
8794
8795         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8796
8797         break;
8798     }
8799   }
8800
8801   closeFile(file);
8802
8803   if (tape.no_valid_file)
8804     return;
8805
8806   tape.length_frames  = GetTapeLengthFrames();
8807   tape.length_seconds = GetTapeLengthSeconds();
8808 }
8809
8810 void LoadTapeFromFilename(char *filename)
8811 {
8812   char cookie[MAX_LINE_LEN];
8813   char chunk_name[CHUNK_ID_LEN + 1];
8814   File *file;
8815   int chunk_size;
8816
8817   // always start with reliable default values
8818   setTapeInfoToDefaults();
8819
8820   if (strSuffix(filename, ".sln"))
8821   {
8822     LoadTape_SokobanSolution(filename);
8823
8824     return;
8825   }
8826
8827   if (!(file = openFile(filename, MODE_READ)))
8828   {
8829     tape.no_valid_file = TRUE;
8830
8831     return;
8832   }
8833
8834   getFileChunkBE(file, chunk_name, NULL);
8835   if (strEqual(chunk_name, "RND1"))
8836   {
8837     getFile32BitBE(file);               // not used
8838
8839     getFileChunkBE(file, chunk_name, NULL);
8840     if (!strEqual(chunk_name, "TAPE"))
8841     {
8842       tape.no_valid_file = TRUE;
8843
8844       Warn("unknown format of tape file '%s'", filename);
8845
8846       closeFile(file);
8847
8848       return;
8849     }
8850   }
8851   else  // check for pre-2.0 file format with cookie string
8852   {
8853     strcpy(cookie, chunk_name);
8854     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8855       cookie[4] = '\0';
8856     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8857       cookie[strlen(cookie) - 1] = '\0';
8858
8859     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8860     {
8861       tape.no_valid_file = TRUE;
8862
8863       Warn("unknown format of tape file '%s'", filename);
8864
8865       closeFile(file);
8866
8867       return;
8868     }
8869
8870     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8871     {
8872       tape.no_valid_file = TRUE;
8873
8874       Warn("unsupported version of tape file '%s'", filename);
8875
8876       closeFile(file);
8877
8878       return;
8879     }
8880
8881     // pre-2.0 tape files have no game version, so use file version here
8882     tape.game_version = tape.file_version;
8883   }
8884
8885   if (tape.file_version < FILE_VERSION_1_2)
8886   {
8887     // tape files from versions before 1.2.0 without chunk structure
8888     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8889     LoadTape_BODY(file, 2 * tape.length,      &tape);
8890   }
8891   else
8892   {
8893     static struct
8894     {
8895       char *name;
8896       int size;
8897       int (*loader)(File *, int, struct TapeInfo *);
8898     }
8899     chunk_info[] =
8900     {
8901       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
8902       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
8903       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
8904       { "INFO", -1,                     LoadTape_INFO },
8905       { "BODY", -1,                     LoadTape_BODY },
8906       {  NULL,  0,                      NULL }
8907     };
8908
8909     while (getFileChunkBE(file, chunk_name, &chunk_size))
8910     {
8911       int i = 0;
8912
8913       while (chunk_info[i].name != NULL &&
8914              !strEqual(chunk_name, chunk_info[i].name))
8915         i++;
8916
8917       if (chunk_info[i].name == NULL)
8918       {
8919         Warn("unknown chunk '%s' in tape file '%s'",
8920               chunk_name, filename);
8921
8922         ReadUnusedBytesFromFile(file, chunk_size);
8923       }
8924       else if (chunk_info[i].size != -1 &&
8925                chunk_info[i].size != chunk_size)
8926       {
8927         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8928               chunk_size, chunk_name, filename);
8929
8930         ReadUnusedBytesFromFile(file, chunk_size);
8931       }
8932       else
8933       {
8934         // call function to load this tape chunk
8935         int chunk_size_expected =
8936           (chunk_info[i].loader)(file, chunk_size, &tape);
8937
8938         // the size of some chunks cannot be checked before reading other
8939         // chunks first (like "HEAD" and "BODY") that contain some header
8940         // information, so check them here
8941         if (chunk_size_expected != chunk_size)
8942         {
8943           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8944                 chunk_size, chunk_name, filename);
8945         }
8946       }
8947     }
8948   }
8949
8950   closeFile(file);
8951
8952   tape.length_frames  = GetTapeLengthFrames();
8953   tape.length_seconds = GetTapeLengthSeconds();
8954
8955 #if 0
8956   Debug("files:LoadTapeFromFilename", "tape file version: %d",
8957         tape.file_version);
8958   Debug("files:LoadTapeFromFilename", "tape game version: %d",
8959         tape.game_version);
8960   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8961         tape.engine_version);
8962 #endif
8963 }
8964
8965 void LoadTape(int nr)
8966 {
8967   char *filename = getTapeFilename(nr);
8968
8969   LoadTapeFromFilename(filename);
8970 }
8971
8972 void LoadSolutionTape(int nr)
8973 {
8974   char *filename = getSolutionTapeFilename(nr);
8975
8976   LoadTapeFromFilename(filename);
8977
8978   if (TAPE_IS_EMPTY(tape))
8979   {
8980     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8981         level.native_bd_level->replay != NULL)
8982       CopyNativeTape_BD_to_RND(&level);
8983     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8984         level.native_sp_level->demo.is_available)
8985       CopyNativeTape_SP_to_RND(&level);
8986   }
8987 }
8988
8989 void LoadScoreTape(char *score_tape_basename, int nr)
8990 {
8991   char *filename = getScoreTapeFilename(score_tape_basename, nr);
8992
8993   LoadTapeFromFilename(filename);
8994 }
8995
8996 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8997 {
8998   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8999
9000   LoadTapeFromFilename(filename);
9001 }
9002
9003 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9004 {
9005   // chunk required for team mode tapes with non-default screen size
9006   return (tape->num_participating_players > 1 &&
9007           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9008            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9009 }
9010
9011 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9012 {
9013   putFileVersion(file, tape->file_version);
9014   putFileVersion(file, tape->game_version);
9015 }
9016
9017 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9018 {
9019   int i;
9020   byte store_participating_players = 0;
9021
9022   // set bits for participating players for compact storage
9023   for (i = 0; i < MAX_PLAYERS; i++)
9024     if (tape->player_participates[i])
9025       store_participating_players |= (1 << i);
9026
9027   putFile32BitBE(file, tape->random_seed);
9028   putFile32BitBE(file, tape->date);
9029   putFile32BitBE(file, tape->length);
9030
9031   putFile8Bit(file, store_participating_players);
9032
9033   putFile8Bit(file, getTapeActionValue(tape));
9034
9035   putFile8Bit(file, tape->property_bits);
9036   putFile8Bit(file, tape->solved);
9037
9038   putFileVersion(file, tape->engine_version);
9039 }
9040
9041 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9042 {
9043   putFile8Bit(file, tape->scr_fieldx);
9044   putFile8Bit(file, tape->scr_fieldy);
9045 }
9046
9047 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9048 {
9049   int level_identifier_size = strlen(tape->level_identifier) + 1;
9050   int i;
9051
9052   putFile16BitBE(file, level_identifier_size);
9053
9054   for (i = 0; i < level_identifier_size; i++)
9055     putFile8Bit(file, tape->level_identifier[i]);
9056
9057   putFile16BitBE(file, tape->level_nr);
9058 }
9059
9060 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9061 {
9062   int i, j;
9063
9064   for (i = 0; i < tape->length; i++)
9065   {
9066     if (tape->use_key_actions)
9067     {
9068       for (j = 0; j < MAX_PLAYERS; j++)
9069         if (tape->player_participates[j])
9070           putFile8Bit(file, tape->pos[i].action[j]);
9071     }
9072
9073     if (tape->use_mouse_actions)
9074     {
9075       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9076       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9077       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9078     }
9079
9080     putFile8Bit(file, tape->pos[i].delay);
9081   }
9082 }
9083
9084 void SaveTapeToFilename(char *filename)
9085 {
9086   FILE *file;
9087   int tape_pos_size;
9088   int info_chunk_size;
9089   int body_chunk_size;
9090
9091   if (!(file = fopen(filename, MODE_WRITE)))
9092   {
9093     Warn("cannot save level recording file '%s'", filename);
9094
9095     return;
9096   }
9097
9098   tape_pos_size = getTapePosSize(&tape);
9099
9100   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9101   body_chunk_size = tape_pos_size * tape.length;
9102
9103   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9104   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9105
9106   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9107   SaveTape_VERS(file, &tape);
9108
9109   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9110   SaveTape_HEAD(file, &tape);
9111
9112   if (checkSaveTape_SCRN(&tape))
9113   {
9114     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9115     SaveTape_SCRN(file, &tape);
9116   }
9117
9118   putFileChunkBE(file, "INFO", info_chunk_size);
9119   SaveTape_INFO(file, &tape);
9120
9121   putFileChunkBE(file, "BODY", body_chunk_size);
9122   SaveTape_BODY(file, &tape);
9123
9124   fclose(file);
9125
9126   SetFilePermissions(filename, PERMS_PRIVATE);
9127 }
9128
9129 static void SaveTapeExt(char *filename)
9130 {
9131   int i;
9132
9133   tape.file_version = FILE_VERSION_ACTUAL;
9134   tape.game_version = GAME_VERSION_ACTUAL;
9135
9136   tape.num_participating_players = 0;
9137
9138   // count number of participating players
9139   for (i = 0; i < MAX_PLAYERS; i++)
9140     if (tape.player_participates[i])
9141       tape.num_participating_players++;
9142
9143   SaveTapeToFilename(filename);
9144
9145   tape.changed = FALSE;
9146 }
9147
9148 void SaveTape(int nr)
9149 {
9150   char *filename = getTapeFilename(nr);
9151
9152   InitTapeDirectory(leveldir_current->subdir);
9153
9154   SaveTapeExt(filename);
9155 }
9156
9157 void SaveScoreTape(int nr)
9158 {
9159   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9160
9161   // used instead of "leveldir_current->subdir" (for network games)
9162   InitScoreTapeDirectory(levelset.identifier, nr);
9163
9164   SaveTapeExt(filename);
9165 }
9166
9167 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9168                                   unsigned int req_state_added)
9169 {
9170   char *filename = getTapeFilename(nr);
9171   boolean new_tape = !fileExists(filename);
9172   boolean tape_saved = FALSE;
9173
9174   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9175   {
9176     SaveTape(nr);
9177
9178     if (new_tape)
9179       Request(msg_saved, REQ_CONFIRM | req_state_added);
9180
9181     tape_saved = TRUE;
9182   }
9183
9184   return tape_saved;
9185 }
9186
9187 boolean SaveTapeChecked(int nr)
9188 {
9189   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9190 }
9191
9192 boolean SaveTapeChecked_LevelSolved(int nr)
9193 {
9194   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9195                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9196 }
9197
9198 void DumpTape(struct TapeInfo *tape)
9199 {
9200   int tape_frame_counter;
9201   int i, j;
9202
9203   if (tape->no_valid_file)
9204   {
9205     Warn("cannot dump -- no valid tape file found");
9206
9207     return;
9208   }
9209
9210   PrintLine("-", 79);
9211
9212   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9213         tape->level_nr, tape->file_version, tape->game_version);
9214   Print("                  (effective engine version %08d)\n",
9215         tape->engine_version);
9216   Print("Level series identifier: '%s'\n", tape->level_identifier);
9217
9218   Print("Solution tape: %s\n",
9219         tape->solved ? "yes" :
9220         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9221
9222   Print("Special tape properties: ");
9223   if (tape->property_bits == TAPE_PROPERTY_NONE)
9224     Print("[none]");
9225   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9226     Print("[em_random_bug]");
9227   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9228     Print("[game_speed]");
9229   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9230     Print("[pause]");
9231   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9232     Print("[single_step]");
9233   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9234     Print("[snapshot]");
9235   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9236     Print("[replayed]");
9237   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9238     Print("[tas_keys]");
9239   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9240     Print("[small_graphics]");
9241   Print("\n");
9242
9243   int year2 = tape->date / 10000;
9244   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9245   int month_index_raw = (tape->date / 100) % 100;
9246   int month_index = month_index_raw % 12;       // prevent invalid index
9247   int month = month_index + 1;
9248   int day = tape->date % 100;
9249
9250   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9251
9252   PrintLine("-", 79);
9253
9254   tape_frame_counter = 0;
9255
9256   for (i = 0; i < tape->length; i++)
9257   {
9258     if (i >= MAX_TAPE_LEN)
9259       break;
9260
9261     Print("%04d: ", i);
9262
9263     for (j = 0; j < MAX_PLAYERS; j++)
9264     {
9265       if (tape->player_participates[j])
9266       {
9267         int action = tape->pos[i].action[j];
9268
9269         Print("%d:%02x ", j, action);
9270         Print("[%c%c%c%c|%c%c] - ",
9271               (action & JOY_LEFT ? '<' : ' '),
9272               (action & JOY_RIGHT ? '>' : ' '),
9273               (action & JOY_UP ? '^' : ' '),
9274               (action & JOY_DOWN ? 'v' : ' '),
9275               (action & JOY_BUTTON_1 ? '1' : ' '),
9276               (action & JOY_BUTTON_2 ? '2' : ' '));
9277       }
9278     }
9279
9280     Print("(%03d) ", tape->pos[i].delay);
9281     Print("[%05d]\n", tape_frame_counter);
9282
9283     tape_frame_counter += tape->pos[i].delay;
9284   }
9285
9286   PrintLine("-", 79);
9287 }
9288
9289 void DumpTapes(void)
9290 {
9291   static LevelDirTree *dumptape_leveldir = NULL;
9292
9293   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9294                                                 global.dumptape_leveldir);
9295
9296   if (dumptape_leveldir == NULL)
9297     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9298
9299   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9300       global.dumptape_level_nr > dumptape_leveldir->last_level)
9301     Fail("no such level number: %d", global.dumptape_level_nr);
9302
9303   leveldir_current = dumptape_leveldir;
9304
9305   if (options.mytapes)
9306     LoadTape(global.dumptape_level_nr);
9307   else
9308     LoadSolutionTape(global.dumptape_level_nr);
9309
9310   DumpTape(&tape);
9311
9312   CloseAllAndExit(0);
9313 }
9314
9315
9316 // ============================================================================
9317 // score file functions
9318 // ============================================================================
9319
9320 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9321 {
9322   int i;
9323
9324   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9325   {
9326     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9327     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9328     scores->entry[i].score = 0;
9329     scores->entry[i].time = 0;
9330
9331     scores->entry[i].id = -1;
9332     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9333     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9334     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9335     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9336     strcpy(scores->entry[i].country_code, "??");
9337   }
9338
9339   scores->num_entries = 0;
9340   scores->last_added = -1;
9341   scores->last_added_local = -1;
9342
9343   scores->updated = FALSE;
9344   scores->uploaded = FALSE;
9345   scores->tape_downloaded = FALSE;
9346   scores->force_last_added = FALSE;
9347
9348   // The following values are intentionally not reset here:
9349   // - last_level_nr
9350   // - last_entry_nr
9351   // - next_level_nr
9352   // - continue_playing
9353   // - continue_on_return
9354 }
9355
9356 static void setScoreInfoToDefaults(void)
9357 {
9358   setScoreInfoToDefaultsExt(&scores);
9359 }
9360
9361 static void setServerScoreInfoToDefaults(void)
9362 {
9363   setScoreInfoToDefaultsExt(&server_scores);
9364 }
9365
9366 static void LoadScore_OLD(int nr)
9367 {
9368   int i;
9369   char *filename = getScoreFilename(nr);
9370   char cookie[MAX_LINE_LEN];
9371   char line[MAX_LINE_LEN];
9372   char *line_ptr;
9373   FILE *file;
9374
9375   if (!(file = fopen(filename, MODE_READ)))
9376     return;
9377
9378   // check file identifier
9379   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9380     cookie[0] = '\0';
9381   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9382     cookie[strlen(cookie) - 1] = '\0';
9383
9384   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9385   {
9386     Warn("unknown format of score file '%s'", filename);
9387
9388     fclose(file);
9389
9390     return;
9391   }
9392
9393   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9394   {
9395     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9396       Warn("fscanf() failed; %s", strerror(errno));
9397
9398     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9399       line[0] = '\0';
9400
9401     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9402       line[strlen(line) - 1] = '\0';
9403
9404     for (line_ptr = line; *line_ptr; line_ptr++)
9405     {
9406       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9407       {
9408         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9409         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9410         break;
9411       }
9412     }
9413   }
9414
9415   fclose(file);
9416 }
9417
9418 static void ConvertScore_OLD(void)
9419 {
9420   // only convert score to time for levels that rate playing time over score
9421   if (!level.rate_time_over_score)
9422     return;
9423
9424   // convert old score to playing time for score-less levels (like Supaplex)
9425   int time_final_max = 999;
9426   int i;
9427
9428   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9429   {
9430     int score = scores.entry[i].score;
9431
9432     if (score > 0 && score < time_final_max)
9433       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9434   }
9435 }
9436
9437 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9438 {
9439   scores->file_version = getFileVersion(file);
9440   scores->game_version = getFileVersion(file);
9441
9442   return chunk_size;
9443 }
9444
9445 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9446 {
9447   char *level_identifier = NULL;
9448   int level_identifier_size;
9449   int i;
9450
9451   level_identifier_size = getFile16BitBE(file);
9452
9453   level_identifier = checked_malloc(level_identifier_size);
9454
9455   for (i = 0; i < level_identifier_size; i++)
9456     level_identifier[i] = getFile8Bit(file);
9457
9458   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9459   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9460
9461   checked_free(level_identifier);
9462
9463   scores->level_nr = getFile16BitBE(file);
9464   scores->num_entries = getFile16BitBE(file);
9465
9466   chunk_size = 2 + level_identifier_size + 2 + 2;
9467
9468   return chunk_size;
9469 }
9470
9471 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9472 {
9473   int i, j;
9474
9475   for (i = 0; i < scores->num_entries; i++)
9476   {
9477     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9478       scores->entry[i].name[j] = getFile8Bit(file);
9479
9480     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9481   }
9482
9483   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9484
9485   return chunk_size;
9486 }
9487
9488 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9489 {
9490   int i;
9491
9492   for (i = 0; i < scores->num_entries; i++)
9493     scores->entry[i].score = getFile16BitBE(file);
9494
9495   chunk_size = scores->num_entries * 2;
9496
9497   return chunk_size;
9498 }
9499
9500 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9501 {
9502   int i;
9503
9504   for (i = 0; i < scores->num_entries; i++)
9505     scores->entry[i].score = getFile32BitBE(file);
9506
9507   chunk_size = scores->num_entries * 4;
9508
9509   return chunk_size;
9510 }
9511
9512 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9513 {
9514   int i;
9515
9516   for (i = 0; i < scores->num_entries; i++)
9517     scores->entry[i].time = getFile32BitBE(file);
9518
9519   chunk_size = scores->num_entries * 4;
9520
9521   return chunk_size;
9522 }
9523
9524 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9525 {
9526   int i, j;
9527
9528   for (i = 0; i < scores->num_entries; i++)
9529   {
9530     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9531       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9532
9533     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9534   }
9535
9536   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9537
9538   return chunk_size;
9539 }
9540
9541 void LoadScore(int nr)
9542 {
9543   char *filename = getScoreFilename(nr);
9544   char cookie[MAX_LINE_LEN];
9545   char chunk_name[CHUNK_ID_LEN + 1];
9546   int chunk_size;
9547   boolean old_score_file_format = FALSE;
9548   File *file;
9549
9550   // always start with reliable default values
9551   setScoreInfoToDefaults();
9552
9553   if (!(file = openFile(filename, MODE_READ)))
9554     return;
9555
9556   getFileChunkBE(file, chunk_name, NULL);
9557   if (strEqual(chunk_name, "RND1"))
9558   {
9559     getFile32BitBE(file);               // not used
9560
9561     getFileChunkBE(file, chunk_name, NULL);
9562     if (!strEqual(chunk_name, "SCOR"))
9563     {
9564       Warn("unknown format of score file '%s'", filename);
9565
9566       closeFile(file);
9567
9568       return;
9569     }
9570   }
9571   else  // check for old file format with cookie string
9572   {
9573     strcpy(cookie, chunk_name);
9574     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9575       cookie[4] = '\0';
9576     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9577       cookie[strlen(cookie) - 1] = '\0';
9578
9579     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9580     {
9581       Warn("unknown format of score file '%s'", filename);
9582
9583       closeFile(file);
9584
9585       return;
9586     }
9587
9588     old_score_file_format = TRUE;
9589   }
9590
9591   if (old_score_file_format)
9592   {
9593     // score files from versions before 4.2.4.0 without chunk structure
9594     LoadScore_OLD(nr);
9595
9596     // convert score to time, if possible (mainly for Supaplex levels)
9597     ConvertScore_OLD();
9598   }
9599   else
9600   {
9601     static struct
9602     {
9603       char *name;
9604       int size;
9605       int (*loader)(File *, int, struct ScoreInfo *);
9606     }
9607     chunk_info[] =
9608     {
9609       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
9610       { "INFO", -1,                     LoadScore_INFO },
9611       { "NAME", -1,                     LoadScore_NAME },
9612       { "SCOR", -1,                     LoadScore_SCOR },
9613       { "SC4R", -1,                     LoadScore_SC4R },
9614       { "TIME", -1,                     LoadScore_TIME },
9615       { "TAPE", -1,                     LoadScore_TAPE },
9616
9617       {  NULL,  0,                      NULL }
9618     };
9619
9620     while (getFileChunkBE(file, chunk_name, &chunk_size))
9621     {
9622       int i = 0;
9623
9624       while (chunk_info[i].name != NULL &&
9625              !strEqual(chunk_name, chunk_info[i].name))
9626         i++;
9627
9628       if (chunk_info[i].name == NULL)
9629       {
9630         Warn("unknown chunk '%s' in score file '%s'",
9631               chunk_name, filename);
9632
9633         ReadUnusedBytesFromFile(file, chunk_size);
9634       }
9635       else if (chunk_info[i].size != -1 &&
9636                chunk_info[i].size != chunk_size)
9637       {
9638         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9639               chunk_size, chunk_name, filename);
9640
9641         ReadUnusedBytesFromFile(file, chunk_size);
9642       }
9643       else
9644       {
9645         // call function to load this score chunk
9646         int chunk_size_expected =
9647           (chunk_info[i].loader)(file, chunk_size, &scores);
9648
9649         // the size of some chunks cannot be checked before reading other
9650         // chunks first (like "HEAD" and "BODY") that contain some header
9651         // information, so check them here
9652         if (chunk_size_expected != chunk_size)
9653         {
9654           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9655                 chunk_size, chunk_name, filename);
9656         }
9657       }
9658     }
9659   }
9660
9661   closeFile(file);
9662 }
9663
9664 #if ENABLE_HISTORIC_CHUNKS
9665 void SaveScore_OLD(int nr)
9666 {
9667   int i;
9668   char *filename = getScoreFilename(nr);
9669   FILE *file;
9670
9671   // used instead of "leveldir_current->subdir" (for network games)
9672   InitScoreDirectory(levelset.identifier);
9673
9674   if (!(file = fopen(filename, MODE_WRITE)))
9675   {
9676     Warn("cannot save score for level %d", nr);
9677
9678     return;
9679   }
9680
9681   fprintf(file, "%s\n\n", SCORE_COOKIE);
9682
9683   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9684     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9685
9686   fclose(file);
9687
9688   SetFilePermissions(filename, PERMS_PRIVATE);
9689 }
9690 #endif
9691
9692 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9693 {
9694   putFileVersion(file, scores->file_version);
9695   putFileVersion(file, scores->game_version);
9696 }
9697
9698 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9699 {
9700   int level_identifier_size = strlen(scores->level_identifier) + 1;
9701   int i;
9702
9703   putFile16BitBE(file, level_identifier_size);
9704
9705   for (i = 0; i < level_identifier_size; i++)
9706     putFile8Bit(file, scores->level_identifier[i]);
9707
9708   putFile16BitBE(file, scores->level_nr);
9709   putFile16BitBE(file, scores->num_entries);
9710 }
9711
9712 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9713 {
9714   int i, j;
9715
9716   for (i = 0; i < scores->num_entries; i++)
9717   {
9718     int name_size = strlen(scores->entry[i].name);
9719
9720     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9721       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9722   }
9723 }
9724
9725 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9726 {
9727   int i;
9728
9729   for (i = 0; i < scores->num_entries; i++)
9730     putFile16BitBE(file, scores->entry[i].score);
9731 }
9732
9733 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9734 {
9735   int i;
9736
9737   for (i = 0; i < scores->num_entries; i++)
9738     putFile32BitBE(file, scores->entry[i].score);
9739 }
9740
9741 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9742 {
9743   int i;
9744
9745   for (i = 0; i < scores->num_entries; i++)
9746     putFile32BitBE(file, scores->entry[i].time);
9747 }
9748
9749 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9750 {
9751   int i, j;
9752
9753   for (i = 0; i < scores->num_entries; i++)
9754   {
9755     int size = strlen(scores->entry[i].tape_basename);
9756
9757     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9758       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9759   }
9760 }
9761
9762 static void SaveScoreToFilename(char *filename)
9763 {
9764   FILE *file;
9765   int info_chunk_size;
9766   int name_chunk_size;
9767   int scor_chunk_size;
9768   int sc4r_chunk_size;
9769   int time_chunk_size;
9770   int tape_chunk_size;
9771   boolean has_large_score_values;
9772   int i;
9773
9774   if (!(file = fopen(filename, MODE_WRITE)))
9775   {
9776     Warn("cannot save score file '%s'", filename);
9777
9778     return;
9779   }
9780
9781   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9782   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9783   scor_chunk_size = scores.num_entries * 2;
9784   sc4r_chunk_size = scores.num_entries * 4;
9785   time_chunk_size = scores.num_entries * 4;
9786   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9787
9788   has_large_score_values = FALSE;
9789   for (i = 0; i < scores.num_entries; i++)
9790     if (scores.entry[i].score > 0xffff)
9791       has_large_score_values = TRUE;
9792
9793   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9794   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9795
9796   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9797   SaveScore_VERS(file, &scores);
9798
9799   putFileChunkBE(file, "INFO", info_chunk_size);
9800   SaveScore_INFO(file, &scores);
9801
9802   putFileChunkBE(file, "NAME", name_chunk_size);
9803   SaveScore_NAME(file, &scores);
9804
9805   if (has_large_score_values)
9806   {
9807     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9808     SaveScore_SC4R(file, &scores);
9809   }
9810   else
9811   {
9812     putFileChunkBE(file, "SCOR", scor_chunk_size);
9813     SaveScore_SCOR(file, &scores);
9814   }
9815
9816   putFileChunkBE(file, "TIME", time_chunk_size);
9817   SaveScore_TIME(file, &scores);
9818
9819   putFileChunkBE(file, "TAPE", tape_chunk_size);
9820   SaveScore_TAPE(file, &scores);
9821
9822   fclose(file);
9823
9824   SetFilePermissions(filename, PERMS_PRIVATE);
9825 }
9826
9827 void SaveScore(int nr)
9828 {
9829   char *filename = getScoreFilename(nr);
9830   int i;
9831
9832   // used instead of "leveldir_current->subdir" (for network games)
9833   InitScoreDirectory(levelset.identifier);
9834
9835   scores.file_version = FILE_VERSION_ACTUAL;
9836   scores.game_version = GAME_VERSION_ACTUAL;
9837
9838   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9839   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9840   scores.level_nr = level_nr;
9841
9842   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9843     if (scores.entry[i].score == 0 &&
9844         scores.entry[i].time == 0 &&
9845         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9846       break;
9847
9848   scores.num_entries = i;
9849
9850   if (scores.num_entries == 0)
9851     return;
9852
9853   SaveScoreToFilename(filename);
9854 }
9855
9856 static void LoadServerScoreFromCache(int nr)
9857 {
9858   struct ScoreEntry score_entry;
9859   struct
9860   {
9861     void *value;
9862     boolean is_string;
9863     int string_size;
9864   }
9865   score_mapping[] =
9866   {
9867     { &score_entry.score,               FALSE,  0                       },
9868     { &score_entry.time,                FALSE,  0                       },
9869     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
9870     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
9871     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
9872     { &score_entry.id,                  FALSE,  0                       },
9873     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
9874     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
9875     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
9876     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
9877
9878     { NULL,                             FALSE,  0                       }
9879   };
9880   char *filename = getScoreCacheFilename(nr);
9881   SetupFileHash *score_hash = loadSetupFileHash(filename);
9882   int i, j;
9883
9884   server_scores.num_entries = 0;
9885
9886   if (score_hash == NULL)
9887     return;
9888
9889   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9890   {
9891     score_entry = server_scores.entry[i];
9892
9893     for (j = 0; score_mapping[j].value != NULL; j++)
9894     {
9895       char token[10];
9896
9897       sprintf(token, "%02d.%d", i, j);
9898
9899       char *value = getHashEntry(score_hash, token);
9900
9901       if (value == NULL)
9902         continue;
9903
9904       if (score_mapping[j].is_string)
9905       {
9906         char *score_value = (char *)score_mapping[j].value;
9907         int value_size = score_mapping[j].string_size;
9908
9909         strncpy(score_value, value, value_size);
9910         score_value[value_size] = '\0';
9911       }
9912       else
9913       {
9914         int *score_value = (int *)score_mapping[j].value;
9915
9916         *score_value = atoi(value);
9917       }
9918
9919       server_scores.num_entries = i + 1;
9920     }
9921
9922     server_scores.entry[i] = score_entry;
9923   }
9924
9925   freeSetupFileHash(score_hash);
9926 }
9927
9928 void LoadServerScore(int nr, boolean download_score)
9929 {
9930   if (!setup.use_api_server)
9931     return;
9932
9933   // always start with reliable default values
9934   setServerScoreInfoToDefaults();
9935
9936   // 1st step: load server scores from cache file (which may not exist)
9937   // (this should prevent reading it while the thread is writing to it)
9938   LoadServerScoreFromCache(nr);
9939
9940   if (download_score && runtime.use_api_server)
9941   {
9942     // 2nd step: download server scores from score server to cache file
9943     // (as thread, as it might time out if the server is not reachable)
9944     ApiGetScoreAsThread(nr);
9945   }
9946 }
9947
9948 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9949 {
9950   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9951
9952   // if score tape not uploaded, ask for uploading missing tapes later
9953   if (!setup.has_remaining_tapes)
9954     setup.ask_for_remaining_tapes = TRUE;
9955
9956   setup.provide_uploading_tapes = TRUE;
9957   setup.has_remaining_tapes = TRUE;
9958
9959   SaveSetup_ServerSetup();
9960 }
9961
9962 void SaveServerScore(int nr, boolean tape_saved)
9963 {
9964   if (!runtime.use_api_server)
9965   {
9966     PrepareScoreTapesForUpload(leveldir_current->subdir);
9967
9968     return;
9969   }
9970
9971   ApiAddScoreAsThread(nr, tape_saved, NULL);
9972 }
9973
9974 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9975                              char *score_tape_filename)
9976 {
9977   if (!runtime.use_api_server)
9978     return;
9979
9980   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9981 }
9982
9983 void LoadLocalAndServerScore(int nr, boolean download_score)
9984 {
9985   int last_added_local = scores.last_added_local;
9986   boolean force_last_added = scores.force_last_added;
9987
9988   // needed if only showing server scores
9989   setScoreInfoToDefaults();
9990
9991   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9992     LoadScore(nr);
9993
9994   // restore last added local score entry (before merging server scores)
9995   scores.last_added = scores.last_added_local = last_added_local;
9996
9997   if (setup.use_api_server &&
9998       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9999   {
10000     // load server scores from cache file and trigger update from server
10001     LoadServerScore(nr, download_score);
10002
10003     // merge local scores with scores from server
10004     MergeServerScore();
10005   }
10006
10007   if (force_last_added)
10008     scores.force_last_added = force_last_added;
10009 }
10010
10011
10012 // ============================================================================
10013 // setup file functions
10014 // ============================================================================
10015
10016 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10017
10018
10019 static struct TokenInfo global_setup_tokens[] =
10020 {
10021   {
10022     TYPE_STRING,
10023     &setup.player_name,                         "player_name"
10024   },
10025   {
10026     TYPE_SWITCH,
10027     &setup.multiple_users,                      "multiple_users"
10028   },
10029   {
10030     TYPE_SWITCH,
10031     &setup.sound,                               "sound"
10032   },
10033   {
10034     TYPE_SWITCH,
10035     &setup.sound_loops,                         "repeating_sound_loops"
10036   },
10037   {
10038     TYPE_SWITCH,
10039     &setup.sound_music,                         "background_music"
10040   },
10041   {
10042     TYPE_SWITCH,
10043     &setup.sound_simple,                        "simple_sound_effects"
10044   },
10045   {
10046     TYPE_SWITCH,
10047     &setup.toons,                               "toons"
10048   },
10049   {
10050     TYPE_SWITCH,
10051     &setup.global_animations,                   "global_animations"
10052   },
10053   {
10054     TYPE_SWITCH,
10055     &setup.scroll_delay,                        "scroll_delay"
10056   },
10057   {
10058     TYPE_SWITCH,
10059     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10060   },
10061   {
10062     TYPE_INTEGER,
10063     &setup.scroll_delay_value,                  "scroll_delay_value"
10064   },
10065   {
10066     TYPE_STRING,
10067     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10068   },
10069   {
10070     TYPE_INTEGER,
10071     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10072   },
10073   {
10074     TYPE_SWITCH,
10075     &setup.fade_screens,                        "fade_screens"
10076   },
10077   {
10078     TYPE_SWITCH,
10079     &setup.autorecord,                          "automatic_tape_recording"
10080   },
10081   {
10082     TYPE_SWITCH,
10083     &setup.autorecord_after_replay,             "autorecord_after_replay"
10084   },
10085   {
10086     TYPE_SWITCH,
10087     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10088   },
10089   {
10090     TYPE_SWITCH,
10091     &setup.show_titlescreen,                    "show_titlescreen"
10092   },
10093   {
10094     TYPE_SWITCH,
10095     &setup.quick_doors,                         "quick_doors"
10096   },
10097   {
10098     TYPE_SWITCH,
10099     &setup.team_mode,                           "team_mode"
10100   },
10101   {
10102     TYPE_SWITCH,
10103     &setup.handicap,                            "handicap"
10104   },
10105   {
10106     TYPE_SWITCH,
10107     &setup.skip_levels,                         "skip_levels"
10108   },
10109   {
10110     TYPE_SWITCH,
10111     &setup.increment_levels,                    "increment_levels"
10112   },
10113   {
10114     TYPE_SWITCH,
10115     &setup.auto_play_next_level,                "auto_play_next_level"
10116   },
10117   {
10118     TYPE_SWITCH,
10119     &setup.count_score_after_game,              "count_score_after_game"
10120   },
10121   {
10122     TYPE_SWITCH,
10123     &setup.show_scores_after_game,              "show_scores_after_game"
10124   },
10125   {
10126     TYPE_SWITCH,
10127     &setup.time_limit,                          "time_limit"
10128   },
10129   {
10130     TYPE_SWITCH,
10131     &setup.fullscreen,                          "fullscreen"
10132   },
10133   {
10134     TYPE_INTEGER,
10135     &setup.window_scaling_percent,              "window_scaling_percent"
10136   },
10137   {
10138     TYPE_STRING,
10139     &setup.window_scaling_quality,              "window_scaling_quality"
10140   },
10141   {
10142     TYPE_STRING,
10143     &setup.screen_rendering_mode,               "screen_rendering_mode"
10144   },
10145   {
10146     TYPE_STRING,
10147     &setup.vsync_mode,                          "vsync_mode"
10148   },
10149   {
10150     TYPE_SWITCH,
10151     &setup.ask_on_escape,                       "ask_on_escape"
10152   },
10153   {
10154     TYPE_SWITCH,
10155     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10156   },
10157   {
10158     TYPE_SWITCH,
10159     &setup.ask_on_game_over,                    "ask_on_game_over"
10160   },
10161   {
10162     TYPE_SWITCH,
10163     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10164   },
10165   {
10166     TYPE_SWITCH,
10167     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10168   },
10169   {
10170     TYPE_SWITCH,
10171     &setup.quick_switch,                        "quick_player_switch"
10172   },
10173   {
10174     TYPE_SWITCH,
10175     &setup.input_on_focus,                      "input_on_focus"
10176   },
10177   {
10178     TYPE_SWITCH,
10179     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10180   },
10181   {
10182     TYPE_SWITCH,
10183     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10184   },
10185   {
10186     TYPE_SWITCH,
10187     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10188   },
10189   {
10190     TYPE_SWITCH,
10191     &setup.game_speed_extended,                 "game_speed_extended"
10192   },
10193   {
10194     TYPE_INTEGER,
10195     &setup.game_frame_delay,                    "game_frame_delay"
10196   },
10197   {
10198     TYPE_SWITCH,
10199     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10200   },
10201   {
10202     TYPE_SWITCH,
10203     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10204   },
10205   {
10206     TYPE_SWITCH,
10207     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10208   },
10209   {
10210     TYPE_SWITCH3,
10211     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10212   },
10213   {
10214     TYPE_SWITCH,
10215     &setup.sp_show_border_elements,             "sp_show_border_elements"
10216   },
10217   {
10218     TYPE_SWITCH,
10219     &setup.small_game_graphics,                 "small_game_graphics"
10220   },
10221   {
10222     TYPE_SWITCH,
10223     &setup.show_load_save_buttons,              "show_load_save_buttons"
10224   },
10225   {
10226     TYPE_SWITCH,
10227     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10228   },
10229   {
10230     TYPE_STRING,
10231     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10232   },
10233   {
10234     TYPE_STRING,
10235     &setup.graphics_set,                        "graphics_set"
10236   },
10237   {
10238     TYPE_STRING,
10239     &setup.sounds_set,                          "sounds_set"
10240   },
10241   {
10242     TYPE_STRING,
10243     &setup.music_set,                           "music_set"
10244   },
10245   {
10246     TYPE_SWITCH3,
10247     &setup.override_level_graphics,             "override_level_graphics"
10248   },
10249   {
10250     TYPE_SWITCH3,
10251     &setup.override_level_sounds,               "override_level_sounds"
10252   },
10253   {
10254     TYPE_SWITCH3,
10255     &setup.override_level_music,                "override_level_music"
10256   },
10257   {
10258     TYPE_INTEGER,
10259     &setup.volume_simple,                       "volume_simple"
10260   },
10261   {
10262     TYPE_INTEGER,
10263     &setup.volume_loops,                        "volume_loops"
10264   },
10265   {
10266     TYPE_INTEGER,
10267     &setup.volume_music,                        "volume_music"
10268   },
10269   {
10270     TYPE_SWITCH,
10271     &setup.network_mode,                        "network_mode"
10272   },
10273   {
10274     TYPE_PLAYER,
10275     &setup.network_player_nr,                   "network_player"
10276   },
10277   {
10278     TYPE_STRING,
10279     &setup.network_server_hostname,             "network_server_hostname"
10280   },
10281   {
10282     TYPE_STRING,
10283     &setup.touch.control_type,                  "touch.control_type"
10284   },
10285   {
10286     TYPE_INTEGER,
10287     &setup.touch.move_distance,                 "touch.move_distance"
10288   },
10289   {
10290     TYPE_INTEGER,
10291     &setup.touch.drop_distance,                 "touch.drop_distance"
10292   },
10293   {
10294     TYPE_INTEGER,
10295     &setup.touch.transparency,                  "touch.transparency"
10296   },
10297   {
10298     TYPE_INTEGER,
10299     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10300   },
10301   {
10302     TYPE_INTEGER,
10303     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10304   },
10305   {
10306     TYPE_INTEGER,
10307     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10308   },
10309   {
10310     TYPE_INTEGER,
10311     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10312   },
10313   {
10314     TYPE_INTEGER,
10315     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10316   },
10317   {
10318     TYPE_INTEGER,
10319     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10320   },
10321   {
10322     TYPE_SWITCH,
10323     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10324   },
10325 };
10326
10327 static struct TokenInfo auto_setup_tokens[] =
10328 {
10329   {
10330     TYPE_INTEGER,
10331     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10332   },
10333 };
10334
10335 static struct TokenInfo server_setup_tokens[] =
10336 {
10337   {
10338     TYPE_STRING,
10339     &setup.player_uuid,                         "player_uuid"
10340   },
10341   {
10342     TYPE_INTEGER,
10343     &setup.player_version,                      "player_version"
10344   },
10345   {
10346     TYPE_SWITCH,
10347     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10348   },
10349   {
10350     TYPE_STRING,
10351     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10352   },
10353   {
10354     TYPE_STRING,
10355     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10356   },
10357   {
10358     TYPE_SWITCH,
10359     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10360   },
10361   {
10362     TYPE_SWITCH,
10363     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10364   },
10365   {
10366     TYPE_SWITCH,
10367     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10368   },
10369   {
10370     TYPE_SWITCH,
10371     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10372   },
10373   {
10374     TYPE_SWITCH,
10375     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10376   },
10377 };
10378
10379 static struct TokenInfo editor_setup_tokens[] =
10380 {
10381   {
10382     TYPE_SWITCH,
10383     &setup.editor.el_classic,                   "editor.el_classic"
10384   },
10385   {
10386     TYPE_SWITCH,
10387     &setup.editor.el_custom,                    "editor.el_custom"
10388   },
10389   {
10390     TYPE_SWITCH,
10391     &setup.editor.el_user_defined,              "editor.el_user_defined"
10392   },
10393   {
10394     TYPE_SWITCH,
10395     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10396   },
10397   {
10398     TYPE_SWITCH,
10399     &setup.editor.el_headlines,                 "editor.el_headlines"
10400   },
10401   {
10402     TYPE_SWITCH,
10403     &setup.editor.show_element_token,           "editor.show_element_token"
10404   },
10405   {
10406     TYPE_SWITCH,
10407     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10408   },
10409 };
10410
10411 static struct TokenInfo editor_cascade_setup_tokens[] =
10412 {
10413   {
10414     TYPE_SWITCH,
10415     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10416   },
10417   {
10418     TYPE_SWITCH,
10419     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10420   },
10421   {
10422     TYPE_SWITCH,
10423     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10424   },
10425   {
10426     TYPE_SWITCH,
10427     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10428   },
10429   {
10430     TYPE_SWITCH,
10431     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10432   },
10433   {
10434     TYPE_SWITCH,
10435     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10436   },
10437   {
10438     TYPE_SWITCH,
10439     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10440   },
10441   {
10442     TYPE_SWITCH,
10443     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10444   },
10445   {
10446     TYPE_SWITCH,
10447     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10448   },
10449   {
10450     TYPE_SWITCH,
10451     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10452   },
10453   {
10454     TYPE_SWITCH,
10455     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10456   },
10457   {
10458     TYPE_SWITCH,
10459     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10460   },
10461   {
10462     TYPE_SWITCH,
10463     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10464   },
10465   {
10466     TYPE_SWITCH,
10467     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10468   },
10469   {
10470     TYPE_SWITCH,
10471     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10472   },
10473   {
10474     TYPE_SWITCH,
10475     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10476   },
10477   {
10478     TYPE_SWITCH,
10479     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10480   },
10481   {
10482     TYPE_SWITCH,
10483     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10484   },
10485   {
10486     TYPE_SWITCH,
10487     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10488   },
10489 };
10490
10491 static struct TokenInfo shortcut_setup_tokens[] =
10492 {
10493   {
10494     TYPE_KEY_X11,
10495     &setup.shortcut.save_game,                  "shortcut.save_game"
10496   },
10497   {
10498     TYPE_KEY_X11,
10499     &setup.shortcut.load_game,                  "shortcut.load_game"
10500   },
10501   {
10502     TYPE_KEY_X11,
10503     &setup.shortcut.restart_game,               "shortcut.restart_game"
10504   },
10505   {
10506     TYPE_KEY_X11,
10507     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10508   },
10509   {
10510     TYPE_KEY_X11,
10511     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10512   },
10513   {
10514     TYPE_KEY_X11,
10515     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10516   },
10517   {
10518     TYPE_KEY_X11,
10519     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10520   },
10521   {
10522     TYPE_KEY_X11,
10523     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10524   },
10525   {
10526     TYPE_KEY_X11,
10527     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10528   },
10529   {
10530     TYPE_KEY_X11,
10531     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10532   },
10533   {
10534     TYPE_KEY_X11,
10535     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10536   },
10537   {
10538     TYPE_KEY_X11,
10539     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10540   },
10541   {
10542     TYPE_KEY_X11,
10543     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10544   },
10545   {
10546     TYPE_KEY_X11,
10547     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10548   },
10549   {
10550     TYPE_KEY_X11,
10551     &setup.shortcut.tape_record,                "shortcut.tape_record"
10552   },
10553   {
10554     TYPE_KEY_X11,
10555     &setup.shortcut.tape_play,                  "shortcut.tape_play"
10556   },
10557   {
10558     TYPE_KEY_X11,
10559     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
10560   },
10561   {
10562     TYPE_KEY_X11,
10563     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
10564   },
10565   {
10566     TYPE_KEY_X11,
10567     &setup.shortcut.sound_music,                "shortcut.sound_music"
10568   },
10569   {
10570     TYPE_KEY_X11,
10571     &setup.shortcut.snap_left,                  "shortcut.snap_left"
10572   },
10573   {
10574     TYPE_KEY_X11,
10575     &setup.shortcut.snap_right,                 "shortcut.snap_right"
10576   },
10577   {
10578     TYPE_KEY_X11,
10579     &setup.shortcut.snap_up,                    "shortcut.snap_up"
10580   },
10581   {
10582     TYPE_KEY_X11,
10583     &setup.shortcut.snap_down,                  "shortcut.snap_down"
10584   },
10585 };
10586
10587 static struct SetupInputInfo setup_input;
10588 static struct TokenInfo player_setup_tokens[] =
10589 {
10590   {
10591     TYPE_BOOLEAN,
10592     &setup_input.use_joystick,                  ".use_joystick"
10593   },
10594   {
10595     TYPE_STRING,
10596     &setup_input.joy.device_name,               ".joy.device_name"
10597   },
10598   {
10599     TYPE_INTEGER,
10600     &setup_input.joy.xleft,                     ".joy.xleft"
10601   },
10602   {
10603     TYPE_INTEGER,
10604     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
10605   },
10606   {
10607     TYPE_INTEGER,
10608     &setup_input.joy.xright,                    ".joy.xright"
10609   },
10610   {
10611     TYPE_INTEGER,
10612     &setup_input.joy.yupper,                    ".joy.yupper"
10613   },
10614   {
10615     TYPE_INTEGER,
10616     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
10617   },
10618   {
10619     TYPE_INTEGER,
10620     &setup_input.joy.ylower,                    ".joy.ylower"
10621   },
10622   {
10623     TYPE_INTEGER,
10624     &setup_input.joy.snap,                      ".joy.snap_field"
10625   },
10626   {
10627     TYPE_INTEGER,
10628     &setup_input.joy.drop,                      ".joy.place_bomb"
10629   },
10630   {
10631     TYPE_KEY_X11,
10632     &setup_input.key.left,                      ".key.move_left"
10633   },
10634   {
10635     TYPE_KEY_X11,
10636     &setup_input.key.right,                     ".key.move_right"
10637   },
10638   {
10639     TYPE_KEY_X11,
10640     &setup_input.key.up,                        ".key.move_up"
10641   },
10642   {
10643     TYPE_KEY_X11,
10644     &setup_input.key.down,                      ".key.move_down"
10645   },
10646   {
10647     TYPE_KEY_X11,
10648     &setup_input.key.snap,                      ".key.snap_field"
10649   },
10650   {
10651     TYPE_KEY_X11,
10652     &setup_input.key.drop,                      ".key.place_bomb"
10653   },
10654 };
10655
10656 static struct TokenInfo system_setup_tokens[] =
10657 {
10658   {
10659     TYPE_STRING,
10660     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10661   },
10662   {
10663     TYPE_STRING,
10664     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10665   },
10666   {
10667     TYPE_STRING,
10668     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10669   },
10670   {
10671     TYPE_INTEGER,
10672     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10673   },
10674 };
10675
10676 static struct TokenInfo internal_setup_tokens[] =
10677 {
10678   {
10679     TYPE_STRING,
10680     &setup.internal.program_title,              "program_title"
10681   },
10682   {
10683     TYPE_STRING,
10684     &setup.internal.program_version,            "program_version"
10685   },
10686   {
10687     TYPE_STRING,
10688     &setup.internal.program_author,             "program_author"
10689   },
10690   {
10691     TYPE_STRING,
10692     &setup.internal.program_email,              "program_email"
10693   },
10694   {
10695     TYPE_STRING,
10696     &setup.internal.program_website,            "program_website"
10697   },
10698   {
10699     TYPE_STRING,
10700     &setup.internal.program_copyright,          "program_copyright"
10701   },
10702   {
10703     TYPE_STRING,
10704     &setup.internal.program_company,            "program_company"
10705   },
10706   {
10707     TYPE_STRING,
10708     &setup.internal.program_icon_file,          "program_icon_file"
10709   },
10710   {
10711     TYPE_STRING,
10712     &setup.internal.default_graphics_set,       "default_graphics_set"
10713   },
10714   {
10715     TYPE_STRING,
10716     &setup.internal.default_sounds_set,         "default_sounds_set"
10717   },
10718   {
10719     TYPE_STRING,
10720     &setup.internal.default_music_set,          "default_music_set"
10721   },
10722   {
10723     TYPE_STRING,
10724     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
10725   },
10726   {
10727     TYPE_STRING,
10728     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
10729   },
10730   {
10731     TYPE_STRING,
10732     &setup.internal.fallback_music_file,        "fallback_music_file"
10733   },
10734   {
10735     TYPE_STRING,
10736     &setup.internal.default_level_series,       "default_level_series"
10737   },
10738   {
10739     TYPE_INTEGER,
10740     &setup.internal.default_window_width,       "default_window_width"
10741   },
10742   {
10743     TYPE_INTEGER,
10744     &setup.internal.default_window_height,      "default_window_height"
10745   },
10746   {
10747     TYPE_BOOLEAN,
10748     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
10749   },
10750   {
10751     TYPE_BOOLEAN,
10752     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
10753   },
10754   {
10755     TYPE_BOOLEAN,
10756     &setup.internal.create_user_levelset,       "create_user_levelset"
10757   },
10758   {
10759     TYPE_BOOLEAN,
10760     &setup.internal.info_screens_from_main,     "info_screens_from_main"
10761   },
10762   {
10763     TYPE_BOOLEAN,
10764     &setup.internal.menu_game,                  "menu_game"
10765   },
10766   {
10767     TYPE_BOOLEAN,
10768     &setup.internal.menu_engines,               "menu_engines"
10769   },
10770   {
10771     TYPE_BOOLEAN,
10772     &setup.internal.menu_editor,                "menu_editor"
10773   },
10774   {
10775     TYPE_BOOLEAN,
10776     &setup.internal.menu_graphics,              "menu_graphics"
10777   },
10778   {
10779     TYPE_BOOLEAN,
10780     &setup.internal.menu_sound,                 "menu_sound"
10781   },
10782   {
10783     TYPE_BOOLEAN,
10784     &setup.internal.menu_artwork,               "menu_artwork"
10785   },
10786   {
10787     TYPE_BOOLEAN,
10788     &setup.internal.menu_input,                 "menu_input"
10789   },
10790   {
10791     TYPE_BOOLEAN,
10792     &setup.internal.menu_touch,                 "menu_touch"
10793   },
10794   {
10795     TYPE_BOOLEAN,
10796     &setup.internal.menu_shortcuts,             "menu_shortcuts"
10797   },
10798   {
10799     TYPE_BOOLEAN,
10800     &setup.internal.menu_exit,                  "menu_exit"
10801   },
10802   {
10803     TYPE_BOOLEAN,
10804     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
10805   },
10806   {
10807     TYPE_BOOLEAN,
10808     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
10809   },
10810   {
10811     TYPE_BOOLEAN,
10812     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
10813   },
10814   {
10815     TYPE_BOOLEAN,
10816     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
10817   },
10818   {
10819     TYPE_BOOLEAN,
10820     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
10821   },
10822   {
10823     TYPE_BOOLEAN,
10824     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
10825   },
10826   {
10827     TYPE_BOOLEAN,
10828     &setup.internal.info_title,                 "info_title"
10829   },
10830   {
10831     TYPE_BOOLEAN,
10832     &setup.internal.info_elements,              "info_elements"
10833   },
10834   {
10835     TYPE_BOOLEAN,
10836     &setup.internal.info_music,                 "info_music"
10837   },
10838   {
10839     TYPE_BOOLEAN,
10840     &setup.internal.info_credits,               "info_credits"
10841   },
10842   {
10843     TYPE_BOOLEAN,
10844     &setup.internal.info_program,               "info_program"
10845   },
10846   {
10847     TYPE_BOOLEAN,
10848     &setup.internal.info_version,               "info_version"
10849   },
10850   {
10851     TYPE_BOOLEAN,
10852     &setup.internal.info_levelset,              "info_levelset"
10853   },
10854   {
10855     TYPE_BOOLEAN,
10856     &setup.internal.info_exit,                  "info_exit"
10857   },
10858 };
10859
10860 static struct TokenInfo debug_setup_tokens[] =
10861 {
10862   {
10863     TYPE_INTEGER,
10864     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
10865   },
10866   {
10867     TYPE_INTEGER,
10868     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
10869   },
10870   {
10871     TYPE_INTEGER,
10872     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
10873   },
10874   {
10875     TYPE_INTEGER,
10876     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
10877   },
10878   {
10879     TYPE_INTEGER,
10880     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
10881   },
10882   {
10883     TYPE_INTEGER,
10884     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
10885   },
10886   {
10887     TYPE_INTEGER,
10888     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
10889   },
10890   {
10891     TYPE_INTEGER,
10892     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
10893   },
10894   {
10895     TYPE_INTEGER,
10896     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
10897   },
10898   {
10899     TYPE_INTEGER,
10900     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
10901   },
10902   {
10903     TYPE_KEY_X11,
10904     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
10905   },
10906   {
10907     TYPE_KEY_X11,
10908     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
10909   },
10910   {
10911     TYPE_KEY_X11,
10912     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
10913   },
10914   {
10915     TYPE_KEY_X11,
10916     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
10917   },
10918   {
10919     TYPE_KEY_X11,
10920     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
10921   },
10922   {
10923     TYPE_KEY_X11,
10924     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
10925   },
10926   {
10927     TYPE_KEY_X11,
10928     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
10929   },
10930   {
10931     TYPE_KEY_X11,
10932     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
10933   },
10934   {
10935     TYPE_KEY_X11,
10936     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
10937   },
10938   {
10939     TYPE_KEY_X11,
10940     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
10941   },
10942   {
10943     TYPE_BOOLEAN,
10944     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
10945   {
10946     TYPE_BOOLEAN,
10947     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
10948   },
10949   {
10950     TYPE_BOOLEAN,
10951     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
10952   },
10953   {
10954     TYPE_SWITCH3,
10955     &setup.debug.xsn_mode,                      "debug.xsn_mode"
10956   },
10957   {
10958     TYPE_INTEGER,
10959     &setup.debug.xsn_percent,                   "debug.xsn_percent"
10960   },
10961 };
10962
10963 static struct TokenInfo options_setup_tokens[] =
10964 {
10965   {
10966     TYPE_BOOLEAN,
10967     &setup.options.verbose,                     "options.verbose"
10968   },
10969   {
10970     TYPE_BOOLEAN,
10971     &setup.options.debug,                       "options.debug"
10972   },
10973   {
10974     TYPE_STRING,
10975     &setup.options.debug_mode,                  "options.debug_mode"
10976   },
10977 };
10978
10979 static void setSetupInfoToDefaults(struct SetupInfo *si)
10980 {
10981   int i;
10982
10983   si->player_name = getStringCopy(getDefaultUserName(user.nr));
10984
10985   si->multiple_users = TRUE;
10986
10987   si->sound = TRUE;
10988   si->sound_loops = TRUE;
10989   si->sound_music = TRUE;
10990   si->sound_simple = TRUE;
10991   si->toons = TRUE;
10992   si->global_animations = TRUE;
10993   si->scroll_delay = TRUE;
10994   si->forced_scroll_delay = FALSE;
10995   si->scroll_delay_value = STD_SCROLL_DELAY;
10996   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10997   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10998   si->fade_screens = TRUE;
10999   si->autorecord = TRUE;
11000   si->autorecord_after_replay = TRUE;
11001   si->auto_pause_on_start = FALSE;
11002   si->show_titlescreen = TRUE;
11003   si->quick_doors = FALSE;
11004   si->team_mode = FALSE;
11005   si->handicap = TRUE;
11006   si->skip_levels = TRUE;
11007   si->increment_levels = TRUE;
11008   si->auto_play_next_level = TRUE;
11009   si->count_score_after_game = TRUE;
11010   si->show_scores_after_game = TRUE;
11011   si->time_limit = TRUE;
11012   si->fullscreen = FALSE;
11013   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11014   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11015   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11016   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11017   si->ask_on_escape = TRUE;
11018   si->ask_on_escape_editor = TRUE;
11019   si->ask_on_game_over = TRUE;
11020   si->ask_on_quit_game = TRUE;
11021   si->ask_on_quit_program = TRUE;
11022   si->quick_switch = FALSE;
11023   si->input_on_focus = FALSE;
11024   si->prefer_aga_graphics = TRUE;
11025   si->prefer_lowpass_sounds = FALSE;
11026   si->prefer_extra_panel_items = TRUE;
11027   si->game_speed_extended = FALSE;
11028   si->game_frame_delay = GAME_FRAME_DELAY;
11029   si->bd_skip_uncovering = FALSE;
11030   si->bd_skip_hatching = FALSE;
11031   si->bd_scroll_delay = TRUE;
11032   si->bd_smooth_movements = AUTO;
11033   si->sp_show_border_elements = FALSE;
11034   si->small_game_graphics = FALSE;
11035   si->show_load_save_buttons = FALSE;
11036   si->show_undo_redo_buttons = FALSE;
11037   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11038
11039   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11040   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11041   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11042
11043   si->override_level_graphics = FALSE;
11044   si->override_level_sounds = FALSE;
11045   si->override_level_music = FALSE;
11046
11047   si->volume_simple = 100;              // percent
11048   si->volume_loops = 100;               // percent
11049   si->volume_music = 100;               // percent
11050
11051   si->network_mode = FALSE;
11052   si->network_player_nr = 0;            // first player
11053   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11054
11055   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11056   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11057   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11058   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11059   si->touch.draw_outlined = TRUE;
11060   si->touch.draw_pressed = TRUE;
11061
11062   for (i = 0; i < 2; i++)
11063   {
11064     char *default_grid_button[6][2] =
11065     {
11066       { "      ", "  ^^  " },
11067       { "      ", "  ^^  " },
11068       { "      ", "<<  >>" },
11069       { "      ", "<<  >>" },
11070       { "111222", "  vv  " },
11071       { "111222", "  vv  " }
11072     };
11073     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11074     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11075     int min_xsize = MIN(6, grid_xsize);
11076     int min_ysize = MIN(6, grid_ysize);
11077     int startx = grid_xsize - min_xsize;
11078     int starty = grid_ysize - min_ysize;
11079     int x, y;
11080
11081     // virtual buttons grid can only be set to defaults if video is initialized
11082     // (this will be repeated if virtual buttons are not loaded from setup file)
11083     if (video.initialized)
11084     {
11085       si->touch.grid_xsize[i] = grid_xsize;
11086       si->touch.grid_ysize[i] = grid_ysize;
11087     }
11088     else
11089     {
11090       si->touch.grid_xsize[i] = -1;
11091       si->touch.grid_ysize[i] = -1;
11092     }
11093
11094     for (x = 0; x < MAX_GRID_XSIZE; x++)
11095       for (y = 0; y < MAX_GRID_YSIZE; y++)
11096         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11097
11098     for (x = 0; x < min_xsize; x++)
11099       for (y = 0; y < min_ysize; y++)
11100         si->touch.grid_button[i][x][starty + y] =
11101           default_grid_button[y][0][x];
11102
11103     for (x = 0; x < min_xsize; x++)
11104       for (y = 0; y < min_ysize; y++)
11105         si->touch.grid_button[i][startx + x][starty + y] =
11106           default_grid_button[y][1][x];
11107   }
11108
11109   si->touch.grid_initialized            = video.initialized;
11110
11111   si->touch.overlay_buttons             = FALSE;
11112
11113   si->editor.el_boulderdash             = TRUE;
11114   si->editor.el_boulderdash_native      = TRUE;
11115   si->editor.el_emerald_mine            = TRUE;
11116   si->editor.el_emerald_mine_club       = TRUE;
11117   si->editor.el_more                    = TRUE;
11118   si->editor.el_sokoban                 = TRUE;
11119   si->editor.el_supaplex                = TRUE;
11120   si->editor.el_diamond_caves           = TRUE;
11121   si->editor.el_dx_boulderdash          = TRUE;
11122
11123   si->editor.el_mirror_magic            = TRUE;
11124   si->editor.el_deflektor               = TRUE;
11125
11126   si->editor.el_chars                   = TRUE;
11127   si->editor.el_steel_chars             = TRUE;
11128
11129   si->editor.el_classic                 = TRUE;
11130   si->editor.el_custom                  = TRUE;
11131
11132   si->editor.el_user_defined            = FALSE;
11133   si->editor.el_dynamic                 = TRUE;
11134
11135   si->editor.el_headlines               = TRUE;
11136
11137   si->editor.show_element_token         = FALSE;
11138
11139   si->editor.show_read_only_warning     = TRUE;
11140
11141   si->editor.use_template_for_new_levels = TRUE;
11142
11143   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11144   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11145   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11146   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11147   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11148
11149   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11150   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11151   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11152   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11153   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11154
11155   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11156   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11157   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11158   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11159   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11160   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11161
11162   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11163   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11164   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11165
11166   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11167   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11168   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11169   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11170
11171   for (i = 0; i < MAX_PLAYERS; i++)
11172   {
11173     si->input[i].use_joystick = FALSE;
11174     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11175     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11176     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11177     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11178     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11179     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11180     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11181     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11182     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11183     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11184     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11185     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11186     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11187     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11188     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11189   }
11190
11191   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11192   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11193   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11194   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11195
11196   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11197   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11198   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11199   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11200   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11201   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11202   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11203
11204   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11205
11206   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11207   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11208   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11209
11210   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11211   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11212   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11213
11214   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11215   si->internal.choose_from_top_leveldir = FALSE;
11216   si->internal.show_scaling_in_title = TRUE;
11217   si->internal.create_user_levelset = TRUE;
11218   si->internal.info_screens_from_main = FALSE;
11219
11220   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11221   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11222
11223   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11224   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11225   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11226   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11227   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11228   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11229   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11230   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11231   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11232   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11233
11234   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11235   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11236   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11237   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11238   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11239   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11240   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11241   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11242   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11243   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11244
11245   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11246   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11247
11248   si->debug.show_frames_per_second = FALSE;
11249
11250   si->debug.xsn_mode = AUTO;
11251   si->debug.xsn_percent = 0;
11252
11253   si->options.verbose = FALSE;
11254   si->options.debug = FALSE;
11255   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11256
11257 #if defined(PLATFORM_ANDROID)
11258   si->fullscreen = TRUE;
11259   si->touch.overlay_buttons = TRUE;
11260 #endif
11261
11262   setHideSetupEntry(&setup.debug.xsn_mode);
11263 }
11264
11265 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11266 {
11267   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11268 }
11269
11270 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11271 {
11272   si->player_uuid = NULL;       // (will be set later)
11273   si->player_version = 1;       // (will be set later)
11274
11275   si->use_api_server = TRUE;
11276   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11277   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11278   si->ask_for_uploading_tapes = TRUE;
11279   si->ask_for_remaining_tapes = FALSE;
11280   si->provide_uploading_tapes = TRUE;
11281   si->ask_for_using_api_server = TRUE;
11282   si->has_remaining_tapes = FALSE;
11283 }
11284
11285 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11286 {
11287   si->editor_cascade.el_bd              = TRUE;
11288   si->editor_cascade.el_bd_native       = TRUE;
11289   si->editor_cascade.el_em              = TRUE;
11290   si->editor_cascade.el_emc             = TRUE;
11291   si->editor_cascade.el_rnd             = TRUE;
11292   si->editor_cascade.el_sb              = TRUE;
11293   si->editor_cascade.el_sp              = TRUE;
11294   si->editor_cascade.el_dc              = TRUE;
11295   si->editor_cascade.el_dx              = TRUE;
11296
11297   si->editor_cascade.el_mm              = TRUE;
11298   si->editor_cascade.el_df              = TRUE;
11299
11300   si->editor_cascade.el_chars           = FALSE;
11301   si->editor_cascade.el_steel_chars     = FALSE;
11302   si->editor_cascade.el_ce              = FALSE;
11303   si->editor_cascade.el_ge              = FALSE;
11304   si->editor_cascade.el_es              = FALSE;
11305   si->editor_cascade.el_ref             = FALSE;
11306   si->editor_cascade.el_user            = FALSE;
11307   si->editor_cascade.el_dynamic         = FALSE;
11308 }
11309
11310 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11311
11312 static char *getHideSetupToken(void *setup_value)
11313 {
11314   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11315
11316   if (setup_value != NULL)
11317     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11318
11319   return hide_setup_token;
11320 }
11321
11322 void setHideSetupEntry(void *setup_value)
11323 {
11324   char *hide_setup_token = getHideSetupToken(setup_value);
11325
11326   if (hide_setup_hash == NULL)
11327     hide_setup_hash = newSetupFileHash();
11328
11329   if (setup_value != NULL)
11330     setHashEntry(hide_setup_hash, hide_setup_token, "");
11331 }
11332
11333 void removeHideSetupEntry(void *setup_value)
11334 {
11335   char *hide_setup_token = getHideSetupToken(setup_value);
11336
11337   if (setup_value != NULL)
11338     removeHashEntry(hide_setup_hash, hide_setup_token);
11339 }
11340
11341 boolean hideSetupEntry(void *setup_value)
11342 {
11343   char *hide_setup_token = getHideSetupToken(setup_value);
11344
11345   return (setup_value != NULL &&
11346           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11347 }
11348
11349 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11350                                       struct TokenInfo *token_info,
11351                                       int token_nr, char *token_text)
11352 {
11353   char *token_hide_text = getStringCat2(token_text, ".hide");
11354   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11355
11356   // set the value of this setup option in the setup option structure
11357   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11358
11359   // check if this setup option should be hidden in the setup menu
11360   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11361     setHideSetupEntry(token_info[token_nr].value);
11362
11363   free(token_hide_text);
11364 }
11365
11366 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11367                                       struct TokenInfo *token_info,
11368                                       int token_nr)
11369 {
11370   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11371                             token_info[token_nr].text);
11372 }
11373
11374 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11375 {
11376   int i, pnr;
11377
11378   if (!setup_file_hash)
11379     return;
11380
11381   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11382     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11383
11384   setup.touch.grid_initialized = TRUE;
11385   for (i = 0; i < 2; i++)
11386   {
11387     int grid_xsize = setup.touch.grid_xsize[i];
11388     int grid_ysize = setup.touch.grid_ysize[i];
11389     int x, y;
11390
11391     // if virtual buttons are not loaded from setup file, repeat initializing
11392     // virtual buttons grid with default values later when video is initialized
11393     if (grid_xsize == -1 ||
11394         grid_ysize == -1)
11395     {
11396       setup.touch.grid_initialized = FALSE;
11397
11398       continue;
11399     }
11400
11401     for (y = 0; y < grid_ysize; y++)
11402     {
11403       char token_string[MAX_LINE_LEN];
11404
11405       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11406
11407       char *value_string = getHashEntry(setup_file_hash, token_string);
11408
11409       if (value_string == NULL)
11410         continue;
11411
11412       for (x = 0; x < grid_xsize; x++)
11413       {
11414         char c = value_string[x];
11415
11416         setup.touch.grid_button[i][x][y] =
11417           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11418       }
11419     }
11420   }
11421
11422   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11423     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11424
11425   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11426     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11427
11428   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11429   {
11430     char prefix[30];
11431
11432     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11433
11434     setup_input = setup.input[pnr];
11435     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11436     {
11437       char full_token[100];
11438
11439       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11440       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11441                                 full_token);
11442     }
11443     setup.input[pnr] = setup_input;
11444   }
11445
11446   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11447     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11448
11449   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11450     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11451
11452   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11453     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11454
11455   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11456     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11457
11458   setHideRelatedSetupEntries();
11459 }
11460
11461 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11462 {
11463   int i;
11464
11465   if (!setup_file_hash)
11466     return;
11467
11468   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11469     setSetupInfo(auto_setup_tokens, i,
11470                  getHashEntry(setup_file_hash,
11471                               auto_setup_tokens[i].text));
11472 }
11473
11474 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11475 {
11476   int i;
11477
11478   if (!setup_file_hash)
11479     return;
11480
11481   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11482     setSetupInfo(server_setup_tokens, i,
11483                  getHashEntry(setup_file_hash,
11484                               server_setup_tokens[i].text));
11485 }
11486
11487 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11488 {
11489   int i;
11490
11491   if (!setup_file_hash)
11492     return;
11493
11494   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11495     setSetupInfo(editor_cascade_setup_tokens, i,
11496                  getHashEntry(setup_file_hash,
11497                               editor_cascade_setup_tokens[i].text));
11498 }
11499
11500 void LoadUserNames(void)
11501 {
11502   int last_user_nr = user.nr;
11503   int i;
11504
11505   if (global.user_names != NULL)
11506   {
11507     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11508       checked_free(global.user_names[i]);
11509
11510     checked_free(global.user_names);
11511   }
11512
11513   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11514
11515   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11516   {
11517     user.nr = i;
11518
11519     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11520
11521     if (setup_file_hash)
11522     {
11523       char *player_name = getHashEntry(setup_file_hash, "player_name");
11524
11525       global.user_names[i] = getFixedUserName(player_name);
11526
11527       freeSetupFileHash(setup_file_hash);
11528     }
11529
11530     if (global.user_names[i] == NULL)
11531       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11532   }
11533
11534   user.nr = last_user_nr;
11535 }
11536
11537 void LoadSetupFromFilename(char *filename)
11538 {
11539   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11540
11541   if (setup_file_hash)
11542   {
11543     decodeSetupFileHash_Default(setup_file_hash);
11544
11545     freeSetupFileHash(setup_file_hash);
11546   }
11547   else
11548   {
11549     Debug("setup", "using default setup values");
11550   }
11551 }
11552
11553 static void LoadSetup_SpecialPostProcessing(void)
11554 {
11555   char *player_name_new;
11556
11557   // needed to work around problems with fixed length strings
11558   player_name_new = getFixedUserName(setup.player_name);
11559   free(setup.player_name);
11560   setup.player_name = player_name_new;
11561
11562   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11563   if (setup.scroll_delay == FALSE)
11564   {
11565     setup.scroll_delay_value = MIN_SCROLL_DELAY;
11566     setup.scroll_delay = TRUE;                  // now always "on"
11567   }
11568
11569   // make sure that scroll delay value stays inside valid range
11570   setup.scroll_delay_value =
11571     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11572 }
11573
11574 void LoadSetup_Default(void)
11575 {
11576   char *filename;
11577
11578   // always start with reliable default values
11579   setSetupInfoToDefaults(&setup);
11580
11581   // try to load setup values from default setup file
11582   filename = getDefaultSetupFilename();
11583
11584   if (fileExists(filename))
11585     LoadSetupFromFilename(filename);
11586
11587   // try to load setup values from platform setup file
11588   filename = getPlatformSetupFilename();
11589
11590   if (fileExists(filename))
11591     LoadSetupFromFilename(filename);
11592
11593   // try to load setup values from user setup file
11594   filename = getSetupFilename();
11595
11596   LoadSetupFromFilename(filename);
11597
11598   LoadSetup_SpecialPostProcessing();
11599 }
11600
11601 void LoadSetup_AutoSetup(void)
11602 {
11603   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11604   SetupFileHash *setup_file_hash = NULL;
11605
11606   // always start with reliable default values
11607   setSetupInfoToDefaults_AutoSetup(&setup);
11608
11609   setup_file_hash = loadSetupFileHash(filename);
11610
11611   if (setup_file_hash)
11612   {
11613     decodeSetupFileHash_AutoSetup(setup_file_hash);
11614
11615     freeSetupFileHash(setup_file_hash);
11616   }
11617
11618   free(filename);
11619 }
11620
11621 void LoadSetup_ServerSetup(void)
11622 {
11623   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11624   SetupFileHash *setup_file_hash = NULL;
11625
11626   // always start with reliable default values
11627   setSetupInfoToDefaults_ServerSetup(&setup);
11628
11629   setup_file_hash = loadSetupFileHash(filename);
11630
11631   if (setup_file_hash)
11632   {
11633     decodeSetupFileHash_ServerSetup(setup_file_hash);
11634
11635     freeSetupFileHash(setup_file_hash);
11636   }
11637
11638   free(filename);
11639
11640   if (setup.player_uuid == NULL)
11641   {
11642     // player UUID does not yet exist in setup file
11643     setup.player_uuid = getStringCopy(getUUID());
11644     setup.player_version = 2;
11645
11646     SaveSetup_ServerSetup();
11647   }
11648 }
11649
11650 void LoadSetup_EditorCascade(void)
11651 {
11652   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11653   SetupFileHash *setup_file_hash = NULL;
11654
11655   // always start with reliable default values
11656   setSetupInfoToDefaults_EditorCascade(&setup);
11657
11658   setup_file_hash = loadSetupFileHash(filename);
11659
11660   if (setup_file_hash)
11661   {
11662     decodeSetupFileHash_EditorCascade(setup_file_hash);
11663
11664     freeSetupFileHash(setup_file_hash);
11665   }
11666
11667   free(filename);
11668 }
11669
11670 void LoadSetup(void)
11671 {
11672   LoadSetup_Default();
11673   LoadSetup_AutoSetup();
11674   LoadSetup_ServerSetup();
11675   LoadSetup_EditorCascade();
11676 }
11677
11678 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11679                                            char *mapping_line)
11680 {
11681   char mapping_guid[MAX_LINE_LEN];
11682   char *mapping_start, *mapping_end;
11683
11684   // get GUID from game controller mapping line: copy complete line
11685   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11686   mapping_guid[MAX_LINE_LEN - 1] = '\0';
11687
11688   // get GUID from game controller mapping line: cut after GUID part
11689   mapping_start = strchr(mapping_guid, ',');
11690   if (mapping_start != NULL)
11691     *mapping_start = '\0';
11692
11693   // cut newline from game controller mapping line
11694   mapping_end = strchr(mapping_line, '\n');
11695   if (mapping_end != NULL)
11696     *mapping_end = '\0';
11697
11698   // add mapping entry to game controller mappings hash
11699   setHashEntry(mappings_hash, mapping_guid, mapping_line);
11700 }
11701
11702 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11703                                                  char *filename)
11704 {
11705   FILE *file;
11706
11707   if (!(file = fopen(filename, MODE_READ)))
11708   {
11709     Warn("cannot read game controller mappings file '%s'", filename);
11710
11711     return;
11712   }
11713
11714   while (!feof(file))
11715   {
11716     char line[MAX_LINE_LEN];
11717
11718     if (!fgets(line, MAX_LINE_LEN, file))
11719       break;
11720
11721     addGameControllerMappingToHash(mappings_hash, line);
11722   }
11723
11724   fclose(file);
11725 }
11726
11727 void SaveSetup_Default(void)
11728 {
11729   char *filename = getSetupFilename();
11730   FILE *file;
11731   int i, pnr;
11732
11733   InitUserDataDirectory();
11734
11735   if (!(file = fopen(filename, MODE_WRITE)))
11736   {
11737     Warn("cannot write setup file '%s'", filename);
11738
11739     return;
11740   }
11741
11742   fprintFileHeader(file, SETUP_FILENAME);
11743
11744   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11745   {
11746     // just to make things nicer :)
11747     if (global_setup_tokens[i].value == &setup.multiple_users           ||
11748         global_setup_tokens[i].value == &setup.sound                    ||
11749         global_setup_tokens[i].value == &setup.graphics_set             ||
11750         global_setup_tokens[i].value == &setup.volume_simple            ||
11751         global_setup_tokens[i].value == &setup.network_mode             ||
11752         global_setup_tokens[i].value == &setup.touch.control_type       ||
11753         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
11754         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11755       fprintf(file, "\n");
11756
11757     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11758   }
11759
11760   for (i = 0; i < 2; i++)
11761   {
11762     int grid_xsize = setup.touch.grid_xsize[i];
11763     int grid_ysize = setup.touch.grid_ysize[i];
11764     int x, y;
11765
11766     fprintf(file, "\n");
11767
11768     for (y = 0; y < grid_ysize; y++)
11769     {
11770       char token_string[MAX_LINE_LEN];
11771       char value_string[MAX_LINE_LEN];
11772
11773       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11774
11775       for (x = 0; x < grid_xsize; x++)
11776       {
11777         char c = setup.touch.grid_button[i][x][y];
11778
11779         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11780       }
11781
11782       value_string[grid_xsize] = '\0';
11783
11784       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11785     }
11786   }
11787
11788   fprintf(file, "\n");
11789   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11790     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11791
11792   fprintf(file, "\n");
11793   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11794     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11795
11796   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11797   {
11798     char prefix[30];
11799
11800     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11801     fprintf(file, "\n");
11802
11803     setup_input = setup.input[pnr];
11804     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11805       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11806   }
11807
11808   fprintf(file, "\n");
11809   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11810     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11811
11812   // (internal setup values not saved to user setup file)
11813
11814   fprintf(file, "\n");
11815   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11816     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11817         setup.debug.xsn_mode != AUTO)
11818       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11819
11820   fprintf(file, "\n");
11821   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11822     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11823
11824   fclose(file);
11825
11826   SetFilePermissions(filename, PERMS_PRIVATE);
11827 }
11828
11829 void SaveSetup_AutoSetup(void)
11830 {
11831   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11832   FILE *file;
11833   int i;
11834
11835   InitUserDataDirectory();
11836
11837   if (!(file = fopen(filename, MODE_WRITE)))
11838   {
11839     Warn("cannot write auto setup file '%s'", filename);
11840
11841     free(filename);
11842
11843     return;
11844   }
11845
11846   fprintFileHeader(file, AUTOSETUP_FILENAME);
11847
11848   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11849     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11850
11851   fclose(file);
11852
11853   SetFilePermissions(filename, PERMS_PRIVATE);
11854
11855   free(filename);
11856 }
11857
11858 void SaveSetup_ServerSetup(void)
11859 {
11860   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11861   FILE *file;
11862   int i;
11863
11864   InitUserDataDirectory();
11865
11866   if (!(file = fopen(filename, MODE_WRITE)))
11867   {
11868     Warn("cannot write server setup file '%s'", filename);
11869
11870     free(filename);
11871
11872     return;
11873   }
11874
11875   fprintFileHeader(file, SERVERSETUP_FILENAME);
11876
11877   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11878   {
11879     // just to make things nicer :)
11880     if (server_setup_tokens[i].value == &setup.use_api_server)
11881       fprintf(file, "\n");
11882
11883     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11884   }
11885
11886   fclose(file);
11887
11888   SetFilePermissions(filename, PERMS_PRIVATE);
11889
11890   free(filename);
11891 }
11892
11893 void SaveSetup_EditorCascade(void)
11894 {
11895   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11896   FILE *file;
11897   int i;
11898
11899   InitUserDataDirectory();
11900
11901   if (!(file = fopen(filename, MODE_WRITE)))
11902   {
11903     Warn("cannot write editor cascade state file '%s'", filename);
11904
11905     free(filename);
11906
11907     return;
11908   }
11909
11910   fprintFileHeader(file, EDITORCASCADE_FILENAME);
11911
11912   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11913     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11914
11915   fclose(file);
11916
11917   SetFilePermissions(filename, PERMS_PRIVATE);
11918
11919   free(filename);
11920 }
11921
11922 void SaveSetup(void)
11923 {
11924   SaveSetup_Default();
11925   SaveSetup_AutoSetup();
11926   SaveSetup_ServerSetup();
11927   SaveSetup_EditorCascade();
11928 }
11929
11930 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11931                                                   char *filename)
11932 {
11933   FILE *file;
11934
11935   if (!(file = fopen(filename, MODE_WRITE)))
11936   {
11937     Warn("cannot write game controller mappings file '%s'", filename);
11938
11939     return;
11940   }
11941
11942   BEGIN_HASH_ITERATION(mappings_hash, itr)
11943   {
11944     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11945   }
11946   END_HASH_ITERATION(mappings_hash, itr)
11947
11948   fclose(file);
11949 }
11950
11951 void SaveSetup_AddGameControllerMapping(char *mapping)
11952 {
11953   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11954   SetupFileHash *mappings_hash = newSetupFileHash();
11955
11956   InitUserDataDirectory();
11957
11958   // load existing personal game controller mappings
11959   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11960
11961   // add new mapping to personal game controller mappings
11962   addGameControllerMappingToHash(mappings_hash, mapping);
11963
11964   // save updated personal game controller mappings
11965   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11966
11967   freeSetupFileHash(mappings_hash);
11968   free(filename);
11969 }
11970
11971 void LoadCustomElementDescriptions(void)
11972 {
11973   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11974   SetupFileHash *setup_file_hash;
11975   int i;
11976
11977   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11978   {
11979     if (element_info[i].custom_description != NULL)
11980     {
11981       free(element_info[i].custom_description);
11982       element_info[i].custom_description = NULL;
11983     }
11984   }
11985
11986   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11987     return;
11988
11989   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11990   {
11991     char *token = getStringCat2(element_info[i].token_name, ".name");
11992     char *value = getHashEntry(setup_file_hash, token);
11993
11994     if (value != NULL)
11995       element_info[i].custom_description = getStringCopy(value);
11996
11997     free(token);
11998   }
11999
12000   freeSetupFileHash(setup_file_hash);
12001 }
12002
12003 static int getElementFromToken(char *token)
12004 {
12005   char *value = getHashEntry(element_token_hash, token);
12006
12007   if (value != NULL)
12008     return atoi(value);
12009
12010   Warn("unknown element token '%s'", token);
12011
12012   return EL_UNDEFINED;
12013 }
12014
12015 void FreeGlobalAnimEventInfo(void)
12016 {
12017   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12018
12019   if (gaei->event_list == NULL)
12020     return;
12021
12022   int i;
12023
12024   for (i = 0; i < gaei->num_event_lists; i++)
12025   {
12026     checked_free(gaei->event_list[i]->event_value);
12027     checked_free(gaei->event_list[i]);
12028   }
12029
12030   checked_free(gaei->event_list);
12031
12032   gaei->event_list = NULL;
12033   gaei->num_event_lists = 0;
12034 }
12035
12036 static int AddGlobalAnimEventList(void)
12037 {
12038   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12039   int list_pos = gaei->num_event_lists++;
12040
12041   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12042                                      sizeof(struct GlobalAnimEventListInfo *));
12043
12044   gaei->event_list[list_pos] =
12045     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12046
12047   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12048
12049   gaeli->event_value = NULL;
12050   gaeli->num_event_values = 0;
12051
12052   return list_pos;
12053 }
12054
12055 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12056 {
12057   // do not add empty global animation events
12058   if (event_value == ANIM_EVENT_NONE)
12059     return list_pos;
12060
12061   // if list position is undefined, create new list
12062   if (list_pos == ANIM_EVENT_UNDEFINED)
12063     list_pos = AddGlobalAnimEventList();
12064
12065   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12066   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12067   int value_pos = gaeli->num_event_values++;
12068
12069   gaeli->event_value = checked_realloc(gaeli->event_value,
12070                                        gaeli->num_event_values * sizeof(int *));
12071
12072   gaeli->event_value[value_pos] = event_value;
12073
12074   return list_pos;
12075 }
12076
12077 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12078 {
12079   if (list_pos == ANIM_EVENT_UNDEFINED)
12080     return ANIM_EVENT_NONE;
12081
12082   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12083   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12084
12085   return gaeli->event_value[value_pos];
12086 }
12087
12088 int GetGlobalAnimEventValueCount(int list_pos)
12089 {
12090   if (list_pos == ANIM_EVENT_UNDEFINED)
12091     return 0;
12092
12093   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12094   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12095
12096   return gaeli->num_event_values;
12097 }
12098
12099 // This function checks if a string <s> of the format "string1, string2, ..."
12100 // exactly contains a string <s_contained>.
12101
12102 static boolean string_has_parameter(char *s, char *s_contained)
12103 {
12104   char *substring;
12105
12106   if (s == NULL || s_contained == NULL)
12107     return FALSE;
12108
12109   if (strlen(s_contained) > strlen(s))
12110     return FALSE;
12111
12112   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12113   {
12114     char next_char = s[strlen(s_contained)];
12115
12116     // check if next character is delimiter or whitespace
12117     if (next_char == ',' || next_char == '\0' ||
12118         next_char == ' ' || next_char == '\t')
12119       return TRUE;
12120   }
12121
12122   // check if string contains another parameter string after a comma
12123   substring = strchr(s, ',');
12124   if (substring == NULL)        // string does not contain a comma
12125     return FALSE;
12126
12127   // advance string pointer to next character after the comma
12128   substring++;
12129
12130   // skip potential whitespaces after the comma
12131   while (*substring == ' ' || *substring == '\t')
12132     substring++;
12133
12134   return string_has_parameter(substring, s_contained);
12135 }
12136
12137 static int get_anim_parameter_value_ce(char *s)
12138 {
12139   char *s_ptr = s;
12140   char *pattern_1 = "ce_change:custom_";
12141   char *pattern_2 = ".page_";
12142   int pattern_1_len = strlen(pattern_1);
12143   char *matching_char = strstr(s_ptr, pattern_1);
12144   int result = ANIM_EVENT_NONE;
12145
12146   if (matching_char == NULL)
12147     return ANIM_EVENT_NONE;
12148
12149   result = ANIM_EVENT_CE_CHANGE;
12150
12151   s_ptr = matching_char + pattern_1_len;
12152
12153   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12154   if (*s_ptr >= '0' && *s_ptr <= '9')
12155   {
12156     int gic_ce_nr = (*s_ptr++ - '0');
12157
12158     if (*s_ptr >= '0' && *s_ptr <= '9')
12159     {
12160       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12161
12162       if (*s_ptr >= '0' && *s_ptr <= '9')
12163         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12164     }
12165
12166     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12167       return ANIM_EVENT_NONE;
12168
12169     // custom element stored as 0 to 255
12170     gic_ce_nr--;
12171
12172     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12173   }
12174   else
12175   {
12176     // invalid custom element number specified
12177
12178     return ANIM_EVENT_NONE;
12179   }
12180
12181   // check for change page number ("page_X" or "page_XX") (optional)
12182   if (strPrefix(s_ptr, pattern_2))
12183   {
12184     s_ptr += strlen(pattern_2);
12185
12186     if (*s_ptr >= '0' && *s_ptr <= '9')
12187     {
12188       int gic_page_nr = (*s_ptr++ - '0');
12189
12190       if (*s_ptr >= '0' && *s_ptr <= '9')
12191         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12192
12193       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12194         return ANIM_EVENT_NONE;
12195
12196       // change page stored as 1 to 32 (0 means "all change pages")
12197
12198       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12199     }
12200     else
12201     {
12202       // invalid animation part number specified
12203
12204       return ANIM_EVENT_NONE;
12205     }
12206   }
12207
12208   // discard result if next character is neither delimiter nor whitespace
12209   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12210         *s_ptr == ' ' || *s_ptr == '\t'))
12211     return ANIM_EVENT_NONE;
12212
12213   return result;
12214 }
12215
12216 static int get_anim_parameter_value(char *s)
12217 {
12218   int event_value[] =
12219   {
12220     ANIM_EVENT_CLICK,
12221     ANIM_EVENT_INIT,
12222     ANIM_EVENT_START,
12223     ANIM_EVENT_END,
12224     ANIM_EVENT_POST
12225   };
12226   char *pattern_1[] =
12227   {
12228     "click:anim_",
12229     "init:anim_",
12230     "start:anim_",
12231     "end:anim_",
12232     "post:anim_"
12233   };
12234   char *pattern_2 = ".part_";
12235   char *matching_char = NULL;
12236   char *s_ptr = s;
12237   int pattern_1_len = 0;
12238   int result = ANIM_EVENT_NONE;
12239   int i;
12240
12241   result = get_anim_parameter_value_ce(s);
12242
12243   if (result != ANIM_EVENT_NONE)
12244     return result;
12245
12246   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12247   {
12248     matching_char = strstr(s_ptr, pattern_1[i]);
12249     pattern_1_len = strlen(pattern_1[i]);
12250     result = event_value[i];
12251
12252     if (matching_char != NULL)
12253       break;
12254   }
12255
12256   if (matching_char == NULL)
12257     return ANIM_EVENT_NONE;
12258
12259   s_ptr = matching_char + pattern_1_len;
12260
12261   // check for main animation number ("anim_X" or "anim_XX")
12262   if (*s_ptr >= '0' && *s_ptr <= '9')
12263   {
12264     int gic_anim_nr = (*s_ptr++ - '0');
12265
12266     if (*s_ptr >= '0' && *s_ptr <= '9')
12267       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12268
12269     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12270       return ANIM_EVENT_NONE;
12271
12272     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12273   }
12274   else
12275   {
12276     // invalid main animation number specified
12277
12278     return ANIM_EVENT_NONE;
12279   }
12280
12281   // check for animation part number ("part_X" or "part_XX") (optional)
12282   if (strPrefix(s_ptr, pattern_2))
12283   {
12284     s_ptr += strlen(pattern_2);
12285
12286     if (*s_ptr >= '0' && *s_ptr <= '9')
12287     {
12288       int gic_part_nr = (*s_ptr++ - '0');
12289
12290       if (*s_ptr >= '0' && *s_ptr <= '9')
12291         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12292
12293       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12294         return ANIM_EVENT_NONE;
12295
12296       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12297     }
12298     else
12299     {
12300       // invalid animation part number specified
12301
12302       return ANIM_EVENT_NONE;
12303     }
12304   }
12305
12306   // discard result if next character is neither delimiter nor whitespace
12307   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12308         *s_ptr == ' ' || *s_ptr == '\t'))
12309     return ANIM_EVENT_NONE;
12310
12311   return result;
12312 }
12313
12314 static int get_anim_parameter_values(char *s)
12315 {
12316   int list_pos = ANIM_EVENT_UNDEFINED;
12317   int event_value = ANIM_EVENT_DEFAULT;
12318
12319   if (string_has_parameter(s, "any"))
12320     event_value |= ANIM_EVENT_ANY;
12321
12322   if (string_has_parameter(s, "click:self") ||
12323       string_has_parameter(s, "click") ||
12324       string_has_parameter(s, "self"))
12325     event_value |= ANIM_EVENT_SELF;
12326
12327   if (string_has_parameter(s, "unclick:any"))
12328     event_value |= ANIM_EVENT_UNCLICK_ANY;
12329
12330   // if animation event found, add it to global animation event list
12331   if (event_value != ANIM_EVENT_NONE)
12332     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12333
12334   while (s != NULL)
12335   {
12336     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12337     event_value = get_anim_parameter_value(s);
12338
12339     // if animation event found, add it to global animation event list
12340     if (event_value != ANIM_EVENT_NONE)
12341       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12342
12343     // continue with next part of the string, starting with next comma
12344     s = strchr(s + 1, ',');
12345   }
12346
12347   return list_pos;
12348 }
12349
12350 static int get_anim_action_parameter_value(char *token)
12351 {
12352   // check most common default case first to massively speed things up
12353   if (strEqual(token, ARG_UNDEFINED))
12354     return ANIM_EVENT_ACTION_NONE;
12355
12356   int result = getImageIDFromToken(token);
12357
12358   if (result == -1)
12359   {
12360     char *gfx_token = getStringCat2("gfx.", token);
12361
12362     result = getImageIDFromToken(gfx_token);
12363
12364     checked_free(gfx_token);
12365   }
12366
12367   if (result == -1)
12368   {
12369     Key key = getKeyFromX11KeyName(token);
12370
12371     if (key != KSYM_UNDEFINED)
12372       result = -(int)key;
12373   }
12374
12375   if (result == -1)
12376   {
12377     if (isURL(token))
12378     {
12379       result = get_hash_from_string(token);     // unsigned int => int
12380       result = ABS(result);                     // may be negative now
12381       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12382
12383       setHashEntry(anim_url_hash, int2str(result, 0), token);
12384     }
12385   }
12386
12387   if (result == -1)
12388     result = ANIM_EVENT_ACTION_NONE;
12389
12390   return result;
12391 }
12392
12393 int get_parameter_value(char *value_raw, char *suffix, int type)
12394 {
12395   char *value = getStringToLower(value_raw);
12396   int result = 0;       // probably a save default value
12397
12398   if (strEqual(suffix, ".direction"))
12399   {
12400     result = (strEqual(value, "left")  ? MV_LEFT :
12401               strEqual(value, "right") ? MV_RIGHT :
12402               strEqual(value, "up")    ? MV_UP :
12403               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12404   }
12405   else if (strEqual(suffix, ".position"))
12406   {
12407     result = (strEqual(value, "left")   ? POS_LEFT :
12408               strEqual(value, "right")  ? POS_RIGHT :
12409               strEqual(value, "top")    ? POS_TOP :
12410               strEqual(value, "upper")  ? POS_UPPER :
12411               strEqual(value, "middle") ? POS_MIDDLE :
12412               strEqual(value, "lower")  ? POS_LOWER :
12413               strEqual(value, "bottom") ? POS_BOTTOM :
12414               strEqual(value, "any")    ? POS_ANY :
12415               strEqual(value, "ce")     ? POS_CE :
12416               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12417               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12418   }
12419   else if (strEqual(suffix, ".align"))
12420   {
12421     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12422               strEqual(value, "right")  ? ALIGN_RIGHT :
12423               strEqual(value, "center") ? ALIGN_CENTER :
12424               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12425   }
12426   else if (strEqual(suffix, ".valign"))
12427   {
12428     result = (strEqual(value, "top")    ? VALIGN_TOP :
12429               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12430               strEqual(value, "middle") ? VALIGN_MIDDLE :
12431               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12432   }
12433   else if (strEqual(suffix, ".anim_mode"))
12434   {
12435     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12436               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12437               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12438               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12439               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12440               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12441               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12442               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12443               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12444               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12445               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12446               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12447               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12448               string_has_parameter(value, "all")        ? ANIM_ALL :
12449               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12450               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12451               ANIM_DEFAULT);
12452
12453     if (string_has_parameter(value, "once"))
12454       result |= ANIM_ONCE;
12455
12456     if (string_has_parameter(value, "reverse"))
12457       result |= ANIM_REVERSE;
12458
12459     if (string_has_parameter(value, "opaque_player"))
12460       result |= ANIM_OPAQUE_PLAYER;
12461
12462     if (string_has_parameter(value, "static_panel"))
12463       result |= ANIM_STATIC_PANEL;
12464   }
12465   else if (strEqual(suffix, ".init_event") ||
12466            strEqual(suffix, ".anim_event"))
12467   {
12468     result = get_anim_parameter_values(value);
12469   }
12470   else if (strEqual(suffix, ".init_delay_action") ||
12471            strEqual(suffix, ".anim_delay_action") ||
12472            strEqual(suffix, ".post_delay_action") ||
12473            strEqual(suffix, ".init_event_action") ||
12474            strEqual(suffix, ".anim_event_action"))
12475   {
12476     result = get_anim_action_parameter_value(value_raw);
12477   }
12478   else if (strEqual(suffix, ".class"))
12479   {
12480     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12481               get_hash_from_string(value));
12482   }
12483   else if (strEqual(suffix, ".style"))
12484   {
12485     result = STYLE_DEFAULT;
12486
12487     if (string_has_parameter(value, "accurate_borders"))
12488       result |= STYLE_ACCURATE_BORDERS;
12489
12490     if (string_has_parameter(value, "inner_corners"))
12491       result |= STYLE_INNER_CORNERS;
12492
12493     if (string_has_parameter(value, "reverse"))
12494       result |= STYLE_REVERSE;
12495
12496     if (string_has_parameter(value, "leftmost_position"))
12497       result |= STYLE_LEFTMOST_POSITION;
12498
12499     if (string_has_parameter(value, "block_clicks"))
12500       result |= STYLE_BLOCK;
12501
12502     if (string_has_parameter(value, "passthrough_clicks"))
12503       result |= STYLE_PASSTHROUGH;
12504
12505     if (string_has_parameter(value, "multiple_actions"))
12506       result |= STYLE_MULTIPLE_ACTIONS;
12507
12508     if (string_has_parameter(value, "consume_ce_event"))
12509       result |= STYLE_CONSUME_CE_EVENT;
12510   }
12511   else if (strEqual(suffix, ".fade_mode"))
12512   {
12513     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12514               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12515               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12516               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12517               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12518               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12519               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12520               FADE_MODE_DEFAULT);
12521   }
12522   else if (strEqual(suffix, ".auto_delay_unit"))
12523   {
12524     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12525               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12526               AUTO_DELAY_UNIT_DEFAULT);
12527   }
12528   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12529   {
12530     result = gfx.get_font_from_token_function(value);
12531   }
12532   else          // generic parameter of type integer or boolean
12533   {
12534     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12535               type == TYPE_INTEGER ? get_integer_from_string(value) :
12536               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12537               ARG_UNDEFINED_VALUE);
12538   }
12539
12540   free(value);
12541
12542   return result;
12543 }
12544
12545 static int get_token_parameter_value(char *token, char *value_raw)
12546 {
12547   char *suffix;
12548
12549   if (token == NULL || value_raw == NULL)
12550     return ARG_UNDEFINED_VALUE;
12551
12552   suffix = strrchr(token, '.');
12553   if (suffix == NULL)
12554     suffix = token;
12555
12556   if (strEqual(suffix, ".element"))
12557     return getElementFromToken(value_raw);
12558
12559   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12560   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12561 }
12562
12563 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12564                                      boolean ignore_defaults)
12565 {
12566   int i;
12567
12568   for (i = 0; image_config_vars[i].token != NULL; i++)
12569   {
12570     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12571
12572     // (ignore definitions set to "[DEFAULT]" which are already initialized)
12573     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12574       continue;
12575
12576     if (value != NULL)
12577       *image_config_vars[i].value =
12578         get_token_parameter_value(image_config_vars[i].token, value);
12579   }
12580 }
12581
12582 void InitMenuDesignSettings_Static(void)
12583 {
12584   // always start with reliable default values from static default config
12585   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12586 }
12587
12588 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12589 {
12590   int i;
12591
12592   // the following initializes hierarchical values from static configuration
12593
12594   // special case: initialize "ARG_DEFAULT" values in static default config
12595   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12596   titlescreen_initial_first_default.fade_mode  =
12597     title_initial_first_default.fade_mode;
12598   titlescreen_initial_first_default.fade_delay =
12599     title_initial_first_default.fade_delay;
12600   titlescreen_initial_first_default.post_delay =
12601     title_initial_first_default.post_delay;
12602   titlescreen_initial_first_default.auto_delay =
12603     title_initial_first_default.auto_delay;
12604   titlescreen_initial_first_default.auto_delay_unit =
12605     title_initial_first_default.auto_delay_unit;
12606   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
12607   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12608   titlescreen_first_default.post_delay = title_first_default.post_delay;
12609   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12610   titlescreen_first_default.auto_delay_unit =
12611     title_first_default.auto_delay_unit;
12612   titlemessage_initial_first_default.fade_mode  =
12613     title_initial_first_default.fade_mode;
12614   titlemessage_initial_first_default.fade_delay =
12615     title_initial_first_default.fade_delay;
12616   titlemessage_initial_first_default.post_delay =
12617     title_initial_first_default.post_delay;
12618   titlemessage_initial_first_default.auto_delay =
12619     title_initial_first_default.auto_delay;
12620   titlemessage_initial_first_default.auto_delay_unit =
12621     title_initial_first_default.auto_delay_unit;
12622   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
12623   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12624   titlemessage_first_default.post_delay = title_first_default.post_delay;
12625   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12626   titlemessage_first_default.auto_delay_unit =
12627     title_first_default.auto_delay_unit;
12628
12629   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
12630   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12631   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12632   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12633   titlescreen_initial_default.auto_delay_unit =
12634     title_initial_default.auto_delay_unit;
12635   titlescreen_default.fade_mode  = title_default.fade_mode;
12636   titlescreen_default.fade_delay = title_default.fade_delay;
12637   titlescreen_default.post_delay = title_default.post_delay;
12638   titlescreen_default.auto_delay = title_default.auto_delay;
12639   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12640   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
12641   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12642   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12643   titlemessage_initial_default.auto_delay_unit =
12644     title_initial_default.auto_delay_unit;
12645   titlemessage_default.fade_mode  = title_default.fade_mode;
12646   titlemessage_default.fade_delay = title_default.fade_delay;
12647   titlemessage_default.post_delay = title_default.post_delay;
12648   titlemessage_default.auto_delay = title_default.auto_delay;
12649   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12650
12651   // special case: initialize "ARG_DEFAULT" values in static default config
12652   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12653   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12654   {
12655     titlescreen_initial_first[i] = titlescreen_initial_first_default;
12656     titlescreen_first[i] = titlescreen_first_default;
12657     titlemessage_initial_first[i] = titlemessage_initial_first_default;
12658     titlemessage_first[i] = titlemessage_first_default;
12659
12660     titlescreen_initial[i] = titlescreen_initial_default;
12661     titlescreen[i] = titlescreen_default;
12662     titlemessage_initial[i] = titlemessage_initial_default;
12663     titlemessage[i] = titlemessage_default;
12664   }
12665
12666   // special case: initialize "ARG_DEFAULT" values in static default config
12667   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12668   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12669   {
12670     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
12671       continue;
12672
12673     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12674     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12675     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12676   }
12677
12678   // special case: initialize "ARG_DEFAULT" values in static default config
12679   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12680   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12681   {
12682     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12683     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12684     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12685
12686     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
12687       continue;
12688
12689     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12690   }
12691 }
12692
12693 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12694 {
12695   static struct
12696   {
12697     struct XY *dst, *src;
12698   }
12699   game_buttons_xy[] =
12700   {
12701     { &game.button.save,        &game.button.stop       },
12702     { &game.button.pause2,      &game.button.pause      },
12703     { &game.button.load,        &game.button.play       },
12704     { &game.button.undo,        &game.button.stop       },
12705     { &game.button.redo,        &game.button.play       },
12706
12707     { NULL,                     NULL                    }
12708   };
12709   int i, j;
12710
12711   // special case: initialize later added SETUP list size from LEVELS value
12712   if (menu.list_size[GAME_MODE_SETUP] == -1)
12713     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12714
12715   // set default position for snapshot buttons to stop/pause/play buttons
12716   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12717     if ((*game_buttons_xy[i].dst).x == -1 &&
12718         (*game_buttons_xy[i].dst).y == -1)
12719       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12720
12721   // --------------------------------------------------------------------------
12722   // dynamic viewports (including playfield margins, borders and alignments)
12723   // --------------------------------------------------------------------------
12724
12725   // dynamic viewports currently only supported for landscape mode
12726   int display_width  = MAX(video.display_width, video.display_height);
12727   int display_height = MIN(video.display_width, video.display_height);
12728
12729   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12730   {
12731     struct RectWithBorder *vp_window    = &viewport.window[i];
12732     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12733     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
12734     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
12735     boolean dynamic_window_width     = (vp_window->min_width     != -1);
12736     boolean dynamic_window_height    = (vp_window->min_height    != -1);
12737     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
12738     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12739
12740     // adjust window size if min/max width/height is specified
12741
12742     if (vp_window->min_width != -1)
12743     {
12744       int window_width = display_width;
12745
12746       // when using static window height, use aspect ratio of display
12747       if (vp_window->min_height == -1)
12748         window_width = vp_window->height * display_width / display_height;
12749
12750       vp_window->width = MAX(vp_window->min_width, window_width);
12751     }
12752
12753     if (vp_window->min_height != -1)
12754     {
12755       int window_height = display_height;
12756
12757       // when using static window width, use aspect ratio of display
12758       if (vp_window->min_width == -1)
12759         window_height = vp_window->width * display_height / display_width;
12760
12761       vp_window->height = MAX(vp_window->min_height, window_height);
12762     }
12763
12764     if (vp_window->max_width != -1)
12765       vp_window->width = MIN(vp_window->width, vp_window->max_width);
12766
12767     if (vp_window->max_height != -1)
12768       vp_window->height = MIN(vp_window->height, vp_window->max_height);
12769
12770     int playfield_width  = vp_window->width;
12771     int playfield_height = vp_window->height;
12772
12773     // adjust playfield size and position according to specified margins
12774
12775     playfield_width  -= vp_playfield->margin_left;
12776     playfield_width  -= vp_playfield->margin_right;
12777
12778     playfield_height -= vp_playfield->margin_top;
12779     playfield_height -= vp_playfield->margin_bottom;
12780
12781     // adjust playfield size if min/max width/height is specified
12782
12783     if (vp_playfield->min_width != -1)
12784       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12785
12786     if (vp_playfield->min_height != -1)
12787       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12788
12789     if (vp_playfield->max_width != -1)
12790       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12791
12792     if (vp_playfield->max_height != -1)
12793       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12794
12795     // adjust playfield position according to specified alignment
12796
12797     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12798       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12799     else if (vp_playfield->align == ALIGN_CENTER)
12800       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12801     else if (vp_playfield->align == ALIGN_RIGHT)
12802       vp_playfield->x += playfield_width - vp_playfield->width;
12803
12804     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12805       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12806     else if (vp_playfield->valign == VALIGN_MIDDLE)
12807       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12808     else if (vp_playfield->valign == VALIGN_BOTTOM)
12809       vp_playfield->y += playfield_height - vp_playfield->height;
12810
12811     vp_playfield->x += vp_playfield->margin_left;
12812     vp_playfield->y += vp_playfield->margin_top;
12813
12814     // adjust individual playfield borders if only default border is specified
12815
12816     if (vp_playfield->border_left == -1)
12817       vp_playfield->border_left = vp_playfield->border_size;
12818     if (vp_playfield->border_right == -1)
12819       vp_playfield->border_right = vp_playfield->border_size;
12820     if (vp_playfield->border_top == -1)
12821       vp_playfield->border_top = vp_playfield->border_size;
12822     if (vp_playfield->border_bottom == -1)
12823       vp_playfield->border_bottom = vp_playfield->border_size;
12824
12825     // set dynamic playfield borders if borders are specified as undefined
12826     // (but only if window size was dynamic and playfield size was static)
12827
12828     if (dynamic_window_width && !dynamic_playfield_width)
12829     {
12830       if (vp_playfield->border_left == -1)
12831       {
12832         vp_playfield->border_left = (vp_playfield->x -
12833                                      vp_playfield->margin_left);
12834         vp_playfield->x     -= vp_playfield->border_left;
12835         vp_playfield->width += vp_playfield->border_left;
12836       }
12837
12838       if (vp_playfield->border_right == -1)
12839       {
12840         vp_playfield->border_right = (vp_window->width -
12841                                       vp_playfield->x -
12842                                       vp_playfield->width -
12843                                       vp_playfield->margin_right);
12844         vp_playfield->width += vp_playfield->border_right;
12845       }
12846     }
12847
12848     if (dynamic_window_height && !dynamic_playfield_height)
12849     {
12850       if (vp_playfield->border_top == -1)
12851       {
12852         vp_playfield->border_top = (vp_playfield->y -
12853                                     vp_playfield->margin_top);
12854         vp_playfield->y      -= vp_playfield->border_top;
12855         vp_playfield->height += vp_playfield->border_top;
12856       }
12857
12858       if (vp_playfield->border_bottom == -1)
12859       {
12860         vp_playfield->border_bottom = (vp_window->height -
12861                                        vp_playfield->y -
12862                                        vp_playfield->height -
12863                                        vp_playfield->margin_bottom);
12864         vp_playfield->height += vp_playfield->border_bottom;
12865       }
12866     }
12867
12868     // adjust playfield size to be a multiple of a defined alignment tile size
12869
12870     int align_size = vp_playfield->align_size;
12871     int playfield_xtiles = vp_playfield->width  / align_size;
12872     int playfield_ytiles = vp_playfield->height / align_size;
12873     int playfield_width_corrected  = playfield_xtiles * align_size;
12874     int playfield_height_corrected = playfield_ytiles * align_size;
12875     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12876                                  i == GFX_SPECIAL_ARG_EDITOR);
12877
12878     if (is_playfield_mode &&
12879         dynamic_playfield_width &&
12880         vp_playfield->width != playfield_width_corrected)
12881     {
12882       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12883
12884       vp_playfield->width = playfield_width_corrected;
12885
12886       if (vp_playfield->align == ALIGN_LEFT)
12887       {
12888         vp_playfield->border_left += playfield_xdiff;
12889       }
12890       else if (vp_playfield->align == ALIGN_RIGHT)
12891       {
12892         vp_playfield->border_right += playfield_xdiff;
12893       }
12894       else if (vp_playfield->align == ALIGN_CENTER)
12895       {
12896         int border_left_diff  = playfield_xdiff / 2;
12897         int border_right_diff = playfield_xdiff - border_left_diff;
12898
12899         vp_playfield->border_left  += border_left_diff;
12900         vp_playfield->border_right += border_right_diff;
12901       }
12902     }
12903
12904     if (is_playfield_mode &&
12905         dynamic_playfield_height &&
12906         vp_playfield->height != playfield_height_corrected)
12907     {
12908       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12909
12910       vp_playfield->height = playfield_height_corrected;
12911
12912       if (vp_playfield->valign == VALIGN_TOP)
12913       {
12914         vp_playfield->border_top += playfield_ydiff;
12915       }
12916       else if (vp_playfield->align == VALIGN_BOTTOM)
12917       {
12918         vp_playfield->border_right += playfield_ydiff;
12919       }
12920       else if (vp_playfield->align == VALIGN_MIDDLE)
12921       {
12922         int border_top_diff    = playfield_ydiff / 2;
12923         int border_bottom_diff = playfield_ydiff - border_top_diff;
12924
12925         vp_playfield->border_top    += border_top_diff;
12926         vp_playfield->border_bottom += border_bottom_diff;
12927       }
12928     }
12929
12930     // adjust door positions according to specified alignment
12931
12932     for (j = 0; j < 2; j++)
12933     {
12934       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12935
12936       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12937         vp_door->x = ALIGNED_VP_XPOS(vp_door);
12938       else if (vp_door->align == ALIGN_CENTER)
12939         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12940       else if (vp_door->align == ALIGN_RIGHT)
12941         vp_door->x += vp_window->width - vp_door->width;
12942
12943       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12944         vp_door->y = ALIGNED_VP_YPOS(vp_door);
12945       else if (vp_door->valign == VALIGN_MIDDLE)
12946         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12947       else if (vp_door->valign == VALIGN_BOTTOM)
12948         vp_door->y += vp_window->height - vp_door->height;
12949     }
12950   }
12951 }
12952
12953 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12954 {
12955   static struct
12956   {
12957     struct XYTileSize *dst, *src;
12958     int graphic;
12959   }
12960   editor_buttons_xy[] =
12961   {
12962     {
12963       &editor.button.element_left,      &editor.palette.element_left,
12964       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12965     },
12966     {
12967       &editor.button.element_middle,    &editor.palette.element_middle,
12968       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12969     },
12970     {
12971       &editor.button.element_right,     &editor.palette.element_right,
12972       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12973     },
12974
12975     { NULL,                     NULL                    }
12976   };
12977   int i;
12978
12979   // set default position for element buttons to element graphics
12980   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12981   {
12982     if ((*editor_buttons_xy[i].dst).x == -1 &&
12983         (*editor_buttons_xy[i].dst).y == -1)
12984     {
12985       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12986
12987       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12988
12989       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12990     }
12991   }
12992
12993   // adjust editor palette rows and columns if specified to be dynamic
12994
12995   if (editor.palette.cols == -1)
12996   {
12997     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12998     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12999     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13000
13001     editor.palette.cols = (vp_width - sc_width) / bt_width;
13002
13003     if (editor.palette.x == -1)
13004     {
13005       int palette_width = editor.palette.cols * bt_width + sc_width;
13006
13007       editor.palette.x = (vp_width - palette_width) / 2;
13008     }
13009   }
13010
13011   if (editor.palette.rows == -1)
13012   {
13013     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13014     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13015     int tx_height = getFontHeight(FONT_TEXT_2);
13016
13017     editor.palette.rows = (vp_height - tx_height) / bt_height;
13018
13019     if (editor.palette.y == -1)
13020     {
13021       int palette_height = editor.palette.rows * bt_height + tx_height;
13022
13023       editor.palette.y = (vp_height - palette_height) / 2;
13024     }
13025   }
13026 }
13027
13028 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13029                                                       boolean initialize)
13030 {
13031   // special case: check if network and preview player positions are redefined,
13032   // to compare this later against the main menu level preview being redefined
13033   struct TokenIntPtrInfo menu_config_players[] =
13034   {
13035     { "main.network_players.x", &menu.main.network_players.redefined    },
13036     { "main.network_players.y", &menu.main.network_players.redefined    },
13037     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13038     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13039     { "preview.x",              &preview.redefined                      },
13040     { "preview.y",              &preview.redefined                      }
13041   };
13042   int i;
13043
13044   if (initialize)
13045   {
13046     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13047       *menu_config_players[i].value = FALSE;
13048   }
13049   else
13050   {
13051     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13052       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13053         *menu_config_players[i].value = TRUE;
13054   }
13055 }
13056
13057 static void InitMenuDesignSettings_PreviewPlayers(void)
13058 {
13059   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13060 }
13061
13062 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13063 {
13064   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13065 }
13066
13067 static void LoadMenuDesignSettingsFromFilename(char *filename)
13068 {
13069   static struct TitleFadingInfo tfi;
13070   static struct TitleMessageInfo tmi;
13071   static struct TokenInfo title_tokens[] =
13072   {
13073     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13074     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13075     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13076     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13077     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13078
13079     { -1,               NULL,                   NULL                    }
13080   };
13081   static struct TokenInfo titlemessage_tokens[] =
13082   {
13083     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13084     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13085     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13086     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13087     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13088     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13089     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13090     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13091     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13092     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13093     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13094     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13095     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13096     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13097     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13098     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13099     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13100     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13101
13102     { -1,               NULL,                   NULL                    }
13103   };
13104   static struct
13105   {
13106     struct TitleFadingInfo *info;
13107     char *text;
13108   }
13109   title_info[] =
13110   {
13111     // initialize first titles from "enter screen" definitions, if defined
13112     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13113     { &title_first_default,             "menu.enter_screen.TITLE"       },
13114
13115     // initialize title screens from "next screen" definitions, if defined
13116     { &title_initial_default,           "menu.next_screen.TITLE"        },
13117     { &title_default,                   "menu.next_screen.TITLE"        },
13118
13119     { NULL,                             NULL                            }
13120   };
13121   static struct
13122   {
13123     struct TitleMessageInfo *array;
13124     char *text;
13125   }
13126   titlemessage_arrays[] =
13127   {
13128     // initialize first titles from "enter screen" definitions, if defined
13129     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13130     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13131     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13132     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13133
13134     // initialize titles from "next screen" definitions, if defined
13135     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13136     { titlescreen,                      "menu.next_screen.TITLE"        },
13137     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13138     { titlemessage,                     "menu.next_screen.TITLE"        },
13139
13140     // overwrite titles with title definitions, if defined
13141     { titlescreen_initial_first,        "[title_initial]"               },
13142     { titlescreen_first,                "[title]"                       },
13143     { titlemessage_initial_first,       "[title_initial]"               },
13144     { titlemessage_first,               "[title]"                       },
13145
13146     { titlescreen_initial,              "[title_initial]"               },
13147     { titlescreen,                      "[title]"                       },
13148     { titlemessage_initial,             "[title_initial]"               },
13149     { titlemessage,                     "[title]"                       },
13150
13151     // overwrite titles with title screen/message definitions, if defined
13152     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13153     { titlescreen_first,                "[titlescreen]"                 },
13154     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13155     { titlemessage_first,               "[titlemessage]"                },
13156
13157     { titlescreen_initial,              "[titlescreen_initial]"         },
13158     { titlescreen,                      "[titlescreen]"                 },
13159     { titlemessage_initial,             "[titlemessage_initial]"        },
13160     { titlemessage,                     "[titlemessage]"                },
13161
13162     { NULL,                             NULL                            }
13163   };
13164   SetupFileHash *setup_file_hash;
13165   int i, j, k;
13166
13167   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13168     return;
13169
13170   // the following initializes hierarchical values from dynamic configuration
13171
13172   // special case: initialize with default values that may be overwritten
13173   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13174   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13175   {
13176     struct TokenIntPtrInfo menu_config[] =
13177     {
13178       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13179       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13180       { "menu.list_size",       &menu.list_size[i]      }
13181     };
13182
13183     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13184     {
13185       char *token = menu_config[j].token;
13186       char *value = getHashEntry(setup_file_hash, token);
13187
13188       if (value != NULL)
13189         *menu_config[j].value = get_integer_from_string(value);
13190     }
13191   }
13192
13193   // special case: initialize with default values that may be overwritten
13194   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13195   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13196   {
13197     struct TokenIntPtrInfo menu_config[] =
13198     {
13199       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13200       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13201       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13202       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13203       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13204     };
13205
13206     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13207     {
13208       char *token = menu_config[j].token;
13209       char *value = getHashEntry(setup_file_hash, token);
13210
13211       if (value != NULL)
13212         *menu_config[j].value = get_integer_from_string(value);
13213     }
13214   }
13215
13216   // special case: initialize with default values that may be overwritten
13217   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13218   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13219   {
13220     struct TokenIntPtrInfo menu_config[] =
13221     {
13222       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13223       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13224     };
13225
13226     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13227     {
13228       char *token = menu_config[j].token;
13229       char *value = getHashEntry(setup_file_hash, token);
13230
13231       if (value != NULL)
13232         *menu_config[j].value = get_integer_from_string(value);
13233     }
13234   }
13235
13236   // special case: initialize with default values that may be overwritten
13237   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13238   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13239   {
13240     struct TokenIntPtrInfo menu_config[] =
13241     {
13242       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13243       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13244       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13245       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13246       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13247       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13248       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13249       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13250       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13251       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13252     };
13253
13254     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13255     {
13256       char *token = menu_config[j].token;
13257       char *value = getHashEntry(setup_file_hash, token);
13258
13259       if (value != NULL)
13260         *menu_config[j].value = get_integer_from_string(value);
13261     }
13262   }
13263
13264   // special case: initialize with default values that may be overwritten
13265   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13266   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13267   {
13268     struct TokenIntPtrInfo menu_config[] =
13269     {
13270       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13271       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13272       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13273       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13274       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13275       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13276       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13277       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13278       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13279     };
13280
13281     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13282     {
13283       char *token = menu_config[j].token;
13284       char *value = getHashEntry(setup_file_hash, token);
13285
13286       if (value != NULL)
13287         *menu_config[j].value = get_token_parameter_value(token, value);
13288     }
13289   }
13290
13291   // special case: initialize with default values that may be overwritten
13292   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13293   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13294   {
13295     struct
13296     {
13297       char *token_prefix;
13298       struct RectWithBorder *struct_ptr;
13299     }
13300     vp_struct[] =
13301     {
13302       { "viewport.window",      &viewport.window[i]     },
13303       { "viewport.playfield",   &viewport.playfield[i]  },
13304       { "viewport.door_1",      &viewport.door_1[i]     },
13305       { "viewport.door_2",      &viewport.door_2[i]     }
13306     };
13307
13308     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13309     {
13310       struct TokenIntPtrInfo vp_config[] =
13311       {
13312         { ".x",                 &vp_struct[j].struct_ptr->x             },
13313         { ".y",                 &vp_struct[j].struct_ptr->y             },
13314         { ".width",             &vp_struct[j].struct_ptr->width         },
13315         { ".height",            &vp_struct[j].struct_ptr->height        },
13316         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13317         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13318         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13319         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13320         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13321         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13322         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13323         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13324         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13325         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13326         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13327         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13328         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13329         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13330         { ".align",             &vp_struct[j].struct_ptr->align         },
13331         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13332       };
13333
13334       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13335       {
13336         char *token = getStringCat2(vp_struct[j].token_prefix,
13337                                     vp_config[k].token);
13338         char *value = getHashEntry(setup_file_hash, token);
13339
13340         if (value != NULL)
13341           *vp_config[k].value = get_token_parameter_value(token, value);
13342
13343         free(token);
13344       }
13345     }
13346   }
13347
13348   // special case: initialize with default values that may be overwritten
13349   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13350   for (i = 0; title_info[i].info != NULL; i++)
13351   {
13352     struct TitleFadingInfo *info = title_info[i].info;
13353     char *base_token = title_info[i].text;
13354
13355     for (j = 0; title_tokens[j].type != -1; j++)
13356     {
13357       char *token = getStringCat2(base_token, title_tokens[j].text);
13358       char *value = getHashEntry(setup_file_hash, token);
13359
13360       if (value != NULL)
13361       {
13362         int parameter_value = get_token_parameter_value(token, value);
13363
13364         tfi = *info;
13365
13366         *(int *)title_tokens[j].value = (int)parameter_value;
13367
13368         *info = tfi;
13369       }
13370
13371       free(token);
13372     }
13373   }
13374
13375   // special case: initialize with default values that may be overwritten
13376   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13377   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13378   {
13379     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13380     char *base_token = titlemessage_arrays[i].text;
13381
13382     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13383     {
13384       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13385       char *value = getHashEntry(setup_file_hash, token);
13386
13387       if (value != NULL)
13388       {
13389         int parameter_value = get_token_parameter_value(token, value);
13390
13391         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13392         {
13393           tmi = array[k];
13394
13395           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13396             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13397           else
13398             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13399
13400           array[k] = tmi;
13401         }
13402       }
13403
13404       free(token);
13405     }
13406   }
13407
13408   // read (and overwrite with) values that may be specified in config file
13409   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13410
13411   // special case: check if network and preview player positions are redefined
13412   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13413
13414   freeSetupFileHash(setup_file_hash);
13415 }
13416
13417 void LoadMenuDesignSettings(void)
13418 {
13419   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13420
13421   InitMenuDesignSettings_Static();
13422   InitMenuDesignSettings_SpecialPreProcessing();
13423   InitMenuDesignSettings_PreviewPlayers();
13424
13425   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13426   {
13427     // first look for special settings configured in level series config
13428     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13429
13430     if (fileExists(filename_base))
13431       LoadMenuDesignSettingsFromFilename(filename_base);
13432   }
13433
13434   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13435
13436   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13437     LoadMenuDesignSettingsFromFilename(filename_local);
13438
13439   InitMenuDesignSettings_SpecialPostProcessing();
13440 }
13441
13442 void LoadMenuDesignSettings_AfterGraphics(void)
13443 {
13444   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13445 }
13446
13447 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13448                                 boolean ignore_defaults)
13449 {
13450   int i;
13451
13452   for (i = 0; sound_config_vars[i].token != NULL; i++)
13453   {
13454     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13455
13456     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13457     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13458       continue;
13459
13460     if (value != NULL)
13461       *sound_config_vars[i].value =
13462         get_token_parameter_value(sound_config_vars[i].token, value);
13463   }
13464 }
13465
13466 void InitSoundSettings_Static(void)
13467 {
13468   // always start with reliable default values from static default config
13469   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13470 }
13471
13472 static void LoadSoundSettingsFromFilename(char *filename)
13473 {
13474   SetupFileHash *setup_file_hash;
13475
13476   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13477     return;
13478
13479   // read (and overwrite with) values that may be specified in config file
13480   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13481
13482   freeSetupFileHash(setup_file_hash);
13483 }
13484
13485 void LoadSoundSettings(void)
13486 {
13487   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13488
13489   InitSoundSettings_Static();
13490
13491   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13492   {
13493     // first look for special settings configured in level series config
13494     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13495
13496     if (fileExists(filename_base))
13497       LoadSoundSettingsFromFilename(filename_base);
13498   }
13499
13500   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13501
13502   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13503     LoadSoundSettingsFromFilename(filename_local);
13504 }
13505
13506 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13507 {
13508   char *filename = getEditorSetupFilename();
13509   SetupFileList *setup_file_list, *list;
13510   SetupFileHash *element_hash;
13511   int num_unknown_tokens = 0;
13512   int i;
13513
13514   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13515     return;
13516
13517   element_hash = newSetupFileHash();
13518
13519   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13520     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13521
13522   // determined size may be larger than needed (due to unknown elements)
13523   *num_elements = 0;
13524   for (list = setup_file_list; list != NULL; list = list->next)
13525     (*num_elements)++;
13526
13527   // add space for up to 3 more elements for padding that may be needed
13528   *num_elements += 3;
13529
13530   // free memory for old list of elements, if needed
13531   checked_free(*elements);
13532
13533   // allocate memory for new list of elements
13534   *elements = checked_malloc(*num_elements * sizeof(int));
13535
13536   *num_elements = 0;
13537   for (list = setup_file_list; list != NULL; list = list->next)
13538   {
13539     char *value = getHashEntry(element_hash, list->token);
13540
13541     if (value == NULL)          // try to find obsolete token mapping
13542     {
13543       char *mapped_token = get_mapped_token(list->token);
13544
13545       if (mapped_token != NULL)
13546       {
13547         value = getHashEntry(element_hash, mapped_token);
13548
13549         free(mapped_token);
13550       }
13551     }
13552
13553     if (value != NULL)
13554     {
13555       (*elements)[(*num_elements)++] = atoi(value);
13556     }
13557     else
13558     {
13559       if (num_unknown_tokens == 0)
13560       {
13561         Warn("---");
13562         Warn("unknown token(s) found in config file:");
13563         Warn("- config file: '%s'", filename);
13564
13565         num_unknown_tokens++;
13566       }
13567
13568       Warn("- token: '%s'", list->token);
13569     }
13570   }
13571
13572   if (num_unknown_tokens > 0)
13573     Warn("---");
13574
13575   while (*num_elements % 4)     // pad with empty elements, if needed
13576     (*elements)[(*num_elements)++] = EL_EMPTY;
13577
13578   freeSetupFileList(setup_file_list);
13579   freeSetupFileHash(element_hash);
13580
13581 #if 0
13582   for (i = 0; i < *num_elements; i++)
13583     Debug("editor", "element '%s' [%d]\n",
13584           element_info[(*elements)[i]].token_name, (*elements)[i]);
13585 #endif
13586 }
13587
13588 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13589                                                      boolean is_sound)
13590 {
13591   SetupFileHash *setup_file_hash = NULL;
13592   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13593   char *filename_music, *filename_prefix, *filename_info;
13594   struct
13595   {
13596     char *token;
13597     char **value_ptr;
13598   }
13599   token_to_value_ptr[] =
13600   {
13601     { "title_header",   &tmp_music_file_info.title_header       },
13602     { "artist_header",  &tmp_music_file_info.artist_header      },
13603     { "album_header",   &tmp_music_file_info.album_header       },
13604     { "year_header",    &tmp_music_file_info.year_header        },
13605     { "played_header",  &tmp_music_file_info.played_header      },
13606
13607     { "title",          &tmp_music_file_info.title              },
13608     { "artist",         &tmp_music_file_info.artist             },
13609     { "album",          &tmp_music_file_info.album              },
13610     { "year",           &tmp_music_file_info.year               },
13611     { "played",         &tmp_music_file_info.played             },
13612
13613     { NULL,             NULL                                    },
13614   };
13615   int i;
13616
13617   filename_music = (is_sound ? getCustomSoundFilename(basename) :
13618                     getCustomMusicFilename(basename));
13619
13620   if (filename_music == NULL)
13621     return NULL;
13622
13623   // ---------- try to replace file extension ----------
13624
13625   filename_prefix = getStringCopy(filename_music);
13626   if (strrchr(filename_prefix, '.') != NULL)
13627     *strrchr(filename_prefix, '.') = '\0';
13628   filename_info = getStringCat2(filename_prefix, ".txt");
13629
13630   if (fileExists(filename_info))
13631     setup_file_hash = loadSetupFileHash(filename_info);
13632
13633   free(filename_prefix);
13634   free(filename_info);
13635
13636   if (setup_file_hash == NULL)
13637   {
13638     // ---------- try to add file extension ----------
13639
13640     filename_prefix = getStringCopy(filename_music);
13641     filename_info = getStringCat2(filename_prefix, ".txt");
13642
13643     if (fileExists(filename_info))
13644       setup_file_hash = loadSetupFileHash(filename_info);
13645
13646     free(filename_prefix);
13647     free(filename_info);
13648   }
13649
13650   if (setup_file_hash == NULL)
13651     return NULL;
13652
13653   // ---------- music file info found ----------
13654
13655   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13656
13657   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13658   {
13659     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13660
13661     *token_to_value_ptr[i].value_ptr =
13662       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13663   }
13664
13665   tmp_music_file_info.basename = getStringCopy(basename);
13666   tmp_music_file_info.music = music;
13667   tmp_music_file_info.is_sound = is_sound;
13668
13669   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13670   *new_music_file_info = tmp_music_file_info;
13671
13672   return new_music_file_info;
13673 }
13674
13675 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13676 {
13677   return get_music_file_info_ext(basename, music, FALSE);
13678 }
13679
13680 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13681 {
13682   return get_music_file_info_ext(basename, sound, TRUE);
13683 }
13684
13685 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13686                                      char *basename, boolean is_sound)
13687 {
13688   for (; list != NULL; list = list->next)
13689     if (list->is_sound == is_sound && strEqual(list->basename, basename))
13690       return TRUE;
13691
13692   return FALSE;
13693 }
13694
13695 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13696 {
13697   return music_info_listed_ext(list, basename, FALSE);
13698 }
13699
13700 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13701 {
13702   return music_info_listed_ext(list, basename, TRUE);
13703 }
13704
13705 void LoadMusicInfo(void)
13706 {
13707   int num_music_noconf = getMusicListSize_NoConf();
13708   int num_music = getMusicListSize();
13709   int num_sounds = getSoundListSize();
13710   struct FileInfo *music, *sound;
13711   struct MusicFileInfo *next, **new;
13712
13713   int i;
13714
13715   while (music_file_info != NULL)
13716   {
13717     next = music_file_info->next;
13718
13719     checked_free(music_file_info->basename);
13720
13721     checked_free(music_file_info->title_header);
13722     checked_free(music_file_info->artist_header);
13723     checked_free(music_file_info->album_header);
13724     checked_free(music_file_info->year_header);
13725     checked_free(music_file_info->played_header);
13726
13727     checked_free(music_file_info->title);
13728     checked_free(music_file_info->artist);
13729     checked_free(music_file_info->album);
13730     checked_free(music_file_info->year);
13731     checked_free(music_file_info->played);
13732
13733     free(music_file_info);
13734
13735     music_file_info = next;
13736   }
13737
13738   new = &music_file_info;
13739
13740   // get (configured or unconfigured) music file info for all levels
13741   for (i = leveldir_current->first_level;
13742        i <= leveldir_current->last_level; i++)
13743   {
13744     int music_nr;
13745
13746     if (levelset.music[i] != MUS_UNDEFINED)
13747     {
13748       // get music file info for configured level music
13749       music_nr = levelset.music[i];
13750     }
13751     else if (num_music_noconf > 0)
13752     {
13753       // get music file info for unconfigured level music
13754       int level_pos = i - leveldir_current->first_level;
13755
13756       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13757     }
13758     else
13759     {
13760       continue;
13761     }
13762
13763     char *basename = getMusicInfoEntryFilename(music_nr);
13764
13765     if (basename == NULL)
13766       continue;
13767
13768     if (!music_info_listed(music_file_info, basename))
13769     {
13770       *new = get_music_file_info(basename, music_nr);
13771
13772       if (*new != NULL)
13773         new = &(*new)->next;
13774     }
13775   }
13776
13777   // get music file info for all remaining configured music files
13778   for (i = 0; i < num_music; i++)
13779   {
13780     music = getMusicListEntry(i);
13781
13782     if (music->filename == NULL)
13783       continue;
13784
13785     if (strEqual(music->filename, UNDEFINED_FILENAME))
13786       continue;
13787
13788     // a configured file may be not recognized as music
13789     if (!FileIsMusic(music->filename))
13790       continue;
13791
13792     if (!music_info_listed(music_file_info, music->filename))
13793     {
13794       *new = get_music_file_info(music->filename, i);
13795
13796       if (*new != NULL)
13797         new = &(*new)->next;
13798     }
13799   }
13800
13801   // get sound file info for all configured sound files
13802   for (i = 0; i < num_sounds; i++)
13803   {
13804     sound = getSoundListEntry(i);
13805
13806     if (sound->filename == NULL)
13807       continue;
13808
13809     if (strEqual(sound->filename, UNDEFINED_FILENAME))
13810       continue;
13811
13812     // a configured file may be not recognized as sound
13813     if (!FileIsSound(sound->filename))
13814       continue;
13815
13816     if (!sound_info_listed(music_file_info, sound->filename))
13817     {
13818       *new = get_sound_file_info(sound->filename, i);
13819       if (*new != NULL)
13820         new = &(*new)->next;
13821     }
13822   }
13823
13824   // add pointers to previous list nodes
13825
13826   struct MusicFileInfo *node = music_file_info;
13827
13828   while (node != NULL)
13829   {
13830     if (node->next)
13831       node->next->prev = node;
13832
13833     node = node->next;
13834   }
13835 }
13836
13837 static void add_helpanim_entry(int element, int action, int direction,
13838                                int delay, int *num_list_entries)
13839 {
13840   struct HelpAnimInfo *new_list_entry;
13841   (*num_list_entries)++;
13842
13843   helpanim_info =
13844     checked_realloc(helpanim_info,
13845                     *num_list_entries * sizeof(struct HelpAnimInfo));
13846   new_list_entry = &helpanim_info[*num_list_entries - 1];
13847
13848   new_list_entry->element = element;
13849   new_list_entry->action = action;
13850   new_list_entry->direction = direction;
13851   new_list_entry->delay = delay;
13852 }
13853
13854 static void print_unknown_token(char *filename, char *token, int token_nr)
13855 {
13856   if (token_nr == 0)
13857   {
13858     Warn("---");
13859     Warn("unknown token(s) found in config file:");
13860     Warn("- config file: '%s'", filename);
13861   }
13862
13863   Warn("- token: '%s'", token);
13864 }
13865
13866 static void print_unknown_token_end(int token_nr)
13867 {
13868   if (token_nr > 0)
13869     Warn("---");
13870 }
13871
13872 void LoadHelpAnimInfo(void)
13873 {
13874   char *filename = getHelpAnimFilename();
13875   SetupFileList *setup_file_list = NULL, *list;
13876   SetupFileHash *element_hash, *action_hash, *direction_hash;
13877   int num_list_entries = 0;
13878   int num_unknown_tokens = 0;
13879   int i;
13880
13881   if (fileExists(filename))
13882     setup_file_list = loadSetupFileList(filename);
13883
13884   if (setup_file_list == NULL)
13885   {
13886     // use reliable default values from static configuration
13887     SetupFileList *insert_ptr;
13888
13889     insert_ptr = setup_file_list =
13890       newSetupFileList(helpanim_config[0].token,
13891                        helpanim_config[0].value);
13892
13893     for (i = 1; helpanim_config[i].token; i++)
13894       insert_ptr = addListEntry(insert_ptr,
13895                                 helpanim_config[i].token,
13896                                 helpanim_config[i].value);
13897   }
13898
13899   element_hash   = newSetupFileHash();
13900   action_hash    = newSetupFileHash();
13901   direction_hash = newSetupFileHash();
13902
13903   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13904     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13905
13906   for (i = 0; i < NUM_ACTIONS; i++)
13907     setHashEntry(action_hash, element_action_info[i].suffix,
13908                  i_to_a(element_action_info[i].value));
13909
13910   // do not store direction index (bit) here, but direction value!
13911   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13912     setHashEntry(direction_hash, element_direction_info[i].suffix,
13913                  i_to_a(1 << element_direction_info[i].value));
13914
13915   for (list = setup_file_list; list != NULL; list = list->next)
13916   {
13917     char *element_token, *action_token, *direction_token;
13918     char *element_value, *action_value, *direction_value;
13919     int delay = atoi(list->value);
13920
13921     if (strEqual(list->token, "end"))
13922     {
13923       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13924
13925       continue;
13926     }
13927
13928     /* first try to break element into element/action/direction parts;
13929        if this does not work, also accept combined "element[.act][.dir]"
13930        elements (like "dynamite.active"), which are unique elements */
13931
13932     if (strchr(list->token, '.') == NULL)       // token contains no '.'
13933     {
13934       element_value = getHashEntry(element_hash, list->token);
13935       if (element_value != NULL)        // element found
13936         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13937                            &num_list_entries);
13938       else
13939       {
13940         // no further suffixes found -- this is not an element
13941         print_unknown_token(filename, list->token, num_unknown_tokens++);
13942       }
13943
13944       continue;
13945     }
13946
13947     // token has format "<prefix>.<something>"
13948
13949     action_token = strchr(list->token, '.');    // suffix may be action ...
13950     direction_token = action_token;             // ... or direction
13951
13952     element_token = getStringCopy(list->token);
13953     *strchr(element_token, '.') = '\0';
13954
13955     element_value = getHashEntry(element_hash, element_token);
13956
13957     if (element_value == NULL)          // this is no element
13958     {
13959       element_value = getHashEntry(element_hash, list->token);
13960       if (element_value != NULL)        // combined element found
13961         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13962                            &num_list_entries);
13963       else
13964         print_unknown_token(filename, list->token, num_unknown_tokens++);
13965
13966       free(element_token);
13967
13968       continue;
13969     }
13970
13971     action_value = getHashEntry(action_hash, action_token);
13972
13973     if (action_value != NULL)           // action found
13974     {
13975       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13976                     &num_list_entries);
13977
13978       free(element_token);
13979
13980       continue;
13981     }
13982
13983     direction_value = getHashEntry(direction_hash, direction_token);
13984
13985     if (direction_value != NULL)        // direction found
13986     {
13987       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13988                          &num_list_entries);
13989
13990       free(element_token);
13991
13992       continue;
13993     }
13994
13995     if (strchr(action_token + 1, '.') == NULL)
13996     {
13997       // no further suffixes found -- this is not an action nor direction
13998
13999       element_value = getHashEntry(element_hash, list->token);
14000       if (element_value != NULL)        // combined element found
14001         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14002                            &num_list_entries);
14003       else
14004         print_unknown_token(filename, list->token, num_unknown_tokens++);
14005
14006       free(element_token);
14007
14008       continue;
14009     }
14010
14011     // token has format "<prefix>.<suffix>.<something>"
14012
14013     direction_token = strchr(action_token + 1, '.');
14014
14015     action_token = getStringCopy(action_token);
14016     *strchr(action_token + 1, '.') = '\0';
14017
14018     action_value = getHashEntry(action_hash, action_token);
14019
14020     if (action_value == NULL)           // this is no action
14021     {
14022       element_value = getHashEntry(element_hash, list->token);
14023       if (element_value != NULL)        // combined element found
14024         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14025                            &num_list_entries);
14026       else
14027         print_unknown_token(filename, list->token, num_unknown_tokens++);
14028
14029       free(element_token);
14030       free(action_token);
14031
14032       continue;
14033     }
14034
14035     direction_value = getHashEntry(direction_hash, direction_token);
14036
14037     if (direction_value != NULL)        // direction found
14038     {
14039       add_helpanim_entry(atoi(element_value), atoi(action_value),
14040                          atoi(direction_value), delay, &num_list_entries);
14041
14042       free(element_token);
14043       free(action_token);
14044
14045       continue;
14046     }
14047
14048     // this is no direction
14049
14050     element_value = getHashEntry(element_hash, list->token);
14051     if (element_value != NULL)          // combined element found
14052       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14053                          &num_list_entries);
14054     else
14055       print_unknown_token(filename, list->token, num_unknown_tokens++);
14056
14057     free(element_token);
14058     free(action_token);
14059   }
14060
14061   print_unknown_token_end(num_unknown_tokens);
14062
14063   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14064   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14065
14066   freeSetupFileList(setup_file_list);
14067   freeSetupFileHash(element_hash);
14068   freeSetupFileHash(action_hash);
14069   freeSetupFileHash(direction_hash);
14070
14071 #if 0
14072   for (i = 0; i < num_list_entries; i++)
14073     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14074           EL_NAME(helpanim_info[i].element),
14075           helpanim_info[i].element,
14076           helpanim_info[i].action,
14077           helpanim_info[i].direction,
14078           helpanim_info[i].delay);
14079 #endif
14080 }
14081
14082 void LoadHelpTextInfo(void)
14083 {
14084   char *filename = getHelpTextFilename();
14085   int i;
14086
14087   if (helptext_info != NULL)
14088   {
14089     freeSetupFileHash(helptext_info);
14090     helptext_info = NULL;
14091   }
14092
14093   if (fileExists(filename))
14094     helptext_info = loadSetupFileHash(filename);
14095
14096   if (helptext_info == NULL)
14097   {
14098     // use reliable default values from static configuration
14099     helptext_info = newSetupFileHash();
14100
14101     for (i = 0; helptext_config[i].token; i++)
14102       setHashEntry(helptext_info,
14103                    helptext_config[i].token,
14104                    helptext_config[i].value);
14105   }
14106
14107 #if 0
14108   BEGIN_HASH_ITERATION(helptext_info, itr)
14109   {
14110     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14111           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14112   }
14113   END_HASH_ITERATION(hash, itr)
14114 #endif
14115 }
14116
14117
14118 // ----------------------------------------------------------------------------
14119 // convert levels
14120 // ----------------------------------------------------------------------------
14121
14122 #define MAX_NUM_CONVERT_LEVELS          1000
14123
14124 void ConvertLevels(void)
14125 {
14126   static LevelDirTree *convert_leveldir = NULL;
14127   static int convert_level_nr = -1;
14128   static int num_levels_handled = 0;
14129   static int num_levels_converted = 0;
14130   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14131   int i;
14132
14133   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14134                                                global.convert_leveldir);
14135
14136   if (convert_leveldir == NULL)
14137     Fail("no such level identifier: '%s'", global.convert_leveldir);
14138
14139   leveldir_current = convert_leveldir;
14140
14141   if (global.convert_level_nr != -1)
14142   {
14143     convert_leveldir->first_level = global.convert_level_nr;
14144     convert_leveldir->last_level  = global.convert_level_nr;
14145   }
14146
14147   convert_level_nr = convert_leveldir->first_level;
14148
14149   PrintLine("=", 79);
14150   Print("Converting levels\n");
14151   PrintLine("-", 79);
14152   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14153   Print("Level series name:       '%s'\n", convert_leveldir->name);
14154   Print("Level series author:     '%s'\n", convert_leveldir->author);
14155   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14156   PrintLine("=", 79);
14157   Print("\n");
14158
14159   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14160     levels_failed[i] = FALSE;
14161
14162   while (convert_level_nr <= convert_leveldir->last_level)
14163   {
14164     char *level_filename;
14165     boolean new_level;
14166
14167     level_nr = convert_level_nr++;
14168
14169     Print("Level %03d: ", level_nr);
14170
14171     LoadLevel(level_nr);
14172     if (level.no_level_file || level.no_valid_file)
14173     {
14174       Print("(no level)\n");
14175       continue;
14176     }
14177
14178     Print("converting level ... ");
14179
14180 #if 0
14181     // special case: conversion of some EMC levels as requested by ACME
14182     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14183 #endif
14184
14185     level_filename = getDefaultLevelFilename(level_nr);
14186     new_level = !fileExists(level_filename);
14187
14188     if (new_level)
14189     {
14190       SaveLevel(level_nr);
14191
14192       num_levels_converted++;
14193
14194       Print("converted.\n");
14195     }
14196     else
14197     {
14198       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14199         levels_failed[level_nr] = TRUE;
14200
14201       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14202     }
14203
14204     num_levels_handled++;
14205   }
14206
14207   Print("\n");
14208   PrintLine("=", 79);
14209   Print("Number of levels handled: %d\n", num_levels_handled);
14210   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14211          (num_levels_handled ?
14212           num_levels_converted * 100 / num_levels_handled : 0));
14213   PrintLine("-", 79);
14214   Print("Summary (for automatic parsing by scripts):\n");
14215   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14216          convert_leveldir->identifier, num_levels_converted,
14217          num_levels_handled,
14218          (num_levels_handled ?
14219           num_levels_converted * 100 / num_levels_handled : 0));
14220
14221   if (num_levels_handled != num_levels_converted)
14222   {
14223     Print(", FAILED:");
14224     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14225       if (levels_failed[i])
14226         Print(" %03d", i);
14227   }
14228
14229   Print("\n");
14230   PrintLine("=", 79);
14231
14232   CloseAllAndExit(0);
14233 }
14234
14235
14236 // ----------------------------------------------------------------------------
14237 // create and save images for use in level sketches (raw BMP format)
14238 // ----------------------------------------------------------------------------
14239
14240 void CreateLevelSketchImages(void)
14241 {
14242   Bitmap *bitmap1;
14243   Bitmap *bitmap2;
14244   int i;
14245
14246   InitElementPropertiesGfxElement();
14247
14248   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14249   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14250
14251   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14252   {
14253     int element = getMappedElement(i);
14254     char basename1[16];
14255     char basename2[16];
14256     char *filename1;
14257     char *filename2;
14258
14259     sprintf(basename1, "%04d.bmp", i);
14260     sprintf(basename2, "%04ds.bmp", i);
14261
14262     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14263     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14264
14265     DrawSizedElement(0, 0, element, TILESIZE);
14266     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14267
14268     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14269       Fail("cannot save level sketch image file '%s'", filename1);
14270
14271     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14272     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14273
14274     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14275       Fail("cannot save level sketch image file '%s'", filename2);
14276
14277     free(filename1);
14278     free(filename2);
14279
14280     // create corresponding SQL statements (for normal and small images)
14281     if (i < 1000)
14282     {
14283       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14284       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14285     }
14286
14287     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14288     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14289
14290     // optional: create content for forum level sketch demonstration post
14291     if (options.debug)
14292       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14293   }
14294
14295   FreeBitmap(bitmap1);
14296   FreeBitmap(bitmap2);
14297
14298   if (options.debug)
14299     fprintf(stderr, "\n");
14300
14301   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14302
14303   CloseAllAndExit(0);
14304 }
14305
14306
14307 // ----------------------------------------------------------------------------
14308 // create and save images for element collecting animations (raw BMP format)
14309 // ----------------------------------------------------------------------------
14310
14311 static boolean createCollectImage(int element)
14312 {
14313   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14314 }
14315
14316 void CreateCollectElementImages(void)
14317 {
14318   int i, j;
14319   int num_steps = 8;
14320   int anim_frames = num_steps - 1;
14321   int tile_size = TILESIZE;
14322   int anim_width  = tile_size * anim_frames;
14323   int anim_height = tile_size;
14324   int num_collect_images = 0;
14325   int pos_collect_images = 0;
14326
14327   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14328     if (createCollectImage(i))
14329       num_collect_images++;
14330
14331   Info("Creating %d element collecting animation images ...",
14332        num_collect_images);
14333
14334   int dst_width  = anim_width * 2;
14335   int dst_height = anim_height * num_collect_images / 2;
14336   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14337   char *basename_bmp = "RocksCollect.bmp";
14338   char *basename_png = "RocksCollect.png";
14339   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14340   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14341   int len_filename_bmp = strlen(filename_bmp);
14342   int len_filename_png = strlen(filename_png);
14343   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14344   char cmd_convert[max_command_len];
14345
14346   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14347            filename_bmp,
14348            filename_png);
14349
14350   // force using RGBA surface for destination bitmap
14351   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14352                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14353
14354   dst_bitmap->surface =
14355     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14356
14357   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14358   {
14359     if (!createCollectImage(i))
14360       continue;
14361
14362     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14363     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14364     int graphic = el2img(i);
14365     char *token_name = element_info[i].token_name;
14366     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14367     Bitmap *src_bitmap;
14368     int src_x, src_y;
14369
14370     Info("- creating collecting image for '%s' ...", token_name);
14371
14372     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14373
14374     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14375                tile_size, tile_size, 0, 0);
14376
14377     // force using RGBA surface for temporary bitmap (using transparent black)
14378     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14379                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14380
14381     tmp_bitmap->surface =
14382       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14383
14384     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14385
14386     for (j = 0; j < anim_frames; j++)
14387     {
14388       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14389       int frame_size = frame_size_final * num_steps;
14390       int offset = (tile_size - frame_size_final) / 2;
14391       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14392
14393       while (frame_size > frame_size_final)
14394       {
14395         frame_size /= 2;
14396
14397         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14398
14399         FreeBitmap(frame_bitmap);
14400
14401         frame_bitmap = half_bitmap;
14402       }
14403
14404       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14405                        frame_size_final, frame_size_final,
14406                        dst_x + j * tile_size + offset, dst_y + offset);
14407
14408       FreeBitmap(frame_bitmap);
14409     }
14410
14411     tmp_bitmap->surface_masked = NULL;
14412
14413     FreeBitmap(tmp_bitmap);
14414
14415     pos_collect_images++;
14416   }
14417
14418   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14419     Fail("cannot save element collecting image file '%s'", filename_bmp);
14420
14421   FreeBitmap(dst_bitmap);
14422
14423   Info("Converting image file from BMP to PNG ...");
14424
14425   if (system(cmd_convert) != 0)
14426     Fail("converting image file failed");
14427
14428   unlink(filename_bmp);
14429
14430   Info("Done.");
14431
14432   CloseAllAndExit(0);
14433 }
14434
14435
14436 // ----------------------------------------------------------------------------
14437 // create and save images for custom and group elements (raw BMP format)
14438 // ----------------------------------------------------------------------------
14439
14440 void CreateCustomElementImages(char *directory)
14441 {
14442   char *src_basename = "RocksCE-template.ilbm";
14443   char *dst_basename = "RocksCE.bmp";
14444   char *src_filename = getPath2(directory, src_basename);
14445   char *dst_filename = getPath2(directory, dst_basename);
14446   Bitmap *src_bitmap;
14447   Bitmap *bitmap;
14448   int yoffset_ce = 0;
14449   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14450   int i;
14451
14452   InitVideoDefaults();
14453
14454   ReCreateBitmap(&backbuffer, video.width, video.height);
14455
14456   src_bitmap = LoadImage(src_filename);
14457
14458   bitmap = CreateBitmap(TILEX * 16 * 2,
14459                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14460                         DEFAULT_DEPTH);
14461
14462   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14463   {
14464     int x = i % 16;
14465     int y = i / 16;
14466     int ii = i + 1;
14467     int j;
14468
14469     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14470                TILEX * x, TILEY * y + yoffset_ce);
14471
14472     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14473                TILEX, TILEY,
14474                TILEX * x + TILEX * 16,
14475                TILEY * y + yoffset_ce);
14476
14477     for (j = 2; j >= 0; j--)
14478     {
14479       int c = ii % 10;
14480
14481       BlitBitmap(src_bitmap, bitmap,
14482                  TILEX + c * 7, 0, 6, 10,
14483                  TILEX * x + 6 + j * 7,
14484                  TILEY * y + 11 + yoffset_ce);
14485
14486       BlitBitmap(src_bitmap, bitmap,
14487                  TILEX + c * 8, TILEY, 6, 10,
14488                  TILEX * 16 + TILEX * x + 6 + j * 8,
14489                  TILEY * y + 10 + yoffset_ce);
14490
14491       ii /= 10;
14492     }
14493   }
14494
14495   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14496   {
14497     int x = i % 16;
14498     int y = i / 16;
14499     int ii = i + 1;
14500     int j;
14501
14502     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14503                TILEX * x, TILEY * y + yoffset_ge);
14504
14505     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14506                TILEX, TILEY,
14507                TILEX * x + TILEX * 16,
14508                TILEY * y + yoffset_ge);
14509
14510     for (j = 1; j >= 0; j--)
14511     {
14512       int c = ii % 10;
14513
14514       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14515                  TILEX * x + 6 + j * 10,
14516                  TILEY * y + 11 + yoffset_ge);
14517
14518       BlitBitmap(src_bitmap, bitmap,
14519                  TILEX + c * 8, TILEY + 12, 6, 10,
14520                  TILEX * 16 + TILEX * x + 10 + j * 8,
14521                  TILEY * y + 10 + yoffset_ge);
14522
14523       ii /= 10;
14524     }
14525   }
14526
14527   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14528     Fail("cannot save CE graphics file '%s'", dst_filename);
14529
14530   FreeBitmap(bitmap);
14531
14532   CloseAllAndExit(0);
14533 }