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