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