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