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