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