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