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