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