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