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