added step delay option to custom element movement configuration
[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   int level_identifier_size;
7769   int i;
7770
7771   level_identifier_size = getFile16BitBE(file);
7772
7773   tape->level_identifier =
7774     checked_realloc(tape->level_identifier, level_identifier_size);
7775
7776   for (i = 0; i < level_identifier_size; i++)
7777     tape->level_identifier[i] = getFile8Bit(file);
7778
7779   tape->level_nr = getFile16BitBE(file);
7780
7781   chunk_size = 2 + level_identifier_size + 2;
7782
7783   return chunk_size;
7784 }
7785
7786 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7787 {
7788   int i, j;
7789   int tape_pos_size = getTapePosSize(tape);
7790   int chunk_size_expected = tape_pos_size * tape->length;
7791
7792   if (chunk_size_expected != chunk_size)
7793   {
7794     ReadUnusedBytesFromFile(file, chunk_size);
7795     return chunk_size_expected;
7796   }
7797
7798   for (i = 0; i < tape->length; i++)
7799   {
7800     if (i >= MAX_TAPE_LEN)
7801     {
7802       Warn("tape truncated -- size exceeds maximum tape size %d",
7803             MAX_TAPE_LEN);
7804
7805       // tape too large; read and ignore remaining tape data from this chunk
7806       for (;i < tape->length; i++)
7807         ReadUnusedBytesFromFile(file, tape_pos_size);
7808
7809       break;
7810     }
7811
7812     if (tape->use_key_actions)
7813     {
7814       for (j = 0; j < MAX_PLAYERS; j++)
7815       {
7816         tape->pos[i].action[j] = MV_NONE;
7817
7818         if (tape->player_participates[j])
7819           tape->pos[i].action[j] = getFile8Bit(file);
7820       }
7821     }
7822
7823     if (tape->use_mouse_actions)
7824     {
7825       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
7826       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
7827       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7828     }
7829
7830     tape->pos[i].delay = getFile8Bit(file);
7831
7832     if (tape->file_version == FILE_VERSION_1_0)
7833     {
7834       // eliminate possible diagonal moves in old tapes
7835       // this is only for backward compatibility
7836
7837       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7838       byte action = tape->pos[i].action[0];
7839       int k, num_moves = 0;
7840
7841       for (k = 0; k<4; k++)
7842       {
7843         if (action & joy_dir[k])
7844         {
7845           tape->pos[i + num_moves].action[0] = joy_dir[k];
7846           if (num_moves > 0)
7847             tape->pos[i + num_moves].delay = 0;
7848           num_moves++;
7849         }
7850       }
7851
7852       if (num_moves > 1)
7853       {
7854         num_moves--;
7855         i += num_moves;
7856         tape->length += num_moves;
7857       }
7858     }
7859     else if (tape->file_version < FILE_VERSION_2_0)
7860     {
7861       // convert pre-2.0 tapes to new tape format
7862
7863       if (tape->pos[i].delay > 1)
7864       {
7865         // action part
7866         tape->pos[i + 1] = tape->pos[i];
7867         tape->pos[i + 1].delay = 1;
7868
7869         // delay part
7870         for (j = 0; j < MAX_PLAYERS; j++)
7871           tape->pos[i].action[j] = MV_NONE;
7872         tape->pos[i].delay--;
7873
7874         i++;
7875         tape->length++;
7876       }
7877     }
7878
7879     if (checkEndOfFile(file))
7880       break;
7881   }
7882
7883   if (i != tape->length)
7884     chunk_size = tape_pos_size * i;
7885
7886   return chunk_size;
7887 }
7888
7889 static void LoadTape_SokobanSolution(char *filename)
7890 {
7891   File *file;
7892   int move_delay = TILESIZE / level.initial_player_stepsize[0];
7893
7894   if (!(file = openFile(filename, MODE_READ)))
7895   {
7896     tape.no_valid_file = TRUE;
7897
7898     return;
7899   }
7900
7901   while (!checkEndOfFile(file))
7902   {
7903     unsigned char c = getByteFromFile(file);
7904
7905     if (checkEndOfFile(file))
7906       break;
7907
7908     switch (c)
7909     {
7910       case 'u':
7911       case 'U':
7912         tape.pos[tape.length].action[0] = MV_UP;
7913         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7914         tape.length++;
7915         break;
7916
7917       case 'd':
7918       case 'D':
7919         tape.pos[tape.length].action[0] = MV_DOWN;
7920         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7921         tape.length++;
7922         break;
7923
7924       case 'l':
7925       case 'L':
7926         tape.pos[tape.length].action[0] = MV_LEFT;
7927         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7928         tape.length++;
7929         break;
7930
7931       case 'r':
7932       case 'R':
7933         tape.pos[tape.length].action[0] = MV_RIGHT;
7934         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7935         tape.length++;
7936         break;
7937
7938       case '\n':
7939       case '\r':
7940       case '\t':
7941       case ' ':
7942         // ignore white-space characters
7943         break;
7944
7945       default:
7946         tape.no_valid_file = TRUE;
7947
7948         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
7949
7950         break;
7951     }
7952   }
7953
7954   closeFile(file);
7955
7956   if (tape.no_valid_file)
7957     return;
7958
7959   tape.length_frames  = GetTapeLengthFrames();
7960   tape.length_seconds = GetTapeLengthSeconds();
7961 }
7962
7963 void LoadTapeFromFilename(char *filename)
7964 {
7965   char cookie[MAX_LINE_LEN];
7966   char chunk_name[CHUNK_ID_LEN + 1];
7967   File *file;
7968   int chunk_size;
7969
7970   // always start with reliable default values
7971   setTapeInfoToDefaults();
7972
7973   if (strSuffix(filename, ".sln"))
7974   {
7975     LoadTape_SokobanSolution(filename);
7976
7977     return;
7978   }
7979
7980   if (!(file = openFile(filename, MODE_READ)))
7981   {
7982     tape.no_valid_file = TRUE;
7983
7984     return;
7985   }
7986
7987   getFileChunkBE(file, chunk_name, NULL);
7988   if (strEqual(chunk_name, "RND1"))
7989   {
7990     getFile32BitBE(file);               // not used
7991
7992     getFileChunkBE(file, chunk_name, NULL);
7993     if (!strEqual(chunk_name, "TAPE"))
7994     {
7995       tape.no_valid_file = TRUE;
7996
7997       Warn("unknown format of tape file '%s'", filename);
7998
7999       closeFile(file);
8000
8001       return;
8002     }
8003   }
8004   else  // check for pre-2.0 file format with cookie string
8005   {
8006     strcpy(cookie, chunk_name);
8007     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8008       cookie[4] = '\0';
8009     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8010       cookie[strlen(cookie) - 1] = '\0';
8011
8012     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8013     {
8014       tape.no_valid_file = TRUE;
8015
8016       Warn("unknown format of tape file '%s'", filename);
8017
8018       closeFile(file);
8019
8020       return;
8021     }
8022
8023     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8024     {
8025       tape.no_valid_file = TRUE;
8026
8027       Warn("unsupported version of tape file '%s'", filename);
8028
8029       closeFile(file);
8030
8031       return;
8032     }
8033
8034     // pre-2.0 tape files have no game version, so use file version here
8035     tape.game_version = tape.file_version;
8036   }
8037
8038   if (tape.file_version < FILE_VERSION_1_2)
8039   {
8040     // tape files from versions before 1.2.0 without chunk structure
8041     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8042     LoadTape_BODY(file, 2 * tape.length,      &tape);
8043   }
8044   else
8045   {
8046     static struct
8047     {
8048       char *name;
8049       int size;
8050       int (*loader)(File *, int, struct TapeInfo *);
8051     }
8052     chunk_info[] =
8053     {
8054       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
8055       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
8056       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
8057       { "INFO", -1,                     LoadTape_INFO },
8058       { "BODY", -1,                     LoadTape_BODY },
8059       {  NULL,  0,                      NULL }
8060     };
8061
8062     while (getFileChunkBE(file, chunk_name, &chunk_size))
8063     {
8064       int i = 0;
8065
8066       while (chunk_info[i].name != NULL &&
8067              !strEqual(chunk_name, chunk_info[i].name))
8068         i++;
8069
8070       if (chunk_info[i].name == NULL)
8071       {
8072         Warn("unknown chunk '%s' in tape file '%s'",
8073               chunk_name, filename);
8074
8075         ReadUnusedBytesFromFile(file, chunk_size);
8076       }
8077       else if (chunk_info[i].size != -1 &&
8078                chunk_info[i].size != chunk_size)
8079       {
8080         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8081               chunk_size, chunk_name, filename);
8082
8083         ReadUnusedBytesFromFile(file, chunk_size);
8084       }
8085       else
8086       {
8087         // call function to load this tape chunk
8088         int chunk_size_expected =
8089           (chunk_info[i].loader)(file, chunk_size, &tape);
8090
8091         // the size of some chunks cannot be checked before reading other
8092         // chunks first (like "HEAD" and "BODY") that contain some header
8093         // information, so check them here
8094         if (chunk_size_expected != chunk_size)
8095         {
8096           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8097                 chunk_size, chunk_name, filename);
8098         }
8099       }
8100     }
8101   }
8102
8103   closeFile(file);
8104
8105   tape.length_frames  = GetTapeLengthFrames();
8106   tape.length_seconds = GetTapeLengthSeconds();
8107
8108 #if 0
8109   Debug("files:LoadTapeFromFilename", "tape file version: %d",
8110         tape.file_version);
8111   Debug("files:LoadTapeFromFilename", "tape game version: %d",
8112         tape.game_version);
8113   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8114         tape.engine_version);
8115 #endif
8116 }
8117
8118 void LoadTape(int nr)
8119 {
8120   char *filename = getTapeFilename(nr);
8121
8122   LoadTapeFromFilename(filename);
8123 }
8124
8125 void LoadSolutionTape(int nr)
8126 {
8127   char *filename = getSolutionTapeFilename(nr);
8128
8129   LoadTapeFromFilename(filename);
8130
8131   if (TAPE_IS_EMPTY(tape) &&
8132       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8133       level.native_sp_level->demo.is_available)
8134     CopyNativeTape_SP_to_RND(&level);
8135 }
8136
8137 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8138 {
8139   // chunk required for team mode tapes with non-default screen size
8140   return (tape->num_participating_players > 1 &&
8141           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8142            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8143 }
8144
8145 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8146 {
8147   putFileVersion(file, tape->file_version);
8148   putFileVersion(file, tape->game_version);
8149 }
8150
8151 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8152 {
8153   int i;
8154   byte store_participating_players = 0;
8155
8156   // set bits for participating players for compact storage
8157   for (i = 0; i < MAX_PLAYERS; i++)
8158     if (tape->player_participates[i])
8159       store_participating_players |= (1 << i);
8160
8161   putFile32BitBE(file, tape->random_seed);
8162   putFile32BitBE(file, tape->date);
8163   putFile32BitBE(file, tape->length);
8164
8165   putFile8Bit(file, store_participating_players);
8166
8167   putFile8Bit(file, getTapeActionValue(tape));
8168
8169   putFile8Bit(file, tape->property_bits);
8170
8171   // unused bytes not at the end here for 4-byte alignment of engine_version
8172   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8173
8174   putFileVersion(file, tape->engine_version);
8175 }
8176
8177 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8178 {
8179   putFile8Bit(file, tape->scr_fieldx);
8180   putFile8Bit(file, tape->scr_fieldy);
8181 }
8182
8183 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8184 {
8185   int level_identifier_size = strlen(tape->level_identifier) + 1;
8186   int i;
8187
8188   putFile16BitBE(file, level_identifier_size);
8189
8190   for (i = 0; i < level_identifier_size; i++)
8191     putFile8Bit(file, tape->level_identifier[i]);
8192
8193   putFile16BitBE(file, tape->level_nr);
8194 }
8195
8196 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8197 {
8198   int i, j;
8199
8200   for (i = 0; i < tape->length; i++)
8201   {
8202     if (tape->use_key_actions)
8203     {
8204       for (j = 0; j < MAX_PLAYERS; j++)
8205         if (tape->player_participates[j])
8206           putFile8Bit(file, tape->pos[i].action[j]);
8207     }
8208
8209     if (tape->use_mouse_actions)
8210     {
8211       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8212       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8213       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8214     }
8215
8216     putFile8Bit(file, tape->pos[i].delay);
8217   }
8218 }
8219
8220 void SaveTapeToFilename(char *filename)
8221 {
8222   FILE *file;
8223   int tape_pos_size;
8224   int info_chunk_size;
8225   int body_chunk_size;
8226
8227   if (!(file = fopen(filename, MODE_WRITE)))
8228   {
8229     Warn("cannot save level recording file '%s'", filename);
8230
8231     return;
8232   }
8233
8234   tape_pos_size = getTapePosSize(&tape);
8235
8236   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8237   body_chunk_size = tape_pos_size * tape.length;
8238
8239   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8240   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8241
8242   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8243   SaveTape_VERS(file, &tape);
8244
8245   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8246   SaveTape_HEAD(file, &tape);
8247
8248   if (checkSaveTape_SCRN(&tape))
8249   {
8250     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8251     SaveTape_SCRN(file, &tape);
8252   }
8253
8254   putFileChunkBE(file, "INFO", info_chunk_size);
8255   SaveTape_INFO(file, &tape);
8256
8257   putFileChunkBE(file, "BODY", body_chunk_size);
8258   SaveTape_BODY(file, &tape);
8259
8260   fclose(file);
8261
8262   SetFilePermissions(filename, PERMS_PRIVATE);
8263 }
8264
8265 void SaveTape(int nr)
8266 {
8267   char *filename = getTapeFilename(nr);
8268   int i;
8269
8270   InitTapeDirectory(leveldir_current->subdir);
8271
8272   tape.file_version = FILE_VERSION_ACTUAL;
8273   tape.game_version = GAME_VERSION_ACTUAL;
8274
8275   tape.num_participating_players = 0;
8276
8277   // count number of participating players
8278   for (i = 0; i < MAX_PLAYERS; i++)
8279     if (tape.player_participates[i])
8280       tape.num_participating_players++;
8281
8282   SaveTapeToFilename(filename);
8283
8284   tape.changed = FALSE;
8285 }
8286
8287 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8288                                   unsigned int req_state_added)
8289 {
8290   char *filename = getTapeFilename(nr);
8291   boolean new_tape = !fileExists(filename);
8292   boolean tape_saved = FALSE;
8293
8294   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8295   {
8296     SaveTape(nr);
8297
8298     if (new_tape)
8299       Request(msg_saved, REQ_CONFIRM | req_state_added);
8300
8301     tape_saved = TRUE;
8302   }
8303
8304   return tape_saved;
8305 }
8306
8307 boolean SaveTapeChecked(int nr)
8308 {
8309   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8310 }
8311
8312 boolean SaveTapeChecked_LevelSolved(int nr)
8313 {
8314   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8315                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
8316 }
8317
8318 void DumpTape(struct TapeInfo *tape)
8319 {
8320   int tape_frame_counter;
8321   int i, j;
8322
8323   if (tape->no_valid_file)
8324   {
8325     Warn("cannot dump -- no valid tape file found");
8326
8327     return;
8328   }
8329
8330   PrintLine("-", 79);
8331   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8332         tape->level_nr, tape->file_version, tape->game_version);
8333   Print("                  (effective engine version %08d)\n",
8334         tape->engine_version);
8335   Print("Level series identifier: '%s'\n", tape->level_identifier);
8336   PrintLine("-", 79);
8337
8338   tape_frame_counter = 0;
8339
8340   for (i = 0; i < tape->length; i++)
8341   {
8342     if (i >= MAX_TAPE_LEN)
8343       break;
8344
8345     Print("%04d: ", i);
8346
8347     for (j = 0; j < MAX_PLAYERS; j++)
8348     {
8349       if (tape->player_participates[j])
8350       {
8351         int action = tape->pos[i].action[j];
8352
8353         Print("%d:%02x ", j, action);
8354         Print("[%c%c%c%c|%c%c] - ",
8355               (action & JOY_LEFT ? '<' : ' '),
8356               (action & JOY_RIGHT ? '>' : ' '),
8357               (action & JOY_UP ? '^' : ' '),
8358               (action & JOY_DOWN ? 'v' : ' '),
8359               (action & JOY_BUTTON_1 ? '1' : ' '),
8360               (action & JOY_BUTTON_2 ? '2' : ' '));
8361       }
8362     }
8363
8364     Print("(%03d) ", tape->pos[i].delay);
8365     Print("[%05d]\n", tape_frame_counter);
8366
8367     tape_frame_counter += tape->pos[i].delay;
8368   }
8369
8370   PrintLine("-", 79);
8371 }
8372
8373
8374 // ============================================================================
8375 // score file functions
8376 // ============================================================================
8377
8378 void LoadScore(int nr)
8379 {
8380   int i;
8381   char *filename = getScoreFilename(nr);
8382   char cookie[MAX_LINE_LEN];
8383   char line[MAX_LINE_LEN];
8384   char *line_ptr;
8385   FILE *file;
8386
8387   // always start with reliable default values
8388   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8389   {
8390     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8391     highscore[i].Score = 0;
8392   }
8393
8394   if (!(file = fopen(filename, MODE_READ)))
8395     return;
8396
8397   // check file identifier
8398   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8399     cookie[0] = '\0';
8400   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8401     cookie[strlen(cookie) - 1] = '\0';
8402
8403   if (!checkCookieString(cookie, SCORE_COOKIE))
8404   {
8405     Warn("unknown format of score file '%s'", filename);
8406
8407     fclose(file);
8408
8409     return;
8410   }
8411
8412   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8413   {
8414     if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8415       Warn("fscanf() failed; %s", strerror(errno));
8416
8417     if (fgets(line, MAX_LINE_LEN, file) == NULL)
8418       line[0] = '\0';
8419
8420     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8421       line[strlen(line) - 1] = '\0';
8422
8423     for (line_ptr = line; *line_ptr; line_ptr++)
8424     {
8425       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8426       {
8427         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8428         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8429         break;
8430       }
8431     }
8432   }
8433
8434   fclose(file);
8435 }
8436
8437 void SaveScore(int nr)
8438 {
8439   int i;
8440   int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8441   char *filename = getScoreFilename(nr);
8442   FILE *file;
8443
8444   // used instead of "leveldir_current->subdir" (for network games)
8445   InitScoreDirectory(levelset.identifier);
8446
8447   if (!(file = fopen(filename, MODE_WRITE)))
8448   {
8449     Warn("cannot save score for level %d", nr);
8450
8451     return;
8452   }
8453
8454   fprintf(file, "%s\n\n", SCORE_COOKIE);
8455
8456   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8457     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8458
8459   fclose(file);
8460
8461   SetFilePermissions(filename, permissions);
8462 }
8463
8464
8465 // ============================================================================
8466 // setup file functions
8467 // ============================================================================
8468
8469 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
8470
8471
8472 static struct TokenInfo global_setup_tokens[] =
8473 {
8474   {
8475     TYPE_STRING,
8476     &setup.player_name,                         "player_name"
8477   },
8478   {
8479     TYPE_SWITCH,
8480     &setup.multiple_users,                      "multiple_users"
8481   },
8482   {
8483     TYPE_SWITCH,
8484     &setup.sound,                               "sound"
8485   },
8486   {
8487     TYPE_SWITCH,
8488     &setup.sound_loops,                         "repeating_sound_loops"
8489   },
8490   {
8491     TYPE_SWITCH,
8492     &setup.sound_music,                         "background_music"
8493   },
8494   {
8495     TYPE_SWITCH,
8496     &setup.sound_simple,                        "simple_sound_effects"
8497   },
8498   {
8499     TYPE_SWITCH,
8500     &setup.toons,                               "toons"
8501   },
8502   {
8503     TYPE_SWITCH,
8504     &setup.scroll_delay,                        "scroll_delay"
8505   },
8506   {
8507     TYPE_SWITCH,
8508     &setup.forced_scroll_delay,                 "forced_scroll_delay"
8509   },
8510   {
8511     TYPE_INTEGER,
8512     &setup.scroll_delay_value,                  "scroll_delay_value"
8513   },
8514   {
8515     TYPE_STRING,
8516     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
8517   },
8518   {
8519     TYPE_INTEGER,
8520     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
8521   },
8522   {
8523     TYPE_SWITCH,
8524     &setup.fade_screens,                        "fade_screens"
8525   },
8526   {
8527     TYPE_SWITCH,
8528     &setup.autorecord,                          "automatic_tape_recording"
8529   },
8530   {
8531     TYPE_SWITCH,
8532     &setup.show_titlescreen,                    "show_titlescreen"
8533   },
8534   {
8535     TYPE_SWITCH,
8536     &setup.quick_doors,                         "quick_doors"
8537   },
8538   {
8539     TYPE_SWITCH,
8540     &setup.team_mode,                           "team_mode"
8541   },
8542   {
8543     TYPE_SWITCH,
8544     &setup.handicap,                            "handicap"
8545   },
8546   {
8547     TYPE_SWITCH,
8548     &setup.skip_levels,                         "skip_levels"
8549   },
8550   {
8551     TYPE_SWITCH,
8552     &setup.increment_levels,                    "increment_levels"
8553   },
8554   {
8555     TYPE_SWITCH,
8556     &setup.auto_play_next_level,                "auto_play_next_level"
8557   },
8558   {
8559     TYPE_SWITCH,
8560     &setup.count_score_after_game,              "count_score_after_game"
8561   },
8562   {
8563     TYPE_SWITCH,
8564     &setup.show_scores_after_game,              "show_scores_after_game"
8565   },
8566   {
8567     TYPE_SWITCH,
8568     &setup.time_limit,                          "time_limit"
8569   },
8570   {
8571     TYPE_SWITCH,
8572     &setup.fullscreen,                          "fullscreen"
8573   },
8574   {
8575     TYPE_INTEGER,
8576     &setup.window_scaling_percent,              "window_scaling_percent"
8577   },
8578   {
8579     TYPE_STRING,
8580     &setup.window_scaling_quality,              "window_scaling_quality"
8581   },
8582   {
8583     TYPE_STRING,
8584     &setup.screen_rendering_mode,               "screen_rendering_mode"
8585   },
8586   {
8587     TYPE_STRING,
8588     &setup.vsync_mode,                          "vsync_mode"
8589   },
8590   {
8591     TYPE_SWITCH,
8592     &setup.ask_on_escape,                       "ask_on_escape"
8593   },
8594   {
8595     TYPE_SWITCH,
8596     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
8597   },
8598   {
8599     TYPE_SWITCH,
8600     &setup.ask_on_game_over,                    "ask_on_game_over"
8601   },
8602   {
8603     TYPE_SWITCH,
8604     &setup.quick_switch,                        "quick_player_switch"
8605   },
8606   {
8607     TYPE_SWITCH,
8608     &setup.input_on_focus,                      "input_on_focus"
8609   },
8610   {
8611     TYPE_SWITCH,
8612     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
8613   },
8614   {
8615     TYPE_SWITCH,
8616     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
8617   },
8618   {
8619     TYPE_SWITCH,
8620     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
8621   },
8622   {
8623     TYPE_SWITCH,
8624     &setup.game_speed_extended,                 "game_speed_extended"
8625   },
8626   {
8627     TYPE_INTEGER,
8628     &setup.game_frame_delay,                    "game_frame_delay"
8629   },
8630   {
8631     TYPE_SWITCH,
8632     &setup.sp_show_border_elements,             "sp_show_border_elements"
8633   },
8634   {
8635     TYPE_SWITCH,
8636     &setup.small_game_graphics,                 "small_game_graphics"
8637   },
8638   {
8639     TYPE_SWITCH,
8640     &setup.show_snapshot_buttons,               "show_snapshot_buttons"
8641   },
8642   {
8643     TYPE_STRING,
8644     &setup.graphics_set,                        "graphics_set"
8645   },
8646   {
8647     TYPE_STRING,
8648     &setup.sounds_set,                          "sounds_set"
8649   },
8650   {
8651     TYPE_STRING,
8652     &setup.music_set,                           "music_set"
8653   },
8654   {
8655     TYPE_SWITCH3,
8656     &setup.override_level_graphics,             "override_level_graphics"
8657   },
8658   {
8659     TYPE_SWITCH3,
8660     &setup.override_level_sounds,               "override_level_sounds"
8661   },
8662   {
8663     TYPE_SWITCH3,
8664     &setup.override_level_music,                "override_level_music"
8665   },
8666   {
8667     TYPE_INTEGER,
8668     &setup.volume_simple,                       "volume_simple"
8669   },
8670   {
8671     TYPE_INTEGER,
8672     &setup.volume_loops,                        "volume_loops"
8673   },
8674   {
8675     TYPE_INTEGER,
8676     &setup.volume_music,                        "volume_music"
8677   },
8678   {
8679     TYPE_SWITCH,
8680     &setup.network_mode,                        "network_mode"
8681   },
8682   {
8683     TYPE_PLAYER,
8684     &setup.network_player_nr,                   "network_player"
8685   },
8686   {
8687     TYPE_STRING,
8688     &setup.network_server_hostname,             "network_server_hostname"
8689   },
8690   {
8691     TYPE_STRING,
8692     &setup.touch.control_type,                  "touch.control_type"
8693   },
8694   {
8695     TYPE_INTEGER,
8696     &setup.touch.move_distance,                 "touch.move_distance"
8697   },
8698   {
8699     TYPE_INTEGER,
8700     &setup.touch.drop_distance,                 "touch.drop_distance"
8701   },
8702   {
8703     TYPE_INTEGER,
8704     &setup.touch.transparency,                  "touch.transparency"
8705   },
8706   {
8707     TYPE_INTEGER,
8708     &setup.touch.draw_outlined,                 "touch.draw_outlined"
8709   },
8710   {
8711     TYPE_INTEGER,
8712     &setup.touch.draw_pressed,                  "touch.draw_pressed"
8713   },
8714   {
8715     TYPE_INTEGER,
8716     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
8717   },
8718   {
8719     TYPE_INTEGER,
8720     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
8721   },
8722   {
8723     TYPE_INTEGER,
8724     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
8725   },
8726   {
8727     TYPE_INTEGER,
8728     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
8729   },
8730 };
8731
8732 static struct TokenInfo auto_setup_tokens[] =
8733 {
8734   {
8735     TYPE_INTEGER,
8736     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
8737   },
8738 };
8739
8740 static struct TokenInfo editor_setup_tokens[] =
8741 {
8742   {
8743     TYPE_SWITCH,
8744     &setup.editor.el_classic,                   "editor.el_classic"
8745   },
8746   {
8747     TYPE_SWITCH,
8748     &setup.editor.el_custom,                    "editor.el_custom"
8749   },
8750   {
8751     TYPE_SWITCH,
8752     &setup.editor.el_user_defined,              "editor.el_user_defined"
8753   },
8754   {
8755     TYPE_SWITCH,
8756     &setup.editor.el_dynamic,                   "editor.el_dynamic"
8757   },
8758   {
8759     TYPE_SWITCH,
8760     &setup.editor.el_headlines,                 "editor.el_headlines"
8761   },
8762   {
8763     TYPE_SWITCH,
8764     &setup.editor.show_element_token,           "editor.show_element_token"
8765   },
8766   {
8767     TYPE_SWITCH,
8768     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
8769   },
8770 };
8771
8772 static struct TokenInfo editor_cascade_setup_tokens[] =
8773 {
8774   {
8775     TYPE_SWITCH,
8776     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
8777   },
8778   {
8779     TYPE_SWITCH,
8780     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
8781   },
8782   {
8783     TYPE_SWITCH,
8784     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
8785   },
8786   {
8787     TYPE_SWITCH,
8788     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
8789   },
8790   {
8791     TYPE_SWITCH,
8792     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
8793   },
8794   {
8795     TYPE_SWITCH,
8796     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
8797   },
8798   {
8799     TYPE_SWITCH,
8800     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
8801   },
8802   {
8803     TYPE_SWITCH,
8804     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
8805   },
8806   {
8807     TYPE_SWITCH,
8808     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
8809   },
8810   {
8811     TYPE_SWITCH,
8812     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
8813   },
8814   {
8815     TYPE_SWITCH,
8816     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
8817   },
8818   {
8819     TYPE_SWITCH,
8820     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
8821   },
8822   {
8823     TYPE_SWITCH,
8824     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
8825   },
8826   {
8827     TYPE_SWITCH,
8828     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
8829   },
8830   {
8831     TYPE_SWITCH,
8832     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
8833   },
8834   {
8835     TYPE_SWITCH,
8836     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
8837   },
8838   {
8839     TYPE_SWITCH,
8840     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
8841   },
8842 };
8843
8844 static struct TokenInfo shortcut_setup_tokens[] =
8845 {
8846   {
8847     TYPE_KEY_X11,
8848     &setup.shortcut.save_game,                  "shortcut.save_game"
8849   },
8850   {
8851     TYPE_KEY_X11,
8852     &setup.shortcut.load_game,                  "shortcut.load_game"
8853   },
8854   {
8855     TYPE_KEY_X11,
8856     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
8857   },
8858   {
8859     TYPE_KEY_X11,
8860     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
8861   },
8862   {
8863     TYPE_KEY_X11,
8864     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
8865   },
8866   {
8867     TYPE_KEY_X11,
8868     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
8869   },
8870   {
8871     TYPE_KEY_X11,
8872     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
8873   },
8874   {
8875     TYPE_KEY_X11,
8876     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
8877   },
8878   {
8879     TYPE_KEY_X11,
8880     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
8881   },
8882   {
8883     TYPE_KEY_X11,
8884     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
8885   },
8886   {
8887     TYPE_KEY_X11,
8888     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
8889   },
8890   {
8891     TYPE_KEY_X11,
8892     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
8893   },
8894   {
8895     TYPE_KEY_X11,
8896     &setup.shortcut.tape_record,                "shortcut.tape_record"
8897   },
8898   {
8899     TYPE_KEY_X11,
8900     &setup.shortcut.tape_play,                  "shortcut.tape_play"
8901   },
8902   {
8903     TYPE_KEY_X11,
8904     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
8905   },
8906   {
8907     TYPE_KEY_X11,
8908     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
8909   },
8910   {
8911     TYPE_KEY_X11,
8912     &setup.shortcut.sound_music,                "shortcut.sound_music"
8913   },
8914   {
8915     TYPE_KEY_X11,
8916     &setup.shortcut.snap_left,                  "shortcut.snap_left"
8917   },
8918   {
8919     TYPE_KEY_X11,
8920     &setup.shortcut.snap_right,                 "shortcut.snap_right"
8921   },
8922   {
8923     TYPE_KEY_X11,
8924     &setup.shortcut.snap_up,                    "shortcut.snap_up"
8925   },
8926   {
8927     TYPE_KEY_X11,
8928     &setup.shortcut.snap_down,                  "shortcut.snap_down"
8929   },
8930 };
8931
8932 static struct SetupInputInfo setup_input;
8933 static struct TokenInfo player_setup_tokens[] =
8934 {
8935   {
8936     TYPE_BOOLEAN,
8937     &setup_input.use_joystick,                  ".use_joystick"
8938   },
8939   {
8940     TYPE_STRING,
8941     &setup_input.joy.device_name,               ".joy.device_name"
8942   },
8943   {
8944     TYPE_INTEGER,
8945     &setup_input.joy.xleft,                     ".joy.xleft"
8946   },
8947   {
8948     TYPE_INTEGER,
8949     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
8950   },
8951   {
8952     TYPE_INTEGER,
8953     &setup_input.joy.xright,                    ".joy.xright"
8954   },
8955   {
8956     TYPE_INTEGER,
8957     &setup_input.joy.yupper,                    ".joy.yupper"
8958   },
8959   {
8960     TYPE_INTEGER,
8961     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
8962   },
8963   {
8964     TYPE_INTEGER,
8965     &setup_input.joy.ylower,                    ".joy.ylower"
8966   },
8967   {
8968     TYPE_INTEGER,
8969     &setup_input.joy.snap,                      ".joy.snap_field"
8970   },
8971   {
8972     TYPE_INTEGER,
8973     &setup_input.joy.drop,                      ".joy.place_bomb"
8974   },
8975   {
8976     TYPE_KEY_X11,
8977     &setup_input.key.left,                      ".key.move_left"
8978   },
8979   {
8980     TYPE_KEY_X11,
8981     &setup_input.key.right,                     ".key.move_right"
8982   },
8983   {
8984     TYPE_KEY_X11,
8985     &setup_input.key.up,                        ".key.move_up"
8986   },
8987   {
8988     TYPE_KEY_X11,
8989     &setup_input.key.down,                      ".key.move_down"
8990   },
8991   {
8992     TYPE_KEY_X11,
8993     &setup_input.key.snap,                      ".key.snap_field"
8994   },
8995   {
8996     TYPE_KEY_X11,
8997     &setup_input.key.drop,                      ".key.place_bomb"
8998   },
8999 };
9000
9001 static struct TokenInfo system_setup_tokens[] =
9002 {
9003   {
9004     TYPE_STRING,
9005     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
9006   },
9007   {
9008     TYPE_STRING,
9009     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
9010   },
9011   {
9012     TYPE_STRING,
9013     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
9014   },
9015   {
9016     TYPE_INTEGER,
9017     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
9018   },
9019 };
9020
9021 static struct TokenInfo internal_setup_tokens[] =
9022 {
9023   {
9024     TYPE_STRING,
9025     &setup.internal.program_title,              "program_title"
9026   },
9027   {
9028     TYPE_STRING,
9029     &setup.internal.program_version,            "program_version"
9030   },
9031   {
9032     TYPE_STRING,
9033     &setup.internal.program_author,             "program_author"
9034   },
9035   {
9036     TYPE_STRING,
9037     &setup.internal.program_email,              "program_email"
9038   },
9039   {
9040     TYPE_STRING,
9041     &setup.internal.program_website,            "program_website"
9042   },
9043   {
9044     TYPE_STRING,
9045     &setup.internal.program_copyright,          "program_copyright"
9046   },
9047   {
9048     TYPE_STRING,
9049     &setup.internal.program_company,            "program_company"
9050   },
9051   {
9052     TYPE_STRING,
9053     &setup.internal.program_icon_file,          "program_icon_file"
9054   },
9055   {
9056     TYPE_STRING,
9057     &setup.internal.default_graphics_set,       "default_graphics_set"
9058   },
9059   {
9060     TYPE_STRING,
9061     &setup.internal.default_sounds_set,         "default_sounds_set"
9062   },
9063   {
9064     TYPE_STRING,
9065     &setup.internal.default_music_set,          "default_music_set"
9066   },
9067   {
9068     TYPE_STRING,
9069     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
9070   },
9071   {
9072     TYPE_STRING,
9073     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
9074   },
9075   {
9076     TYPE_STRING,
9077     &setup.internal.fallback_music_file,        "fallback_music_file"
9078   },
9079   {
9080     TYPE_STRING,
9081     &setup.internal.default_level_series,       "default_level_series"
9082   },
9083   {
9084     TYPE_INTEGER,
9085     &setup.internal.default_window_width,       "default_window_width"
9086   },
9087   {
9088     TYPE_INTEGER,
9089     &setup.internal.default_window_height,      "default_window_height"
9090   },
9091   {
9092     TYPE_BOOLEAN,
9093     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
9094   },
9095   {
9096     TYPE_BOOLEAN,
9097     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
9098   },
9099   {
9100     TYPE_BOOLEAN,
9101     &setup.internal.create_user_levelset,       "create_user_levelset"
9102   },
9103   {
9104     TYPE_BOOLEAN,
9105     &setup.internal.menu_game,                  "menu_game"
9106   },
9107   {
9108     TYPE_BOOLEAN,
9109     &setup.internal.menu_editor,                "menu_editor"
9110   },
9111   {
9112     TYPE_BOOLEAN,
9113     &setup.internal.menu_graphics,              "menu_graphics"
9114   },
9115   {
9116     TYPE_BOOLEAN,
9117     &setup.internal.menu_sound,                 "menu_sound"
9118   },
9119   {
9120     TYPE_BOOLEAN,
9121     &setup.internal.menu_artwork,               "menu_artwork"
9122   },
9123   {
9124     TYPE_BOOLEAN,
9125     &setup.internal.menu_input,                 "menu_input"
9126   },
9127   {
9128     TYPE_BOOLEAN,
9129     &setup.internal.menu_touch,                 "menu_touch"
9130   },
9131   {
9132     TYPE_BOOLEAN,
9133     &setup.internal.menu_shortcuts,             "menu_shortcuts"
9134   },
9135   {
9136     TYPE_BOOLEAN,
9137     &setup.internal.menu_exit,                  "menu_exit"
9138   },
9139   {
9140     TYPE_BOOLEAN,
9141     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
9142   },
9143 };
9144
9145 static struct TokenInfo debug_setup_tokens[] =
9146 {
9147   {
9148     TYPE_INTEGER,
9149     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
9150   },
9151   {
9152     TYPE_INTEGER,
9153     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
9154   },
9155   {
9156     TYPE_INTEGER,
9157     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
9158   },
9159   {
9160     TYPE_INTEGER,
9161     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
9162   },
9163   {
9164     TYPE_INTEGER,
9165     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
9166   },
9167   {
9168     TYPE_INTEGER,
9169     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
9170   },
9171   {
9172     TYPE_INTEGER,
9173     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
9174   },
9175   {
9176     TYPE_INTEGER,
9177     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
9178   },
9179   {
9180     TYPE_INTEGER,
9181     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
9182   },
9183   {
9184     TYPE_INTEGER,
9185     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
9186   },
9187   {
9188     TYPE_KEY_X11,
9189     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
9190   },
9191   {
9192     TYPE_KEY_X11,
9193     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
9194   },
9195   {
9196     TYPE_KEY_X11,
9197     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
9198   },
9199   {
9200     TYPE_KEY_X11,
9201     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
9202   },
9203   {
9204     TYPE_KEY_X11,
9205     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
9206   },
9207   {
9208     TYPE_KEY_X11,
9209     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
9210   },
9211   {
9212     TYPE_KEY_X11,
9213     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
9214   },
9215   {
9216     TYPE_KEY_X11,
9217     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
9218   },
9219   {
9220     TYPE_KEY_X11,
9221     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
9222   },
9223   {
9224     TYPE_KEY_X11,
9225     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
9226   },
9227   {
9228     TYPE_BOOLEAN,
9229     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
9230   {
9231     TYPE_BOOLEAN,
9232     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
9233   },
9234   {
9235     TYPE_BOOLEAN,
9236     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
9237   },
9238   {
9239     TYPE_SWITCH3,
9240     &setup.debug.xsn_mode,                      "debug.xsn_mode"
9241   },
9242   {
9243     TYPE_INTEGER,
9244     &setup.debug.xsn_percent,                   "debug.xsn_percent"
9245   },
9246 };
9247
9248 static struct TokenInfo options_setup_tokens[] =
9249 {
9250   {
9251     TYPE_BOOLEAN,
9252     &setup.options.verbose,                     "options.verbose"
9253   },
9254 };
9255
9256 static void setSetupInfoToDefaults(struct SetupInfo *si)
9257 {
9258   int i;
9259
9260   si->player_name = getStringCopy(getDefaultUserName(user.nr));
9261
9262   si->multiple_users = TRUE;
9263
9264   si->sound = TRUE;
9265   si->sound_loops = TRUE;
9266   si->sound_music = TRUE;
9267   si->sound_simple = TRUE;
9268   si->toons = TRUE;
9269   si->scroll_delay = TRUE;
9270   si->forced_scroll_delay = FALSE;
9271   si->scroll_delay_value = STD_SCROLL_DELAY;
9272   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9273   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9274   si->fade_screens = TRUE;
9275   si->autorecord = TRUE;
9276   si->show_titlescreen = TRUE;
9277   si->quick_doors = FALSE;
9278   si->team_mode = FALSE;
9279   si->handicap = TRUE;
9280   si->skip_levels = TRUE;
9281   si->increment_levels = TRUE;
9282   si->auto_play_next_level = TRUE;
9283   si->count_score_after_game = TRUE;
9284   si->show_scores_after_game = TRUE;
9285   si->time_limit = TRUE;
9286   si->fullscreen = FALSE;
9287   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9288   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9289   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9290   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9291   si->ask_on_escape = TRUE;
9292   si->ask_on_escape_editor = TRUE;
9293   si->ask_on_game_over = TRUE;
9294   si->quick_switch = FALSE;
9295   si->input_on_focus = FALSE;
9296   si->prefer_aga_graphics = TRUE;
9297   si->prefer_lowpass_sounds = FALSE;
9298   si->prefer_extra_panel_items = TRUE;
9299   si->game_speed_extended = FALSE;
9300   si->game_frame_delay = GAME_FRAME_DELAY;
9301   si->sp_show_border_elements = FALSE;
9302   si->small_game_graphics = FALSE;
9303   si->show_snapshot_buttons = FALSE;
9304
9305   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9306   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
9307   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
9308
9309   si->override_level_graphics = FALSE;
9310   si->override_level_sounds = FALSE;
9311   si->override_level_music = FALSE;
9312
9313   si->volume_simple = 100;              // percent
9314   si->volume_loops = 100;               // percent
9315   si->volume_music = 100;               // percent
9316
9317   si->network_mode = FALSE;
9318   si->network_player_nr = 0;            // first player
9319   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9320
9321   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9322   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
9323   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
9324   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
9325   si->touch.draw_outlined = TRUE;
9326   si->touch.draw_pressed = TRUE;
9327
9328   for (i = 0; i < 2; i++)
9329   {
9330     char *default_grid_button[6][2] =
9331     {
9332       { "      ", "  ^^  " },
9333       { "      ", "  ^^  " },
9334       { "      ", "<<  >>" },
9335       { "      ", "<<  >>" },
9336       { "111222", "  vv  " },
9337       { "111222", "  vv  " }
9338     };
9339     int grid_xsize = DEFAULT_GRID_XSIZE(i);
9340     int grid_ysize = DEFAULT_GRID_YSIZE(i);
9341     int min_xsize = MIN(6, grid_xsize);
9342     int min_ysize = MIN(6, grid_ysize);
9343     int startx = grid_xsize - min_xsize;
9344     int starty = grid_ysize - min_ysize;
9345     int x, y;
9346
9347     // virtual buttons grid can only be set to defaults if video is initialized
9348     // (this will be repeated if virtual buttons are not loaded from setup file)
9349     if (video.initialized)
9350     {
9351       si->touch.grid_xsize[i] = grid_xsize;
9352       si->touch.grid_ysize[i] = grid_ysize;
9353     }
9354     else
9355     {
9356       si->touch.grid_xsize[i] = -1;
9357       si->touch.grid_ysize[i] = -1;
9358     }
9359
9360     for (x = 0; x < MAX_GRID_XSIZE; x++)
9361       for (y = 0; y < MAX_GRID_YSIZE; y++)
9362         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9363
9364     for (x = 0; x < min_xsize; x++)
9365       for (y = 0; y < min_ysize; y++)
9366         si->touch.grid_button[i][x][starty + y] =
9367           default_grid_button[y][0][x];
9368
9369     for (x = 0; x < min_xsize; x++)
9370       for (y = 0; y < min_ysize; y++)
9371         si->touch.grid_button[i][startx + x][starty + y] =
9372           default_grid_button[y][1][x];
9373   }
9374
9375   si->touch.grid_initialized            = video.initialized;
9376
9377   si->editor.el_boulderdash             = TRUE;
9378   si->editor.el_emerald_mine            = TRUE;
9379   si->editor.el_emerald_mine_club       = TRUE;
9380   si->editor.el_more                    = TRUE;
9381   si->editor.el_sokoban                 = TRUE;
9382   si->editor.el_supaplex                = TRUE;
9383   si->editor.el_diamond_caves           = TRUE;
9384   si->editor.el_dx_boulderdash          = TRUE;
9385
9386   si->editor.el_mirror_magic            = TRUE;
9387   si->editor.el_deflektor               = TRUE;
9388
9389   si->editor.el_chars                   = TRUE;
9390   si->editor.el_steel_chars             = TRUE;
9391
9392   si->editor.el_classic                 = TRUE;
9393   si->editor.el_custom                  = TRUE;
9394
9395   si->editor.el_user_defined            = FALSE;
9396   si->editor.el_dynamic                 = TRUE;
9397
9398   si->editor.el_headlines               = TRUE;
9399
9400   si->editor.show_element_token         = FALSE;
9401
9402   si->editor.show_read_only_warning     = TRUE;
9403
9404   si->editor.use_template_for_new_levels = TRUE;
9405
9406   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
9407   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
9408   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
9409
9410   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
9411   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
9412   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
9413   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
9414   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9415
9416   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
9417   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
9418   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
9419   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
9420   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
9421   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
9422
9423   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
9424   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
9425   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
9426
9427   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
9428   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
9429   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
9430   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
9431
9432   for (i = 0; i < MAX_PLAYERS; i++)
9433   {
9434     si->input[i].use_joystick = FALSE;
9435     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9436     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
9437     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9438     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
9439     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
9440     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9441     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
9442     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
9443     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
9444     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
9445     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9446     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
9447     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
9448     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
9449     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
9450   }
9451
9452   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
9453   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9454   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9455   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9456
9457   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
9458   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
9459   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
9460   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
9461   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
9462   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9463   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
9464
9465   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9466
9467   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9468   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
9469   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
9470
9471   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9472   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
9473   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
9474
9475   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9476   si->internal.choose_from_top_leveldir = FALSE;
9477   si->internal.show_scaling_in_title = TRUE;
9478   si->internal.create_user_levelset = TRUE;
9479
9480   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
9481   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9482
9483   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9484   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9485   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9486   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9487   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9488   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9489   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9490   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9491   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9492   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9493
9494   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9495   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9496   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9497   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9498   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9499   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9500   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9501   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9502   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9503   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9504
9505   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9506   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
9507
9508   si->debug.show_frames_per_second = FALSE;
9509
9510   si->debug.xsn_mode = AUTO;
9511   si->debug.xsn_percent = 0;
9512
9513   si->options.verbose = FALSE;
9514
9515 #if defined(PLATFORM_ANDROID)
9516   si->fullscreen = TRUE;
9517 #endif
9518
9519   setHideSetupEntry(&setup.debug.xsn_mode);
9520 }
9521
9522 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9523 {
9524   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9525 }
9526
9527 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9528 {
9529   si->editor_cascade.el_bd              = TRUE;
9530   si->editor_cascade.el_em              = TRUE;
9531   si->editor_cascade.el_emc             = TRUE;
9532   si->editor_cascade.el_rnd             = TRUE;
9533   si->editor_cascade.el_sb              = TRUE;
9534   si->editor_cascade.el_sp              = TRUE;
9535   si->editor_cascade.el_dc              = TRUE;
9536   si->editor_cascade.el_dx              = TRUE;
9537
9538   si->editor_cascade.el_mm              = TRUE;
9539   si->editor_cascade.el_df              = TRUE;
9540
9541   si->editor_cascade.el_chars           = FALSE;
9542   si->editor_cascade.el_steel_chars     = FALSE;
9543   si->editor_cascade.el_ce              = FALSE;
9544   si->editor_cascade.el_ge              = FALSE;
9545   si->editor_cascade.el_ref             = FALSE;
9546   si->editor_cascade.el_user            = FALSE;
9547   si->editor_cascade.el_dynamic         = FALSE;
9548 }
9549
9550 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
9551
9552 static char *getHideSetupToken(void *setup_value)
9553 {
9554   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9555
9556   if (setup_value != NULL)
9557     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9558
9559   return hide_setup_token;
9560 }
9561
9562 void setHideSetupEntry(void *setup_value)
9563 {
9564   char *hide_setup_token = getHideSetupToken(setup_value);
9565
9566   if (hide_setup_hash == NULL)
9567     hide_setup_hash = newSetupFileHash();
9568
9569   if (setup_value != NULL)
9570     setHashEntry(hide_setup_hash, hide_setup_token, "");
9571 }
9572
9573 void removeHideSetupEntry(void *setup_value)
9574 {
9575   char *hide_setup_token = getHideSetupToken(setup_value);
9576
9577   if (setup_value != NULL)
9578     removeHashEntry(hide_setup_hash, hide_setup_token);
9579 }
9580
9581 boolean hideSetupEntry(void *setup_value)
9582 {
9583   char *hide_setup_token = getHideSetupToken(setup_value);
9584
9585   return (setup_value != NULL &&
9586           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9587 }
9588
9589 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9590                                       struct TokenInfo *token_info,
9591                                       int token_nr, char *token_text)
9592 {
9593   char *token_hide_text = getStringCat2(token_text, ".hide");
9594   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9595
9596   // set the value of this setup option in the setup option structure
9597   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9598
9599   // check if this setup option should be hidden in the setup menu
9600   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9601     setHideSetupEntry(token_info[token_nr].value);
9602
9603   free(token_hide_text);
9604 }
9605
9606 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9607                                       struct TokenInfo *token_info,
9608                                       int token_nr)
9609 {
9610   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9611                             token_info[token_nr].text);
9612 }
9613
9614 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9615 {
9616   int i, pnr;
9617
9618   if (!setup_file_hash)
9619     return;
9620
9621   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9622     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9623
9624   setup.touch.grid_initialized = TRUE;
9625   for (i = 0; i < 2; i++)
9626   {
9627     int grid_xsize = setup.touch.grid_xsize[i];
9628     int grid_ysize = setup.touch.grid_ysize[i];
9629     int x, y;
9630
9631     // if virtual buttons are not loaded from setup file, repeat initializing
9632     // virtual buttons grid with default values later when video is initialized
9633     if (grid_xsize == -1 ||
9634         grid_ysize == -1)
9635     {
9636       setup.touch.grid_initialized = FALSE;
9637
9638       continue;
9639     }
9640
9641     for (y = 0; y < grid_ysize; y++)
9642     {
9643       char token_string[MAX_LINE_LEN];
9644
9645       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9646
9647       char *value_string = getHashEntry(setup_file_hash, token_string);
9648
9649       if (value_string == NULL)
9650         continue;
9651
9652       for (x = 0; x < grid_xsize; x++)
9653       {
9654         char c = value_string[x];
9655
9656         setup.touch.grid_button[i][x][y] =
9657           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9658       }
9659     }
9660   }
9661
9662   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9663     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9664
9665   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9666     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9667
9668   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9669   {
9670     char prefix[30];
9671
9672     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9673
9674     setup_input = setup.input[pnr];
9675     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9676     {
9677       char full_token[100];
9678
9679       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9680       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9681                                 full_token);
9682     }
9683     setup.input[pnr] = setup_input;
9684   }
9685
9686   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9687     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9688
9689   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
9690     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9691
9692   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9693     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9694
9695   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9696     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9697
9698   setHideRelatedSetupEntries();
9699 }
9700
9701 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9702 {
9703   int i;
9704
9705   if (!setup_file_hash)
9706     return;
9707
9708   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9709     setSetupInfo(auto_setup_tokens, i,
9710                  getHashEntry(setup_file_hash,
9711                               auto_setup_tokens[i].text));
9712 }
9713
9714 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9715 {
9716   int i;
9717
9718   if (!setup_file_hash)
9719     return;
9720
9721   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9722     setSetupInfo(editor_cascade_setup_tokens, i,
9723                  getHashEntry(setup_file_hash,
9724                               editor_cascade_setup_tokens[i].text));
9725 }
9726
9727 void LoadUserNames(void)
9728 {
9729   int last_user_nr = user.nr;
9730   int i;
9731
9732   if (global.user_names != NULL)
9733   {
9734     for (i = 0; i < MAX_PLAYER_NAMES; i++)
9735       checked_free(global.user_names[i]);
9736
9737     checked_free(global.user_names);
9738   }
9739
9740   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
9741
9742   for (i = 0; i < MAX_PLAYER_NAMES; i++)
9743   {
9744     user.nr = i;
9745
9746     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
9747
9748     if (setup_file_hash)
9749     {
9750       char *player_name = getHashEntry(setup_file_hash, "player_name");
9751
9752       global.user_names[i] = getFixedUserName(player_name);
9753
9754       freeSetupFileHash(setup_file_hash);
9755     }
9756
9757     if (global.user_names[i] == NULL)
9758       global.user_names[i] = getStringCopy(getDefaultUserName(i));
9759   }
9760
9761   user.nr = last_user_nr;
9762 }
9763
9764 void LoadSetupFromFilename(char *filename)
9765 {
9766   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9767
9768   if (setup_file_hash)
9769   {
9770     decodeSetupFileHash(setup_file_hash);
9771
9772     freeSetupFileHash(setup_file_hash);
9773   }
9774   else
9775   {
9776     Debug("setup", "using default setup values");
9777   }
9778 }
9779
9780 static void LoadSetup_SpecialPostProcessing(void)
9781 {
9782   char *player_name_new;
9783
9784   // needed to work around problems with fixed length strings
9785   player_name_new = getFixedUserName(setup.player_name);
9786   free(setup.player_name);
9787   setup.player_name = player_name_new;
9788
9789   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
9790   if (setup.scroll_delay == FALSE)
9791   {
9792     setup.scroll_delay_value = MIN_SCROLL_DELAY;
9793     setup.scroll_delay = TRUE;                  // now always "on"
9794   }
9795
9796   // make sure that scroll delay value stays inside valid range
9797   setup.scroll_delay_value =
9798     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9799 }
9800
9801 void LoadSetup(void)
9802 {
9803   char *filename;
9804
9805   // always start with reliable default values
9806   setSetupInfoToDefaults(&setup);
9807
9808   // try to load setup values from default setup file
9809   filename = getDefaultSetupFilename();
9810
9811   if (fileExists(filename))
9812     LoadSetupFromFilename(filename);
9813
9814   // try to load setup values from user setup file
9815   filename = getSetupFilename();
9816
9817   LoadSetupFromFilename(filename);
9818
9819   LoadSetup_SpecialPostProcessing();
9820 }
9821
9822 void LoadSetup_AutoSetup(void)
9823 {
9824   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9825   SetupFileHash *setup_file_hash = NULL;
9826
9827   // always start with reliable default values
9828   setSetupInfoToDefaults_AutoSetup(&setup);
9829
9830   setup_file_hash = loadSetupFileHash(filename);
9831
9832   if (setup_file_hash)
9833   {
9834     decodeSetupFileHash_AutoSetup(setup_file_hash);
9835
9836     freeSetupFileHash(setup_file_hash);
9837   }
9838
9839   free(filename);
9840 }
9841
9842 void LoadSetup_EditorCascade(void)
9843 {
9844   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9845   SetupFileHash *setup_file_hash = NULL;
9846
9847   // always start with reliable default values
9848   setSetupInfoToDefaults_EditorCascade(&setup);
9849
9850   setup_file_hash = loadSetupFileHash(filename);
9851
9852   if (setup_file_hash)
9853   {
9854     decodeSetupFileHash_EditorCascade(setup_file_hash);
9855
9856     freeSetupFileHash(setup_file_hash);
9857   }
9858
9859   free(filename);
9860 }
9861
9862 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9863                                            char *mapping_line)
9864 {
9865   char mapping_guid[MAX_LINE_LEN];
9866   char *mapping_start, *mapping_end;
9867
9868   // get GUID from game controller mapping line: copy complete line
9869   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9870   mapping_guid[MAX_LINE_LEN - 1] = '\0';
9871
9872   // get GUID from game controller mapping line: cut after GUID part
9873   mapping_start = strchr(mapping_guid, ',');
9874   if (mapping_start != NULL)
9875     *mapping_start = '\0';
9876
9877   // cut newline from game controller mapping line
9878   mapping_end = strchr(mapping_line, '\n');
9879   if (mapping_end != NULL)
9880     *mapping_end = '\0';
9881
9882   // add mapping entry to game controller mappings hash
9883   setHashEntry(mappings_hash, mapping_guid, mapping_line);
9884 }
9885
9886 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9887                                                  char *filename)
9888 {
9889   FILE *file;
9890
9891   if (!(file = fopen(filename, MODE_READ)))
9892   {
9893     Warn("cannot read game controller mappings file '%s'", filename);
9894
9895     return;
9896   }
9897
9898   while (!feof(file))
9899   {
9900     char line[MAX_LINE_LEN];
9901
9902     if (!fgets(line, MAX_LINE_LEN, file))
9903       break;
9904
9905     addGameControllerMappingToHash(mappings_hash, line);
9906   }
9907
9908   fclose(file);
9909 }
9910
9911 void SaveSetup(void)
9912 {
9913   char *filename = getSetupFilename();
9914   FILE *file;
9915   int i, pnr;
9916
9917   InitUserDataDirectory();
9918
9919   if (!(file = fopen(filename, MODE_WRITE)))
9920   {
9921     Warn("cannot write setup file '%s'", filename);
9922
9923     return;
9924   }
9925
9926   fprintFileHeader(file, SETUP_FILENAME);
9927
9928   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9929   {
9930     // just to make things nicer :)
9931     if (global_setup_tokens[i].value == &setup.multiple_users           ||
9932         global_setup_tokens[i].value == &setup.sound                    ||
9933         global_setup_tokens[i].value == &setup.graphics_set             ||
9934         global_setup_tokens[i].value == &setup.volume_simple            ||
9935         global_setup_tokens[i].value == &setup.network_mode             ||
9936         global_setup_tokens[i].value == &setup.touch.control_type       ||
9937         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
9938         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
9939       fprintf(file, "\n");
9940
9941     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9942   }
9943
9944   for (i = 0; i < 2; i++)
9945   {
9946     int grid_xsize = setup.touch.grid_xsize[i];
9947     int grid_ysize = setup.touch.grid_ysize[i];
9948     int x, y;
9949
9950     fprintf(file, "\n");
9951
9952     for (y = 0; y < grid_ysize; y++)
9953     {
9954       char token_string[MAX_LINE_LEN];
9955       char value_string[MAX_LINE_LEN];
9956
9957       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9958
9959       for (x = 0; x < grid_xsize; x++)
9960       {
9961         char c = setup.touch.grid_button[i][x][y];
9962
9963         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9964       }
9965
9966       value_string[grid_xsize] = '\0';
9967
9968       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9969     }
9970   }
9971
9972   fprintf(file, "\n");
9973   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9974     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9975
9976   fprintf(file, "\n");
9977   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9978     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9979
9980   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9981   {
9982     char prefix[30];
9983
9984     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9985     fprintf(file, "\n");
9986
9987     setup_input = setup.input[pnr];
9988     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9989       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9990   }
9991
9992   fprintf(file, "\n");
9993   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9994     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9995
9996   // (internal setup values not saved to user setup file)
9997
9998   fprintf(file, "\n");
9999   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10000     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
10001         setup.debug.xsn_mode != AUTO)
10002       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
10003
10004   fprintf(file, "\n");
10005   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10006     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
10007
10008   fclose(file);
10009
10010   SetFilePermissions(filename, PERMS_PRIVATE);
10011 }
10012
10013 void SaveSetup_AutoSetup(void)
10014 {
10015   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10016   FILE *file;
10017   int i;
10018
10019   InitUserDataDirectory();
10020
10021   if (!(file = fopen(filename, MODE_WRITE)))
10022   {
10023     Warn("cannot write auto setup file '%s'", filename);
10024
10025     free(filename);
10026
10027     return;
10028   }
10029
10030   fprintFileHeader(file, AUTOSETUP_FILENAME);
10031
10032   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10033     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
10034
10035   fclose(file);
10036
10037   SetFilePermissions(filename, PERMS_PRIVATE);
10038
10039   free(filename);
10040 }
10041
10042 void SaveSetup_EditorCascade(void)
10043 {
10044   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10045   FILE *file;
10046   int i;
10047
10048   InitUserDataDirectory();
10049
10050   if (!(file = fopen(filename, MODE_WRITE)))
10051   {
10052     Warn("cannot write editor cascade state file '%s'", filename);
10053
10054     free(filename);
10055
10056     return;
10057   }
10058
10059   fprintFileHeader(file, EDITORCASCADE_FILENAME);
10060
10061   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10062     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
10063
10064   fclose(file);
10065
10066   SetFilePermissions(filename, PERMS_PRIVATE);
10067
10068   free(filename);
10069 }
10070
10071 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
10072                                                   char *filename)
10073 {
10074   FILE *file;
10075
10076   if (!(file = fopen(filename, MODE_WRITE)))
10077   {
10078     Warn("cannot write game controller mappings file '%s'", filename);
10079
10080     return;
10081   }
10082
10083   BEGIN_HASH_ITERATION(mappings_hash, itr)
10084   {
10085     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
10086   }
10087   END_HASH_ITERATION(mappings_hash, itr)
10088
10089   fclose(file);
10090 }
10091
10092 void SaveSetup_AddGameControllerMapping(char *mapping)
10093 {
10094   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
10095   SetupFileHash *mappings_hash = newSetupFileHash();
10096
10097   InitUserDataDirectory();
10098
10099   // load existing personal game controller mappings
10100   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
10101
10102   // add new mapping to personal game controller mappings
10103   addGameControllerMappingToHash(mappings_hash, mapping);
10104
10105   // save updated personal game controller mappings
10106   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
10107
10108   freeSetupFileHash(mappings_hash);
10109   free(filename);
10110 }
10111
10112 void LoadCustomElementDescriptions(void)
10113 {
10114   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10115   SetupFileHash *setup_file_hash;
10116   int i;
10117
10118   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10119   {
10120     if (element_info[i].custom_description != NULL)
10121     {
10122       free(element_info[i].custom_description);
10123       element_info[i].custom_description = NULL;
10124     }
10125   }
10126
10127   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10128     return;
10129
10130   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10131   {
10132     char *token = getStringCat2(element_info[i].token_name, ".name");
10133     char *value = getHashEntry(setup_file_hash, token);
10134
10135     if (value != NULL)
10136       element_info[i].custom_description = getStringCopy(value);
10137
10138     free(token);
10139   }
10140
10141   freeSetupFileHash(setup_file_hash);
10142 }
10143
10144 static int getElementFromToken(char *token)
10145 {
10146   char *value = getHashEntry(element_token_hash, token);
10147
10148   if (value != NULL)
10149     return atoi(value);
10150
10151   Warn("unknown element token '%s'", token);
10152
10153   return EL_UNDEFINED;
10154 }
10155
10156 void FreeGlobalAnimEventInfo(void)
10157 {
10158   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10159
10160   if (gaei->event_list == NULL)
10161     return;
10162
10163   int i;
10164
10165   for (i = 0; i < gaei->num_event_lists; i++)
10166   {
10167     checked_free(gaei->event_list[i]->event_value);
10168     checked_free(gaei->event_list[i]);
10169   }
10170
10171   checked_free(gaei->event_list);
10172
10173   gaei->event_list = NULL;
10174   gaei->num_event_lists = 0;
10175 }
10176
10177 static int AddGlobalAnimEventList(void)
10178 {
10179   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10180   int list_pos = gaei->num_event_lists++;
10181
10182   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
10183                                      sizeof(struct GlobalAnimEventListInfo *));
10184
10185   gaei->event_list[list_pos] =
10186     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
10187
10188   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10189
10190   gaeli->event_value = NULL;
10191   gaeli->num_event_values = 0;
10192
10193   return list_pos;
10194 }
10195
10196 static int AddGlobalAnimEventValue(int list_pos, int event_value)
10197 {
10198   // do not add empty global animation events
10199   if (event_value == ANIM_EVENT_NONE)
10200     return list_pos;
10201
10202   // if list position is undefined, create new list
10203   if (list_pos == ANIM_EVENT_UNDEFINED)
10204     list_pos = AddGlobalAnimEventList();
10205
10206   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10207   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10208   int value_pos = gaeli->num_event_values++;
10209
10210   gaeli->event_value = checked_realloc(gaeli->event_value,
10211                                        gaeli->num_event_values * sizeof(int *));
10212
10213   gaeli->event_value[value_pos] = event_value;
10214
10215   return list_pos;
10216 }
10217
10218 int GetGlobalAnimEventValue(int list_pos, int value_pos)
10219 {
10220   if (list_pos == ANIM_EVENT_UNDEFINED)
10221     return ANIM_EVENT_NONE;
10222
10223   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10224   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10225
10226   return gaeli->event_value[value_pos];
10227 }
10228
10229 int GetGlobalAnimEventValueCount(int list_pos)
10230 {
10231   if (list_pos == ANIM_EVENT_UNDEFINED)
10232     return 0;
10233
10234   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10235   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10236
10237   return gaeli->num_event_values;
10238 }
10239
10240 // This function checks if a string <s> of the format "string1, string2, ..."
10241 // exactly contains a string <s_contained>.
10242
10243 static boolean string_has_parameter(char *s, char *s_contained)
10244 {
10245   char *substring;
10246
10247   if (s == NULL || s_contained == NULL)
10248     return FALSE;
10249
10250   if (strlen(s_contained) > strlen(s))
10251     return FALSE;
10252
10253   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10254   {
10255     char next_char = s[strlen(s_contained)];
10256
10257     // check if next character is delimiter or whitespace
10258     return (next_char == ',' || next_char == '\0' ||
10259             next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10260   }
10261
10262   // check if string contains another parameter string after a comma
10263   substring = strchr(s, ',');
10264   if (substring == NULL)        // string does not contain a comma
10265     return FALSE;
10266
10267   // advance string pointer to next character after the comma
10268   substring++;
10269
10270   // skip potential whitespaces after the comma
10271   while (*substring == ' ' || *substring == '\t')
10272     substring++;
10273
10274   return string_has_parameter(substring, s_contained);
10275 }
10276
10277 static int get_anim_parameter_value(char *s)
10278 {
10279   int event_value[] =
10280   {
10281     ANIM_EVENT_CLICK,
10282     ANIM_EVENT_INIT,
10283     ANIM_EVENT_START,
10284     ANIM_EVENT_END,
10285     ANIM_EVENT_POST
10286   };
10287   char *pattern_1[] =
10288   {
10289     "click:anim_",
10290     "init:anim_",
10291     "start:anim_",
10292     "end:anim_",
10293     "post:anim_"
10294   };
10295   char *pattern_2 = ".part_";
10296   char *matching_char = NULL;
10297   char *s_ptr = s;
10298   int pattern_1_len = 0;
10299   int result = ANIM_EVENT_NONE;
10300   int i;
10301
10302   for (i = 0; i < ARRAY_SIZE(event_value); i++)
10303   {
10304     matching_char = strstr(s_ptr, pattern_1[i]);
10305     pattern_1_len = strlen(pattern_1[i]);
10306     result = event_value[i];
10307
10308     if (matching_char != NULL)
10309       break;
10310   }
10311
10312   if (matching_char == NULL)
10313     return ANIM_EVENT_NONE;
10314
10315   s_ptr = matching_char + pattern_1_len;
10316
10317   // check for main animation number ("anim_X" or "anim_XX")
10318   if (*s_ptr >= '0' && *s_ptr <= '9')
10319   {
10320     int gic_anim_nr = (*s_ptr++ - '0');
10321
10322     if (*s_ptr >= '0' && *s_ptr <= '9')
10323       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10324
10325     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10326       return ANIM_EVENT_NONE;
10327
10328     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10329   }
10330   else
10331   {
10332     // invalid main animation number specified
10333
10334     return ANIM_EVENT_NONE;
10335   }
10336
10337   // check for animation part number ("part_X" or "part_XX") (optional)
10338   if (strPrefix(s_ptr, pattern_2))
10339   {
10340     s_ptr += strlen(pattern_2);
10341
10342     if (*s_ptr >= '0' && *s_ptr <= '9')
10343     {
10344       int gic_part_nr = (*s_ptr++ - '0');
10345
10346       if (*s_ptr >= '0' && *s_ptr <= '9')
10347         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10348
10349       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10350         return ANIM_EVENT_NONE;
10351
10352       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10353     }
10354     else
10355     {
10356       // invalid animation part number specified
10357
10358       return ANIM_EVENT_NONE;
10359     }
10360   }
10361
10362   // discard result if next character is neither delimiter nor whitespace
10363   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10364         *s_ptr == ' ' || *s_ptr == '\t'))
10365     return ANIM_EVENT_NONE;
10366
10367   return result;
10368 }
10369
10370 static int get_anim_parameter_values(char *s)
10371 {
10372   int list_pos = ANIM_EVENT_UNDEFINED;
10373   int event_value = ANIM_EVENT_DEFAULT;
10374
10375   if (string_has_parameter(s, "any"))
10376     event_value |= ANIM_EVENT_ANY;
10377
10378   if (string_has_parameter(s, "click:self") ||
10379       string_has_parameter(s, "click") ||
10380       string_has_parameter(s, "self"))
10381     event_value |= ANIM_EVENT_SELF;
10382
10383   if (string_has_parameter(s, "unclick:any"))
10384     event_value |= ANIM_EVENT_UNCLICK_ANY;
10385
10386   // if animation event found, add it to global animation event list
10387   if (event_value != ANIM_EVENT_NONE)
10388     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10389
10390   while (s != NULL)
10391   {
10392     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10393     event_value = get_anim_parameter_value(s);
10394
10395     // if animation event found, add it to global animation event list
10396     if (event_value != ANIM_EVENT_NONE)
10397       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10398
10399     // continue with next part of the string, starting with next comma
10400     s = strchr(s + 1, ',');
10401   }
10402
10403   return list_pos;
10404 }
10405
10406 static int get_anim_action_parameter_value(char *token)
10407 {
10408   // check most common default case first to massively speed things up
10409   if (strEqual(token, ARG_UNDEFINED))
10410     return ANIM_EVENT_ACTION_NONE;
10411
10412   int result = getImageIDFromToken(token);
10413
10414   if (result == -1)
10415   {
10416     char *gfx_token = getStringCat2("gfx.", token);
10417
10418     result = getImageIDFromToken(gfx_token);
10419
10420     checked_free(gfx_token);
10421   }
10422
10423   if (result == -1)
10424   {
10425     Key key = getKeyFromX11KeyName(token);
10426
10427     if (key != KSYM_UNDEFINED)
10428       result = -(int)key;
10429   }
10430
10431   if (result == -1)
10432     result = ANIM_EVENT_ACTION_NONE;
10433
10434   return result;
10435 }
10436
10437 int get_parameter_value(char *value_raw, char *suffix, int type)
10438 {
10439   char *value = getStringToLower(value_raw);
10440   int result = 0;       // probably a save default value
10441
10442   if (strEqual(suffix, ".direction"))
10443   {
10444     result = (strEqual(value, "left")  ? MV_LEFT :
10445               strEqual(value, "right") ? MV_RIGHT :
10446               strEqual(value, "up")    ? MV_UP :
10447               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
10448   }
10449   else if (strEqual(suffix, ".position"))
10450   {
10451     result = (strEqual(value, "left")   ? POS_LEFT :
10452               strEqual(value, "right")  ? POS_RIGHT :
10453               strEqual(value, "top")    ? POS_TOP :
10454               strEqual(value, "upper")  ? POS_UPPER :
10455               strEqual(value, "middle") ? POS_MIDDLE :
10456               strEqual(value, "lower")  ? POS_LOWER :
10457               strEqual(value, "bottom") ? POS_BOTTOM :
10458               strEqual(value, "any")    ? POS_ANY :
10459               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
10460   }
10461   else if (strEqual(suffix, ".align"))
10462   {
10463     result = (strEqual(value, "left")   ? ALIGN_LEFT :
10464               strEqual(value, "right")  ? ALIGN_RIGHT :
10465               strEqual(value, "center") ? ALIGN_CENTER :
10466               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10467   }
10468   else if (strEqual(suffix, ".valign"))
10469   {
10470     result = (strEqual(value, "top")    ? VALIGN_TOP :
10471               strEqual(value, "bottom") ? VALIGN_BOTTOM :
10472               strEqual(value, "middle") ? VALIGN_MIDDLE :
10473               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10474   }
10475   else if (strEqual(suffix, ".anim_mode"))
10476   {
10477     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
10478               string_has_parameter(value, "loop")       ? ANIM_LOOP :
10479               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
10480               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
10481               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
10482               string_has_parameter(value, "random")     ? ANIM_RANDOM :
10483               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
10484               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
10485               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
10486               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10487               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
10488               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
10489               string_has_parameter(value, "all")        ? ANIM_ALL :
10490               ANIM_DEFAULT);
10491
10492     if (string_has_parameter(value, "once"))
10493       result |= ANIM_ONCE;
10494
10495     if (string_has_parameter(value, "reverse"))
10496       result |= ANIM_REVERSE;
10497
10498     if (string_has_parameter(value, "opaque_player"))
10499       result |= ANIM_OPAQUE_PLAYER;
10500
10501     if (string_has_parameter(value, "static_panel"))
10502       result |= ANIM_STATIC_PANEL;
10503   }
10504   else if (strEqual(suffix, ".init_event") ||
10505            strEqual(suffix, ".anim_event"))
10506   {
10507     result = get_anim_parameter_values(value);
10508   }
10509   else if (strEqual(suffix, ".init_delay_action") ||
10510            strEqual(suffix, ".anim_delay_action") ||
10511            strEqual(suffix, ".post_delay_action") ||
10512            strEqual(suffix, ".init_event_action") ||
10513            strEqual(suffix, ".anim_event_action"))
10514   {
10515     result = get_anim_action_parameter_value(value_raw);
10516   }
10517   else if (strEqual(suffix, ".class"))
10518   {
10519     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10520               get_hash_from_key(value));
10521   }
10522   else if (strEqual(suffix, ".style"))
10523   {
10524     result = STYLE_DEFAULT;
10525
10526     if (string_has_parameter(value, "accurate_borders"))
10527       result |= STYLE_ACCURATE_BORDERS;
10528
10529     if (string_has_parameter(value, "inner_corners"))
10530       result |= STYLE_INNER_CORNERS;
10531
10532     if (string_has_parameter(value, "reverse"))
10533       result |= STYLE_REVERSE;
10534
10535     if (string_has_parameter(value, "leftmost_position"))
10536       result |= STYLE_LEFTMOST_POSITION;
10537
10538     if (string_has_parameter(value, "block_clicks"))
10539       result |= STYLE_BLOCK;
10540
10541     if (string_has_parameter(value, "passthrough_clicks"))
10542       result |= STYLE_PASSTHROUGH;
10543
10544     if (string_has_parameter(value, "multiple_actions"))
10545       result |= STYLE_MULTIPLE_ACTIONS;
10546   }
10547   else if (strEqual(suffix, ".fade_mode"))
10548   {
10549     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
10550               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
10551               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
10552               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
10553               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
10554               FADE_MODE_DEFAULT);
10555   }
10556   else if (strEqual(suffix, ".auto_delay_unit"))
10557   {
10558     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
10559               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10560               AUTO_DELAY_UNIT_DEFAULT);
10561   }
10562   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
10563   {
10564     result = gfx.get_font_from_token_function(value);
10565   }
10566   else          // generic parameter of type integer or boolean
10567   {
10568     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10569               type == TYPE_INTEGER ? get_integer_from_string(value) :
10570               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10571               ARG_UNDEFINED_VALUE);
10572   }
10573
10574   free(value);
10575
10576   return result;
10577 }
10578
10579 static int get_token_parameter_value(char *token, char *value_raw)
10580 {
10581   char *suffix;
10582
10583   if (token == NULL || value_raw == NULL)
10584     return ARG_UNDEFINED_VALUE;
10585
10586   suffix = strrchr(token, '.');
10587   if (suffix == NULL)
10588     suffix = token;
10589
10590   if (strEqual(suffix, ".element"))
10591     return getElementFromToken(value_raw);
10592
10593   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10594   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10595 }
10596
10597 void InitMenuDesignSettings_Static(void)
10598 {
10599   int i;
10600
10601   // always start with reliable default values from static default config
10602   for (i = 0; image_config_vars[i].token != NULL; i++)
10603   {
10604     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10605
10606     if (value != NULL)
10607       *image_config_vars[i].value =
10608         get_token_parameter_value(image_config_vars[i].token, value);
10609   }
10610 }
10611
10612 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10613 {
10614   int i;
10615
10616   // the following initializes hierarchical values from static configuration
10617
10618   // special case: initialize "ARG_DEFAULT" values in static default config
10619   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10620   titlescreen_initial_first_default.fade_mode  =
10621     title_initial_first_default.fade_mode;
10622   titlescreen_initial_first_default.fade_delay =
10623     title_initial_first_default.fade_delay;
10624   titlescreen_initial_first_default.post_delay =
10625     title_initial_first_default.post_delay;
10626   titlescreen_initial_first_default.auto_delay =
10627     title_initial_first_default.auto_delay;
10628   titlescreen_initial_first_default.auto_delay_unit =
10629     title_initial_first_default.auto_delay_unit;
10630   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
10631   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10632   titlescreen_first_default.post_delay = title_first_default.post_delay;
10633   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10634   titlescreen_first_default.auto_delay_unit =
10635     title_first_default.auto_delay_unit;
10636   titlemessage_initial_first_default.fade_mode  =
10637     title_initial_first_default.fade_mode;
10638   titlemessage_initial_first_default.fade_delay =
10639     title_initial_first_default.fade_delay;
10640   titlemessage_initial_first_default.post_delay =
10641     title_initial_first_default.post_delay;
10642   titlemessage_initial_first_default.auto_delay =
10643     title_initial_first_default.auto_delay;
10644   titlemessage_initial_first_default.auto_delay_unit =
10645     title_initial_first_default.auto_delay_unit;
10646   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
10647   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10648   titlemessage_first_default.post_delay = title_first_default.post_delay;
10649   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10650   titlemessage_first_default.auto_delay_unit =
10651     title_first_default.auto_delay_unit;
10652
10653   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
10654   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10655   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10656   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10657   titlescreen_initial_default.auto_delay_unit =
10658     title_initial_default.auto_delay_unit;
10659   titlescreen_default.fade_mode  = title_default.fade_mode;
10660   titlescreen_default.fade_delay = title_default.fade_delay;
10661   titlescreen_default.post_delay = title_default.post_delay;
10662   titlescreen_default.auto_delay = title_default.auto_delay;
10663   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
10664   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
10665   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10666   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10667   titlemessage_initial_default.auto_delay_unit =
10668     title_initial_default.auto_delay_unit;
10669   titlemessage_default.fade_mode  = title_default.fade_mode;
10670   titlemessage_default.fade_delay = title_default.fade_delay;
10671   titlemessage_default.post_delay = title_default.post_delay;
10672   titlemessage_default.auto_delay = title_default.auto_delay;
10673   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
10674
10675   // special case: initialize "ARG_DEFAULT" values in static default config
10676   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
10677   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10678   {
10679     titlescreen_initial_first[i] = titlescreen_initial_first_default;
10680     titlescreen_first[i] = titlescreen_first_default;
10681     titlemessage_initial_first[i] = titlemessage_initial_first_default;
10682     titlemessage_first[i] = titlemessage_first_default;
10683
10684     titlescreen_initial[i] = titlescreen_initial_default;
10685     titlescreen[i] = titlescreen_default;
10686     titlemessage_initial[i] = titlemessage_initial_default;
10687     titlemessage[i] = titlemessage_default;
10688   }
10689
10690   // special case: initialize "ARG_DEFAULT" values in static default config
10691   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
10692   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10693   {
10694     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
10695       continue;
10696
10697     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
10698     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
10699     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
10700   }
10701
10702   // special case: initialize "ARG_DEFAULT" values in static default config
10703   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
10704   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10705   {
10706     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
10707     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
10708     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
10709
10710     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
10711       continue;
10712
10713     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
10714   }
10715 }
10716
10717 static void InitMenuDesignSettings_SpecialPostProcessing(void)
10718 {
10719   static struct
10720   {
10721     struct XY *dst, *src;
10722   }
10723   game_buttons_xy[] =
10724   {
10725     { &game.button.save,        &game.button.stop       },
10726     { &game.button.pause2,      &game.button.pause      },
10727     { &game.button.load,        &game.button.play       },
10728     { &game.button.undo,        &game.button.stop       },
10729     { &game.button.redo,        &game.button.play       },
10730
10731     { NULL,                     NULL                    }
10732   };
10733   int i, j;
10734
10735   // special case: initialize later added SETUP list size from LEVELS value
10736   if (menu.list_size[GAME_MODE_SETUP] == -1)
10737     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
10738
10739   // set default position for snapshot buttons to stop/pause/play buttons
10740   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
10741     if ((*game_buttons_xy[i].dst).x == -1 &&
10742         (*game_buttons_xy[i].dst).y == -1)
10743       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
10744
10745   // --------------------------------------------------------------------------
10746   // dynamic viewports (including playfield margins, borders and alignments)
10747   // --------------------------------------------------------------------------
10748
10749   // dynamic viewports currently only supported for landscape mode
10750   int display_width  = MAX(video.display_width, video.display_height);
10751   int display_height = MIN(video.display_width, video.display_height);
10752
10753   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10754   {
10755     struct RectWithBorder *vp_window    = &viewport.window[i];
10756     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
10757     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
10758     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
10759     boolean dynamic_window_width     = (vp_window->min_width     != -1);
10760     boolean dynamic_window_height    = (vp_window->min_height    != -1);
10761     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
10762     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
10763
10764     // adjust window size if min/max width/height is specified
10765
10766     if (vp_window->min_width != -1)
10767     {
10768       int window_width = display_width;
10769
10770       // when using static window height, use aspect ratio of display
10771       if (vp_window->min_height == -1)
10772         window_width = vp_window->height * display_width / display_height;
10773
10774       vp_window->width = MAX(vp_window->min_width, window_width);
10775     }
10776
10777     if (vp_window->min_height != -1)
10778     {
10779       int window_height = display_height;
10780
10781       // when using static window width, use aspect ratio of display
10782       if (vp_window->min_width == -1)
10783         window_height = vp_window->width * display_height / display_width;
10784
10785       vp_window->height = MAX(vp_window->min_height, window_height);
10786     }
10787
10788     if (vp_window->max_width != -1)
10789       vp_window->width = MIN(vp_window->width, vp_window->max_width);
10790
10791     if (vp_window->max_height != -1)
10792       vp_window->height = MIN(vp_window->height, vp_window->max_height);
10793
10794     int playfield_width  = vp_window->width;
10795     int playfield_height = vp_window->height;
10796
10797     // adjust playfield size and position according to specified margins
10798
10799     playfield_width  -= vp_playfield->margin_left;
10800     playfield_width  -= vp_playfield->margin_right;
10801
10802     playfield_height -= vp_playfield->margin_top;
10803     playfield_height -= vp_playfield->margin_bottom;
10804
10805     // adjust playfield size if min/max width/height is specified
10806
10807     if (vp_playfield->min_width != -1)
10808       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
10809
10810     if (vp_playfield->min_height != -1)
10811       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
10812
10813     if (vp_playfield->max_width != -1)
10814       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
10815
10816     if (vp_playfield->max_height != -1)
10817       vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
10818
10819     // adjust playfield position according to specified alignment
10820
10821     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
10822       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
10823     else if (vp_playfield->align == ALIGN_CENTER)
10824       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
10825     else if (vp_playfield->align == ALIGN_RIGHT)
10826       vp_playfield->x += playfield_width - vp_playfield->width;
10827
10828     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
10829       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
10830     else if (vp_playfield->valign == VALIGN_MIDDLE)
10831       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
10832     else if (vp_playfield->valign == VALIGN_BOTTOM)
10833       vp_playfield->y += playfield_height - vp_playfield->height;
10834
10835     vp_playfield->x += vp_playfield->margin_left;
10836     vp_playfield->y += vp_playfield->margin_top;
10837
10838     // adjust individual playfield borders if only default border is specified
10839
10840     if (vp_playfield->border_left == -1)
10841       vp_playfield->border_left = vp_playfield->border_size;
10842     if (vp_playfield->border_right == -1)
10843       vp_playfield->border_right = vp_playfield->border_size;
10844     if (vp_playfield->border_top == -1)
10845       vp_playfield->border_top = vp_playfield->border_size;
10846     if (vp_playfield->border_bottom == -1)
10847       vp_playfield->border_bottom = vp_playfield->border_size;
10848
10849     // set dynamic playfield borders if borders are specified as undefined
10850     // (but only if window size was dynamic and playfield size was static)
10851
10852     if (dynamic_window_width && !dynamic_playfield_width)
10853     {
10854       if (vp_playfield->border_left == -1)
10855       {
10856         vp_playfield->border_left = (vp_playfield->x -
10857                                      vp_playfield->margin_left);
10858         vp_playfield->x     -= vp_playfield->border_left;
10859         vp_playfield->width += vp_playfield->border_left;
10860       }
10861
10862       if (vp_playfield->border_right == -1)
10863       {
10864         vp_playfield->border_right = (vp_window->width -
10865                                       vp_playfield->x -
10866                                       vp_playfield->width -
10867                                       vp_playfield->margin_right);
10868         vp_playfield->width += vp_playfield->border_right;
10869       }
10870     }
10871
10872     if (dynamic_window_height && !dynamic_playfield_height)
10873     {
10874       if (vp_playfield->border_top == -1)
10875       {
10876         vp_playfield->border_top = (vp_playfield->y -
10877                                     vp_playfield->margin_top);
10878         vp_playfield->y      -= vp_playfield->border_top;
10879         vp_playfield->height += vp_playfield->border_top;
10880       }
10881
10882       if (vp_playfield->border_bottom == -1)
10883       {
10884         vp_playfield->border_bottom = (vp_window->height -
10885                                        vp_playfield->y -
10886                                        vp_playfield->height -
10887                                        vp_playfield->margin_bottom);
10888         vp_playfield->height += vp_playfield->border_bottom;
10889       }
10890     }
10891
10892     // adjust playfield size to be a multiple of a defined alignment tile size
10893
10894     int align_size = vp_playfield->align_size;
10895     int playfield_xtiles = vp_playfield->width  / align_size;
10896     int playfield_ytiles = vp_playfield->height / align_size;
10897     int playfield_width_corrected  = playfield_xtiles * align_size;
10898     int playfield_height_corrected = playfield_ytiles * align_size;
10899     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
10900                                  i == GFX_SPECIAL_ARG_EDITOR);
10901
10902     if (is_playfield_mode &&
10903         dynamic_playfield_width &&
10904         vp_playfield->width != playfield_width_corrected)
10905     {
10906       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
10907
10908       vp_playfield->width = playfield_width_corrected;
10909
10910       if (vp_playfield->align == ALIGN_LEFT)
10911       {
10912         vp_playfield->border_left += playfield_xdiff;
10913       }
10914       else if (vp_playfield->align == ALIGN_RIGHT)
10915       {
10916         vp_playfield->border_right += playfield_xdiff;
10917       }
10918       else if (vp_playfield->align == ALIGN_CENTER)
10919       {
10920         int border_left_diff  = playfield_xdiff / 2;
10921         int border_right_diff = playfield_xdiff - border_left_diff;
10922
10923         vp_playfield->border_left  += border_left_diff;
10924         vp_playfield->border_right += border_right_diff;
10925       }
10926     }
10927
10928     if (is_playfield_mode &&
10929         dynamic_playfield_height &&
10930         vp_playfield->height != playfield_height_corrected)
10931     {
10932       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
10933
10934       vp_playfield->height = playfield_height_corrected;
10935
10936       if (vp_playfield->valign == VALIGN_TOP)
10937       {
10938         vp_playfield->border_top += playfield_ydiff;
10939       }
10940       else if (vp_playfield->align == VALIGN_BOTTOM)
10941       {
10942         vp_playfield->border_right += playfield_ydiff;
10943       }
10944       else if (vp_playfield->align == VALIGN_MIDDLE)
10945       {
10946         int border_top_diff    = playfield_ydiff / 2;
10947         int border_bottom_diff = playfield_ydiff - border_top_diff;
10948
10949         vp_playfield->border_top    += border_top_diff;
10950         vp_playfield->border_bottom += border_bottom_diff;
10951       }
10952     }
10953
10954     // adjust door positions according to specified alignment
10955
10956     for (j = 0; j < 2; j++)
10957     {
10958       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
10959
10960       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
10961         vp_door->x = ALIGNED_VP_XPOS(vp_door);
10962       else if (vp_door->align == ALIGN_CENTER)
10963         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
10964       else if (vp_door->align == ALIGN_RIGHT)
10965         vp_door->x += vp_window->width - vp_door->width;
10966
10967       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
10968         vp_door->y = ALIGNED_VP_YPOS(vp_door);
10969       else if (vp_door->valign == VALIGN_MIDDLE)
10970         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
10971       else if (vp_door->valign == VALIGN_BOTTOM)
10972         vp_door->y += vp_window->height - vp_door->height;
10973     }
10974   }
10975 }
10976
10977 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
10978 {
10979   static struct
10980   {
10981     struct XYTileSize *dst, *src;
10982     int graphic;
10983   }
10984   editor_buttons_xy[] =
10985   {
10986     {
10987       &editor.button.element_left,      &editor.palette.element_left,
10988       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
10989     },
10990     {
10991       &editor.button.element_middle,    &editor.palette.element_middle,
10992       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
10993     },
10994     {
10995       &editor.button.element_right,     &editor.palette.element_right,
10996       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
10997     },
10998
10999     { NULL,                     NULL                    }
11000   };
11001   int i;
11002
11003   // set default position for element buttons to element graphics
11004   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
11005   {
11006     if ((*editor_buttons_xy[i].dst).x == -1 &&
11007         (*editor_buttons_xy[i].dst).y == -1)
11008     {
11009       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
11010
11011       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
11012
11013       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
11014     }
11015   }
11016
11017   // adjust editor palette rows and columns if specified to be dynamic
11018
11019   if (editor.palette.cols == -1)
11020   {
11021     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
11022     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
11023     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
11024
11025     editor.palette.cols = (vp_width - sc_width) / bt_width;
11026
11027     if (editor.palette.x == -1)
11028     {
11029       int palette_width = editor.palette.cols * bt_width + sc_width;
11030
11031       editor.palette.x = (vp_width - palette_width) / 2;
11032     }
11033   }
11034
11035   if (editor.palette.rows == -1)
11036   {
11037     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
11038     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
11039     int tx_height = getFontHeight(FONT_TEXT_2);
11040
11041     editor.palette.rows = (vp_height - tx_height) / bt_height;
11042
11043     if (editor.palette.y == -1)
11044     {
11045       int palette_height = editor.palette.rows * bt_height + tx_height;
11046
11047       editor.palette.y = (vp_height - palette_height) / 2;
11048     }
11049   }
11050 }
11051
11052 static void LoadMenuDesignSettingsFromFilename(char *filename)
11053 {
11054   static struct TitleFadingInfo tfi;
11055   static struct TitleMessageInfo tmi;
11056   static struct TokenInfo title_tokens[] =
11057   {
11058     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
11059     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
11060     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
11061     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
11062     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
11063
11064     { -1,               NULL,                   NULL                    }
11065   };
11066   static struct TokenInfo titlemessage_tokens[] =
11067   {
11068     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
11069     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
11070     { TYPE_INTEGER,     &tmi.width,             ".width"                },
11071     { TYPE_INTEGER,     &tmi.height,            ".height"               },
11072     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
11073     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
11074     { TYPE_INTEGER,     &tmi.align,             ".align"                },
11075     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
11076     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
11077     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
11078     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
11079     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
11080     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
11081     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
11082     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
11083     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
11084     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
11085     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
11086
11087     { -1,               NULL,                   NULL                    }
11088   };
11089   static struct
11090   {
11091     struct TitleFadingInfo *info;
11092     char *text;
11093   }
11094   title_info[] =
11095   {
11096     // initialize first titles from "enter screen" definitions, if defined
11097     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
11098     { &title_first_default,             "menu.enter_screen.TITLE"       },
11099
11100     // initialize title screens from "next screen" definitions, if defined
11101     { &title_initial_default,           "menu.next_screen.TITLE"        },
11102     { &title_default,                   "menu.next_screen.TITLE"        },
11103
11104     { NULL,                             NULL                            }
11105   };
11106   static struct
11107   {
11108     struct TitleMessageInfo *array;
11109     char *text;
11110   }
11111   titlemessage_arrays[] =
11112   {
11113     // initialize first titles from "enter screen" definitions, if defined
11114     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
11115     { titlescreen_first,                "menu.enter_screen.TITLE"       },
11116     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
11117     { titlemessage_first,               "menu.enter_screen.TITLE"       },
11118
11119     // initialize titles from "next screen" definitions, if defined
11120     { titlescreen_initial,              "menu.next_screen.TITLE"        },
11121     { titlescreen,                      "menu.next_screen.TITLE"        },
11122     { titlemessage_initial,             "menu.next_screen.TITLE"        },
11123     { titlemessage,                     "menu.next_screen.TITLE"        },
11124
11125     // overwrite titles with title definitions, if defined
11126     { titlescreen_initial_first,        "[title_initial]"               },
11127     { titlescreen_first,                "[title]"                       },
11128     { titlemessage_initial_first,       "[title_initial]"               },
11129     { titlemessage_first,               "[title]"                       },
11130
11131     { titlescreen_initial,              "[title_initial]"               },
11132     { titlescreen,                      "[title]"                       },
11133     { titlemessage_initial,             "[title_initial]"               },
11134     { titlemessage,                     "[title]"                       },
11135
11136     // overwrite titles with title screen/message definitions, if defined
11137     { titlescreen_initial_first,        "[titlescreen_initial]"         },
11138     { titlescreen_first,                "[titlescreen]"                 },
11139     { titlemessage_initial_first,       "[titlemessage_initial]"        },
11140     { titlemessage_first,               "[titlemessage]"                },
11141
11142     { titlescreen_initial,              "[titlescreen_initial]"         },
11143     { titlescreen,                      "[titlescreen]"                 },
11144     { titlemessage_initial,             "[titlemessage_initial]"        },
11145     { titlemessage,                     "[titlemessage]"                },
11146
11147     { NULL,                             NULL                            }
11148   };
11149   SetupFileHash *setup_file_hash;
11150   int i, j, k;
11151
11152   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11153     return;
11154
11155   // the following initializes hierarchical values from dynamic configuration
11156
11157   // special case: initialize with default values that may be overwritten
11158   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
11159   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11160   {
11161     struct TokenIntPtrInfo menu_config[] =
11162     {
11163       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
11164       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
11165       { "menu.list_size",       &menu.list_size[i]      }
11166     };
11167
11168     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11169     {
11170       char *token = menu_config[j].token;
11171       char *value = getHashEntry(setup_file_hash, token);
11172
11173       if (value != NULL)
11174         *menu_config[j].value = get_integer_from_string(value);
11175     }
11176   }
11177
11178   // special case: initialize with default values that may be overwritten
11179   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
11180   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11181   {
11182     struct TokenIntPtrInfo menu_config[] =
11183     {
11184       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
11185       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
11186       { "menu.list_size.INFO",          &menu.list_size_info[i]         }
11187     };
11188
11189     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11190     {
11191       char *token = menu_config[j].token;
11192       char *value = getHashEntry(setup_file_hash, token);
11193
11194       if (value != NULL)
11195         *menu_config[j].value = get_integer_from_string(value);
11196     }
11197   }
11198
11199   // special case: initialize with default values that may be overwritten
11200   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
11201   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
11202   {
11203     struct TokenIntPtrInfo menu_config[] =
11204     {
11205       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
11206       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
11207     };
11208
11209     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11210     {
11211       char *token = menu_config[j].token;
11212       char *value = getHashEntry(setup_file_hash, token);
11213
11214       if (value != NULL)
11215         *menu_config[j].value = get_integer_from_string(value);
11216     }
11217   }
11218
11219   // special case: initialize with default values that may be overwritten
11220   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
11221   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11222   {
11223     struct TokenIntPtrInfo menu_config[] =
11224     {
11225       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
11226       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
11227       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
11228       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
11229       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
11230       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
11231       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
11232       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
11233       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
11234     };
11235
11236     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11237     {
11238       char *token = menu_config[j].token;
11239       char *value = getHashEntry(setup_file_hash, token);
11240
11241       if (value != NULL)
11242         *menu_config[j].value = get_integer_from_string(value);
11243     }
11244   }
11245
11246   // special case: initialize with default values that may be overwritten
11247   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11248   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11249   {
11250     struct TokenIntPtrInfo menu_config[] =
11251     {
11252       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
11253       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11254       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11255       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
11256       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11257       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11258       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
11259       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
11260       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
11261     };
11262
11263     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11264     {
11265       char *token = menu_config[j].token;
11266       char *value = getHashEntry(setup_file_hash, token);
11267
11268       if (value != NULL)
11269         *menu_config[j].value = get_token_parameter_value(token, value);
11270     }
11271   }
11272
11273   // special case: initialize with default values that may be overwritten
11274   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11275   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11276   {
11277     struct
11278     {
11279       char *token_prefix;
11280       struct RectWithBorder *struct_ptr;
11281     }
11282     vp_struct[] =
11283     {
11284       { "viewport.window",      &viewport.window[i]     },
11285       { "viewport.playfield",   &viewport.playfield[i]  },
11286       { "viewport.door_1",      &viewport.door_1[i]     },
11287       { "viewport.door_2",      &viewport.door_2[i]     }
11288     };
11289
11290     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11291     {
11292       struct TokenIntPtrInfo vp_config[] =
11293       {
11294         { ".x",                 &vp_struct[j].struct_ptr->x             },
11295         { ".y",                 &vp_struct[j].struct_ptr->y             },
11296         { ".width",             &vp_struct[j].struct_ptr->width         },
11297         { ".height",            &vp_struct[j].struct_ptr->height        },
11298         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
11299         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
11300         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
11301         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
11302         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
11303         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
11304         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
11305         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
11306         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
11307         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
11308         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
11309         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
11310         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
11311         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
11312         { ".align",             &vp_struct[j].struct_ptr->align         },
11313         { ".valign",            &vp_struct[j].struct_ptr->valign        }
11314       };
11315
11316       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11317       {
11318         char *token = getStringCat2(vp_struct[j].token_prefix,
11319                                     vp_config[k].token);
11320         char *value = getHashEntry(setup_file_hash, token);
11321
11322         if (value != NULL)
11323           *vp_config[k].value = get_token_parameter_value(token, value);
11324
11325         free(token);
11326       }
11327     }
11328   }
11329
11330   // special case: initialize with default values that may be overwritten
11331   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11332   for (i = 0; title_info[i].info != NULL; i++)
11333   {
11334     struct TitleFadingInfo *info = title_info[i].info;
11335     char *base_token = title_info[i].text;
11336
11337     for (j = 0; title_tokens[j].type != -1; j++)
11338     {
11339       char *token = getStringCat2(base_token, title_tokens[j].text);
11340       char *value = getHashEntry(setup_file_hash, token);
11341
11342       if (value != NULL)
11343       {
11344         int parameter_value = get_token_parameter_value(token, value);
11345
11346         tfi = *info;
11347
11348         *(int *)title_tokens[j].value = (int)parameter_value;
11349
11350         *info = tfi;
11351       }
11352
11353       free(token);
11354     }
11355   }
11356
11357   // special case: initialize with default values that may be overwritten
11358   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11359   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11360   {
11361     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11362     char *base_token = titlemessage_arrays[i].text;
11363
11364     for (j = 0; titlemessage_tokens[j].type != -1; j++)
11365     {
11366       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11367       char *value = getHashEntry(setup_file_hash, token);
11368
11369       if (value != NULL)
11370       {
11371         int parameter_value = get_token_parameter_value(token, value);
11372
11373         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11374         {
11375           tmi = array[k];
11376
11377           if (titlemessage_tokens[j].type == TYPE_INTEGER)
11378             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
11379           else
11380             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11381
11382           array[k] = tmi;
11383         }
11384       }
11385
11386       free(token);
11387     }
11388   }
11389
11390   // special case: check if network and preview player positions are redefined,
11391   // to compare this later against the main menu level preview being redefined
11392   struct TokenIntPtrInfo menu_config_players[] =
11393   {
11394     { "main.network_players.x", &menu.main.network_players.redefined    },
11395     { "main.network_players.y", &menu.main.network_players.redefined    },
11396     { "main.preview_players.x", &menu.main.preview_players.redefined    },
11397     { "main.preview_players.y", &menu.main.preview_players.redefined    },
11398     { "preview.x",              &preview.redefined                      },
11399     { "preview.y",              &preview.redefined                      }
11400   };
11401
11402   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11403     *menu_config_players[i].value = FALSE;
11404
11405   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11406     if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11407       *menu_config_players[i].value = TRUE;
11408
11409   // read (and overwrite with) values that may be specified in config file
11410   for (i = 0; image_config_vars[i].token != NULL; i++)
11411   {
11412     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11413
11414     // (ignore definitions set to "[DEFAULT]" which are already initialized)
11415     if (value != NULL && !strEqual(value, ARG_DEFAULT))
11416       *image_config_vars[i].value =
11417         get_token_parameter_value(image_config_vars[i].token, value);
11418   }
11419
11420   freeSetupFileHash(setup_file_hash);
11421 }
11422
11423 void LoadMenuDesignSettings(void)
11424 {
11425   char *filename_base = UNDEFINED_FILENAME, *filename_local;
11426
11427   InitMenuDesignSettings_Static();
11428   InitMenuDesignSettings_SpecialPreProcessing();
11429
11430   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11431   {
11432     // first look for special settings configured in level series config
11433     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11434
11435     if (fileExists(filename_base))
11436       LoadMenuDesignSettingsFromFilename(filename_base);
11437   }
11438
11439   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11440
11441   if (filename_local != NULL && !strEqual(filename_base, filename_local))
11442     LoadMenuDesignSettingsFromFilename(filename_local);
11443
11444   InitMenuDesignSettings_SpecialPostProcessing();
11445 }
11446
11447 void LoadMenuDesignSettings_AfterGraphics(void)
11448 {
11449   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11450 }
11451
11452 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11453 {
11454   char *filename = getEditorSetupFilename();
11455   SetupFileList *setup_file_list, *list;
11456   SetupFileHash *element_hash;
11457   int num_unknown_tokens = 0;
11458   int i;
11459
11460   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11461     return;
11462
11463   element_hash = newSetupFileHash();
11464
11465   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11466     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11467
11468   // determined size may be larger than needed (due to unknown elements)
11469   *num_elements = 0;
11470   for (list = setup_file_list; list != NULL; list = list->next)
11471     (*num_elements)++;
11472
11473   // add space for up to 3 more elements for padding that may be needed
11474   *num_elements += 3;
11475
11476   // free memory for old list of elements, if needed
11477   checked_free(*elements);
11478
11479   // allocate memory for new list of elements
11480   *elements = checked_malloc(*num_elements * sizeof(int));
11481
11482   *num_elements = 0;
11483   for (list = setup_file_list; list != NULL; list = list->next)
11484   {
11485     char *value = getHashEntry(element_hash, list->token);
11486
11487     if (value == NULL)          // try to find obsolete token mapping
11488     {
11489       char *mapped_token = get_mapped_token(list->token);
11490
11491       if (mapped_token != NULL)
11492       {
11493         value = getHashEntry(element_hash, mapped_token);
11494
11495         free(mapped_token);
11496       }
11497     }
11498
11499     if (value != NULL)
11500     {
11501       (*elements)[(*num_elements)++] = atoi(value);
11502     }
11503     else
11504     {
11505       if (num_unknown_tokens == 0)
11506       {
11507         Warn("---");
11508         Warn("unknown token(s) found in config file:");
11509         Warn("- config file: '%s'", filename);
11510
11511         num_unknown_tokens++;
11512       }
11513
11514       Warn("- token: '%s'", list->token);
11515     }
11516   }
11517
11518   if (num_unknown_tokens > 0)
11519     Warn("---");
11520
11521   while (*num_elements % 4)     // pad with empty elements, if needed
11522     (*elements)[(*num_elements)++] = EL_EMPTY;
11523
11524   freeSetupFileList(setup_file_list);
11525   freeSetupFileHash(element_hash);
11526
11527 #if 0
11528   for (i = 0; i < *num_elements; i++)
11529     Debug("editor", "element '%s' [%d]\n",
11530           element_info[(*elements)[i]].token_name, (*elements)[i]);
11531 #endif
11532 }
11533
11534 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11535                                                      boolean is_sound)
11536 {
11537   SetupFileHash *setup_file_hash = NULL;
11538   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11539   char *filename_music, *filename_prefix, *filename_info;
11540   struct
11541   {
11542     char *token;
11543     char **value_ptr;
11544   }
11545   token_to_value_ptr[] =
11546   {
11547     { "title_header",   &tmp_music_file_info.title_header       },
11548     { "artist_header",  &tmp_music_file_info.artist_header      },
11549     { "album_header",   &tmp_music_file_info.album_header       },
11550     { "year_header",    &tmp_music_file_info.year_header        },
11551
11552     { "title",          &tmp_music_file_info.title              },
11553     { "artist",         &tmp_music_file_info.artist             },
11554     { "album",          &tmp_music_file_info.album              },
11555     { "year",           &tmp_music_file_info.year               },
11556
11557     { NULL,             NULL                                    },
11558   };
11559   int i;
11560
11561   filename_music = (is_sound ? getCustomSoundFilename(basename) :
11562                     getCustomMusicFilename(basename));
11563
11564   if (filename_music == NULL)
11565     return NULL;
11566
11567   // ---------- try to replace file extension ----------
11568
11569   filename_prefix = getStringCopy(filename_music);
11570   if (strrchr(filename_prefix, '.') != NULL)
11571     *strrchr(filename_prefix, '.') = '\0';
11572   filename_info = getStringCat2(filename_prefix, ".txt");
11573
11574   if (fileExists(filename_info))
11575     setup_file_hash = loadSetupFileHash(filename_info);
11576
11577   free(filename_prefix);
11578   free(filename_info);
11579
11580   if (setup_file_hash == NULL)
11581   {
11582     // ---------- try to add file extension ----------
11583
11584     filename_prefix = getStringCopy(filename_music);
11585     filename_info = getStringCat2(filename_prefix, ".txt");
11586
11587     if (fileExists(filename_info))
11588       setup_file_hash = loadSetupFileHash(filename_info);
11589
11590     free(filename_prefix);
11591     free(filename_info);
11592   }
11593
11594   if (setup_file_hash == NULL)
11595     return NULL;
11596
11597   // ---------- music file info found ----------
11598
11599   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11600
11601   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11602   {
11603     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11604
11605     *token_to_value_ptr[i].value_ptr =
11606       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11607   }
11608
11609   tmp_music_file_info.basename = getStringCopy(basename);
11610   tmp_music_file_info.music = music;
11611   tmp_music_file_info.is_sound = is_sound;
11612
11613   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11614   *new_music_file_info = tmp_music_file_info;
11615
11616   return new_music_file_info;
11617 }
11618
11619 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11620 {
11621   return get_music_file_info_ext(basename, music, FALSE);
11622 }
11623
11624 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11625 {
11626   return get_music_file_info_ext(basename, sound, TRUE);
11627 }
11628
11629 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11630                                      char *basename, boolean is_sound)
11631 {
11632   for (; list != NULL; list = list->next)
11633     if (list->is_sound == is_sound && strEqual(list->basename, basename))
11634       return TRUE;
11635
11636   return FALSE;
11637 }
11638
11639 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11640 {
11641   return music_info_listed_ext(list, basename, FALSE);
11642 }
11643
11644 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11645 {
11646   return music_info_listed_ext(list, basename, TRUE);
11647 }
11648
11649 void LoadMusicInfo(void)
11650 {
11651   char *music_directory = getCustomMusicDirectory();
11652   int num_music = getMusicListSize();
11653   int num_music_noconf = 0;
11654   int num_sounds = getSoundListSize();
11655   Directory *dir;
11656   DirectoryEntry *dir_entry;
11657   struct FileInfo *music, *sound;
11658   struct MusicFileInfo *next, **new;
11659   int i;
11660
11661   while (music_file_info != NULL)
11662   {
11663     next = music_file_info->next;
11664
11665     checked_free(music_file_info->basename);
11666
11667     checked_free(music_file_info->title_header);
11668     checked_free(music_file_info->artist_header);
11669     checked_free(music_file_info->album_header);
11670     checked_free(music_file_info->year_header);
11671
11672     checked_free(music_file_info->title);
11673     checked_free(music_file_info->artist);
11674     checked_free(music_file_info->album);
11675     checked_free(music_file_info->year);
11676
11677     free(music_file_info);
11678
11679     music_file_info = next;
11680   }
11681
11682   new = &music_file_info;
11683
11684   for (i = 0; i < num_music; i++)
11685   {
11686     music = getMusicListEntry(i);
11687
11688     if (music->filename == NULL)
11689       continue;
11690
11691     if (strEqual(music->filename, UNDEFINED_FILENAME))
11692       continue;
11693
11694     // a configured file may be not recognized as music
11695     if (!FileIsMusic(music->filename))
11696       continue;
11697
11698     if (!music_info_listed(music_file_info, music->filename))
11699     {
11700       *new = get_music_file_info(music->filename, i);
11701
11702       if (*new != NULL)
11703         new = &(*new)->next;
11704     }
11705   }
11706
11707   if ((dir = openDirectory(music_directory)) == NULL)
11708   {
11709     Warn("cannot read music directory '%s'", music_directory);
11710
11711     return;
11712   }
11713
11714   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
11715   {
11716     char *basename = dir_entry->basename;
11717     boolean music_already_used = FALSE;
11718     int i;
11719
11720     // skip all music files that are configured in music config file
11721     for (i = 0; i < num_music; i++)
11722     {
11723       music = getMusicListEntry(i);
11724
11725       if (music->filename == NULL)
11726         continue;
11727
11728       if (strEqual(basename, music->filename))
11729       {
11730         music_already_used = TRUE;
11731         break;
11732       }
11733     }
11734
11735     if (music_already_used)
11736       continue;
11737
11738     if (!FileIsMusic(dir_entry->filename))
11739       continue;
11740
11741     if (!music_info_listed(music_file_info, basename))
11742     {
11743       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
11744
11745       if (*new != NULL)
11746         new = &(*new)->next;
11747     }
11748
11749     num_music_noconf++;
11750   }
11751
11752   closeDirectory(dir);
11753
11754   for (i = 0; i < num_sounds; i++)
11755   {
11756     sound = getSoundListEntry(i);
11757
11758     if (sound->filename == NULL)
11759       continue;
11760
11761     if (strEqual(sound->filename, UNDEFINED_FILENAME))
11762       continue;
11763
11764     // a configured file may be not recognized as sound
11765     if (!FileIsSound(sound->filename))
11766       continue;
11767
11768     if (!sound_info_listed(music_file_info, sound->filename))
11769     {
11770       *new = get_sound_file_info(sound->filename, i);
11771       if (*new != NULL)
11772         new = &(*new)->next;
11773     }
11774   }
11775 }
11776
11777 static void add_helpanim_entry(int element, int action, int direction,
11778                                int delay, int *num_list_entries)
11779 {
11780   struct HelpAnimInfo *new_list_entry;
11781   (*num_list_entries)++;
11782
11783   helpanim_info =
11784     checked_realloc(helpanim_info,
11785                     *num_list_entries * sizeof(struct HelpAnimInfo));
11786   new_list_entry = &helpanim_info[*num_list_entries - 1];
11787
11788   new_list_entry->element = element;
11789   new_list_entry->action = action;
11790   new_list_entry->direction = direction;
11791   new_list_entry->delay = delay;
11792 }
11793
11794 static void print_unknown_token(char *filename, char *token, int token_nr)
11795 {
11796   if (token_nr == 0)
11797   {
11798     Warn("---");
11799     Warn("unknown token(s) found in config file:");
11800     Warn("- config file: '%s'", filename);
11801   }
11802
11803   Warn("- token: '%s'", token);
11804 }
11805
11806 static void print_unknown_token_end(int token_nr)
11807 {
11808   if (token_nr > 0)
11809     Warn("---");
11810 }
11811
11812 void LoadHelpAnimInfo(void)
11813 {
11814   char *filename = getHelpAnimFilename();
11815   SetupFileList *setup_file_list = NULL, *list;
11816   SetupFileHash *element_hash, *action_hash, *direction_hash;
11817   int num_list_entries = 0;
11818   int num_unknown_tokens = 0;
11819   int i;
11820
11821   if (fileExists(filename))
11822     setup_file_list = loadSetupFileList(filename);
11823
11824   if (setup_file_list == NULL)
11825   {
11826     // use reliable default values from static configuration
11827     SetupFileList *insert_ptr;
11828
11829     insert_ptr = setup_file_list =
11830       newSetupFileList(helpanim_config[0].token,
11831                        helpanim_config[0].value);
11832
11833     for (i = 1; helpanim_config[i].token; i++)
11834       insert_ptr = addListEntry(insert_ptr,
11835                                 helpanim_config[i].token,
11836                                 helpanim_config[i].value);
11837   }
11838
11839   element_hash   = newSetupFileHash();
11840   action_hash    = newSetupFileHash();
11841   direction_hash = newSetupFileHash();
11842
11843   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
11844     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11845
11846   for (i = 0; i < NUM_ACTIONS; i++)
11847     setHashEntry(action_hash, element_action_info[i].suffix,
11848                  i_to_a(element_action_info[i].value));
11849
11850   // do not store direction index (bit) here, but direction value!
11851   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
11852     setHashEntry(direction_hash, element_direction_info[i].suffix,
11853                  i_to_a(1 << element_direction_info[i].value));
11854
11855   for (list = setup_file_list; list != NULL; list = list->next)
11856   {
11857     char *element_token, *action_token, *direction_token;
11858     char *element_value, *action_value, *direction_value;
11859     int delay = atoi(list->value);
11860
11861     if (strEqual(list->token, "end"))
11862     {
11863       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11864
11865       continue;
11866     }
11867
11868     /* first try to break element into element/action/direction parts;
11869        if this does not work, also accept combined "element[.act][.dir]"
11870        elements (like "dynamite.active"), which are unique elements */
11871
11872     if (strchr(list->token, '.') == NULL)       // token contains no '.'
11873     {
11874       element_value = getHashEntry(element_hash, list->token);
11875       if (element_value != NULL)        // element found
11876         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11877                            &num_list_entries);
11878       else
11879       {
11880         // no further suffixes found -- this is not an element
11881         print_unknown_token(filename, list->token, num_unknown_tokens++);
11882       }
11883
11884       continue;
11885     }
11886
11887     // token has format "<prefix>.<something>"
11888
11889     action_token = strchr(list->token, '.');    // suffix may be action ...
11890     direction_token = action_token;             // ... or direction
11891
11892     element_token = getStringCopy(list->token);
11893     *strchr(element_token, '.') = '\0';
11894
11895     element_value = getHashEntry(element_hash, element_token);
11896
11897     if (element_value == NULL)          // this is no element
11898     {
11899       element_value = getHashEntry(element_hash, list->token);
11900       if (element_value != NULL)        // combined element found
11901         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11902                            &num_list_entries);
11903       else
11904         print_unknown_token(filename, list->token, num_unknown_tokens++);
11905
11906       free(element_token);
11907
11908       continue;
11909     }
11910
11911     action_value = getHashEntry(action_hash, action_token);
11912
11913     if (action_value != NULL)           // action found
11914     {
11915       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
11916                     &num_list_entries);
11917
11918       free(element_token);
11919
11920       continue;
11921     }
11922
11923     direction_value = getHashEntry(direction_hash, direction_token);
11924
11925     if (direction_value != NULL)        // direction found
11926     {
11927       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
11928                          &num_list_entries);
11929
11930       free(element_token);
11931
11932       continue;
11933     }
11934
11935     if (strchr(action_token + 1, '.') == NULL)
11936     {
11937       // no further suffixes found -- this is not an action nor direction
11938
11939       element_value = getHashEntry(element_hash, list->token);
11940       if (element_value != NULL)        // combined element found
11941         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11942                            &num_list_entries);
11943       else
11944         print_unknown_token(filename, list->token, num_unknown_tokens++);
11945
11946       free(element_token);
11947
11948       continue;
11949     }
11950
11951     // token has format "<prefix>.<suffix>.<something>"
11952
11953     direction_token = strchr(action_token + 1, '.');
11954
11955     action_token = getStringCopy(action_token);
11956     *strchr(action_token + 1, '.') = '\0';
11957
11958     action_value = getHashEntry(action_hash, action_token);
11959
11960     if (action_value == NULL)           // this is no action
11961     {
11962       element_value = getHashEntry(element_hash, list->token);
11963       if (element_value != NULL)        // combined element found
11964         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11965                            &num_list_entries);
11966       else
11967         print_unknown_token(filename, list->token, num_unknown_tokens++);
11968
11969       free(element_token);
11970       free(action_token);
11971
11972       continue;
11973     }
11974
11975     direction_value = getHashEntry(direction_hash, direction_token);
11976
11977     if (direction_value != NULL)        // direction found
11978     {
11979       add_helpanim_entry(atoi(element_value), atoi(action_value),
11980                          atoi(direction_value), delay, &num_list_entries);
11981
11982       free(element_token);
11983       free(action_token);
11984
11985       continue;
11986     }
11987
11988     // this is no direction
11989
11990     element_value = getHashEntry(element_hash, list->token);
11991     if (element_value != NULL)          // combined element found
11992       add_helpanim_entry(atoi(element_value), -1, -1, delay,
11993                          &num_list_entries);
11994     else
11995       print_unknown_token(filename, list->token, num_unknown_tokens++);
11996
11997     free(element_token);
11998     free(action_token);
11999   }
12000
12001   print_unknown_token_end(num_unknown_tokens);
12002
12003   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
12004   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
12005
12006   freeSetupFileList(setup_file_list);
12007   freeSetupFileHash(element_hash);
12008   freeSetupFileHash(action_hash);
12009   freeSetupFileHash(direction_hash);
12010
12011 #if 0
12012   for (i = 0; i < num_list_entries; i++)
12013     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
12014           EL_NAME(helpanim_info[i].element),
12015           helpanim_info[i].element,
12016           helpanim_info[i].action,
12017           helpanim_info[i].direction,
12018           helpanim_info[i].delay);
12019 #endif
12020 }
12021
12022 void LoadHelpTextInfo(void)
12023 {
12024   char *filename = getHelpTextFilename();
12025   int i;
12026
12027   if (helptext_info != NULL)
12028   {
12029     freeSetupFileHash(helptext_info);
12030     helptext_info = NULL;
12031   }
12032
12033   if (fileExists(filename))
12034     helptext_info = loadSetupFileHash(filename);
12035
12036   if (helptext_info == NULL)
12037   {
12038     // use reliable default values from static configuration
12039     helptext_info = newSetupFileHash();
12040
12041     for (i = 0; helptext_config[i].token; i++)
12042       setHashEntry(helptext_info,
12043                    helptext_config[i].token,
12044                    helptext_config[i].value);
12045   }
12046
12047 #if 0
12048   BEGIN_HASH_ITERATION(helptext_info, itr)
12049   {
12050     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
12051           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
12052   }
12053   END_HASH_ITERATION(hash, itr)
12054 #endif
12055 }
12056
12057
12058 // ----------------------------------------------------------------------------
12059 // convert levels
12060 // ----------------------------------------------------------------------------
12061
12062 #define MAX_NUM_CONVERT_LEVELS          1000
12063
12064 void ConvertLevels(void)
12065 {
12066   static LevelDirTree *convert_leveldir = NULL;
12067   static int convert_level_nr = -1;
12068   static int num_levels_handled = 0;
12069   static int num_levels_converted = 0;
12070   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
12071   int i;
12072
12073   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
12074                                                global.convert_leveldir);
12075
12076   if (convert_leveldir == NULL)
12077     Fail("no such level identifier: '%s'", global.convert_leveldir);
12078
12079   leveldir_current = convert_leveldir;
12080
12081   if (global.convert_level_nr != -1)
12082   {
12083     convert_leveldir->first_level = global.convert_level_nr;
12084     convert_leveldir->last_level  = global.convert_level_nr;
12085   }
12086
12087   convert_level_nr = convert_leveldir->first_level;
12088
12089   PrintLine("=", 79);
12090   Print("Converting levels\n");
12091   PrintLine("-", 79);
12092   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
12093   Print("Level series name:       '%s'\n", convert_leveldir->name);
12094   Print("Level series author:     '%s'\n", convert_leveldir->author);
12095   Print("Number of levels:        %d\n",   convert_leveldir->levels);
12096   PrintLine("=", 79);
12097   Print("\n");
12098
12099   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12100     levels_failed[i] = FALSE;
12101
12102   while (convert_level_nr <= convert_leveldir->last_level)
12103   {
12104     char *level_filename;
12105     boolean new_level;
12106
12107     level_nr = convert_level_nr++;
12108
12109     Print("Level %03d: ", level_nr);
12110
12111     LoadLevel(level_nr);
12112     if (level.no_level_file || level.no_valid_file)
12113     {
12114       Print("(no level)\n");
12115       continue;
12116     }
12117
12118     Print("converting level ... ");
12119
12120     level_filename = getDefaultLevelFilename(level_nr);
12121     new_level = !fileExists(level_filename);
12122
12123     if (new_level)
12124     {
12125       SaveLevel(level_nr);
12126
12127       num_levels_converted++;
12128
12129       Print("converted.\n");
12130     }
12131     else
12132     {
12133       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
12134         levels_failed[level_nr] = TRUE;
12135
12136       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
12137     }
12138
12139     num_levels_handled++;
12140   }
12141
12142   Print("\n");
12143   PrintLine("=", 79);
12144   Print("Number of levels handled: %d\n", num_levels_handled);
12145   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
12146          (num_levels_handled ?
12147           num_levels_converted * 100 / num_levels_handled : 0));
12148   PrintLine("-", 79);
12149   Print("Summary (for automatic parsing by scripts):\n");
12150   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
12151          convert_leveldir->identifier, num_levels_converted,
12152          num_levels_handled,
12153          (num_levels_handled ?
12154           num_levels_converted * 100 / num_levels_handled : 0));
12155
12156   if (num_levels_handled != num_levels_converted)
12157   {
12158     Print(", FAILED:");
12159     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12160       if (levels_failed[i])
12161         Print(" %03d", i);
12162   }
12163
12164   Print("\n");
12165   PrintLine("=", 79);
12166
12167   CloseAllAndExit(0);
12168 }
12169
12170
12171 // ----------------------------------------------------------------------------
12172 // create and save images for use in level sketches (raw BMP format)
12173 // ----------------------------------------------------------------------------
12174
12175 void CreateLevelSketchImages(void)
12176 {
12177   Bitmap *bitmap1;
12178   Bitmap *bitmap2;
12179   int i;
12180
12181   InitElementPropertiesGfxElement();
12182
12183   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
12184   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
12185
12186   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12187   {
12188     int element = getMappedElement(i);
12189     char basename1[16];
12190     char basename2[16];
12191     char *filename1;
12192     char *filename2;
12193
12194     sprintf(basename1, "%04d.bmp", i);
12195     sprintf(basename2, "%04ds.bmp", i);
12196
12197     filename1 = getPath2(global.create_images_dir, basename1);
12198     filename2 = getPath2(global.create_images_dir, basename2);
12199
12200     DrawSizedElement(0, 0, element, TILESIZE);
12201     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
12202
12203     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
12204       Fail("cannot save level sketch image file '%s'", filename1);
12205
12206     DrawSizedElement(0, 0, element, MINI_TILESIZE);
12207     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
12208
12209     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
12210       Fail("cannot save level sketch image file '%s'", filename2);
12211
12212     free(filename1);
12213     free(filename2);
12214
12215     // create corresponding SQL statements (for normal and small images)
12216     if (i < 1000)
12217     {
12218       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12219       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12220     }
12221
12222     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12223     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12224
12225     // optional: create content for forum level sketch demonstration post
12226     if (options.debug)
12227       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
12228   }
12229
12230   FreeBitmap(bitmap1);
12231   FreeBitmap(bitmap2);
12232
12233   if (options.debug)
12234     fprintf(stderr, "\n");
12235
12236   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
12237
12238   CloseAllAndExit(0);
12239 }
12240
12241
12242 // ----------------------------------------------------------------------------
12243 // create and save images for custom and group elements (raw BMP format)
12244 // ----------------------------------------------------------------------------
12245
12246 void CreateCustomElementImages(char *directory)
12247 {
12248   char *src_basename = "RocksCE-template.ilbm";
12249   char *dst_basename = "RocksCE.bmp";
12250   char *src_filename = getPath2(directory, src_basename);
12251   char *dst_filename = getPath2(directory, dst_basename);
12252   Bitmap *src_bitmap;
12253   Bitmap *bitmap;
12254   int yoffset_ce = 0;
12255   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12256   int i;
12257
12258   InitVideoDefaults();
12259
12260   ReCreateBitmap(&backbuffer, video.width, video.height);
12261
12262   src_bitmap = LoadImage(src_filename);
12263
12264   bitmap = CreateBitmap(TILEX * 16 * 2,
12265                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12266                         DEFAULT_DEPTH);
12267
12268   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12269   {
12270     int x = i % 16;
12271     int y = i / 16;
12272     int ii = i + 1;
12273     int j;
12274
12275     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12276                TILEX * x, TILEY * y + yoffset_ce);
12277
12278     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12279                TILEX, TILEY,
12280                TILEX * x + TILEX * 16,
12281                TILEY * y + yoffset_ce);
12282
12283     for (j = 2; j >= 0; j--)
12284     {
12285       int c = ii % 10;
12286
12287       BlitBitmap(src_bitmap, bitmap,
12288                  TILEX + c * 7, 0, 6, 10,
12289                  TILEX * x + 6 + j * 7,
12290                  TILEY * y + 11 + yoffset_ce);
12291
12292       BlitBitmap(src_bitmap, bitmap,
12293                  TILEX + c * 8, TILEY, 6, 10,
12294                  TILEX * 16 + TILEX * x + 6 + j * 8,
12295                  TILEY * y + 10 + yoffset_ce);
12296
12297       ii /= 10;
12298     }
12299   }
12300
12301   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12302   {
12303     int x = i % 16;
12304     int y = i / 16;
12305     int ii = i + 1;
12306     int j;
12307
12308     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12309                TILEX * x, TILEY * y + yoffset_ge);
12310
12311     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12312                TILEX, TILEY,
12313                TILEX * x + TILEX * 16,
12314                TILEY * y + yoffset_ge);
12315
12316     for (j = 1; j >= 0; j--)
12317     {
12318       int c = ii % 10;
12319
12320       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12321                  TILEX * x + 6 + j * 10,
12322                  TILEY * y + 11 + yoffset_ge);
12323
12324       BlitBitmap(src_bitmap, bitmap,
12325                  TILEX + c * 8, TILEY + 12, 6, 10,
12326                  TILEX * 16 + TILEX * x + 10 + j * 8,
12327                  TILEY * y + 10 + yoffset_ge);
12328
12329       ii /= 10;
12330     }
12331   }
12332
12333   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12334     Fail("cannot save CE graphics file '%s'", dst_filename);
12335
12336   FreeBitmap(bitmap);
12337
12338   CloseAllAndExit(0);
12339 }