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