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