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