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