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