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