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