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