5e95fb80f0cd908e4aeaa08800cd14b2c7738dd3
[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 & (1 << 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 & (1 << 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 & (1 << (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                                        int nr)
5683 {
5684   byte header[DC_LEVEL_HEADER_SIZE];
5685   int envelope_size;
5686   int envelope_header_pos = 62;
5687   int envelope_content_pos = 94;
5688   int level_name_pos = 251;
5689   int level_author_pos = 292;
5690   int envelope_header_len;
5691   int envelope_content_len;
5692   int level_name_len;
5693   int level_author_len;
5694   int fieldx, fieldy;
5695   int num_yamyam_contents;
5696   int i, x, y;
5697
5698   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
5699
5700   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5701   {
5702     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5703
5704     header[i * 2 + 0] = header_word >> 8;
5705     header[i * 2 + 1] = header_word & 0xff;
5706   }
5707
5708   // read some values from level header to check level decoding integrity
5709   fieldx = header[6] | (header[7] << 8);
5710   fieldy = header[8] | (header[9] << 8);
5711   num_yamyam_contents = header[60] | (header[61] << 8);
5712
5713   // do some simple sanity checks to ensure that level was correctly decoded
5714   if (fieldx < 1 || fieldx > 256 ||
5715       fieldy < 1 || fieldy > 256 ||
5716       num_yamyam_contents < 1 || num_yamyam_contents > 8)
5717   {
5718     level->no_valid_file = TRUE;
5719
5720     Warn("cannot decode level from stream -- using empty level");
5721
5722     return;
5723   }
5724
5725   // maximum envelope header size is 31 bytes
5726   envelope_header_len   = header[envelope_header_pos];
5727   // maximum envelope content size is 110 (156?) bytes
5728   envelope_content_len  = header[envelope_content_pos];
5729
5730   // maximum level title size is 40 bytes
5731   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
5732   // maximum level author size is 30 (51?) bytes
5733   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5734
5735   envelope_size = 0;
5736
5737   for (i = 0; i < envelope_header_len; i++)
5738     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5739       level->envelope[0].text[envelope_size++] =
5740         header[envelope_header_pos + 1 + i];
5741
5742   if (envelope_header_len > 0 && envelope_content_len > 0)
5743   {
5744     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5745       level->envelope[0].text[envelope_size++] = '\n';
5746     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5747       level->envelope[0].text[envelope_size++] = '\n';
5748   }
5749
5750   for (i = 0; i < envelope_content_len; i++)
5751     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5752       level->envelope[0].text[envelope_size++] =
5753         header[envelope_content_pos + 1 + i];
5754
5755   level->envelope[0].text[envelope_size] = '\0';
5756
5757   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5758   level->envelope[0].ysize = 10;
5759   level->envelope[0].autowrap = TRUE;
5760   level->envelope[0].centered = TRUE;
5761
5762   for (i = 0; i < level_name_len; i++)
5763     level->name[i] = header[level_name_pos + 1 + i];
5764   level->name[level_name_len] = '\0';
5765
5766   for (i = 0; i < level_author_len; i++)
5767     level->author[i] = header[level_author_pos + 1 + i];
5768   level->author[level_author_len] = '\0';
5769
5770   num_yamyam_contents = header[60] | (header[61] << 8);
5771   level->num_yamyam_contents =
5772     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5773
5774   for (i = 0; i < num_yamyam_contents; i++)
5775   {
5776     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5777     {
5778       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5779       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5780
5781       if (i < MAX_ELEMENT_CONTENTS)
5782         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5783     }
5784   }
5785
5786   fieldx = header[6] | (header[7] << 8);
5787   fieldy = header[8] | (header[9] << 8);
5788   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5789   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5790
5791   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5792   {
5793     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5794     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5795
5796     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5797       level->field[x][y] = getMappedElement_DC(element_dc);
5798   }
5799
5800   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5801   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5802   level->field[x][y] = EL_PLAYER_1;
5803
5804   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5805   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5806   level->field[x][y] = EL_PLAYER_2;
5807
5808   level->gems_needed            = header[18] | (header[19] << 8);
5809
5810   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
5811   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
5812   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
5813   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
5814   level->score[SC_NUT]          = header[28] | (header[29] << 8);
5815   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
5816   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
5817   level->score[SC_BUG]          = header[34] | (header[35] << 8);
5818   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
5819   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
5820   level->score[SC_KEY]          = header[40] | (header[41] << 8);
5821   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
5822
5823   level->time                   = header[44] | (header[45] << 8);
5824
5825   level->amoeba_speed           = header[46] | (header[47] << 8);
5826   level->time_light             = header[48] | (header[49] << 8);
5827   level->time_timegate          = header[50] | (header[51] << 8);
5828   level->time_wheel             = header[52] | (header[53] << 8);
5829   level->time_magic_wall        = header[54] | (header[55] << 8);
5830   level->extra_time             = header[56] | (header[57] << 8);
5831   level->shield_normal_time     = header[58] | (header[59] << 8);
5832
5833   // shield and extra time elements do not have a score
5834   level->score[SC_SHIELD]       = 0;
5835   level->extra_time_score       = 0;
5836
5837   // set time for normal and deadly shields to the same value
5838   level->shield_deadly_time     = level->shield_normal_time;
5839
5840   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5841   // can slip down from flat walls, like normal walls and steel walls
5842   level->em_slippery_gems = TRUE;
5843
5844   // time score is counted for each 10 seconds left in Diamond Caves levels
5845   level->time_score_base = 10;
5846 }
5847
5848 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5849                                      struct LevelFileInfo *level_file_info,
5850                                      boolean level_info_only)
5851 {
5852   char *filename = level_file_info->filename;
5853   File *file;
5854   int num_magic_bytes = 8;
5855   char magic_bytes[num_magic_bytes + 1];
5856   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5857
5858   if (!(file = openFile(filename, MODE_READ)))
5859   {
5860     level->no_valid_file = TRUE;
5861
5862     if (!level_info_only)
5863       Warn("cannot read level '%s' -- using empty level", filename);
5864
5865     return;
5866   }
5867
5868   // fseek(file, 0x0000, SEEK_SET);
5869
5870   if (level_file_info->packed)
5871   {
5872     // read "magic bytes" from start of file
5873     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5874       magic_bytes[0] = '\0';
5875
5876     // check "magic bytes" for correct file format
5877     if (!strPrefix(magic_bytes, "DC2"))
5878     {
5879       level->no_valid_file = TRUE;
5880
5881       Warn("unknown DC level file '%s' -- using empty level", filename);
5882
5883       return;
5884     }
5885
5886     if (strPrefix(magic_bytes, "DC2Win95") ||
5887         strPrefix(magic_bytes, "DC2Win98"))
5888     {
5889       int position_first_level = 0x00fa;
5890       int extra_bytes = 4;
5891       int skip_bytes;
5892
5893       // advance file stream to first level inside the level package
5894       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5895
5896       // each block of level data is followed by block of non-level data
5897       num_levels_to_skip *= 2;
5898
5899       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5900       while (num_levels_to_skip >= 0)
5901       {
5902         // advance file stream to next level inside the level package
5903         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5904         {
5905           level->no_valid_file = TRUE;
5906
5907           Warn("cannot fseek in file '%s' -- using empty level", filename);
5908
5909           return;
5910         }
5911
5912         // skip apparently unused extra bytes following each level
5913         ReadUnusedBytesFromFile(file, extra_bytes);
5914
5915         // read size of next level in level package
5916         skip_bytes = getFile32BitLE(file);
5917
5918         num_levels_to_skip--;
5919       }
5920     }
5921     else
5922     {
5923       level->no_valid_file = TRUE;
5924
5925       Warn("unknown DC2 level file '%s' -- using empty level", filename);
5926
5927       return;
5928     }
5929   }
5930
5931   LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5932
5933   closeFile(file);
5934 }
5935
5936
5937 // ----------------------------------------------------------------------------
5938 // functions for loading SB level
5939 // ----------------------------------------------------------------------------
5940
5941 int getMappedElement_SB(int element_ascii, boolean use_ces)
5942 {
5943   static struct
5944   {
5945     int ascii;
5946     int sb;
5947     int ce;
5948   }
5949   sb_element_mapping[] =
5950   {
5951     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
5952     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
5953     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
5954     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
5955     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
5956     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
5957     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
5958     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
5959
5960     { 0,   -1,                      -1          },
5961   };
5962
5963   int i;
5964
5965   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5966     if (element_ascii == sb_element_mapping[i].ascii)
5967       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5968
5969   return EL_UNDEFINED;
5970 }
5971
5972 static void SetLevelSettings_SB(struct LevelInfo *level)
5973 {
5974   // time settings
5975   level->time = 0;
5976   level->use_step_counter = TRUE;
5977
5978   // score settings
5979   level->score[SC_TIME_BONUS] = 0;
5980   level->time_score_base = 1;
5981   level->rate_time_over_score = TRUE;
5982
5983   // game settings
5984   level->auto_exit_sokoban = TRUE;
5985 }
5986
5987 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5988                                      struct LevelFileInfo *level_file_info,
5989                                      boolean level_info_only)
5990 {
5991   char *filename = level_file_info->filename;
5992   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5993   char last_comment[MAX_LINE_LEN];
5994   char level_name[MAX_LINE_LEN];
5995   char *line_ptr;
5996   File *file;
5997   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5998   boolean read_continued_line = FALSE;
5999   boolean reading_playfield = FALSE;
6000   boolean got_valid_playfield_line = FALSE;
6001   boolean invalid_playfield_char = FALSE;
6002   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6003   int file_level_nr = 0;
6004   int line_nr = 0;
6005   int x = 0, y = 0;             // initialized to make compilers happy
6006
6007   last_comment[0] = '\0';
6008   level_name[0] = '\0';
6009
6010   if (!(file = openFile(filename, MODE_READ)))
6011   {
6012     level->no_valid_file = TRUE;
6013
6014     if (!level_info_only)
6015       Warn("cannot read level '%s' -- using empty level", filename);
6016
6017     return;
6018   }
6019
6020   while (!checkEndOfFile(file))
6021   {
6022     // level successfully read, but next level may follow here
6023     if (!got_valid_playfield_line && reading_playfield)
6024     {
6025       // read playfield from single level file -- skip remaining file
6026       if (!level_file_info->packed)
6027         break;
6028
6029       if (file_level_nr >= num_levels_to_skip)
6030         break;
6031
6032       file_level_nr++;
6033
6034       last_comment[0] = '\0';
6035       level_name[0] = '\0';
6036
6037       reading_playfield = FALSE;
6038     }
6039
6040     got_valid_playfield_line = FALSE;
6041
6042     // read next line of input file
6043     if (!getStringFromFile(file, line, MAX_LINE_LEN))
6044       break;
6045
6046     // check if line was completely read and is terminated by line break
6047     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6048       line_nr++;
6049
6050     // cut trailing line break (this can be newline and/or carriage return)
6051     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6052       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6053         *line_ptr = '\0';
6054
6055     // copy raw input line for later use (mainly debugging output)
6056     strcpy(line_raw, line);
6057
6058     if (read_continued_line)
6059     {
6060       // append new line to existing line, if there is enough space
6061       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6062         strcat(previous_line, line_ptr);
6063
6064       strcpy(line, previous_line);      // copy storage buffer to line
6065
6066       read_continued_line = FALSE;
6067     }
6068
6069     // if the last character is '\', continue at next line
6070     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6071     {
6072       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
6073       strcpy(previous_line, line);      // copy line to storage buffer
6074
6075       read_continued_line = TRUE;
6076
6077       continue;
6078     }
6079
6080     // skip empty lines
6081     if (line[0] == '\0')
6082       continue;
6083
6084     // extract comment text from comment line
6085     if (line[0] == ';')
6086     {
6087       for (line_ptr = line; *line_ptr; line_ptr++)
6088         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6089           break;
6090
6091       strcpy(last_comment, line_ptr);
6092
6093       continue;
6094     }
6095
6096     // extract level title text from line containing level title
6097     if (line[0] == '\'')
6098     {
6099       strcpy(level_name, &line[1]);
6100
6101       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6102         level_name[strlen(level_name) - 1] = '\0';
6103
6104       continue;
6105     }
6106
6107     // skip lines containing only spaces (or empty lines)
6108     for (line_ptr = line; *line_ptr; line_ptr++)
6109       if (*line_ptr != ' ')
6110         break;
6111     if (*line_ptr == '\0')
6112       continue;
6113
6114     // at this point, we have found a line containing part of a playfield
6115
6116     got_valid_playfield_line = TRUE;
6117
6118     if (!reading_playfield)
6119     {
6120       reading_playfield = TRUE;
6121       invalid_playfield_char = FALSE;
6122
6123       for (x = 0; x < MAX_LEV_FIELDX; x++)
6124         for (y = 0; y < MAX_LEV_FIELDY; y++)
6125           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6126
6127       level->fieldx = 0;
6128       level->fieldy = 0;
6129
6130       // start with topmost tile row
6131       y = 0;
6132     }
6133
6134     // skip playfield line if larger row than allowed
6135     if (y >= MAX_LEV_FIELDY)
6136       continue;
6137
6138     // start with leftmost tile column
6139     x = 0;
6140
6141     // read playfield elements from line
6142     for (line_ptr = line; *line_ptr; line_ptr++)
6143     {
6144       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6145
6146       // stop parsing playfield line if larger column than allowed
6147       if (x >= MAX_LEV_FIELDX)
6148         break;
6149
6150       if (mapped_sb_element == EL_UNDEFINED)
6151       {
6152         invalid_playfield_char = TRUE;
6153
6154         break;
6155       }
6156
6157       level->field[x][y] = mapped_sb_element;
6158
6159       // continue with next tile column
6160       x++;
6161
6162       level->fieldx = MAX(x, level->fieldx);
6163     }
6164
6165     if (invalid_playfield_char)
6166     {
6167       // if first playfield line, treat invalid lines as comment lines
6168       if (y == 0)
6169         reading_playfield = FALSE;
6170
6171       continue;
6172     }
6173
6174     // continue with next tile row
6175     y++;
6176   }
6177
6178   closeFile(file);
6179
6180   level->fieldy = y;
6181
6182   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6183   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6184
6185   if (!reading_playfield)
6186   {
6187     level->no_valid_file = TRUE;
6188
6189     Warn("cannot read level '%s' -- using empty level", filename);
6190
6191     return;
6192   }
6193
6194   if (*level_name != '\0')
6195   {
6196     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6197     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6198   }
6199   else if (*last_comment != '\0')
6200   {
6201     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6202     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6203   }
6204   else
6205   {
6206     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6207   }
6208
6209   // set all empty fields beyond the border walls to invisible steel wall
6210   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6211   {
6212     if ((x == 0 || x == level->fieldx - 1 ||
6213          y == 0 || y == level->fieldy - 1) &&
6214         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6215       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6216                      level->field, level->fieldx, level->fieldy);
6217   }
6218
6219   // set special level settings for Sokoban levels
6220   SetLevelSettings_SB(level);
6221
6222   if (load_xsb_to_ces)
6223   {
6224     // special global settings can now be set in level template
6225     level->use_custom_template = TRUE;
6226   }
6227 }
6228
6229
6230 // -------------------------------------------------------------------------
6231 // functions for handling native levels
6232 // -------------------------------------------------------------------------
6233
6234 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6235                                      struct LevelFileInfo *level_file_info,
6236                                      boolean level_info_only)
6237 {
6238   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6239     level->no_valid_file = TRUE;
6240 }
6241
6242 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6243                                      struct LevelFileInfo *level_file_info,
6244                                      boolean level_info_only)
6245 {
6246   int pos = 0;
6247
6248   // determine position of requested level inside level package
6249   if (level_file_info->packed)
6250     pos = level_file_info->nr - leveldir_current->first_level;
6251
6252   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6253     level->no_valid_file = TRUE;
6254 }
6255
6256 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6257                                      struct LevelFileInfo *level_file_info,
6258                                      boolean level_info_only)
6259 {
6260   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6261     level->no_valid_file = TRUE;
6262 }
6263
6264 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6265 {
6266   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6267     CopyNativeLevel_RND_to_EM(level);
6268   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6269     CopyNativeLevel_RND_to_SP(level);
6270   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6271     CopyNativeLevel_RND_to_MM(level);
6272 }
6273
6274 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6275 {
6276   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6277     CopyNativeLevel_EM_to_RND(level);
6278   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6279     CopyNativeLevel_SP_to_RND(level);
6280   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6281     CopyNativeLevel_MM_to_RND(level);
6282 }
6283
6284 void SaveNativeLevel(struct LevelInfo *level)
6285 {
6286   if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6287   {
6288     char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6289     char *filename = getLevelFilenameFromBasename(basename);
6290
6291     CopyNativeLevel_RND_to_SP(level);
6292     CopyNativeTape_RND_to_SP(level);
6293
6294     SaveNativeLevel_SP(filename);
6295   }
6296 }
6297
6298
6299 // ----------------------------------------------------------------------------
6300 // functions for loading generic level
6301 // ----------------------------------------------------------------------------
6302
6303 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6304                                   struct LevelFileInfo *level_file_info,
6305                                   boolean level_info_only)
6306 {
6307   // always start with reliable default values
6308   setLevelInfoToDefaults(level, level_info_only, TRUE);
6309
6310   switch (level_file_info->type)
6311   {
6312     case LEVEL_FILE_TYPE_RND:
6313       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6314       break;
6315
6316     case LEVEL_FILE_TYPE_EM:
6317       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6318       level->game_engine_type = GAME_ENGINE_TYPE_EM;
6319       break;
6320
6321     case LEVEL_FILE_TYPE_SP:
6322       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6323       level->game_engine_type = GAME_ENGINE_TYPE_SP;
6324       break;
6325
6326     case LEVEL_FILE_TYPE_MM:
6327       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6328       level->game_engine_type = GAME_ENGINE_TYPE_MM;
6329       break;
6330
6331     case LEVEL_FILE_TYPE_DC:
6332       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6333       break;
6334
6335     case LEVEL_FILE_TYPE_SB:
6336       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6337       break;
6338
6339     default:
6340       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6341       break;
6342   }
6343
6344   // if level file is invalid, restore level structure to default values
6345   if (level->no_valid_file)
6346     setLevelInfoToDefaults(level, level_info_only, FALSE);
6347
6348   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6349     level->game_engine_type = GAME_ENGINE_TYPE_RND;
6350
6351   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6352     CopyNativeLevel_Native_to_RND(level);
6353 }
6354
6355 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6356 {
6357   static struct LevelFileInfo level_file_info;
6358
6359   // always start with reliable default values
6360   setFileInfoToDefaults(&level_file_info);
6361
6362   level_file_info.nr = 0;                       // unknown level number
6363   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
6364
6365   setString(&level_file_info.filename, filename);
6366
6367   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6368 }
6369
6370 static void LoadLevel_InitVersion(struct LevelInfo *level)
6371 {
6372   int i, j;
6373
6374   if (leveldir_current == NULL)         // only when dumping level
6375     return;
6376
6377   // all engine modifications also valid for levels which use latest engine
6378   if (level->game_version < VERSION_IDENT(3,2,0,5))
6379   {
6380     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6381     level->time_score_base = 10;
6382   }
6383
6384   if (leveldir_current->latest_engine)
6385   {
6386     // ---------- use latest game engine --------------------------------------
6387
6388     /* For all levels which are forced to use the latest game engine version
6389        (normally all but user contributed, private and undefined levels), set
6390        the game engine version to the actual version; this allows for actual
6391        corrections in the game engine to take effect for existing, converted
6392        levels (from "classic" or other existing games) to make the emulation
6393        of the corresponding game more accurate, while (hopefully) not breaking
6394        existing levels created from other players. */
6395
6396     level->game_version = GAME_VERSION_ACTUAL;
6397
6398     /* Set special EM style gems behaviour: EM style gems slip down from
6399        normal, steel and growing wall. As this is a more fundamental change,
6400        it seems better to set the default behaviour to "off" (as it is more
6401        natural) and make it configurable in the level editor (as a property
6402        of gem style elements). Already existing converted levels (neither
6403        private nor contributed levels) are changed to the new behaviour. */
6404
6405     if (level->file_version < FILE_VERSION_2_0)
6406       level->em_slippery_gems = TRUE;
6407
6408     return;
6409   }
6410
6411   // ---------- use game engine the level was created with --------------------
6412
6413   /* For all levels which are not forced to use the latest game engine
6414      version (normally user contributed, private and undefined levels),
6415      use the version of the game engine the levels were created for.
6416
6417      Since 2.0.1, the game engine version is now directly stored
6418      in the level file (chunk "VERS"), so there is no need anymore
6419      to set the game version from the file version (except for old,
6420      pre-2.0 levels, where the game version is still taken from the
6421      file format version used to store the level -- see above). */
6422
6423   // player was faster than enemies in 1.0.0 and before
6424   if (level->file_version == FILE_VERSION_1_0)
6425     for (i = 0; i < MAX_PLAYERS; i++)
6426       level->initial_player_stepsize[i] = STEPSIZE_FAST;
6427
6428   // default behaviour for EM style gems was "slippery" only in 2.0.1
6429   if (level->game_version == VERSION_IDENT(2,0,1,0))
6430     level->em_slippery_gems = TRUE;
6431
6432   // springs could be pushed over pits before (pre-release version) 2.2.0
6433   if (level->game_version < VERSION_IDENT(2,2,0,0))
6434     level->use_spring_bug = TRUE;
6435
6436   if (level->game_version < VERSION_IDENT(3,2,0,5))
6437   {
6438     // time orb caused limited time in endless time levels before 3.2.0-5
6439     level->use_time_orb_bug = TRUE;
6440
6441     // default behaviour for snapping was "no snap delay" before 3.2.0-5
6442     level->block_snap_field = FALSE;
6443
6444     // extra time score was same value as time left score before 3.2.0-5
6445     level->extra_time_score = level->score[SC_TIME_BONUS];
6446   }
6447
6448   if (level->game_version < VERSION_IDENT(3,2,0,7))
6449   {
6450     // default behaviour for snapping was "not continuous" before 3.2.0-7
6451     level->continuous_snapping = FALSE;
6452   }
6453
6454   // only few elements were able to actively move into acid before 3.1.0
6455   // trigger settings did not exist before 3.1.0; set to default "any"
6456   if (level->game_version < VERSION_IDENT(3,1,0,0))
6457   {
6458     // correct "can move into acid" settings (all zero in old levels)
6459
6460     level->can_move_into_acid_bits = 0; // nothing can move into acid
6461     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6462
6463     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
6464     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6465     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
6466     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
6467
6468     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6469       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6470
6471     // correct trigger settings (stored as zero == "none" in old levels)
6472
6473     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6474     {
6475       int element = EL_CUSTOM_START + i;
6476       struct ElementInfo *ei = &element_info[element];
6477
6478       for (j = 0; j < ei->num_change_pages; j++)
6479       {
6480         struct ElementChangeInfo *change = &ei->change_page[j];
6481
6482         change->trigger_player = CH_PLAYER_ANY;
6483         change->trigger_page = CH_PAGE_ANY;
6484       }
6485     }
6486   }
6487
6488   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6489   {
6490     int element = EL_CUSTOM_256;
6491     struct ElementInfo *ei = &element_info[element];
6492     struct ElementChangeInfo *change = &ei->change_page[0];
6493
6494     /* This is needed to fix a problem that was caused by a bugfix in function
6495        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6496        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6497        not replace walkable elements, but instead just placed the player on it,
6498        without placing the Sokoban field under the player). Unfortunately, this
6499        breaks "Snake Bite" style levels when the snake is halfway through a door
6500        that just closes (the snake head is still alive and can be moved in this
6501        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6502        player (without Sokoban element) which then gets killed as designed). */
6503
6504     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6505          strncmp(ei->description, "pause b4 death", 14) == 0) &&
6506         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6507       change->target_element = EL_PLAYER_1;
6508   }
6509
6510   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6511   if (level->game_version < VERSION_IDENT(3,2,5,0))
6512   {
6513     /* This is needed to fix a problem that was caused by a bugfix in function
6514        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6515        corrects the behaviour when a custom element changes to another custom
6516        element with a higher element number that has change actions defined.
6517        Normally, only one change per frame is allowed for custom elements.
6518        Therefore, it is checked if a custom element already changed in the
6519        current frame; if it did, subsequent changes are suppressed.
6520        Unfortunately, this is only checked for element changes, but not for
6521        change actions, which are still executed. As the function above loops
6522        through all custom elements from lower to higher, an element change
6523        resulting in a lower CE number won't be checked again, while a target
6524        element with a higher number will also be checked, and potential change
6525        actions will get executed for this CE, too (which is wrong), while
6526        further changes are ignored (which is correct). As this bugfix breaks
6527        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6528        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6529        behaviour for existing levels and tapes that make use of this bug */
6530
6531     level->use_action_after_change_bug = TRUE;
6532   }
6533
6534   // not centering level after relocating player was default only in 3.2.3
6535   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
6536     level->shifted_relocation = TRUE;
6537
6538   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6539   if (level->game_version < VERSION_IDENT(3,2,6,0))
6540     level->em_explodes_by_fire = TRUE;
6541
6542   // levels were solved by the first player entering an exit up to 4.1.0.0
6543   if (level->game_version <= VERSION_IDENT(4,1,0,0))
6544     level->solved_by_one_player = TRUE;
6545
6546   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6547   if (level->game_version < VERSION_IDENT(4,1,1,1))
6548     level->use_life_bugs = TRUE;
6549
6550   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6551   if (level->game_version < VERSION_IDENT(4,1,1,1))
6552     level->sb_objects_needed = FALSE;
6553
6554   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6555   if (level->game_version <= VERSION_IDENT(4,2,2,0))
6556     level->finish_dig_collect = FALSE;
6557
6558   // CE changing to player was kept under the player if walkable up to 4.2.3.1
6559   if (level->game_version <= VERSION_IDENT(4,2,3,1))
6560     level->keep_walkable_ce = TRUE;
6561 }
6562
6563 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6564 {
6565   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
6566   int x, y;
6567
6568   // check if this level is (not) a Sokoban level
6569   for (y = 0; y < level->fieldy; y++)
6570     for (x = 0; x < level->fieldx; x++)
6571       if (!IS_SB_ELEMENT(Tile[x][y]))
6572         is_sokoban_level = FALSE;
6573
6574   if (is_sokoban_level)
6575   {
6576     // set special level settings for Sokoban levels
6577     SetLevelSettings_SB(level);
6578   }
6579 }
6580
6581 static void LoadLevel_InitSettings(struct LevelInfo *level)
6582 {
6583   // adjust level settings for (non-native) Sokoban-style levels
6584   LoadLevel_InitSettings_SB(level);
6585
6586   // rename levels with title "nameless level" or if renaming is forced
6587   if (leveldir_current->empty_level_name != NULL &&
6588       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6589        leveldir_current->force_level_name))
6590     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6591              leveldir_current->empty_level_name, level_nr);
6592 }
6593
6594 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6595 {
6596   int i, x, y;
6597
6598   // map elements that have changed in newer versions
6599   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6600                                                     level->game_version);
6601   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6602     for (x = 0; x < 3; x++)
6603       for (y = 0; y < 3; y++)
6604         level->yamyam_content[i].e[x][y] =
6605           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6606                                     level->game_version);
6607
6608 }
6609
6610 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6611 {
6612   int i, j;
6613
6614   // map custom element change events that have changed in newer versions
6615   // (these following values were accidentally changed in version 3.0.1)
6616   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6617   if (level->game_version <= VERSION_IDENT(3,0,0,0))
6618   {
6619     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6620     {
6621       int element = EL_CUSTOM_START + i;
6622
6623       // order of checking and copying events to be mapped is important
6624       // (do not change the start and end value -- they are constant)
6625       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6626       {
6627         if (HAS_CHANGE_EVENT(element, j - 2))
6628         {
6629           SET_CHANGE_EVENT(element, j - 2, FALSE);
6630           SET_CHANGE_EVENT(element, j, TRUE);
6631         }
6632       }
6633
6634       // order of checking and copying events to be mapped is important
6635       // (do not change the start and end value -- they are constant)
6636       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6637       {
6638         if (HAS_CHANGE_EVENT(element, j - 1))
6639         {
6640           SET_CHANGE_EVENT(element, j - 1, FALSE);
6641           SET_CHANGE_EVENT(element, j, TRUE);
6642         }
6643       }
6644     }
6645   }
6646
6647   // initialize "can_change" field for old levels with only one change page
6648   if (level->game_version <= VERSION_IDENT(3,0,2,0))
6649   {
6650     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6651     {
6652       int element = EL_CUSTOM_START + i;
6653
6654       if (CAN_CHANGE(element))
6655         element_info[element].change->can_change = TRUE;
6656     }
6657   }
6658
6659   // correct custom element values (for old levels without these options)
6660   if (level->game_version < VERSION_IDENT(3,1,1,0))
6661   {
6662     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6663     {
6664       int element = EL_CUSTOM_START + i;
6665       struct ElementInfo *ei = &element_info[element];
6666
6667       if (ei->access_direction == MV_NO_DIRECTION)
6668         ei->access_direction = MV_ALL_DIRECTIONS;
6669     }
6670   }
6671
6672   // correct custom element values (fix invalid values for all versions)
6673   if (1)
6674   {
6675     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6676     {
6677       int element = EL_CUSTOM_START + i;
6678       struct ElementInfo *ei = &element_info[element];
6679
6680       for (j = 0; j < ei->num_change_pages; j++)
6681       {
6682         struct ElementChangeInfo *change = &ei->change_page[j];
6683
6684         if (change->trigger_player == CH_PLAYER_NONE)
6685           change->trigger_player = CH_PLAYER_ANY;
6686
6687         if (change->trigger_side == CH_SIDE_NONE)
6688           change->trigger_side = CH_SIDE_ANY;
6689       }
6690     }
6691   }
6692
6693   // initialize "can_explode" field for old levels which did not store this
6694   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6695   if (level->game_version <= VERSION_IDENT(3,1,0,0))
6696   {
6697     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6698     {
6699       int element = EL_CUSTOM_START + i;
6700
6701       if (EXPLODES_1X1_OLD(element))
6702         element_info[element].explosion_type = EXPLODES_1X1;
6703
6704       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6705                                              EXPLODES_SMASHED(element) ||
6706                                              EXPLODES_IMPACT(element)));
6707     }
6708   }
6709
6710   // correct previously hard-coded move delay values for maze runner style
6711   if (level->game_version < VERSION_IDENT(3,1,1,0))
6712   {
6713     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6714     {
6715       int element = EL_CUSTOM_START + i;
6716
6717       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6718       {
6719         // previously hard-coded and therefore ignored
6720         element_info[element].move_delay_fixed = 9;
6721         element_info[element].move_delay_random = 0;
6722       }
6723     }
6724   }
6725
6726   // set some other uninitialized values of custom elements in older levels
6727   if (level->game_version < VERSION_IDENT(3,1,0,0))
6728   {
6729     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6730     {
6731       int element = EL_CUSTOM_START + i;
6732
6733       element_info[element].access_direction = MV_ALL_DIRECTIONS;
6734
6735       element_info[element].explosion_delay = 17;
6736       element_info[element].ignition_delay = 8;
6737     }
6738   }
6739
6740   // set mouse click change events to work for left/middle/right mouse button
6741   if (level->game_version < VERSION_IDENT(4,2,3,0))
6742   {
6743     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6744     {
6745       int element = EL_CUSTOM_START + i;
6746       struct ElementInfo *ei = &element_info[element];
6747
6748       for (j = 0; j < ei->num_change_pages; j++)
6749       {
6750         struct ElementChangeInfo *change = &ei->change_page[j];
6751
6752         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6753             change->has_event[CE_PRESSED_BY_MOUSE] ||
6754             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6755             change->has_event[CE_MOUSE_PRESSED_ON_X])
6756           change->trigger_side = CH_SIDE_ANY;
6757       }
6758     }
6759   }
6760 }
6761
6762 static void LoadLevel_InitElements(struct LevelInfo *level)
6763 {
6764   LoadLevel_InitStandardElements(level);
6765
6766   if (level->file_has_custom_elements)
6767     LoadLevel_InitCustomElements(level);
6768
6769   // initialize element properties for level editor etc.
6770   InitElementPropertiesEngine(level->game_version);
6771   InitElementPropertiesGfxElement();
6772 }
6773
6774 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6775 {
6776   int x, y;
6777
6778   // map elements that have changed in newer versions
6779   for (y = 0; y < level->fieldy; y++)
6780     for (x = 0; x < level->fieldx; x++)
6781       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6782                                                      level->game_version);
6783
6784   // clear unused playfield data (nicer if level gets resized in editor)
6785   for (x = 0; x < MAX_LEV_FIELDX; x++)
6786     for (y = 0; y < MAX_LEV_FIELDY; y++)
6787       if (x >= level->fieldx || y >= level->fieldy)
6788         level->field[x][y] = EL_EMPTY;
6789
6790   // copy elements to runtime playfield array
6791   for (x = 0; x < MAX_LEV_FIELDX; x++)
6792     for (y = 0; y < MAX_LEV_FIELDY; y++)
6793       Tile[x][y] = level->field[x][y];
6794
6795   // initialize level size variables for faster access
6796   lev_fieldx = level->fieldx;
6797   lev_fieldy = level->fieldy;
6798
6799   // determine border element for this level
6800   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6801     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
6802   else
6803     SetBorderElement();
6804 }
6805
6806 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6807 {
6808   struct LevelFileInfo *level_file_info = &level->file_info;
6809
6810   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6811     CopyNativeLevel_RND_to_Native(level);
6812 }
6813
6814 static void LoadLevelTemplate_LoadAndInit(void)
6815 {
6816   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6817
6818   LoadLevel_InitVersion(&level_template);
6819   LoadLevel_InitElements(&level_template);
6820   LoadLevel_InitSettings(&level_template);
6821
6822   ActivateLevelTemplate();
6823 }
6824
6825 void LoadLevelTemplate(int nr)
6826 {
6827   if (!fileExists(getGlobalLevelTemplateFilename()))
6828   {
6829     Warn("no level template found for this level");
6830
6831     return;
6832   }
6833
6834   setLevelFileInfo(&level_template.file_info, nr);
6835
6836   LoadLevelTemplate_LoadAndInit();
6837 }
6838
6839 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6840 {
6841   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6842
6843   LoadLevelTemplate_LoadAndInit();
6844 }
6845
6846 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6847 {
6848   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6849
6850   if (level.use_custom_template)
6851   {
6852     if (network_level != NULL)
6853       LoadNetworkLevelTemplate(network_level);
6854     else
6855       LoadLevelTemplate(-1);
6856   }
6857
6858   LoadLevel_InitVersion(&level);
6859   LoadLevel_InitElements(&level);
6860   LoadLevel_InitPlayfield(&level);
6861   LoadLevel_InitSettings(&level);
6862
6863   LoadLevel_InitNativeEngines(&level);
6864 }
6865
6866 void LoadLevel(int nr)
6867 {
6868   SetLevelSetInfo(leveldir_current->identifier, nr);
6869
6870   setLevelFileInfo(&level.file_info, nr);
6871
6872   LoadLevel_LoadAndInit(NULL);
6873 }
6874
6875 void LoadLevelInfoOnly(int nr)
6876 {
6877   setLevelFileInfo(&level.file_info, nr);
6878
6879   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6880 }
6881
6882 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6883 {
6884   SetLevelSetInfo(network_level->leveldir_identifier,
6885                   network_level->file_info.nr);
6886
6887   copyLevelFileInfo(&network_level->file_info, &level.file_info);
6888
6889   LoadLevel_LoadAndInit(network_level);
6890 }
6891
6892 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6893 {
6894   int chunk_size = 0;
6895
6896   chunk_size += putFileVersion(file, level->file_version);
6897   chunk_size += putFileVersion(file, level->game_version);
6898
6899   return chunk_size;
6900 }
6901
6902 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6903 {
6904   int chunk_size = 0;
6905
6906   chunk_size += putFile16BitBE(file, level->creation_date.year);
6907   chunk_size += putFile8Bit(file,    level->creation_date.month);
6908   chunk_size += putFile8Bit(file,    level->creation_date.day);
6909
6910   return chunk_size;
6911 }
6912
6913 #if ENABLE_HISTORIC_CHUNKS
6914 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6915 {
6916   int i, x, y;
6917
6918   putFile8Bit(file, level->fieldx);
6919   putFile8Bit(file, level->fieldy);
6920
6921   putFile16BitBE(file, level->time);
6922   putFile16BitBE(file, level->gems_needed);
6923
6924   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6925     putFile8Bit(file, level->name[i]);
6926
6927   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6928     putFile8Bit(file, level->score[i]);
6929
6930   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6931     for (y = 0; y < 3; y++)
6932       for (x = 0; x < 3; x++)
6933         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6934                            level->yamyam_content[i].e[x][y]));
6935   putFile8Bit(file, level->amoeba_speed);
6936   putFile8Bit(file, level->time_magic_wall);
6937   putFile8Bit(file, level->time_wheel);
6938   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6939                      level->amoeba_content));
6940   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6941   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6942   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6943   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6944
6945   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6946
6947   putFile8Bit(file, (level->block_last_field ? 1 : 0));
6948   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6949   putFile32BitBE(file, level->can_move_into_acid_bits);
6950   putFile8Bit(file, level->dont_collide_with_bits);
6951
6952   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6953   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6954
6955   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6956   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6957   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6958
6959   putFile8Bit(file, level->game_engine_type);
6960
6961   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6962 }
6963 #endif
6964
6965 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6966 {
6967   int chunk_size = 0;
6968   int i;
6969
6970   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6971     chunk_size += putFile8Bit(file, level->name[i]);
6972
6973   return chunk_size;
6974 }
6975
6976 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6977 {
6978   int chunk_size = 0;
6979   int i;
6980
6981   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6982     chunk_size += putFile8Bit(file, level->author[i]);
6983
6984   return chunk_size;
6985 }
6986
6987 #if ENABLE_HISTORIC_CHUNKS
6988 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6989 {
6990   int chunk_size = 0;
6991   int x, y;
6992
6993   for (y = 0; y < level->fieldy; y++)
6994     for (x = 0; x < level->fieldx; x++)
6995       if (level->encoding_16bit_field)
6996         chunk_size += putFile16BitBE(file, level->field[x][y]);
6997       else
6998         chunk_size += putFile8Bit(file, level->field[x][y]);
6999
7000   return chunk_size;
7001 }
7002 #endif
7003
7004 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7005 {
7006   int chunk_size = 0;
7007   int x, y;
7008
7009   for (y = 0; y < level->fieldy; y++) 
7010     for (x = 0; x < level->fieldx; x++) 
7011       chunk_size += putFile16BitBE(file, level->field[x][y]);
7012
7013   return chunk_size;
7014 }
7015
7016 #if ENABLE_HISTORIC_CHUNKS
7017 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7018 {
7019   int i, x, y;
7020
7021   putFile8Bit(file, EL_YAMYAM);
7022   putFile8Bit(file, level->num_yamyam_contents);
7023   putFile8Bit(file, 0);
7024   putFile8Bit(file, 0);
7025
7026   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7027     for (y = 0; y < 3; y++)
7028       for (x = 0; x < 3; x++)
7029         if (level->encoding_16bit_field)
7030           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7031         else
7032           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7033 }
7034 #endif
7035
7036 #if ENABLE_HISTORIC_CHUNKS
7037 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7038 {
7039   int i, x, y;
7040   int num_contents, content_xsize, content_ysize;
7041   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7042
7043   if (element == EL_YAMYAM)
7044   {
7045     num_contents = level->num_yamyam_contents;
7046     content_xsize = 3;
7047     content_ysize = 3;
7048
7049     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7050       for (y = 0; y < 3; y++)
7051         for (x = 0; x < 3; x++)
7052           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7053   }
7054   else if (element == EL_BD_AMOEBA)
7055   {
7056     num_contents = 1;
7057     content_xsize = 1;
7058     content_ysize = 1;
7059
7060     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7061       for (y = 0; y < 3; y++)
7062         for (x = 0; x < 3; x++)
7063           content_array[i][x][y] = EL_EMPTY;
7064     content_array[0][0][0] = level->amoeba_content;
7065   }
7066   else
7067   {
7068     // chunk header already written -- write empty chunk data
7069     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7070
7071     Warn("cannot save content for element '%d'", element);
7072
7073     return;
7074   }
7075
7076   putFile16BitBE(file, element);
7077   putFile8Bit(file, num_contents);
7078   putFile8Bit(file, content_xsize);
7079   putFile8Bit(file, content_ysize);
7080
7081   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7082
7083   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7084     for (y = 0; y < 3; y++)
7085       for (x = 0; x < 3; x++)
7086         putFile16BitBE(file, content_array[i][x][y]);
7087 }
7088 #endif
7089
7090 #if ENABLE_HISTORIC_CHUNKS
7091 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7092 {
7093   int envelope_nr = element - EL_ENVELOPE_1;
7094   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7095   int chunk_size = 0;
7096   int i;
7097
7098   chunk_size += putFile16BitBE(file, element);
7099   chunk_size += putFile16BitBE(file, envelope_len);
7100   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7101   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7102
7103   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7104   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7105
7106   for (i = 0; i < envelope_len; i++)
7107     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7108
7109   return chunk_size;
7110 }
7111 #endif
7112
7113 #if ENABLE_HISTORIC_CHUNKS
7114 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7115                            int num_changed_custom_elements)
7116 {
7117   int i, check = 0;
7118
7119   putFile16BitBE(file, num_changed_custom_elements);
7120
7121   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7122   {
7123     int element = EL_CUSTOM_START + i;
7124
7125     struct ElementInfo *ei = &element_info[element];
7126
7127     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7128     {
7129       if (check < num_changed_custom_elements)
7130       {
7131         putFile16BitBE(file, element);
7132         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7133       }
7134
7135       check++;
7136     }
7137   }
7138
7139   if (check != num_changed_custom_elements)     // should not happen
7140     Warn("inconsistent number of custom element properties");
7141 }
7142 #endif
7143
7144 #if ENABLE_HISTORIC_CHUNKS
7145 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7146                            int num_changed_custom_elements)
7147 {
7148   int i, check = 0;
7149
7150   putFile16BitBE(file, num_changed_custom_elements);
7151
7152   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7153   {
7154     int element = EL_CUSTOM_START + i;
7155
7156     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7157     {
7158       if (check < num_changed_custom_elements)
7159       {
7160         putFile16BitBE(file, element);
7161         putFile16BitBE(file, element_info[element].change->target_element);
7162       }
7163
7164       check++;
7165     }
7166   }
7167
7168   if (check != num_changed_custom_elements)     // should not happen
7169     Warn("inconsistent number of custom target elements");
7170 }
7171 #endif
7172
7173 #if ENABLE_HISTORIC_CHUNKS
7174 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7175                            int num_changed_custom_elements)
7176 {
7177   int i, j, x, y, check = 0;
7178
7179   putFile16BitBE(file, num_changed_custom_elements);
7180
7181   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7182   {
7183     int element = EL_CUSTOM_START + i;
7184     struct ElementInfo *ei = &element_info[element];
7185
7186     if (ei->modified_settings)
7187     {
7188       if (check < num_changed_custom_elements)
7189       {
7190         putFile16BitBE(file, element);
7191
7192         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7193           putFile8Bit(file, ei->description[j]);
7194
7195         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7196
7197         // some free bytes for future properties and padding
7198         WriteUnusedBytesToFile(file, 7);
7199
7200         putFile8Bit(file, ei->use_gfx_element);
7201         putFile16BitBE(file, ei->gfx_element_initial);
7202
7203         putFile8Bit(file, ei->collect_score_initial);
7204         putFile8Bit(file, ei->collect_count_initial);
7205
7206         putFile16BitBE(file, ei->push_delay_fixed);
7207         putFile16BitBE(file, ei->push_delay_random);
7208         putFile16BitBE(file, ei->move_delay_fixed);
7209         putFile16BitBE(file, ei->move_delay_random);
7210
7211         putFile16BitBE(file, ei->move_pattern);
7212         putFile8Bit(file, ei->move_direction_initial);
7213         putFile8Bit(file, ei->move_stepsize);
7214
7215         for (y = 0; y < 3; y++)
7216           for (x = 0; x < 3; x++)
7217             putFile16BitBE(file, ei->content.e[x][y]);
7218
7219         putFile32BitBE(file, ei->change->events);
7220
7221         putFile16BitBE(file, ei->change->target_element);
7222
7223         putFile16BitBE(file, ei->change->delay_fixed);
7224         putFile16BitBE(file, ei->change->delay_random);
7225         putFile16BitBE(file, ei->change->delay_frames);
7226
7227         putFile16BitBE(file, ei->change->initial_trigger_element);
7228
7229         putFile8Bit(file, ei->change->explode);
7230         putFile8Bit(file, ei->change->use_target_content);
7231         putFile8Bit(file, ei->change->only_if_complete);
7232         putFile8Bit(file, ei->change->use_random_replace);
7233
7234         putFile8Bit(file, ei->change->random_percentage);
7235         putFile8Bit(file, ei->change->replace_when);
7236
7237         for (y = 0; y < 3; y++)
7238           for (x = 0; x < 3; x++)
7239             putFile16BitBE(file, ei->change->content.e[x][y]);
7240
7241         putFile8Bit(file, ei->slippery_type);
7242
7243         // some free bytes for future properties and padding
7244         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7245       }
7246
7247       check++;
7248     }
7249   }
7250
7251   if (check != num_changed_custom_elements)     // should not happen
7252     Warn("inconsistent number of custom element properties");
7253 }
7254 #endif
7255
7256 #if ENABLE_HISTORIC_CHUNKS
7257 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7258 {
7259   struct ElementInfo *ei = &element_info[element];
7260   int i, j, x, y;
7261
7262   // ---------- custom element base property values (96 bytes) ----------------
7263
7264   putFile16BitBE(file, element);
7265
7266   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7267     putFile8Bit(file, ei->description[i]);
7268
7269   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7270
7271   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
7272
7273   putFile8Bit(file, ei->num_change_pages);
7274
7275   putFile16BitBE(file, ei->ce_value_fixed_initial);
7276   putFile16BitBE(file, ei->ce_value_random_initial);
7277   putFile8Bit(file, ei->use_last_ce_value);
7278
7279   putFile8Bit(file, ei->use_gfx_element);
7280   putFile16BitBE(file, ei->gfx_element_initial);
7281
7282   putFile8Bit(file, ei->collect_score_initial);
7283   putFile8Bit(file, ei->collect_count_initial);
7284
7285   putFile8Bit(file, ei->drop_delay_fixed);
7286   putFile8Bit(file, ei->push_delay_fixed);
7287   putFile8Bit(file, ei->drop_delay_random);
7288   putFile8Bit(file, ei->push_delay_random);
7289   putFile16BitBE(file, ei->move_delay_fixed);
7290   putFile16BitBE(file, ei->move_delay_random);
7291
7292   // bits 0 - 15 of "move_pattern" ...
7293   putFile16BitBE(file, ei->move_pattern & 0xffff);
7294   putFile8Bit(file, ei->move_direction_initial);
7295   putFile8Bit(file, ei->move_stepsize);
7296
7297   putFile8Bit(file, ei->slippery_type);
7298
7299   for (y = 0; y < 3; y++)
7300     for (x = 0; x < 3; x++)
7301       putFile16BitBE(file, ei->content.e[x][y]);
7302
7303   putFile16BitBE(file, ei->move_enter_element);
7304   putFile16BitBE(file, ei->move_leave_element);
7305   putFile8Bit(file, ei->move_leave_type);
7306
7307   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7308   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7309
7310   putFile8Bit(file, ei->access_direction);
7311
7312   putFile8Bit(file, ei->explosion_delay);
7313   putFile8Bit(file, ei->ignition_delay);
7314   putFile8Bit(file, ei->explosion_type);
7315
7316   // some free bytes for future custom property values and padding
7317   WriteUnusedBytesToFile(file, 1);
7318
7319   // ---------- change page property values (48 bytes) ------------------------
7320
7321   for (i = 0; i < ei->num_change_pages; i++)
7322   {
7323     struct ElementChangeInfo *change = &ei->change_page[i];
7324     unsigned int event_bits;
7325
7326     // bits 0 - 31 of "has_event[]" ...
7327     event_bits = 0;
7328     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7329       if (change->has_event[j])
7330         event_bits |= (1 << j);
7331     putFile32BitBE(file, event_bits);
7332
7333     putFile16BitBE(file, change->target_element);
7334
7335     putFile16BitBE(file, change->delay_fixed);
7336     putFile16BitBE(file, change->delay_random);
7337     putFile16BitBE(file, change->delay_frames);
7338
7339     putFile16BitBE(file, change->initial_trigger_element);
7340
7341     putFile8Bit(file, change->explode);
7342     putFile8Bit(file, change->use_target_content);
7343     putFile8Bit(file, change->only_if_complete);
7344     putFile8Bit(file, change->use_random_replace);
7345
7346     putFile8Bit(file, change->random_percentage);
7347     putFile8Bit(file, change->replace_when);
7348
7349     for (y = 0; y < 3; y++)
7350       for (x = 0; x < 3; x++)
7351         putFile16BitBE(file, change->target_content.e[x][y]);
7352
7353     putFile8Bit(file, change->can_change);
7354
7355     putFile8Bit(file, change->trigger_side);
7356
7357     putFile8Bit(file, change->trigger_player);
7358     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7359                        log_2(change->trigger_page)));
7360
7361     putFile8Bit(file, change->has_action);
7362     putFile8Bit(file, change->action_type);
7363     putFile8Bit(file, change->action_mode);
7364     putFile16BitBE(file, change->action_arg);
7365
7366     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7367     event_bits = 0;
7368     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7369       if (change->has_event[j])
7370         event_bits |= (1 << (j - 32));
7371     putFile8Bit(file, event_bits);
7372   }
7373 }
7374 #endif
7375
7376 #if ENABLE_HISTORIC_CHUNKS
7377 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7378 {
7379   struct ElementInfo *ei = &element_info[element];
7380   struct ElementGroupInfo *group = ei->group;
7381   int i;
7382
7383   putFile16BitBE(file, element);
7384
7385   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7386     putFile8Bit(file, ei->description[i]);
7387
7388   putFile8Bit(file, group->num_elements);
7389
7390   putFile8Bit(file, ei->use_gfx_element);
7391   putFile16BitBE(file, ei->gfx_element_initial);
7392
7393   putFile8Bit(file, group->choice_mode);
7394
7395   // some free bytes for future values and padding
7396   WriteUnusedBytesToFile(file, 3);
7397
7398   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7399     putFile16BitBE(file, group->element[i]);
7400 }
7401 #endif
7402
7403 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7404                                 boolean write_element)
7405 {
7406   int save_type = entry->save_type;
7407   int data_type = entry->data_type;
7408   int conf_type = entry->conf_type;
7409   int byte_mask = conf_type & CONF_MASK_BYTES;
7410   int element = entry->element;
7411   int default_value = entry->default_value;
7412   int num_bytes = 0;
7413   boolean modified = FALSE;
7414
7415   if (byte_mask != CONF_MASK_MULTI_BYTES)
7416   {
7417     void *value_ptr = entry->value;
7418     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7419                  *(int *)value_ptr);
7420
7421     // check if any settings have been modified before saving them
7422     if (value != default_value)
7423       modified = TRUE;
7424
7425     // do not save if explicitly told or if unmodified default settings
7426     if ((save_type == SAVE_CONF_NEVER) ||
7427         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7428       return 0;
7429
7430     if (write_element)
7431       num_bytes += putFile16BitBE(file, element);
7432
7433     num_bytes += putFile8Bit(file, conf_type);
7434     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
7435                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7436                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7437                   0);
7438   }
7439   else if (data_type == TYPE_STRING)
7440   {
7441     char *default_string = entry->default_string;
7442     char *string = (char *)(entry->value);
7443     int string_length = strlen(string);
7444     int i;
7445
7446     // check if any settings have been modified before saving them
7447     if (!strEqual(string, default_string))
7448       modified = TRUE;
7449
7450     // do not save if explicitly told or if unmodified default settings
7451     if ((save_type == SAVE_CONF_NEVER) ||
7452         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7453       return 0;
7454
7455     if (write_element)
7456       num_bytes += putFile16BitBE(file, element);
7457
7458     num_bytes += putFile8Bit(file, conf_type);
7459     num_bytes += putFile16BitBE(file, string_length);
7460
7461     for (i = 0; i < string_length; i++)
7462       num_bytes += putFile8Bit(file, string[i]);
7463   }
7464   else if (data_type == TYPE_ELEMENT_LIST)
7465   {
7466     int *element_array = (int *)(entry->value);
7467     int num_elements = *(int *)(entry->num_entities);
7468     int i;
7469
7470     // check if any settings have been modified before saving them
7471     for (i = 0; i < num_elements; i++)
7472       if (element_array[i] != default_value)
7473         modified = TRUE;
7474
7475     // do not save if explicitly told or if unmodified default settings
7476     if ((save_type == SAVE_CONF_NEVER) ||
7477         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7478       return 0;
7479
7480     if (write_element)
7481       num_bytes += putFile16BitBE(file, element);
7482
7483     num_bytes += putFile8Bit(file, conf_type);
7484     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7485
7486     for (i = 0; i < num_elements; i++)
7487       num_bytes += putFile16BitBE(file, element_array[i]);
7488   }
7489   else if (data_type == TYPE_CONTENT_LIST)
7490   {
7491     struct Content *content = (struct Content *)(entry->value);
7492     int num_contents = *(int *)(entry->num_entities);
7493     int i, x, y;
7494
7495     // check if any settings have been modified before saving them
7496     for (i = 0; i < num_contents; i++)
7497       for (y = 0; y < 3; y++)
7498         for (x = 0; x < 3; x++)
7499           if (content[i].e[x][y] != default_value)
7500             modified = TRUE;
7501
7502     // do not save if explicitly told or if unmodified default settings
7503     if ((save_type == SAVE_CONF_NEVER) ||
7504         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7505       return 0;
7506
7507     if (write_element)
7508       num_bytes += putFile16BitBE(file, element);
7509
7510     num_bytes += putFile8Bit(file, conf_type);
7511     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7512
7513     for (i = 0; i < num_contents; i++)
7514       for (y = 0; y < 3; y++)
7515         for (x = 0; x < 3; x++)
7516           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7517   }
7518
7519   return num_bytes;
7520 }
7521
7522 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7523 {
7524   int chunk_size = 0;
7525   int i;
7526
7527   li = *level;          // copy level data into temporary buffer
7528
7529   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7530     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7531
7532   return chunk_size;
7533 }
7534
7535 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7536 {
7537   int chunk_size = 0;
7538   int i;
7539
7540   li = *level;          // copy level data into temporary buffer
7541
7542   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7543     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7544
7545   return chunk_size;
7546 }
7547
7548 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7549 {
7550   int envelope_nr = element - EL_ENVELOPE_1;
7551   int chunk_size = 0;
7552   int i;
7553
7554   chunk_size += putFile16BitBE(file, element);
7555
7556   // copy envelope data into temporary buffer
7557   xx_envelope = level->envelope[envelope_nr];
7558
7559   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7560     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7561
7562   return chunk_size;
7563 }
7564
7565 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7566 {
7567   struct ElementInfo *ei = &element_info[element];
7568   int chunk_size = 0;
7569   int i, j;
7570
7571   chunk_size += putFile16BitBE(file, element);
7572
7573   xx_ei = *ei;          // copy element data into temporary buffer
7574
7575   // set default description string for this specific element
7576   strcpy(xx_default_description, getDefaultElementDescription(ei));
7577
7578   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7579     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7580
7581   for (i = 0; i < ei->num_change_pages; i++)
7582   {
7583     struct ElementChangeInfo *change = &ei->change_page[i];
7584
7585     xx_current_change_page = i;
7586
7587     xx_change = *change;        // copy change data into temporary buffer
7588
7589     resetEventBits();
7590     setEventBitsFromEventFlags(change);
7591
7592     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7593       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7594                                          FALSE);
7595   }
7596
7597   return chunk_size;
7598 }
7599
7600 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7601 {
7602   struct ElementInfo *ei = &element_info[element];
7603   struct ElementGroupInfo *group = ei->group;
7604   int chunk_size = 0;
7605   int i;
7606
7607   chunk_size += putFile16BitBE(file, element);
7608
7609   xx_ei = *ei;          // copy element data into temporary buffer
7610   xx_group = *group;    // copy group data into temporary buffer
7611
7612   // set default description string for this specific element
7613   strcpy(xx_default_description, getDefaultElementDescription(ei));
7614
7615   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7616     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7617
7618   return chunk_size;
7619 }
7620
7621 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7622 {
7623   struct ElementInfo *ei = &element_info[element];
7624   int chunk_size = 0;
7625   int i;
7626
7627   chunk_size += putFile16BitBE(file, element);
7628
7629   xx_ei = *ei;          // copy element data into temporary buffer
7630
7631   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7632     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7633
7634   return chunk_size;
7635 }
7636
7637 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7638                                   boolean save_as_template)
7639 {
7640   int chunk_size;
7641   int i;
7642   FILE *file;
7643
7644   if (!(file = fopen(filename, MODE_WRITE)))
7645   {
7646     Warn("cannot save level file '%s'", filename);
7647
7648     return;
7649   }
7650
7651   level->file_version = FILE_VERSION_ACTUAL;
7652   level->game_version = GAME_VERSION_ACTUAL;
7653
7654   level->creation_date = getCurrentDate();
7655
7656   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7657   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7658
7659   chunk_size = SaveLevel_VERS(NULL, level);
7660   putFileChunkBE(file, "VERS", chunk_size);
7661   SaveLevel_VERS(file, level);
7662
7663   chunk_size = SaveLevel_DATE(NULL, level);
7664   putFileChunkBE(file, "DATE", chunk_size);
7665   SaveLevel_DATE(file, level);
7666
7667   chunk_size = SaveLevel_NAME(NULL, level);
7668   putFileChunkBE(file, "NAME", chunk_size);
7669   SaveLevel_NAME(file, level);
7670
7671   chunk_size = SaveLevel_AUTH(NULL, level);
7672   putFileChunkBE(file, "AUTH", chunk_size);
7673   SaveLevel_AUTH(file, level);
7674
7675   chunk_size = SaveLevel_INFO(NULL, level);
7676   putFileChunkBE(file, "INFO", chunk_size);
7677   SaveLevel_INFO(file, level);
7678
7679   chunk_size = SaveLevel_BODY(NULL, level);
7680   putFileChunkBE(file, "BODY", chunk_size);
7681   SaveLevel_BODY(file, level);
7682
7683   chunk_size = SaveLevel_ELEM(NULL, level);
7684   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
7685   {
7686     putFileChunkBE(file, "ELEM", chunk_size);
7687     SaveLevel_ELEM(file, level);
7688   }
7689
7690   for (i = 0; i < NUM_ENVELOPES; i++)
7691   {
7692     int element = EL_ENVELOPE_1 + i;
7693
7694     chunk_size = SaveLevel_NOTE(NULL, level, element);
7695     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
7696     {
7697       putFileChunkBE(file, "NOTE", chunk_size);
7698       SaveLevel_NOTE(file, level, element);
7699     }
7700   }
7701
7702   // if not using template level, check for non-default custom/group elements
7703   if (!level->use_custom_template || save_as_template)
7704   {
7705     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7706     {
7707       int element = EL_CUSTOM_START + i;
7708
7709       chunk_size = SaveLevel_CUSX(NULL, level, element);
7710       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
7711       {
7712         putFileChunkBE(file, "CUSX", chunk_size);
7713         SaveLevel_CUSX(file, level, element);
7714       }
7715     }
7716
7717     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7718     {
7719       int element = EL_GROUP_START + i;
7720
7721       chunk_size = SaveLevel_GRPX(NULL, level, element);
7722       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
7723       {
7724         putFileChunkBE(file, "GRPX", chunk_size);
7725         SaveLevel_GRPX(file, level, element);
7726       }
7727     }
7728
7729     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7730     {
7731       int element = GET_EMPTY_ELEMENT(i);
7732
7733       chunk_size = SaveLevel_EMPX(NULL, level, element);
7734       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
7735       {
7736         putFileChunkBE(file, "EMPX", chunk_size);
7737         SaveLevel_EMPX(file, level, element);
7738       }
7739     }
7740   }
7741
7742   fclose(file);
7743
7744   SetFilePermissions(filename, PERMS_PRIVATE);
7745 }
7746
7747 void SaveLevel(int nr)
7748 {
7749   char *filename = getDefaultLevelFilename(nr);
7750
7751   SaveLevelFromFilename(&level, filename, FALSE);
7752 }
7753
7754 void SaveLevelTemplate(void)
7755 {
7756   char *filename = getLocalLevelTemplateFilename();
7757
7758   SaveLevelFromFilename(&level, filename, TRUE);
7759 }
7760
7761 boolean SaveLevelChecked(int nr)
7762 {
7763   char *filename = getDefaultLevelFilename(nr);
7764   boolean new_level = !fileExists(filename);
7765   boolean level_saved = FALSE;
7766
7767   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7768   {
7769     SaveLevel(nr);
7770
7771     if (new_level)
7772       Request("Level saved!", REQ_CONFIRM);
7773
7774     level_saved = TRUE;
7775   }
7776
7777   return level_saved;
7778 }
7779
7780 void DumpLevel(struct LevelInfo *level)
7781 {
7782   if (level->no_level_file || level->no_valid_file)
7783   {
7784     Warn("cannot dump -- no valid level file found");
7785
7786     return;
7787   }
7788
7789   PrintLine("-", 79);
7790   Print("Level xxx (file version %08d, game version %08d)\n",
7791         level->file_version, level->game_version);
7792   PrintLine("-", 79);
7793
7794   Print("Level author: '%s'\n", level->author);
7795   Print("Level title:  '%s'\n", level->name);
7796   Print("\n");
7797   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7798   Print("\n");
7799   Print("Level time:  %d seconds\n", level->time);
7800   Print("Gems needed: %d\n", level->gems_needed);
7801   Print("\n");
7802   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7803   Print("Time for wheel:      %d seconds\n", level->time_wheel);
7804   Print("Time for light:      %d seconds\n", level->time_light);
7805   Print("Time for timegate:   %d seconds\n", level->time_timegate);
7806   Print("\n");
7807   Print("Amoeba speed: %d\n", level->amoeba_speed);
7808   Print("\n");
7809
7810   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
7811   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
7812   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7813   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7814   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7815   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7816
7817   if (options.debug)
7818   {
7819     int i, j;
7820
7821     for (i = 0; i < NUM_ENVELOPES; i++)
7822     {
7823       char *text = level->envelope[i].text;
7824       int text_len = strlen(text);
7825       boolean has_text = FALSE;
7826
7827       for (j = 0; j < text_len; j++)
7828         if (text[j] != ' ' && text[j] != '\n')
7829           has_text = TRUE;
7830
7831       if (has_text)
7832       {
7833         Print("\n");
7834         Print("Envelope %d:\n'%s'\n", i + 1, text);
7835       }
7836     }
7837   }
7838
7839   PrintLine("-", 79);
7840 }
7841
7842 void DumpLevels(void)
7843 {
7844   static LevelDirTree *dumplevel_leveldir = NULL;
7845
7846   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7847                                                  global.dumplevel_leveldir);
7848
7849   if (dumplevel_leveldir == NULL)
7850     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7851
7852   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7853       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7854     Fail("no such level number: %d", global.dumplevel_level_nr);
7855
7856   leveldir_current = dumplevel_leveldir;
7857
7858   LoadLevel(global.dumplevel_level_nr);
7859   DumpLevel(&level);
7860
7861   CloseAllAndExit(0);
7862 }
7863
7864
7865 // ============================================================================
7866 // tape file functions
7867 // ============================================================================
7868
7869 static void setTapeInfoToDefaults(void)
7870 {
7871   int i;
7872
7873   // always start with reliable default values (empty tape)
7874   TapeErase();
7875
7876   // default values (also for pre-1.2 tapes) with only the first player
7877   tape.player_participates[0] = TRUE;
7878   for (i = 1; i < MAX_PLAYERS; i++)
7879     tape.player_participates[i] = FALSE;
7880
7881   // at least one (default: the first) player participates in every tape
7882   tape.num_participating_players = 1;
7883
7884   tape.property_bits = TAPE_PROPERTY_NONE;
7885
7886   tape.level_nr = level_nr;
7887   tape.counter = 0;
7888   tape.changed = FALSE;
7889   tape.solved = FALSE;
7890
7891   tape.recording = FALSE;
7892   tape.playing = FALSE;
7893   tape.pausing = FALSE;
7894
7895   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7896   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7897
7898   tape.no_info_chunk = TRUE;
7899   tape.no_valid_file = FALSE;
7900 }
7901
7902 static int getTapePosSize(struct TapeInfo *tape)
7903 {
7904   int tape_pos_size = 0;
7905
7906   if (tape->use_key_actions)
7907     tape_pos_size += tape->num_participating_players;
7908
7909   if (tape->use_mouse_actions)
7910     tape_pos_size += 3;         // x and y position and mouse button mask
7911
7912   tape_pos_size += 1;           // tape action delay value
7913
7914   return tape_pos_size;
7915 }
7916
7917 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7918 {
7919   tape->use_key_actions = FALSE;
7920   tape->use_mouse_actions = FALSE;
7921
7922   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7923     tape->use_key_actions = TRUE;
7924
7925   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7926     tape->use_mouse_actions = TRUE;
7927 }
7928
7929 static int getTapeActionValue(struct TapeInfo *tape)
7930 {
7931   return (tape->use_key_actions &&
7932           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7933           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
7934           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7935           TAPE_ACTIONS_DEFAULT);
7936 }
7937
7938 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7939 {
7940   tape->file_version = getFileVersion(file);
7941   tape->game_version = getFileVersion(file);
7942
7943   return chunk_size;
7944 }
7945
7946 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7947 {
7948   int i;
7949
7950   tape->random_seed = getFile32BitBE(file);
7951   tape->date        = getFile32BitBE(file);
7952   tape->length      = getFile32BitBE(file);
7953
7954   // read header fields that are new since version 1.2
7955   if (tape->file_version >= FILE_VERSION_1_2)
7956   {
7957     byte store_participating_players = getFile8Bit(file);
7958     int engine_version;
7959
7960     // since version 1.2, tapes store which players participate in the tape
7961     tape->num_participating_players = 0;
7962     for (i = 0; i < MAX_PLAYERS; i++)
7963     {
7964       tape->player_participates[i] = FALSE;
7965
7966       if (store_participating_players & (1 << i))
7967       {
7968         tape->player_participates[i] = TRUE;
7969         tape->num_participating_players++;
7970       }
7971     }
7972
7973     setTapeActionFlags(tape, getFile8Bit(file));
7974
7975     tape->property_bits = getFile8Bit(file);
7976     tape->solved = getFile8Bit(file);
7977
7978     engine_version = getFileVersion(file);
7979     if (engine_version > 0)
7980       tape->engine_version = engine_version;
7981     else
7982       tape->engine_version = tape->game_version;
7983   }
7984
7985   return chunk_size;
7986 }
7987
7988 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7989 {
7990   tape->scr_fieldx = getFile8Bit(file);
7991   tape->scr_fieldy = getFile8Bit(file);
7992
7993   return chunk_size;
7994 }
7995
7996 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7997 {
7998   char *level_identifier = NULL;
7999   int level_identifier_size;
8000   int i;
8001
8002   tape->no_info_chunk = FALSE;
8003
8004   level_identifier_size = getFile16BitBE(file);
8005
8006   level_identifier = checked_malloc(level_identifier_size);
8007
8008   for (i = 0; i < level_identifier_size; i++)
8009     level_identifier[i] = getFile8Bit(file);
8010
8011   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8012   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8013
8014   checked_free(level_identifier);
8015
8016   tape->level_nr = getFile16BitBE(file);
8017
8018   chunk_size = 2 + level_identifier_size + 2;
8019
8020   return chunk_size;
8021 }
8022
8023 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8024 {
8025   int i, j;
8026   int tape_pos_size = getTapePosSize(tape);
8027   int chunk_size_expected = tape_pos_size * tape->length;
8028
8029   if (chunk_size_expected != chunk_size)
8030   {
8031     ReadUnusedBytesFromFile(file, chunk_size);
8032     return chunk_size_expected;
8033   }
8034
8035   for (i = 0; i < tape->length; i++)
8036   {
8037     if (i >= MAX_TAPE_LEN)
8038     {
8039       Warn("tape truncated -- size exceeds maximum tape size %d",
8040             MAX_TAPE_LEN);
8041
8042       // tape too large; read and ignore remaining tape data from this chunk
8043       for (;i < tape->length; i++)
8044         ReadUnusedBytesFromFile(file, tape_pos_size);
8045
8046       break;
8047     }
8048
8049     if (tape->use_key_actions)
8050     {
8051       for (j = 0; j < MAX_PLAYERS; j++)
8052       {
8053         tape->pos[i].action[j] = MV_NONE;
8054
8055         if (tape->player_participates[j])
8056           tape->pos[i].action[j] = getFile8Bit(file);
8057       }
8058     }
8059
8060     if (tape->use_mouse_actions)
8061     {
8062       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
8063       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
8064       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8065     }
8066
8067     tape->pos[i].delay = getFile8Bit(file);
8068
8069     if (tape->file_version == FILE_VERSION_1_0)
8070     {
8071       // eliminate possible diagonal moves in old tapes
8072       // this is only for backward compatibility
8073
8074       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8075       byte action = tape->pos[i].action[0];
8076       int k, num_moves = 0;
8077
8078       for (k = 0; k<4; k++)
8079       {
8080         if (action & joy_dir[k])
8081         {
8082           tape->pos[i + num_moves].action[0] = joy_dir[k];
8083           if (num_moves > 0)
8084             tape->pos[i + num_moves].delay = 0;
8085           num_moves++;
8086         }
8087       }
8088
8089       if (num_moves > 1)
8090       {
8091         num_moves--;
8092         i += num_moves;
8093         tape->length += num_moves;
8094       }
8095     }
8096     else if (tape->file_version < FILE_VERSION_2_0)
8097     {
8098       // convert pre-2.0 tapes to new tape format
8099
8100       if (tape->pos[i].delay > 1)
8101       {
8102         // action part
8103         tape->pos[i + 1] = tape->pos[i];
8104         tape->pos[i + 1].delay = 1;
8105
8106         // delay part
8107         for (j = 0; j < MAX_PLAYERS; j++)
8108           tape->pos[i].action[j] = MV_NONE;
8109         tape->pos[i].delay--;
8110
8111         i++;
8112         tape->length++;
8113       }
8114     }
8115
8116     if (checkEndOfFile(file))
8117       break;
8118   }
8119
8120   if (i != tape->length)
8121     chunk_size = tape_pos_size * i;
8122
8123   return chunk_size;
8124 }
8125
8126 static void LoadTape_SokobanSolution(char *filename)
8127 {
8128   File *file;
8129   int move_delay = TILESIZE / level.initial_player_stepsize[0];
8130
8131   if (!(file = openFile(filename, MODE_READ)))
8132   {
8133     tape.no_valid_file = TRUE;
8134
8135     return;
8136   }
8137
8138   while (!checkEndOfFile(file))
8139   {
8140     unsigned char c = getByteFromFile(file);
8141
8142     if (checkEndOfFile(file))
8143       break;
8144
8145     switch (c)
8146     {
8147       case 'u':
8148       case 'U':
8149         tape.pos[tape.length].action[0] = MV_UP;
8150         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8151         tape.length++;
8152         break;
8153
8154       case 'd':
8155       case 'D':
8156         tape.pos[tape.length].action[0] = MV_DOWN;
8157         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8158         tape.length++;
8159         break;
8160
8161       case 'l':
8162       case 'L':
8163         tape.pos[tape.length].action[0] = MV_LEFT;
8164         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8165         tape.length++;
8166         break;
8167
8168       case 'r':
8169       case 'R':
8170         tape.pos[tape.length].action[0] = MV_RIGHT;
8171         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8172         tape.length++;
8173         break;
8174
8175       case '\n':
8176       case '\r':
8177       case '\t':
8178       case ' ':
8179         // ignore white-space characters
8180         break;
8181
8182       default:
8183         tape.no_valid_file = TRUE;
8184
8185         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8186
8187         break;
8188     }
8189   }
8190
8191   closeFile(file);
8192
8193   if (tape.no_valid_file)
8194     return;
8195
8196   tape.length_frames  = GetTapeLengthFrames();
8197   tape.length_seconds = GetTapeLengthSeconds();
8198 }
8199
8200 void LoadTapeFromFilename(char *filename)
8201 {
8202   char cookie[MAX_LINE_LEN];
8203   char chunk_name[CHUNK_ID_LEN + 1];
8204   File *file;
8205   int chunk_size;
8206
8207   // always start with reliable default values
8208   setTapeInfoToDefaults();
8209
8210   if (strSuffix(filename, ".sln"))
8211   {
8212     LoadTape_SokobanSolution(filename);
8213
8214     return;
8215   }
8216
8217   if (!(file = openFile(filename, MODE_READ)))
8218   {
8219     tape.no_valid_file = TRUE;
8220
8221     return;
8222   }
8223
8224   getFileChunkBE(file, chunk_name, NULL);
8225   if (strEqual(chunk_name, "RND1"))
8226   {
8227     getFile32BitBE(file);               // not used
8228
8229     getFileChunkBE(file, chunk_name, NULL);
8230     if (!strEqual(chunk_name, "TAPE"))
8231     {
8232       tape.no_valid_file = TRUE;
8233
8234       Warn("unknown format of tape file '%s'", filename);
8235
8236       closeFile(file);
8237
8238       return;
8239     }
8240   }
8241   else  // check for pre-2.0 file format with cookie string
8242   {
8243     strcpy(cookie, chunk_name);
8244     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8245       cookie[4] = '\0';
8246     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8247       cookie[strlen(cookie) - 1] = '\0';
8248
8249     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8250     {
8251       tape.no_valid_file = TRUE;
8252
8253       Warn("unknown format of tape file '%s'", filename);
8254
8255       closeFile(file);
8256
8257       return;
8258     }
8259
8260     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8261     {
8262       tape.no_valid_file = TRUE;
8263
8264       Warn("unsupported version of tape file '%s'", filename);
8265
8266       closeFile(file);
8267
8268       return;
8269     }
8270
8271     // pre-2.0 tape files have no game version, so use file version here
8272     tape.game_version = tape.file_version;
8273   }
8274
8275   if (tape.file_version < FILE_VERSION_1_2)
8276   {
8277     // tape files from versions before 1.2.0 without chunk structure
8278     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8279     LoadTape_BODY(file, 2 * tape.length,      &tape);
8280   }
8281   else
8282   {
8283     static struct
8284     {
8285       char *name;
8286       int size;
8287       int (*loader)(File *, int, struct TapeInfo *);
8288     }
8289     chunk_info[] =
8290     {
8291       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
8292       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
8293       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
8294       { "INFO", -1,                     LoadTape_INFO },
8295       { "BODY", -1,                     LoadTape_BODY },
8296       {  NULL,  0,                      NULL }
8297     };
8298
8299     while (getFileChunkBE(file, chunk_name, &chunk_size))
8300     {
8301       int i = 0;
8302
8303       while (chunk_info[i].name != NULL &&
8304              !strEqual(chunk_name, chunk_info[i].name))
8305         i++;
8306
8307       if (chunk_info[i].name == NULL)
8308       {
8309         Warn("unknown chunk '%s' in tape file '%s'",
8310               chunk_name, filename);
8311
8312         ReadUnusedBytesFromFile(file, chunk_size);
8313       }
8314       else if (chunk_info[i].size != -1 &&
8315                chunk_info[i].size != chunk_size)
8316       {
8317         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8318               chunk_size, chunk_name, filename);
8319
8320         ReadUnusedBytesFromFile(file, chunk_size);
8321       }
8322       else
8323       {
8324         // call function to load this tape chunk
8325         int chunk_size_expected =
8326           (chunk_info[i].loader)(file, chunk_size, &tape);
8327
8328         // the size of some chunks cannot be checked before reading other
8329         // chunks first (like "HEAD" and "BODY") that contain some header
8330         // information, so check them here
8331         if (chunk_size_expected != chunk_size)
8332         {
8333           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8334                 chunk_size, chunk_name, filename);
8335         }
8336       }
8337     }
8338   }
8339
8340   closeFile(file);
8341
8342   tape.length_frames  = GetTapeLengthFrames();
8343   tape.length_seconds = GetTapeLengthSeconds();
8344
8345 #if 0
8346   Debug("files:LoadTapeFromFilename", "tape file version: %d",
8347         tape.file_version);
8348   Debug("files:LoadTapeFromFilename", "tape game version: %d",
8349         tape.game_version);
8350   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8351         tape.engine_version);
8352 #endif
8353 }
8354
8355 void LoadTape(int nr)
8356 {
8357   char *filename = getTapeFilename(nr);
8358
8359   LoadTapeFromFilename(filename);
8360 }
8361
8362 void LoadSolutionTape(int nr)
8363 {
8364   char *filename = getSolutionTapeFilename(nr);
8365
8366   LoadTapeFromFilename(filename);
8367
8368   if (TAPE_IS_EMPTY(tape) &&
8369       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8370       level.native_sp_level->demo.is_available)
8371     CopyNativeTape_SP_to_RND(&level);
8372 }
8373
8374 void LoadScoreTape(char *score_tape_basename, int nr)
8375 {
8376   char *filename = getScoreTapeFilename(score_tape_basename, nr);
8377
8378   LoadTapeFromFilename(filename);
8379 }
8380
8381 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8382 {
8383   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8384
8385   LoadTapeFromFilename(filename);
8386 }
8387
8388 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8389 {
8390   // chunk required for team mode tapes with non-default screen size
8391   return (tape->num_participating_players > 1 &&
8392           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8393            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8394 }
8395
8396 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8397 {
8398   putFileVersion(file, tape->file_version);
8399   putFileVersion(file, tape->game_version);
8400 }
8401
8402 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8403 {
8404   int i;
8405   byte store_participating_players = 0;
8406
8407   // set bits for participating players for compact storage
8408   for (i = 0; i < MAX_PLAYERS; i++)
8409     if (tape->player_participates[i])
8410       store_participating_players |= (1 << i);
8411
8412   putFile32BitBE(file, tape->random_seed);
8413   putFile32BitBE(file, tape->date);
8414   putFile32BitBE(file, tape->length);
8415
8416   putFile8Bit(file, store_participating_players);
8417
8418   putFile8Bit(file, getTapeActionValue(tape));
8419
8420   putFile8Bit(file, tape->property_bits);
8421   putFile8Bit(file, tape->solved);
8422
8423   putFileVersion(file, tape->engine_version);
8424 }
8425
8426 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8427 {
8428   putFile8Bit(file, tape->scr_fieldx);
8429   putFile8Bit(file, tape->scr_fieldy);
8430 }
8431
8432 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8433 {
8434   int level_identifier_size = strlen(tape->level_identifier) + 1;
8435   int i;
8436
8437   putFile16BitBE(file, level_identifier_size);
8438
8439   for (i = 0; i < level_identifier_size; i++)
8440     putFile8Bit(file, tape->level_identifier[i]);
8441
8442   putFile16BitBE(file, tape->level_nr);
8443 }
8444
8445 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8446 {
8447   int i, j;
8448
8449   for (i = 0; i < tape->length; i++)
8450   {
8451     if (tape->use_key_actions)
8452     {
8453       for (j = 0; j < MAX_PLAYERS; j++)
8454         if (tape->player_participates[j])
8455           putFile8Bit(file, tape->pos[i].action[j]);
8456     }
8457
8458     if (tape->use_mouse_actions)
8459     {
8460       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8461       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8462       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8463     }
8464
8465     putFile8Bit(file, tape->pos[i].delay);
8466   }
8467 }
8468
8469 void SaveTapeToFilename(char *filename)
8470 {
8471   FILE *file;
8472   int tape_pos_size;
8473   int info_chunk_size;
8474   int body_chunk_size;
8475
8476   if (!(file = fopen(filename, MODE_WRITE)))
8477   {
8478     Warn("cannot save level recording file '%s'", filename);
8479
8480     return;
8481   }
8482
8483   tape_pos_size = getTapePosSize(&tape);
8484
8485   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8486   body_chunk_size = tape_pos_size * tape.length;
8487
8488   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8489   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8490
8491   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8492   SaveTape_VERS(file, &tape);
8493
8494   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8495   SaveTape_HEAD(file, &tape);
8496
8497   if (checkSaveTape_SCRN(&tape))
8498   {
8499     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8500     SaveTape_SCRN(file, &tape);
8501   }
8502
8503   putFileChunkBE(file, "INFO", info_chunk_size);
8504   SaveTape_INFO(file, &tape);
8505
8506   putFileChunkBE(file, "BODY", body_chunk_size);
8507   SaveTape_BODY(file, &tape);
8508
8509   fclose(file);
8510
8511   SetFilePermissions(filename, PERMS_PRIVATE);
8512 }
8513
8514 static void SaveTapeExt(char *filename)
8515 {
8516   int i;
8517
8518   tape.file_version = FILE_VERSION_ACTUAL;
8519   tape.game_version = GAME_VERSION_ACTUAL;
8520
8521   tape.num_participating_players = 0;
8522
8523   // count number of participating players
8524   for (i = 0; i < MAX_PLAYERS; i++)
8525     if (tape.player_participates[i])
8526       tape.num_participating_players++;
8527
8528   SaveTapeToFilename(filename);
8529
8530   tape.changed = FALSE;
8531 }
8532
8533 void SaveTape(int nr)
8534 {
8535   char *filename = getTapeFilename(nr);
8536
8537   InitTapeDirectory(leveldir_current->subdir);
8538
8539   SaveTapeExt(filename);
8540 }
8541
8542 void SaveScoreTape(int nr)
8543 {
8544   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8545
8546   // used instead of "leveldir_current->subdir" (for network games)
8547   InitScoreTapeDirectory(levelset.identifier, nr);
8548
8549   SaveTapeExt(filename);
8550 }
8551
8552 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8553                                   unsigned int req_state_added)
8554 {
8555   char *filename = getTapeFilename(nr);
8556   boolean new_tape = !fileExists(filename);
8557   boolean tape_saved = FALSE;
8558
8559   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8560   {
8561     SaveTape(nr);
8562
8563     if (new_tape)
8564       Request(msg_saved, REQ_CONFIRM | req_state_added);
8565
8566     tape_saved = TRUE;
8567   }
8568
8569   return tape_saved;
8570 }
8571
8572 boolean SaveTapeChecked(int nr)
8573 {
8574   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8575 }
8576
8577 boolean SaveTapeChecked_LevelSolved(int nr)
8578 {
8579   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8580                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
8581 }
8582
8583 void DumpTape(struct TapeInfo *tape)
8584 {
8585   int tape_frame_counter;
8586   int i, j;
8587
8588   if (tape->no_valid_file)
8589   {
8590     Warn("cannot dump -- no valid tape file found");
8591
8592     return;
8593   }
8594
8595   PrintLine("-", 79);
8596
8597   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8598         tape->level_nr, tape->file_version, tape->game_version);
8599   Print("                  (effective engine version %08d)\n",
8600         tape->engine_version);
8601   Print("Level series identifier: '%s'\n", tape->level_identifier);
8602
8603   Print("Solution tape: %s\n",
8604         tape->solved ? "yes" :
8605         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8606
8607   Print("Special tape properties: ");
8608   if (tape->property_bits == TAPE_PROPERTY_NONE)
8609     Print("[none]");
8610   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8611     Print("[em_random_bug]");
8612   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8613     Print("[game_speed]");
8614   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8615     Print("[pause]");
8616   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8617     Print("[single_step]");
8618   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8619     Print("[snapshot]");
8620   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8621     Print("[replayed]");
8622   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8623     Print("[tas_keys]");
8624   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8625     Print("[small_graphics]");
8626   Print("\n");
8627
8628   int year2 = tape->date / 10000;
8629   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8630   int month_index_raw = (tape->date / 100) % 100;
8631   int month_index = month_index_raw % 12;       // prevent invalid index
8632   int month = month_index + 1;
8633   int day = tape->date % 100;
8634
8635   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8636
8637   PrintLine("-", 79);
8638
8639   tape_frame_counter = 0;
8640
8641   for (i = 0; i < tape->length; i++)
8642   {
8643     if (i >= MAX_TAPE_LEN)
8644       break;
8645
8646     Print("%04d: ", i);
8647
8648     for (j = 0; j < MAX_PLAYERS; j++)
8649     {
8650       if (tape->player_participates[j])
8651       {
8652         int action = tape->pos[i].action[j];
8653
8654         Print("%d:%02x ", j, action);
8655         Print("[%c%c%c%c|%c%c] - ",
8656               (action & JOY_LEFT ? '<' : ' '),
8657               (action & JOY_RIGHT ? '>' : ' '),
8658               (action & JOY_UP ? '^' : ' '),
8659               (action & JOY_DOWN ? 'v' : ' '),
8660               (action & JOY_BUTTON_1 ? '1' : ' '),
8661               (action & JOY_BUTTON_2 ? '2' : ' '));
8662       }
8663     }
8664
8665     Print("(%03d) ", tape->pos[i].delay);
8666     Print("[%05d]\n", tape_frame_counter);
8667
8668     tape_frame_counter += tape->pos[i].delay;
8669   }
8670
8671   PrintLine("-", 79);
8672 }
8673
8674 void DumpTapes(void)
8675 {
8676   static LevelDirTree *dumptape_leveldir = NULL;
8677
8678   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8679                                                 global.dumptape_leveldir);
8680
8681   if (dumptape_leveldir == NULL)
8682     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8683
8684   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8685       global.dumptape_level_nr > dumptape_leveldir->last_level)
8686     Fail("no such level number: %d", global.dumptape_level_nr);
8687
8688   leveldir_current = dumptape_leveldir;
8689
8690   if (options.mytapes)
8691     LoadTape(global.dumptape_level_nr);
8692   else
8693     LoadSolutionTape(global.dumptape_level_nr);
8694
8695   DumpTape(&tape);
8696
8697   CloseAllAndExit(0);
8698 }
8699
8700
8701 // ============================================================================
8702 // score file functions
8703 // ============================================================================
8704
8705 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8706 {
8707   int i;
8708
8709   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8710   {
8711     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8712     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8713     scores->entry[i].score = 0;
8714     scores->entry[i].time = 0;
8715
8716     scores->entry[i].id = -1;
8717     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
8718     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
8719     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
8720     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8721     strcpy(scores->entry[i].country_code, "??");
8722   }
8723
8724   scores->num_entries = 0;
8725   scores->last_added = -1;
8726   scores->last_added_local = -1;
8727
8728   scores->updated = FALSE;
8729   scores->uploaded = FALSE;
8730   scores->tape_downloaded = FALSE;
8731   scores->force_last_added = FALSE;
8732
8733   // The following values are intentionally not reset here:
8734   // - last_level_nr
8735   // - last_entry_nr
8736   // - next_level_nr
8737   // - continue_playing
8738   // - continue_on_return
8739 }
8740
8741 static void setScoreInfoToDefaults(void)
8742 {
8743   setScoreInfoToDefaultsExt(&scores);
8744 }
8745
8746 static void setServerScoreInfoToDefaults(void)
8747 {
8748   setScoreInfoToDefaultsExt(&server_scores);
8749 }
8750
8751 static void LoadScore_OLD(int nr)
8752 {
8753   int i;
8754   char *filename = getScoreFilename(nr);
8755   char cookie[MAX_LINE_LEN];
8756   char line[MAX_LINE_LEN];
8757   char *line_ptr;
8758   FILE *file;
8759
8760   if (!(file = fopen(filename, MODE_READ)))
8761     return;
8762
8763   // check file identifier
8764   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8765     cookie[0] = '\0';
8766   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8767     cookie[strlen(cookie) - 1] = '\0';
8768
8769   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8770   {
8771     Warn("unknown format of score file '%s'", filename);
8772
8773     fclose(file);
8774
8775     return;
8776   }
8777
8778   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8779   {
8780     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8781       Warn("fscanf() failed; %s", strerror(errno));
8782
8783     if (fgets(line, MAX_LINE_LEN, file) == NULL)
8784       line[0] = '\0';
8785
8786     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8787       line[strlen(line) - 1] = '\0';
8788
8789     for (line_ptr = line; *line_ptr; line_ptr++)
8790     {
8791       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8792       {
8793         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8794         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8795         break;
8796       }
8797     }
8798   }
8799
8800   fclose(file);
8801 }
8802
8803 static void ConvertScore_OLD(void)
8804 {
8805   // only convert score to time for levels that rate playing time over score
8806   if (!level.rate_time_over_score)
8807     return;
8808
8809   // convert old score to playing time for score-less levels (like Supaplex)
8810   int time_final_max = 999;
8811   int i;
8812
8813   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8814   {
8815     int score = scores.entry[i].score;
8816
8817     if (score > 0 && score < time_final_max)
8818       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8819   }
8820 }
8821
8822 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8823 {
8824   scores->file_version = getFileVersion(file);
8825   scores->game_version = getFileVersion(file);
8826
8827   return chunk_size;
8828 }
8829
8830 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8831 {
8832   char *level_identifier = NULL;
8833   int level_identifier_size;
8834   int i;
8835
8836   level_identifier_size = getFile16BitBE(file);
8837
8838   level_identifier = checked_malloc(level_identifier_size);
8839
8840   for (i = 0; i < level_identifier_size; i++)
8841     level_identifier[i] = getFile8Bit(file);
8842
8843   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8844   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8845
8846   checked_free(level_identifier);
8847
8848   scores->level_nr = getFile16BitBE(file);
8849   scores->num_entries = getFile16BitBE(file);
8850
8851   chunk_size = 2 + level_identifier_size + 2 + 2;
8852
8853   return chunk_size;
8854 }
8855
8856 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8857 {
8858   int i, j;
8859
8860   for (i = 0; i < scores->num_entries; i++)
8861   {
8862     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8863       scores->entry[i].name[j] = getFile8Bit(file);
8864
8865     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8866   }
8867
8868   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8869
8870   return chunk_size;
8871 }
8872
8873 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8874 {
8875   int i;
8876
8877   for (i = 0; i < scores->num_entries; i++)
8878     scores->entry[i].score = getFile16BitBE(file);
8879
8880   chunk_size = scores->num_entries * 2;
8881
8882   return chunk_size;
8883 }
8884
8885 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8886 {
8887   int i;
8888
8889   for (i = 0; i < scores->num_entries; i++)
8890     scores->entry[i].score = getFile32BitBE(file);
8891
8892   chunk_size = scores->num_entries * 4;
8893
8894   return chunk_size;
8895 }
8896
8897 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8898 {
8899   int i;
8900
8901   for (i = 0; i < scores->num_entries; i++)
8902     scores->entry[i].time = getFile32BitBE(file);
8903
8904   chunk_size = scores->num_entries * 4;
8905
8906   return chunk_size;
8907 }
8908
8909 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8910 {
8911   int i, j;
8912
8913   for (i = 0; i < scores->num_entries; i++)
8914   {
8915     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8916       scores->entry[i].tape_basename[j] = getFile8Bit(file);
8917
8918     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8919   }
8920
8921   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8922
8923   return chunk_size;
8924 }
8925
8926 void LoadScore(int nr)
8927 {
8928   char *filename = getScoreFilename(nr);
8929   char cookie[MAX_LINE_LEN];
8930   char chunk_name[CHUNK_ID_LEN + 1];
8931   int chunk_size;
8932   boolean old_score_file_format = FALSE;
8933   File *file;
8934
8935   // always start with reliable default values
8936   setScoreInfoToDefaults();
8937
8938   if (!(file = openFile(filename, MODE_READ)))
8939     return;
8940
8941   getFileChunkBE(file, chunk_name, NULL);
8942   if (strEqual(chunk_name, "RND1"))
8943   {
8944     getFile32BitBE(file);               // not used
8945
8946     getFileChunkBE(file, chunk_name, NULL);
8947     if (!strEqual(chunk_name, "SCOR"))
8948     {
8949       Warn("unknown format of score file '%s'", filename);
8950
8951       closeFile(file);
8952
8953       return;
8954     }
8955   }
8956   else  // check for old file format with cookie string
8957   {
8958     strcpy(cookie, chunk_name);
8959     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8960       cookie[4] = '\0';
8961     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8962       cookie[strlen(cookie) - 1] = '\0';
8963
8964     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8965     {
8966       Warn("unknown format of score file '%s'", filename);
8967
8968       closeFile(file);
8969
8970       return;
8971     }
8972
8973     old_score_file_format = TRUE;
8974   }
8975
8976   if (old_score_file_format)
8977   {
8978     // score files from versions before 4.2.4.0 without chunk structure
8979     LoadScore_OLD(nr);
8980
8981     // convert score to time, if possible (mainly for Supaplex levels)
8982     ConvertScore_OLD();
8983   }
8984   else
8985   {
8986     static struct
8987     {
8988       char *name;
8989       int size;
8990       int (*loader)(File *, int, struct ScoreInfo *);
8991     }
8992     chunk_info[] =
8993     {
8994       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
8995       { "INFO", -1,                     LoadScore_INFO },
8996       { "NAME", -1,                     LoadScore_NAME },
8997       { "SCOR", -1,                     LoadScore_SCOR },
8998       { "SC4R", -1,                     LoadScore_SC4R },
8999       { "TIME", -1,                     LoadScore_TIME },
9000       { "TAPE", -1,                     LoadScore_TAPE },
9001
9002       {  NULL,  0,                      NULL }
9003     };
9004
9005     while (getFileChunkBE(file, chunk_name, &chunk_size))
9006     {
9007       int i = 0;
9008
9009       while (chunk_info[i].name != NULL &&
9010              !strEqual(chunk_name, chunk_info[i].name))
9011         i++;
9012
9013       if (chunk_info[i].name == NULL)
9014       {
9015         Warn("unknown chunk '%s' in score file '%s'",
9016               chunk_name, filename);
9017
9018         ReadUnusedBytesFromFile(file, chunk_size);
9019       }
9020       else if (chunk_info[i].size != -1 &&
9021                chunk_info[i].size != chunk_size)
9022       {
9023         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9024               chunk_size, chunk_name, filename);
9025
9026         ReadUnusedBytesFromFile(file, chunk_size);
9027       }
9028       else
9029       {
9030         // call function to load this score chunk
9031         int chunk_size_expected =
9032           (chunk_info[i].loader)(file, chunk_size, &scores);
9033
9034         // the size of some chunks cannot be checked before reading other
9035         // chunks first (like "HEAD" and "BODY") that contain some header
9036         // information, so check them here
9037         if (chunk_size_expected != chunk_size)
9038         {
9039           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9040                 chunk_size, chunk_name, filename);
9041         }
9042       }
9043     }
9044   }
9045
9046   closeFile(file);
9047 }
9048
9049 #if ENABLE_HISTORIC_CHUNKS
9050 void SaveScore_OLD(int nr)
9051 {
9052   int i;
9053   char *filename = getScoreFilename(nr);
9054   FILE *file;
9055
9056   // used instead of "leveldir_current->subdir" (for network games)
9057   InitScoreDirectory(levelset.identifier);
9058
9059   if (!(file = fopen(filename, MODE_WRITE)))
9060   {
9061     Warn("cannot save score for level %d", nr);
9062
9063     return;
9064   }
9065
9066   fprintf(file, "%s\n\n", SCORE_COOKIE);
9067
9068   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9069     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9070
9071   fclose(file);
9072
9073   SetFilePermissions(filename, PERMS_PRIVATE);
9074 }
9075 #endif
9076
9077 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9078 {
9079   putFileVersion(file, scores->file_version);
9080   putFileVersion(file, scores->game_version);
9081 }
9082
9083 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9084 {
9085   int level_identifier_size = strlen(scores->level_identifier) + 1;
9086   int i;
9087
9088   putFile16BitBE(file, level_identifier_size);
9089
9090   for (i = 0; i < level_identifier_size; i++)
9091     putFile8Bit(file, scores->level_identifier[i]);
9092
9093   putFile16BitBE(file, scores->level_nr);
9094   putFile16BitBE(file, scores->num_entries);
9095 }
9096
9097 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9098 {
9099   int i, j;
9100
9101   for (i = 0; i < scores->num_entries; i++)
9102   {
9103     int name_size = strlen(scores->entry[i].name);
9104
9105     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9106       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9107   }
9108 }
9109
9110 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9111 {
9112   int i;
9113
9114   for (i = 0; i < scores->num_entries; i++)
9115     putFile16BitBE(file, scores->entry[i].score);
9116 }
9117
9118 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9119 {
9120   int i;
9121
9122   for (i = 0; i < scores->num_entries; i++)
9123     putFile32BitBE(file, scores->entry[i].score);
9124 }
9125
9126 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9127 {
9128   int i;
9129
9130   for (i = 0; i < scores->num_entries; i++)
9131     putFile32BitBE(file, scores->entry[i].time);
9132 }
9133
9134 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9135 {
9136   int i, j;
9137
9138   for (i = 0; i < scores->num_entries; i++)
9139   {
9140     int size = strlen(scores->entry[i].tape_basename);
9141
9142     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9143       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9144   }
9145 }
9146
9147 static void SaveScoreToFilename(char *filename)
9148 {
9149   FILE *file;
9150   int info_chunk_size;
9151   int name_chunk_size;
9152   int scor_chunk_size;
9153   int sc4r_chunk_size;
9154   int time_chunk_size;
9155   int tape_chunk_size;
9156   boolean has_large_score_values;
9157   int i;
9158
9159   if (!(file = fopen(filename, MODE_WRITE)))
9160   {
9161     Warn("cannot save score file '%s'", filename);
9162
9163     return;
9164   }
9165
9166   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9167   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9168   scor_chunk_size = scores.num_entries * 2;
9169   sc4r_chunk_size = scores.num_entries * 4;
9170   time_chunk_size = scores.num_entries * 4;
9171   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9172
9173   has_large_score_values = FALSE;
9174   for (i = 0; i < scores.num_entries; i++)
9175     if (scores.entry[i].score > 0xffff)
9176       has_large_score_values = TRUE;
9177
9178   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9179   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9180
9181   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9182   SaveScore_VERS(file, &scores);
9183
9184   putFileChunkBE(file, "INFO", info_chunk_size);
9185   SaveScore_INFO(file, &scores);
9186
9187   putFileChunkBE(file, "NAME", name_chunk_size);
9188   SaveScore_NAME(file, &scores);
9189
9190   if (has_large_score_values)
9191   {
9192     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9193     SaveScore_SC4R(file, &scores);
9194   }
9195   else
9196   {
9197     putFileChunkBE(file, "SCOR", scor_chunk_size);
9198     SaveScore_SCOR(file, &scores);
9199   }
9200
9201   putFileChunkBE(file, "TIME", time_chunk_size);
9202   SaveScore_TIME(file, &scores);
9203
9204   putFileChunkBE(file, "TAPE", tape_chunk_size);
9205   SaveScore_TAPE(file, &scores);
9206
9207   fclose(file);
9208
9209   SetFilePermissions(filename, PERMS_PRIVATE);
9210 }
9211
9212 void SaveScore(int nr)
9213 {
9214   char *filename = getScoreFilename(nr);
9215   int i;
9216
9217   // used instead of "leveldir_current->subdir" (for network games)
9218   InitScoreDirectory(levelset.identifier);
9219
9220   scores.file_version = FILE_VERSION_ACTUAL;
9221   scores.game_version = GAME_VERSION_ACTUAL;
9222
9223   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9224   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9225   scores.level_nr = level_nr;
9226
9227   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9228     if (scores.entry[i].score == 0 &&
9229         scores.entry[i].time == 0 &&
9230         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9231       break;
9232
9233   scores.num_entries = i;
9234
9235   if (scores.num_entries == 0)
9236     return;
9237
9238   SaveScoreToFilename(filename);
9239 }
9240
9241 static void LoadServerScoreFromCache(int nr)
9242 {
9243   struct ScoreEntry score_entry;
9244   struct
9245   {
9246     void *value;
9247     boolean is_string;
9248     int string_size;
9249   }
9250   score_mapping[] =
9251   {
9252     { &score_entry.score,               FALSE,  0                       },
9253     { &score_entry.time,                FALSE,  0                       },
9254     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
9255     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
9256     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
9257     { &score_entry.id,                  FALSE,  0                       },
9258     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
9259     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
9260     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
9261     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
9262
9263     { NULL,                             FALSE,  0                       }
9264   };
9265   char *filename = getScoreCacheFilename(nr);
9266   SetupFileHash *score_hash = loadSetupFileHash(filename);
9267   int i, j;
9268
9269   server_scores.num_entries = 0;
9270
9271   if (score_hash == NULL)
9272     return;
9273
9274   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9275   {
9276     score_entry = server_scores.entry[i];
9277
9278     for (j = 0; score_mapping[j].value != NULL; j++)
9279     {
9280       char token[10];
9281
9282       sprintf(token, "%02d.%d", i, j);
9283
9284       char *value = getHashEntry(score_hash, token);
9285
9286       if (value == NULL)
9287         continue;
9288
9289       if (score_mapping[j].is_string)
9290       {
9291         char *score_value = (char *)score_mapping[j].value;
9292         int value_size = score_mapping[j].string_size;
9293
9294         strncpy(score_value, value, value_size);
9295         score_value[value_size] = '\0';
9296       }
9297       else
9298       {
9299         int *score_value = (int *)score_mapping[j].value;
9300
9301         *score_value = atoi(value);
9302       }
9303
9304       server_scores.num_entries = i + 1;
9305     }
9306
9307     server_scores.entry[i] = score_entry;
9308   }
9309
9310   freeSetupFileHash(score_hash);
9311 }
9312
9313 void LoadServerScore(int nr, boolean download_score)
9314 {
9315   if (!setup.use_api_server)
9316     return;
9317
9318   // always start with reliable default values
9319   setServerScoreInfoToDefaults();
9320
9321   // 1st step: load server scores from cache file (which may not exist)
9322   // (this should prevent reading it while the thread is writing to it)
9323   LoadServerScoreFromCache(nr);
9324
9325   if (download_score && runtime.use_api_server)
9326   {
9327     // 2nd step: download server scores from score server to cache file
9328     // (as thread, as it might time out if the server is not reachable)
9329     ApiGetScoreAsThread(nr);
9330   }
9331 }
9332
9333 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9334 {
9335   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9336
9337   // if score tape not uploaded, ask for uploading missing tapes later
9338   if (!setup.has_remaining_tapes)
9339     setup.ask_for_remaining_tapes = TRUE;
9340
9341   setup.provide_uploading_tapes = TRUE;
9342   setup.has_remaining_tapes = TRUE;
9343
9344   SaveSetup_ServerSetup();
9345 }
9346
9347 void SaveServerScore(int nr, boolean tape_saved)
9348 {
9349   if (!runtime.use_api_server)
9350   {
9351     PrepareScoreTapesForUpload(leveldir_current->subdir);
9352
9353     return;
9354   }
9355
9356   ApiAddScoreAsThread(nr, tape_saved, NULL);
9357 }
9358
9359 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9360                              char *score_tape_filename)
9361 {
9362   if (!runtime.use_api_server)
9363     return;
9364
9365   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9366 }
9367
9368 void LoadLocalAndServerScore(int nr, boolean download_score)
9369 {
9370   int last_added_local = scores.last_added_local;
9371   boolean force_last_added = scores.force_last_added;
9372
9373   // needed if only showing server scores
9374   setScoreInfoToDefaults();
9375
9376   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9377     LoadScore(nr);
9378
9379   // restore last added local score entry (before merging server scores)
9380   scores.last_added = scores.last_added_local = last_added_local;
9381
9382   if (setup.use_api_server &&
9383       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9384   {
9385     // load server scores from cache file and trigger update from server
9386     LoadServerScore(nr, download_score);
9387
9388     // merge local scores with scores from server
9389     MergeServerScore();
9390   }
9391
9392   if (force_last_added)
9393     scores.force_last_added = force_last_added;
9394 }
9395
9396
9397 // ============================================================================
9398 // setup file functions
9399 // ============================================================================
9400
9401 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
9402
9403
9404 static struct TokenInfo global_setup_tokens[] =
9405 {
9406   {
9407     TYPE_STRING,
9408     &setup.player_name,                         "player_name"
9409   },
9410   {
9411     TYPE_SWITCH,
9412     &setup.multiple_users,                      "multiple_users"
9413   },
9414   {
9415     TYPE_SWITCH,
9416     &setup.sound,                               "sound"
9417   },
9418   {
9419     TYPE_SWITCH,
9420     &setup.sound_loops,                         "repeating_sound_loops"
9421   },
9422   {
9423     TYPE_SWITCH,
9424     &setup.sound_music,                         "background_music"
9425   },
9426   {
9427     TYPE_SWITCH,
9428     &setup.sound_simple,                        "simple_sound_effects"
9429   },
9430   {
9431     TYPE_SWITCH,
9432     &setup.toons,                               "toons"
9433   },
9434   {
9435     TYPE_SWITCH,
9436     &setup.scroll_delay,                        "scroll_delay"
9437   },
9438   {
9439     TYPE_SWITCH,
9440     &setup.forced_scroll_delay,                 "forced_scroll_delay"
9441   },
9442   {
9443     TYPE_INTEGER,
9444     &setup.scroll_delay_value,                  "scroll_delay_value"
9445   },
9446   {
9447     TYPE_STRING,
9448     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
9449   },
9450   {
9451     TYPE_INTEGER,
9452     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
9453   },
9454   {
9455     TYPE_SWITCH,
9456     &setup.fade_screens,                        "fade_screens"
9457   },
9458   {
9459     TYPE_SWITCH,
9460     &setup.autorecord,                          "automatic_tape_recording"
9461   },
9462   {
9463     TYPE_SWITCH,
9464     &setup.auto_pause_on_start,                 "auto_pause_on_start"
9465   },
9466   {
9467     TYPE_SWITCH,
9468     &setup.show_titlescreen,                    "show_titlescreen"
9469   },
9470   {
9471     TYPE_SWITCH,
9472     &setup.quick_doors,                         "quick_doors"
9473   },
9474   {
9475     TYPE_SWITCH,
9476     &setup.team_mode,                           "team_mode"
9477   },
9478   {
9479     TYPE_SWITCH,
9480     &setup.handicap,                            "handicap"
9481   },
9482   {
9483     TYPE_SWITCH,
9484     &setup.skip_levels,                         "skip_levels"
9485   },
9486   {
9487     TYPE_SWITCH,
9488     &setup.increment_levels,                    "increment_levels"
9489   },
9490   {
9491     TYPE_SWITCH,
9492     &setup.auto_play_next_level,                "auto_play_next_level"
9493   },
9494   {
9495     TYPE_SWITCH,
9496     &setup.count_score_after_game,              "count_score_after_game"
9497   },
9498   {
9499     TYPE_SWITCH,
9500     &setup.show_scores_after_game,              "show_scores_after_game"
9501   },
9502   {
9503     TYPE_SWITCH,
9504     &setup.time_limit,                          "time_limit"
9505   },
9506   {
9507     TYPE_SWITCH,
9508     &setup.fullscreen,                          "fullscreen"
9509   },
9510   {
9511     TYPE_INTEGER,
9512     &setup.window_scaling_percent,              "window_scaling_percent"
9513   },
9514   {
9515     TYPE_STRING,
9516     &setup.window_scaling_quality,              "window_scaling_quality"
9517   },
9518   {
9519     TYPE_STRING,
9520     &setup.screen_rendering_mode,               "screen_rendering_mode"
9521   },
9522   {
9523     TYPE_STRING,
9524     &setup.vsync_mode,                          "vsync_mode"
9525   },
9526   {
9527     TYPE_SWITCH,
9528     &setup.ask_on_escape,                       "ask_on_escape"
9529   },
9530   {
9531     TYPE_SWITCH,
9532     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
9533   },
9534   {
9535     TYPE_SWITCH,
9536     &setup.ask_on_game_over,                    "ask_on_game_over"
9537   },
9538   {
9539     TYPE_SWITCH,
9540     &setup.ask_on_quit_game,                    "ask_on_quit_game"
9541   },
9542   {
9543     TYPE_SWITCH,
9544     &setup.ask_on_quit_program,                 "ask_on_quit_program"
9545   },
9546   {
9547     TYPE_SWITCH,
9548     &setup.quick_switch,                        "quick_player_switch"
9549   },
9550   {
9551     TYPE_SWITCH,
9552     &setup.input_on_focus,                      "input_on_focus"
9553   },
9554   {
9555     TYPE_SWITCH,
9556     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
9557   },
9558   {
9559     TYPE_SWITCH,
9560     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
9561   },
9562   {
9563     TYPE_SWITCH,
9564     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
9565   },
9566   {
9567     TYPE_SWITCH,
9568     &setup.game_speed_extended,                 "game_speed_extended"
9569   },
9570   {
9571     TYPE_INTEGER,
9572     &setup.game_frame_delay,                    "game_frame_delay"
9573   },
9574   {
9575     TYPE_SWITCH,
9576     &setup.sp_show_border_elements,             "sp_show_border_elements"
9577   },
9578   {
9579     TYPE_SWITCH,
9580     &setup.small_game_graphics,                 "small_game_graphics"
9581   },
9582   {
9583     TYPE_SWITCH,
9584     &setup.show_load_save_buttons,              "show_load_save_buttons"
9585   },
9586   {
9587     TYPE_SWITCH,
9588     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
9589   },
9590   {
9591     TYPE_STRING,
9592     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
9593   },
9594   {
9595     TYPE_STRING,
9596     &setup.graphics_set,                        "graphics_set"
9597   },
9598   {
9599     TYPE_STRING,
9600     &setup.sounds_set,                          "sounds_set"
9601   },
9602   {
9603     TYPE_STRING,
9604     &setup.music_set,                           "music_set"
9605   },
9606   {
9607     TYPE_SWITCH3,
9608     &setup.override_level_graphics,             "override_level_graphics"
9609   },
9610   {
9611     TYPE_SWITCH3,
9612     &setup.override_level_sounds,               "override_level_sounds"
9613   },
9614   {
9615     TYPE_SWITCH3,
9616     &setup.override_level_music,                "override_level_music"
9617   },
9618   {
9619     TYPE_INTEGER,
9620     &setup.volume_simple,                       "volume_simple"
9621   },
9622   {
9623     TYPE_INTEGER,
9624     &setup.volume_loops,                        "volume_loops"
9625   },
9626   {
9627     TYPE_INTEGER,
9628     &setup.volume_music,                        "volume_music"
9629   },
9630   {
9631     TYPE_SWITCH,
9632     &setup.network_mode,                        "network_mode"
9633   },
9634   {
9635     TYPE_PLAYER,
9636     &setup.network_player_nr,                   "network_player"
9637   },
9638   {
9639     TYPE_STRING,
9640     &setup.network_server_hostname,             "network_server_hostname"
9641   },
9642   {
9643     TYPE_STRING,
9644     &setup.touch.control_type,                  "touch.control_type"
9645   },
9646   {
9647     TYPE_INTEGER,
9648     &setup.touch.move_distance,                 "touch.move_distance"
9649   },
9650   {
9651     TYPE_INTEGER,
9652     &setup.touch.drop_distance,                 "touch.drop_distance"
9653   },
9654   {
9655     TYPE_INTEGER,
9656     &setup.touch.transparency,                  "touch.transparency"
9657   },
9658   {
9659     TYPE_INTEGER,
9660     &setup.touch.draw_outlined,                 "touch.draw_outlined"
9661   },
9662   {
9663     TYPE_INTEGER,
9664     &setup.touch.draw_pressed,                  "touch.draw_pressed"
9665   },
9666   {
9667     TYPE_INTEGER,
9668     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
9669   },
9670   {
9671     TYPE_INTEGER,
9672     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
9673   },
9674   {
9675     TYPE_INTEGER,
9676     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
9677   },
9678   {
9679     TYPE_INTEGER,
9680     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
9681   },
9682   {
9683     TYPE_SWITCH,
9684     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
9685   },
9686 };
9687
9688 static struct TokenInfo auto_setup_tokens[] =
9689 {
9690   {
9691     TYPE_INTEGER,
9692     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
9693   },
9694 };
9695
9696 static struct TokenInfo server_setup_tokens[] =
9697 {
9698   {
9699     TYPE_STRING,
9700     &setup.player_uuid,                         "player_uuid"
9701   },
9702   {
9703     TYPE_INTEGER,
9704     &setup.player_version,                      "player_version"
9705   },
9706   {
9707     TYPE_SWITCH,
9708     &setup.use_api_server,          TEST_PREFIX "use_api_server"
9709   },
9710   {
9711     TYPE_STRING,
9712     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
9713   },
9714   {
9715     TYPE_STRING,
9716     &setup.api_server_password,     TEST_PREFIX "api_server_password"
9717   },
9718   {
9719     TYPE_SWITCH,
9720     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9721   },
9722   {
9723     TYPE_SWITCH,
9724     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9725   },
9726   {
9727     TYPE_SWITCH,
9728     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9729   },
9730   {
9731     TYPE_SWITCH,
9732     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9733   },
9734   {
9735     TYPE_SWITCH,
9736     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
9737   },
9738 };
9739
9740 static struct TokenInfo editor_setup_tokens[] =
9741 {
9742   {
9743     TYPE_SWITCH,
9744     &setup.editor.el_classic,                   "editor.el_classic"
9745   },
9746   {
9747     TYPE_SWITCH,
9748     &setup.editor.el_custom,                    "editor.el_custom"
9749   },
9750   {
9751     TYPE_SWITCH,
9752     &setup.editor.el_user_defined,              "editor.el_user_defined"
9753   },
9754   {
9755     TYPE_SWITCH,
9756     &setup.editor.el_dynamic,                   "editor.el_dynamic"
9757   },
9758   {
9759     TYPE_SWITCH,
9760     &setup.editor.el_headlines,                 "editor.el_headlines"
9761   },
9762   {
9763     TYPE_SWITCH,
9764     &setup.editor.show_element_token,           "editor.show_element_token"
9765   },
9766   {
9767     TYPE_SWITCH,
9768     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
9769   },
9770 };
9771
9772 static struct TokenInfo editor_cascade_setup_tokens[] =
9773 {
9774   {
9775     TYPE_SWITCH,
9776     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
9777   },
9778   {
9779     TYPE_SWITCH,
9780     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
9781   },
9782   {
9783     TYPE_SWITCH,
9784     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
9785   },
9786   {
9787     TYPE_SWITCH,
9788     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
9789   },
9790   {
9791     TYPE_SWITCH,
9792     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
9793   },
9794   {
9795     TYPE_SWITCH,
9796     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
9797   },
9798   {
9799     TYPE_SWITCH,
9800     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
9801   },
9802   {
9803     TYPE_SWITCH,
9804     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
9805   },
9806   {
9807     TYPE_SWITCH,
9808     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
9809   },
9810   {
9811     TYPE_SWITCH,
9812     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
9813   },
9814   {
9815     TYPE_SWITCH,
9816     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
9817   },
9818   {
9819     TYPE_SWITCH,
9820     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
9821   },
9822   {
9823     TYPE_SWITCH,
9824     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
9825   },
9826   {
9827     TYPE_SWITCH,
9828     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
9829   },
9830   {
9831     TYPE_SWITCH,
9832     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
9833   },
9834   {
9835     TYPE_SWITCH,
9836     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
9837   },
9838   {
9839     TYPE_SWITCH,
9840     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
9841   },
9842   {
9843     TYPE_SWITCH,
9844     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
9845   },
9846 };
9847
9848 static struct TokenInfo shortcut_setup_tokens[] =
9849 {
9850   {
9851     TYPE_KEY_X11,
9852     &setup.shortcut.save_game,                  "shortcut.save_game"
9853   },
9854   {
9855     TYPE_KEY_X11,
9856     &setup.shortcut.load_game,                  "shortcut.load_game"
9857   },
9858   {
9859     TYPE_KEY_X11,
9860     &setup.shortcut.restart_game,               "shortcut.restart_game"
9861   },
9862   {
9863     TYPE_KEY_X11,
9864     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
9865   },
9866   {
9867     TYPE_KEY_X11,
9868     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
9869   },
9870   {
9871     TYPE_KEY_X11,
9872     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
9873   },
9874   {
9875     TYPE_KEY_X11,
9876     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
9877   },
9878   {
9879     TYPE_KEY_X11,
9880     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
9881   },
9882   {
9883     TYPE_KEY_X11,
9884     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
9885   },
9886   {
9887     TYPE_KEY_X11,
9888     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
9889   },
9890   {
9891     TYPE_KEY_X11,
9892     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
9893   },
9894   {
9895     TYPE_KEY_X11,
9896     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
9897   },
9898   {
9899     TYPE_KEY_X11,
9900     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
9901   },
9902   {
9903     TYPE_KEY_X11,
9904     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
9905   },
9906   {
9907     TYPE_KEY_X11,
9908     &setup.shortcut.tape_record,                "shortcut.tape_record"
9909   },
9910   {
9911     TYPE_KEY_X11,
9912     &setup.shortcut.tape_play,                  "shortcut.tape_play"
9913   },
9914   {
9915     TYPE_KEY_X11,
9916     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
9917   },
9918   {
9919     TYPE_KEY_X11,
9920     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
9921   },
9922   {
9923     TYPE_KEY_X11,
9924     &setup.shortcut.sound_music,                "shortcut.sound_music"
9925   },
9926   {
9927     TYPE_KEY_X11,
9928     &setup.shortcut.snap_left,                  "shortcut.snap_left"
9929   },
9930   {
9931     TYPE_KEY_X11,
9932     &setup.shortcut.snap_right,                 "shortcut.snap_right"
9933   },
9934   {
9935     TYPE_KEY_X11,
9936     &setup.shortcut.snap_up,                    "shortcut.snap_up"
9937   },
9938   {
9939     TYPE_KEY_X11,
9940     &setup.shortcut.snap_down,                  "shortcut.snap_down"
9941   },
9942 };
9943
9944 static struct SetupInputInfo setup_input;
9945 static struct TokenInfo player_setup_tokens[] =
9946 {
9947   {
9948     TYPE_BOOLEAN,
9949     &setup_input.use_joystick,                  ".use_joystick"
9950   },
9951   {
9952     TYPE_STRING,
9953     &setup_input.joy.device_name,               ".joy.device_name"
9954   },
9955   {
9956     TYPE_INTEGER,
9957     &setup_input.joy.xleft,                     ".joy.xleft"
9958   },
9959   {
9960     TYPE_INTEGER,
9961     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
9962   },
9963   {
9964     TYPE_INTEGER,
9965     &setup_input.joy.xright,                    ".joy.xright"
9966   },
9967   {
9968     TYPE_INTEGER,
9969     &setup_input.joy.yupper,                    ".joy.yupper"
9970   },
9971   {
9972     TYPE_INTEGER,
9973     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
9974   },
9975   {
9976     TYPE_INTEGER,
9977     &setup_input.joy.ylower,                    ".joy.ylower"
9978   },
9979   {
9980     TYPE_INTEGER,
9981     &setup_input.joy.snap,                      ".joy.snap_field"
9982   },
9983   {
9984     TYPE_INTEGER,
9985     &setup_input.joy.drop,                      ".joy.place_bomb"
9986   },
9987   {
9988     TYPE_KEY_X11,
9989     &setup_input.key.left,                      ".key.move_left"
9990   },
9991   {
9992     TYPE_KEY_X11,
9993     &setup_input.key.right,                     ".key.move_right"
9994   },
9995   {
9996     TYPE_KEY_X11,
9997     &setup_input.key.up,                        ".key.move_up"
9998   },
9999   {
10000     TYPE_KEY_X11,
10001     &setup_input.key.down,                      ".key.move_down"
10002   },
10003   {
10004     TYPE_KEY_X11,
10005     &setup_input.key.snap,                      ".key.snap_field"
10006   },
10007   {
10008     TYPE_KEY_X11,
10009     &setup_input.key.drop,                      ".key.place_bomb"
10010   },
10011 };
10012
10013 static struct TokenInfo system_setup_tokens[] =
10014 {
10015   {
10016     TYPE_STRING,
10017     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10018   },
10019   {
10020     TYPE_STRING,
10021     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10022   },
10023   {
10024     TYPE_STRING,
10025     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10026   },
10027   {
10028     TYPE_INTEGER,
10029     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10030   },
10031 };
10032
10033 static struct TokenInfo internal_setup_tokens[] =
10034 {
10035   {
10036     TYPE_STRING,
10037     &setup.internal.program_title,              "program_title"
10038   },
10039   {
10040     TYPE_STRING,
10041     &setup.internal.program_version,            "program_version"
10042   },
10043   {
10044     TYPE_STRING,
10045     &setup.internal.program_author,             "program_author"
10046   },
10047   {
10048     TYPE_STRING,
10049     &setup.internal.program_email,              "program_email"
10050   },
10051   {
10052     TYPE_STRING,
10053     &setup.internal.program_website,            "program_website"
10054   },
10055   {
10056     TYPE_STRING,
10057     &setup.internal.program_copyright,          "program_copyright"
10058   },
10059   {
10060     TYPE_STRING,
10061     &setup.internal.program_company,            "program_company"
10062   },
10063   {
10064     TYPE_STRING,
10065     &setup.internal.program_icon_file,          "program_icon_file"
10066   },
10067   {
10068     TYPE_STRING,
10069     &setup.internal.default_graphics_set,       "default_graphics_set"
10070   },
10071   {
10072     TYPE_STRING,
10073     &setup.internal.default_sounds_set,         "default_sounds_set"
10074   },
10075   {
10076     TYPE_STRING,
10077     &setup.internal.default_music_set,          "default_music_set"
10078   },
10079   {
10080     TYPE_STRING,
10081     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
10082   },
10083   {
10084     TYPE_STRING,
10085     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
10086   },
10087   {
10088     TYPE_STRING,
10089     &setup.internal.fallback_music_file,        "fallback_music_file"
10090   },
10091   {
10092     TYPE_STRING,
10093     &setup.internal.default_level_series,       "default_level_series"
10094   },
10095   {
10096     TYPE_INTEGER,
10097     &setup.internal.default_window_width,       "default_window_width"
10098   },
10099   {
10100     TYPE_INTEGER,
10101     &setup.internal.default_window_height,      "default_window_height"
10102   },
10103   {
10104     TYPE_BOOLEAN,
10105     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
10106   },
10107   {
10108     TYPE_BOOLEAN,
10109     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
10110   },
10111   {
10112     TYPE_BOOLEAN,
10113     &setup.internal.create_user_levelset,       "create_user_levelset"
10114   },
10115   {
10116     TYPE_BOOLEAN,
10117     &setup.internal.menu_game,                  "menu_game"
10118   },
10119   {
10120     TYPE_BOOLEAN,
10121     &setup.internal.menu_editor,                "menu_editor"
10122   },
10123   {
10124     TYPE_BOOLEAN,
10125     &setup.internal.menu_graphics,              "menu_graphics"
10126   },
10127   {
10128     TYPE_BOOLEAN,
10129     &setup.internal.menu_sound,                 "menu_sound"
10130   },
10131   {
10132     TYPE_BOOLEAN,
10133     &setup.internal.menu_artwork,               "menu_artwork"
10134   },
10135   {
10136     TYPE_BOOLEAN,
10137     &setup.internal.menu_input,                 "menu_input"
10138   },
10139   {
10140     TYPE_BOOLEAN,
10141     &setup.internal.menu_touch,                 "menu_touch"
10142   },
10143   {
10144     TYPE_BOOLEAN,
10145     &setup.internal.menu_shortcuts,             "menu_shortcuts"
10146   },
10147   {
10148     TYPE_BOOLEAN,
10149     &setup.internal.menu_exit,                  "menu_exit"
10150   },
10151   {
10152     TYPE_BOOLEAN,
10153     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
10154   },
10155   {
10156     TYPE_BOOLEAN,
10157     &setup.internal.info_title,                 "info_title"
10158   },
10159   {
10160     TYPE_BOOLEAN,
10161     &setup.internal.info_elements,              "info_elements"
10162   },
10163   {
10164     TYPE_BOOLEAN,
10165     &setup.internal.info_music,                 "info_music"
10166   },
10167   {
10168     TYPE_BOOLEAN,
10169     &setup.internal.info_credits,               "info_credits"
10170   },
10171   {
10172     TYPE_BOOLEAN,
10173     &setup.internal.info_program,               "info_program"
10174   },
10175   {
10176     TYPE_BOOLEAN,
10177     &setup.internal.info_version,               "info_version"
10178   },
10179   {
10180     TYPE_BOOLEAN,
10181     &setup.internal.info_levelset,              "info_levelset"
10182   },
10183   {
10184     TYPE_BOOLEAN,
10185     &setup.internal.info_exit,                  "info_exit"
10186   },
10187 };
10188
10189 static struct TokenInfo debug_setup_tokens[] =
10190 {
10191   {
10192     TYPE_INTEGER,
10193     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
10194   },
10195   {
10196     TYPE_INTEGER,
10197     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
10198   },
10199   {
10200     TYPE_INTEGER,
10201     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
10202   },
10203   {
10204     TYPE_INTEGER,
10205     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
10206   },
10207   {
10208     TYPE_INTEGER,
10209     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
10210   },
10211   {
10212     TYPE_INTEGER,
10213     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
10214   },
10215   {
10216     TYPE_INTEGER,
10217     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
10218   },
10219   {
10220     TYPE_INTEGER,
10221     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
10222   },
10223   {
10224     TYPE_INTEGER,
10225     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
10226   },
10227   {
10228     TYPE_INTEGER,
10229     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
10230   },
10231   {
10232     TYPE_KEY_X11,
10233     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
10234   },
10235   {
10236     TYPE_KEY_X11,
10237     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
10238   },
10239   {
10240     TYPE_KEY_X11,
10241     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
10242   },
10243   {
10244     TYPE_KEY_X11,
10245     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
10246   },
10247   {
10248     TYPE_KEY_X11,
10249     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
10250   },
10251   {
10252     TYPE_KEY_X11,
10253     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
10254   },
10255   {
10256     TYPE_KEY_X11,
10257     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
10258   },
10259   {
10260     TYPE_KEY_X11,
10261     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
10262   },
10263   {
10264     TYPE_KEY_X11,
10265     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
10266   },
10267   {
10268     TYPE_KEY_X11,
10269     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
10270   },
10271   {
10272     TYPE_BOOLEAN,
10273     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
10274   {
10275     TYPE_BOOLEAN,
10276     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
10277   },
10278   {
10279     TYPE_BOOLEAN,
10280     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
10281   },
10282   {
10283     TYPE_SWITCH3,
10284     &setup.debug.xsn_mode,                      "debug.xsn_mode"
10285   },
10286   {
10287     TYPE_INTEGER,
10288     &setup.debug.xsn_percent,                   "debug.xsn_percent"
10289   },
10290 };
10291
10292 static struct TokenInfo options_setup_tokens[] =
10293 {
10294   {
10295     TYPE_BOOLEAN,
10296     &setup.options.verbose,                     "options.verbose"
10297   },
10298 };
10299
10300 static void setSetupInfoToDefaults(struct SetupInfo *si)
10301 {
10302   int i;
10303
10304   si->player_name = getStringCopy(getDefaultUserName(user.nr));
10305
10306   si->multiple_users = TRUE;
10307
10308   si->sound = TRUE;
10309   si->sound_loops = TRUE;
10310   si->sound_music = TRUE;
10311   si->sound_simple = TRUE;
10312   si->toons = TRUE;
10313   si->scroll_delay = TRUE;
10314   si->forced_scroll_delay = FALSE;
10315   si->scroll_delay_value = STD_SCROLL_DELAY;
10316   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10317   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10318   si->fade_screens = TRUE;
10319   si->autorecord = TRUE;
10320   si->auto_pause_on_start = FALSE;
10321   si->show_titlescreen = TRUE;
10322   si->quick_doors = FALSE;
10323   si->team_mode = FALSE;
10324   si->handicap = TRUE;
10325   si->skip_levels = TRUE;
10326   si->increment_levels = TRUE;
10327   si->auto_play_next_level = TRUE;
10328   si->count_score_after_game = TRUE;
10329   si->show_scores_after_game = TRUE;
10330   si->time_limit = TRUE;
10331   si->fullscreen = FALSE;
10332   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10333   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10334   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10335   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10336   si->ask_on_escape = TRUE;
10337   si->ask_on_escape_editor = TRUE;
10338   si->ask_on_game_over = TRUE;
10339   si->ask_on_quit_game = TRUE;
10340   si->ask_on_quit_program = TRUE;
10341   si->quick_switch = FALSE;
10342   si->input_on_focus = FALSE;
10343   si->prefer_aga_graphics = TRUE;
10344   si->prefer_lowpass_sounds = FALSE;
10345   si->prefer_extra_panel_items = TRUE;
10346   si->game_speed_extended = FALSE;
10347   si->game_frame_delay = GAME_FRAME_DELAY;
10348   si->sp_show_border_elements = FALSE;
10349   si->small_game_graphics = FALSE;
10350   si->show_load_save_buttons = FALSE;
10351   si->show_undo_redo_buttons = FALSE;
10352   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10353
10354   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10355   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
10356   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
10357
10358   si->override_level_graphics = FALSE;
10359   si->override_level_sounds = FALSE;
10360   si->override_level_music = FALSE;
10361
10362   si->volume_simple = 100;              // percent
10363   si->volume_loops = 100;               // percent
10364   si->volume_music = 100;               // percent
10365
10366   si->network_mode = FALSE;
10367   si->network_player_nr = 0;            // first player
10368   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10369
10370   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10371   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
10372   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
10373   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
10374   si->touch.draw_outlined = TRUE;
10375   si->touch.draw_pressed = TRUE;
10376
10377   for (i = 0; i < 2; i++)
10378   {
10379     char *default_grid_button[6][2] =
10380     {
10381       { "      ", "  ^^  " },
10382       { "      ", "  ^^  " },
10383       { "      ", "<<  >>" },
10384       { "      ", "<<  >>" },
10385       { "111222", "  vv  " },
10386       { "111222", "  vv  " }
10387     };
10388     int grid_xsize = DEFAULT_GRID_XSIZE(i);
10389     int grid_ysize = DEFAULT_GRID_YSIZE(i);
10390     int min_xsize = MIN(6, grid_xsize);
10391     int min_ysize = MIN(6, grid_ysize);
10392     int startx = grid_xsize - min_xsize;
10393     int starty = grid_ysize - min_ysize;
10394     int x, y;
10395
10396     // virtual buttons grid can only be set to defaults if video is initialized
10397     // (this will be repeated if virtual buttons are not loaded from setup file)
10398     if (video.initialized)
10399     {
10400       si->touch.grid_xsize[i] = grid_xsize;
10401       si->touch.grid_ysize[i] = grid_ysize;
10402     }
10403     else
10404     {
10405       si->touch.grid_xsize[i] = -1;
10406       si->touch.grid_ysize[i] = -1;
10407     }
10408
10409     for (x = 0; x < MAX_GRID_XSIZE; x++)
10410       for (y = 0; y < MAX_GRID_YSIZE; y++)
10411         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10412
10413     for (x = 0; x < min_xsize; x++)
10414       for (y = 0; y < min_ysize; y++)
10415         si->touch.grid_button[i][x][starty + y] =
10416           default_grid_button[y][0][x];
10417
10418     for (x = 0; x < min_xsize; x++)
10419       for (y = 0; y < min_ysize; y++)
10420         si->touch.grid_button[i][startx + x][starty + y] =
10421           default_grid_button[y][1][x];
10422   }
10423
10424   si->touch.grid_initialized            = video.initialized;
10425
10426   si->touch.overlay_buttons             = FALSE;
10427
10428   si->editor.el_boulderdash             = TRUE;
10429   si->editor.el_emerald_mine            = TRUE;
10430   si->editor.el_emerald_mine_club       = TRUE;
10431   si->editor.el_more                    = TRUE;
10432   si->editor.el_sokoban                 = TRUE;
10433   si->editor.el_supaplex                = TRUE;
10434   si->editor.el_diamond_caves           = TRUE;
10435   si->editor.el_dx_boulderdash          = TRUE;
10436
10437   si->editor.el_mirror_magic            = TRUE;
10438   si->editor.el_deflektor               = TRUE;
10439
10440   si->editor.el_chars                   = TRUE;
10441   si->editor.el_steel_chars             = TRUE;
10442
10443   si->editor.el_classic                 = TRUE;
10444   si->editor.el_custom                  = TRUE;
10445
10446   si->editor.el_user_defined            = FALSE;
10447   si->editor.el_dynamic                 = TRUE;
10448
10449   si->editor.el_headlines               = TRUE;
10450
10451   si->editor.show_element_token         = FALSE;
10452
10453   si->editor.show_read_only_warning     = TRUE;
10454
10455   si->editor.use_template_for_new_levels = TRUE;
10456
10457   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
10458   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
10459   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
10460   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10461   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
10462
10463   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
10464   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
10465   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
10466   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
10467   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10468
10469   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
10470   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
10471   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
10472   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
10473   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
10474   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
10475
10476   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
10477   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
10478   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
10479
10480   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
10481   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
10482   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
10483   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
10484
10485   for (i = 0; i < MAX_PLAYERS; i++)
10486   {
10487     si->input[i].use_joystick = FALSE;
10488     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10489     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
10490     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10491     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
10492     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
10493     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10494     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
10495     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
10496     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
10497     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
10498     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10499     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
10500     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
10501     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
10502     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
10503   }
10504
10505   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10506   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10507   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10508   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10509
10510   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
10511   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
10512   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
10513   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
10514   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
10515   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10516   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
10517
10518   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10519
10520   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10521   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
10522   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
10523
10524   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10525   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
10526   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
10527
10528   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10529   si->internal.choose_from_top_leveldir = FALSE;
10530   si->internal.show_scaling_in_title = TRUE;
10531   si->internal.create_user_levelset = TRUE;
10532
10533   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
10534   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10535
10536   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10537   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10538   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10539   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10540   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10541   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10542   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10543   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10544   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10545   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10546
10547   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10548   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10549   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10550   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10551   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10552   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10553   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10554   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10555   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10556   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10557
10558   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10559   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
10560
10561   si->debug.show_frames_per_second = FALSE;
10562
10563   si->debug.xsn_mode = AUTO;
10564   si->debug.xsn_percent = 0;
10565
10566   si->options.verbose = FALSE;
10567
10568 #if defined(PLATFORM_ANDROID)
10569   si->fullscreen = TRUE;
10570   si->touch.overlay_buttons = TRUE;
10571 #endif
10572
10573   setHideSetupEntry(&setup.debug.xsn_mode);
10574 }
10575
10576 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10577 {
10578   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10579 }
10580
10581 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10582 {
10583   si->player_uuid = NULL;       // (will be set later)
10584   si->player_version = 1;       // (will be set later)
10585
10586   si->use_api_server = TRUE;
10587   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10588   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10589   si->ask_for_uploading_tapes = TRUE;
10590   si->ask_for_remaining_tapes = FALSE;
10591   si->provide_uploading_tapes = TRUE;
10592   si->ask_for_using_api_server = TRUE;
10593   si->has_remaining_tapes = FALSE;
10594 }
10595
10596 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10597 {
10598   si->editor_cascade.el_bd              = TRUE;
10599   si->editor_cascade.el_em              = TRUE;
10600   si->editor_cascade.el_emc             = TRUE;
10601   si->editor_cascade.el_rnd             = TRUE;
10602   si->editor_cascade.el_sb              = TRUE;
10603   si->editor_cascade.el_sp              = TRUE;
10604   si->editor_cascade.el_dc              = TRUE;
10605   si->editor_cascade.el_dx              = TRUE;
10606
10607   si->editor_cascade.el_mm              = TRUE;
10608   si->editor_cascade.el_df              = TRUE;
10609
10610   si->editor_cascade.el_chars           = FALSE;
10611   si->editor_cascade.el_steel_chars     = FALSE;
10612   si->editor_cascade.el_ce              = FALSE;
10613   si->editor_cascade.el_ge              = FALSE;
10614   si->editor_cascade.el_es              = FALSE;
10615   si->editor_cascade.el_ref             = FALSE;
10616   si->editor_cascade.el_user            = FALSE;
10617   si->editor_cascade.el_dynamic         = FALSE;
10618 }
10619
10620 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
10621
10622 static char *getHideSetupToken(void *setup_value)
10623 {
10624   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10625
10626   if (setup_value != NULL)
10627     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10628
10629   return hide_setup_token;
10630 }
10631
10632 void setHideSetupEntry(void *setup_value)
10633 {
10634   char *hide_setup_token = getHideSetupToken(setup_value);
10635
10636   if (hide_setup_hash == NULL)
10637     hide_setup_hash = newSetupFileHash();
10638
10639   if (setup_value != NULL)
10640     setHashEntry(hide_setup_hash, hide_setup_token, "");
10641 }
10642
10643 void removeHideSetupEntry(void *setup_value)
10644 {
10645   char *hide_setup_token = getHideSetupToken(setup_value);
10646
10647   if (setup_value != NULL)
10648     removeHashEntry(hide_setup_hash, hide_setup_token);
10649 }
10650
10651 boolean hideSetupEntry(void *setup_value)
10652 {
10653   char *hide_setup_token = getHideSetupToken(setup_value);
10654
10655   return (setup_value != NULL &&
10656           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10657 }
10658
10659 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10660                                       struct TokenInfo *token_info,
10661                                       int token_nr, char *token_text)
10662 {
10663   char *token_hide_text = getStringCat2(token_text, ".hide");
10664   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10665
10666   // set the value of this setup option in the setup option structure
10667   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10668
10669   // check if this setup option should be hidden in the setup menu
10670   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10671     setHideSetupEntry(token_info[token_nr].value);
10672
10673   free(token_hide_text);
10674 }
10675
10676 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10677                                       struct TokenInfo *token_info,
10678                                       int token_nr)
10679 {
10680   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10681                             token_info[token_nr].text);
10682 }
10683
10684 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10685 {
10686   int i, pnr;
10687
10688   if (!setup_file_hash)
10689     return;
10690
10691   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10692     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10693
10694   setup.touch.grid_initialized = TRUE;
10695   for (i = 0; i < 2; i++)
10696   {
10697     int grid_xsize = setup.touch.grid_xsize[i];
10698     int grid_ysize = setup.touch.grid_ysize[i];
10699     int x, y;
10700
10701     // if virtual buttons are not loaded from setup file, repeat initializing
10702     // virtual buttons grid with default values later when video is initialized
10703     if (grid_xsize == -1 ||
10704         grid_ysize == -1)
10705     {
10706       setup.touch.grid_initialized = FALSE;
10707
10708       continue;
10709     }
10710
10711     for (y = 0; y < grid_ysize; y++)
10712     {
10713       char token_string[MAX_LINE_LEN];
10714
10715       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10716
10717       char *value_string = getHashEntry(setup_file_hash, token_string);
10718
10719       if (value_string == NULL)
10720         continue;
10721
10722       for (x = 0; x < grid_xsize; x++)
10723       {
10724         char c = value_string[x];
10725
10726         setup.touch.grid_button[i][x][y] =
10727           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10728       }
10729     }
10730   }
10731
10732   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10733     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10734
10735   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10736     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10737
10738   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10739   {
10740     char prefix[30];
10741
10742     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10743
10744     setup_input = setup.input[pnr];
10745     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10746     {
10747       char full_token[100];
10748
10749       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10750       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10751                                 full_token);
10752     }
10753     setup.input[pnr] = setup_input;
10754   }
10755
10756   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10757     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10758
10759   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10760     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10761
10762   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10763     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10764
10765   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10766     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10767
10768   setHideRelatedSetupEntries();
10769 }
10770
10771 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10772 {
10773   int i;
10774
10775   if (!setup_file_hash)
10776     return;
10777
10778   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10779     setSetupInfo(auto_setup_tokens, i,
10780                  getHashEntry(setup_file_hash,
10781                               auto_setup_tokens[i].text));
10782 }
10783
10784 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10785 {
10786   int i;
10787
10788   if (!setup_file_hash)
10789     return;
10790
10791   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10792     setSetupInfo(server_setup_tokens, i,
10793                  getHashEntry(setup_file_hash,
10794                               server_setup_tokens[i].text));
10795 }
10796
10797 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10798 {
10799   int i;
10800
10801   if (!setup_file_hash)
10802     return;
10803
10804   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10805     setSetupInfo(editor_cascade_setup_tokens, i,
10806                  getHashEntry(setup_file_hash,
10807                               editor_cascade_setup_tokens[i].text));
10808 }
10809
10810 void LoadUserNames(void)
10811 {
10812   int last_user_nr = user.nr;
10813   int i;
10814
10815   if (global.user_names != NULL)
10816   {
10817     for (i = 0; i < MAX_PLAYER_NAMES; i++)
10818       checked_free(global.user_names[i]);
10819
10820     checked_free(global.user_names);
10821   }
10822
10823   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10824
10825   for (i = 0; i < MAX_PLAYER_NAMES; i++)
10826   {
10827     user.nr = i;
10828
10829     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10830
10831     if (setup_file_hash)
10832     {
10833       char *player_name = getHashEntry(setup_file_hash, "player_name");
10834
10835       global.user_names[i] = getFixedUserName(player_name);
10836
10837       freeSetupFileHash(setup_file_hash);
10838     }
10839
10840     if (global.user_names[i] == NULL)
10841       global.user_names[i] = getStringCopy(getDefaultUserName(i));
10842   }
10843
10844   user.nr = last_user_nr;
10845 }
10846
10847 void LoadSetupFromFilename(char *filename)
10848 {
10849   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10850
10851   if (setup_file_hash)
10852   {
10853     decodeSetupFileHash_Default(setup_file_hash);
10854
10855     freeSetupFileHash(setup_file_hash);
10856   }
10857   else
10858   {
10859     Debug("setup", "using default setup values");
10860   }
10861 }
10862
10863 static void LoadSetup_SpecialPostProcessing(void)
10864 {
10865   char *player_name_new;
10866
10867   // needed to work around problems with fixed length strings
10868   player_name_new = getFixedUserName(setup.player_name);
10869   free(setup.player_name);
10870   setup.player_name = player_name_new;
10871
10872   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10873   if (setup.scroll_delay == FALSE)
10874   {
10875     setup.scroll_delay_value = MIN_SCROLL_DELAY;
10876     setup.scroll_delay = TRUE;                  // now always "on"
10877   }
10878
10879   // make sure that scroll delay value stays inside valid range
10880   setup.scroll_delay_value =
10881     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10882 }
10883
10884 void LoadSetup_Default(void)
10885 {
10886   char *filename;
10887
10888   // always start with reliable default values
10889   setSetupInfoToDefaults(&setup);
10890
10891   // try to load setup values from default setup file
10892   filename = getDefaultSetupFilename();
10893
10894   if (fileExists(filename))
10895     LoadSetupFromFilename(filename);
10896
10897   // try to load setup values from platform setup file
10898   filename = getPlatformSetupFilename();
10899
10900   if (fileExists(filename))
10901     LoadSetupFromFilename(filename);
10902
10903   // try to load setup values from user setup file
10904   filename = getSetupFilename();
10905
10906   LoadSetupFromFilename(filename);
10907
10908   LoadSetup_SpecialPostProcessing();
10909 }
10910
10911 void LoadSetup_AutoSetup(void)
10912 {
10913   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10914   SetupFileHash *setup_file_hash = NULL;
10915
10916   // always start with reliable default values
10917   setSetupInfoToDefaults_AutoSetup(&setup);
10918
10919   setup_file_hash = loadSetupFileHash(filename);
10920
10921   if (setup_file_hash)
10922   {
10923     decodeSetupFileHash_AutoSetup(setup_file_hash);
10924
10925     freeSetupFileHash(setup_file_hash);
10926   }
10927
10928   free(filename);
10929 }
10930
10931 void LoadSetup_ServerSetup(void)
10932 {
10933   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
10934   SetupFileHash *setup_file_hash = NULL;
10935
10936   // always start with reliable default values
10937   setSetupInfoToDefaults_ServerSetup(&setup);
10938
10939   setup_file_hash = loadSetupFileHash(filename);
10940
10941   if (setup_file_hash)
10942   {
10943     decodeSetupFileHash_ServerSetup(setup_file_hash);
10944
10945     freeSetupFileHash(setup_file_hash);
10946   }
10947
10948   free(filename);
10949
10950   if (setup.player_uuid == NULL)
10951   {
10952     // player UUID does not yet exist in setup file
10953     setup.player_uuid = getStringCopy(getUUID());
10954     setup.player_version = 2;
10955
10956     SaveSetup_ServerSetup();
10957   }
10958 }
10959
10960 void LoadSetup_EditorCascade(void)
10961 {
10962   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10963   SetupFileHash *setup_file_hash = NULL;
10964
10965   // always start with reliable default values
10966   setSetupInfoToDefaults_EditorCascade(&setup);
10967
10968   setup_file_hash = loadSetupFileHash(filename);
10969
10970   if (setup_file_hash)
10971   {
10972     decodeSetupFileHash_EditorCascade(setup_file_hash);
10973
10974     freeSetupFileHash(setup_file_hash);
10975   }
10976
10977   free(filename);
10978 }
10979
10980 void LoadSetup(void)
10981 {
10982   LoadSetup_Default();
10983   LoadSetup_AutoSetup();
10984   LoadSetup_ServerSetup();
10985   LoadSetup_EditorCascade();
10986 }
10987
10988 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
10989                                            char *mapping_line)
10990 {
10991   char mapping_guid[MAX_LINE_LEN];
10992   char *mapping_start, *mapping_end;
10993
10994   // get GUID from game controller mapping line: copy complete line
10995   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
10996   mapping_guid[MAX_LINE_LEN - 1] = '\0';
10997
10998   // get GUID from game controller mapping line: cut after GUID part
10999   mapping_start = strchr(mapping_guid, ',');
11000   if (mapping_start != NULL)
11001     *mapping_start = '\0';
11002
11003   // cut newline from game controller mapping line
11004   mapping_end = strchr(mapping_line, '\n');
11005   if (mapping_end != NULL)
11006     *mapping_end = '\0';
11007
11008   // add mapping entry to game controller mappings hash
11009   setHashEntry(mappings_hash, mapping_guid, mapping_line);
11010 }
11011
11012 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11013                                                  char *filename)
11014 {
11015   FILE *file;
11016
11017   if (!(file = fopen(filename, MODE_READ)))
11018   {
11019     Warn("cannot read game controller mappings file '%s'", filename);
11020
11021     return;
11022   }
11023
11024   while (!feof(file))
11025   {
11026     char line[MAX_LINE_LEN];
11027
11028     if (!fgets(line, MAX_LINE_LEN, file))
11029       break;
11030
11031     addGameControllerMappingToHash(mappings_hash, line);
11032   }
11033
11034   fclose(file);
11035 }
11036
11037 void SaveSetup_Default(void)
11038 {
11039   char *filename = getSetupFilename();
11040   FILE *file;
11041   int i, pnr;
11042
11043   InitUserDataDirectory();
11044
11045   if (!(file = fopen(filename, MODE_WRITE)))
11046   {
11047     Warn("cannot write setup file '%s'", filename);
11048
11049     return;
11050   }
11051
11052   fprintFileHeader(file, SETUP_FILENAME);
11053
11054   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11055   {
11056     // just to make things nicer :)
11057     if (global_setup_tokens[i].value == &setup.multiple_users           ||
11058         global_setup_tokens[i].value == &setup.sound                    ||
11059         global_setup_tokens[i].value == &setup.graphics_set             ||
11060         global_setup_tokens[i].value == &setup.volume_simple            ||
11061         global_setup_tokens[i].value == &setup.network_mode             ||
11062         global_setup_tokens[i].value == &setup.touch.control_type       ||
11063         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
11064         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11065       fprintf(file, "\n");
11066
11067     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11068   }
11069
11070   for (i = 0; i < 2; i++)
11071   {
11072     int grid_xsize = setup.touch.grid_xsize[i];
11073     int grid_ysize = setup.touch.grid_ysize[i];
11074     int x, y;
11075
11076     fprintf(file, "\n");
11077
11078     for (y = 0; y < grid_ysize; y++)
11079     {
11080       char token_string[MAX_LINE_LEN];
11081       char value_string[MAX_LINE_LEN];
11082
11083       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11084
11085       for (x = 0; x < grid_xsize; x++)
11086       {
11087         char c = setup.touch.grid_button[i][x][y];
11088
11089         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11090       }
11091
11092       value_string[grid_xsize] = '\0';
11093
11094       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11095     }
11096   }
11097
11098   fprintf(file, "\n");
11099   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11100     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11101
11102   fprintf(file, "\n");
11103   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11104     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11105
11106   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11107   {
11108     char prefix[30];
11109
11110     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11111     fprintf(file, "\n");
11112
11113     setup_input = setup.input[pnr];
11114     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11115       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11116   }
11117
11118   fprintf(file, "\n");
11119   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11120     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11121
11122   // (internal setup values not saved to user setup file)
11123
11124   fprintf(file, "\n");
11125   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11126     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11127         setup.debug.xsn_mode != AUTO)
11128       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11129
11130   fprintf(file, "\n");
11131   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11132     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11133
11134   fclose(file);
11135
11136   SetFilePermissions(filename, PERMS_PRIVATE);
11137 }
11138
11139 void SaveSetup_AutoSetup(void)
11140 {
11141   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11142   FILE *file;
11143   int i;
11144
11145   InitUserDataDirectory();
11146
11147   if (!(file = fopen(filename, MODE_WRITE)))
11148   {
11149     Warn("cannot write auto setup file '%s'", filename);
11150
11151     free(filename);
11152
11153     return;
11154   }
11155
11156   fprintFileHeader(file, AUTOSETUP_FILENAME);
11157
11158   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11159     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11160
11161   fclose(file);
11162
11163   SetFilePermissions(filename, PERMS_PRIVATE);
11164
11165   free(filename);
11166 }
11167
11168 void SaveSetup_ServerSetup(void)
11169 {
11170   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11171   FILE *file;
11172   int i;
11173
11174   InitUserDataDirectory();
11175
11176   if (!(file = fopen(filename, MODE_WRITE)))
11177   {
11178     Warn("cannot write server setup file '%s'", filename);
11179
11180     free(filename);
11181
11182     return;
11183   }
11184
11185   fprintFileHeader(file, SERVERSETUP_FILENAME);
11186
11187   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11188   {
11189     // just to make things nicer :)
11190     if (server_setup_tokens[i].value == &setup.use_api_server)
11191       fprintf(file, "\n");
11192
11193     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11194   }
11195
11196   fclose(file);
11197
11198   SetFilePermissions(filename, PERMS_PRIVATE);
11199
11200   free(filename);
11201 }
11202
11203 void SaveSetup_EditorCascade(void)
11204 {
11205   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11206   FILE *file;
11207   int i;
11208
11209   InitUserDataDirectory();
11210
11211   if (!(file = fopen(filename, MODE_WRITE)))
11212   {
11213     Warn("cannot write editor cascade state file '%s'", filename);
11214
11215     free(filename);
11216
11217     return;
11218   }
11219
11220   fprintFileHeader(file, EDITORCASCADE_FILENAME);
11221
11222   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11223     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11224
11225   fclose(file);
11226
11227   SetFilePermissions(filename, PERMS_PRIVATE);
11228
11229   free(filename);
11230 }
11231
11232 void SaveSetup(void)
11233 {
11234   SaveSetup_Default();
11235   SaveSetup_AutoSetup();
11236   SaveSetup_ServerSetup();
11237   SaveSetup_EditorCascade();
11238 }
11239
11240 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11241                                                   char *filename)
11242 {
11243   FILE *file;
11244
11245   if (!(file = fopen(filename, MODE_WRITE)))
11246   {
11247     Warn("cannot write game controller mappings file '%s'", filename);
11248
11249     return;
11250   }
11251
11252   BEGIN_HASH_ITERATION(mappings_hash, itr)
11253   {
11254     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11255   }
11256   END_HASH_ITERATION(mappings_hash, itr)
11257
11258   fclose(file);
11259 }
11260
11261 void SaveSetup_AddGameControllerMapping(char *mapping)
11262 {
11263   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11264   SetupFileHash *mappings_hash = newSetupFileHash();
11265
11266   InitUserDataDirectory();
11267
11268   // load existing personal game controller mappings
11269   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11270
11271   // add new mapping to personal game controller mappings
11272   addGameControllerMappingToHash(mappings_hash, mapping);
11273
11274   // save updated personal game controller mappings
11275   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11276
11277   freeSetupFileHash(mappings_hash);
11278   free(filename);
11279 }
11280
11281 void LoadCustomElementDescriptions(void)
11282 {
11283   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11284   SetupFileHash *setup_file_hash;
11285   int i;
11286
11287   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11288   {
11289     if (element_info[i].custom_description != NULL)
11290     {
11291       free(element_info[i].custom_description);
11292       element_info[i].custom_description = NULL;
11293     }
11294   }
11295
11296   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11297     return;
11298
11299   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11300   {
11301     char *token = getStringCat2(element_info[i].token_name, ".name");
11302     char *value = getHashEntry(setup_file_hash, token);
11303
11304     if (value != NULL)
11305       element_info[i].custom_description = getStringCopy(value);
11306
11307     free(token);
11308   }
11309
11310   freeSetupFileHash(setup_file_hash);
11311 }
11312
11313 static int getElementFromToken(char *token)
11314 {
11315   char *value = getHashEntry(element_token_hash, token);
11316
11317   if (value != NULL)
11318     return atoi(value);
11319
11320   Warn("unknown element token '%s'", token);
11321
11322   return EL_UNDEFINED;
11323 }
11324
11325 void FreeGlobalAnimEventInfo(void)
11326 {
11327   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11328
11329   if (gaei->event_list == NULL)
11330     return;
11331
11332   int i;
11333
11334   for (i = 0; i < gaei->num_event_lists; i++)
11335   {
11336     checked_free(gaei->event_list[i]->event_value);
11337     checked_free(gaei->event_list[i]);
11338   }
11339
11340   checked_free(gaei->event_list);
11341
11342   gaei->event_list = NULL;
11343   gaei->num_event_lists = 0;
11344 }
11345
11346 static int AddGlobalAnimEventList(void)
11347 {
11348   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11349   int list_pos = gaei->num_event_lists++;
11350
11351   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11352                                      sizeof(struct GlobalAnimEventListInfo *));
11353
11354   gaei->event_list[list_pos] =
11355     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11356
11357   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11358
11359   gaeli->event_value = NULL;
11360   gaeli->num_event_values = 0;
11361
11362   return list_pos;
11363 }
11364
11365 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11366 {
11367   // do not add empty global animation events
11368   if (event_value == ANIM_EVENT_NONE)
11369     return list_pos;
11370
11371   // if list position is undefined, create new list
11372   if (list_pos == ANIM_EVENT_UNDEFINED)
11373     list_pos = AddGlobalAnimEventList();
11374
11375   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11376   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11377   int value_pos = gaeli->num_event_values++;
11378
11379   gaeli->event_value = checked_realloc(gaeli->event_value,
11380                                        gaeli->num_event_values * sizeof(int *));
11381
11382   gaeli->event_value[value_pos] = event_value;
11383
11384   return list_pos;
11385 }
11386
11387 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11388 {
11389   if (list_pos == ANIM_EVENT_UNDEFINED)
11390     return ANIM_EVENT_NONE;
11391
11392   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11393   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11394
11395   return gaeli->event_value[value_pos];
11396 }
11397
11398 int GetGlobalAnimEventValueCount(int list_pos)
11399 {
11400   if (list_pos == ANIM_EVENT_UNDEFINED)
11401     return 0;
11402
11403   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11404   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11405
11406   return gaeli->num_event_values;
11407 }
11408
11409 // This function checks if a string <s> of the format "string1, string2, ..."
11410 // exactly contains a string <s_contained>.
11411
11412 static boolean string_has_parameter(char *s, char *s_contained)
11413 {
11414   char *substring;
11415
11416   if (s == NULL || s_contained == NULL)
11417     return FALSE;
11418
11419   if (strlen(s_contained) > strlen(s))
11420     return FALSE;
11421
11422   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11423   {
11424     char next_char = s[strlen(s_contained)];
11425
11426     // check if next character is delimiter or whitespace
11427     return (next_char == ',' || next_char == '\0' ||
11428             next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11429   }
11430
11431   // check if string contains another parameter string after a comma
11432   substring = strchr(s, ',');
11433   if (substring == NULL)        // string does not contain a comma
11434     return FALSE;
11435
11436   // advance string pointer to next character after the comma
11437   substring++;
11438
11439   // skip potential whitespaces after the comma
11440   while (*substring == ' ' || *substring == '\t')
11441     substring++;
11442
11443   return string_has_parameter(substring, s_contained);
11444 }
11445
11446 static int get_anim_parameter_value(char *s)
11447 {
11448   int event_value[] =
11449   {
11450     ANIM_EVENT_CLICK,
11451     ANIM_EVENT_INIT,
11452     ANIM_EVENT_START,
11453     ANIM_EVENT_END,
11454     ANIM_EVENT_POST
11455   };
11456   char *pattern_1[] =
11457   {
11458     "click:anim_",
11459     "init:anim_",
11460     "start:anim_",
11461     "end:anim_",
11462     "post:anim_"
11463   };
11464   char *pattern_2 = ".part_";
11465   char *matching_char = NULL;
11466   char *s_ptr = s;
11467   int pattern_1_len = 0;
11468   int result = ANIM_EVENT_NONE;
11469   int i;
11470
11471   for (i = 0; i < ARRAY_SIZE(event_value); i++)
11472   {
11473     matching_char = strstr(s_ptr, pattern_1[i]);
11474     pattern_1_len = strlen(pattern_1[i]);
11475     result = event_value[i];
11476
11477     if (matching_char != NULL)
11478       break;
11479   }
11480
11481   if (matching_char == NULL)
11482     return ANIM_EVENT_NONE;
11483
11484   s_ptr = matching_char + pattern_1_len;
11485
11486   // check for main animation number ("anim_X" or "anim_XX")
11487   if (*s_ptr >= '0' && *s_ptr <= '9')
11488   {
11489     int gic_anim_nr = (*s_ptr++ - '0');
11490
11491     if (*s_ptr >= '0' && *s_ptr <= '9')
11492       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11493
11494     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11495       return ANIM_EVENT_NONE;
11496
11497     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11498   }
11499   else
11500   {
11501     // invalid main animation number specified
11502
11503     return ANIM_EVENT_NONE;
11504   }
11505
11506   // check for animation part number ("part_X" or "part_XX") (optional)
11507   if (strPrefix(s_ptr, pattern_2))
11508   {
11509     s_ptr += strlen(pattern_2);
11510
11511     if (*s_ptr >= '0' && *s_ptr <= '9')
11512     {
11513       int gic_part_nr = (*s_ptr++ - '0');
11514
11515       if (*s_ptr >= '0' && *s_ptr <= '9')
11516         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11517
11518       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11519         return ANIM_EVENT_NONE;
11520
11521       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11522     }
11523     else
11524     {
11525       // invalid animation part number specified
11526
11527       return ANIM_EVENT_NONE;
11528     }
11529   }
11530
11531   // discard result if next character is neither delimiter nor whitespace
11532   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11533         *s_ptr == ' ' || *s_ptr == '\t'))
11534     return ANIM_EVENT_NONE;
11535
11536   return result;
11537 }
11538
11539 static int get_anim_parameter_values(char *s)
11540 {
11541   int list_pos = ANIM_EVENT_UNDEFINED;
11542   int event_value = ANIM_EVENT_DEFAULT;
11543
11544   if (string_has_parameter(s, "any"))
11545     event_value |= ANIM_EVENT_ANY;
11546
11547   if (string_has_parameter(s, "click:self") ||
11548       string_has_parameter(s, "click") ||
11549       string_has_parameter(s, "self"))
11550     event_value |= ANIM_EVENT_SELF;
11551
11552   if (string_has_parameter(s, "unclick:any"))
11553     event_value |= ANIM_EVENT_UNCLICK_ANY;
11554
11555   // if animation event found, add it to global animation event list
11556   if (event_value != ANIM_EVENT_NONE)
11557     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11558
11559   while (s != NULL)
11560   {
11561     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11562     event_value = get_anim_parameter_value(s);
11563
11564     // if animation event found, add it to global animation event list
11565     if (event_value != ANIM_EVENT_NONE)
11566       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11567
11568     // continue with next part of the string, starting with next comma
11569     s = strchr(s + 1, ',');
11570   }
11571
11572   return list_pos;
11573 }
11574
11575 static int get_anim_action_parameter_value(char *token)
11576 {
11577   // check most common default case first to massively speed things up
11578   if (strEqual(token, ARG_UNDEFINED))
11579     return ANIM_EVENT_ACTION_NONE;
11580
11581   int result = getImageIDFromToken(token);
11582
11583   if (result == -1)
11584   {
11585     char *gfx_token = getStringCat2("gfx.", token);
11586
11587     result = getImageIDFromToken(gfx_token);
11588
11589     checked_free(gfx_token);
11590   }
11591
11592   if (result == -1)
11593   {
11594     Key key = getKeyFromX11KeyName(token);
11595
11596     if (key != KSYM_UNDEFINED)
11597       result = -(int)key;
11598   }
11599
11600   if (result == -1)
11601   {
11602     if (isURL(token))
11603     {
11604       result = get_hash_from_key(token);        // unsigned int => int
11605       result = ABS(result);                     // may be negative now
11606       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11607
11608       setHashEntry(anim_url_hash, int2str(result, 0), token);
11609     }
11610   }
11611
11612   if (result == -1)
11613     result = ANIM_EVENT_ACTION_NONE;
11614
11615   return result;
11616 }
11617
11618 int get_parameter_value(char *value_raw, char *suffix, int type)
11619 {
11620   char *value = getStringToLower(value_raw);
11621   int result = 0;       // probably a save default value
11622
11623   if (strEqual(suffix, ".direction"))
11624   {
11625     result = (strEqual(value, "left")  ? MV_LEFT :
11626               strEqual(value, "right") ? MV_RIGHT :
11627               strEqual(value, "up")    ? MV_UP :
11628               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
11629   }
11630   else if (strEqual(suffix, ".position"))
11631   {
11632     result = (strEqual(value, "left")   ? POS_LEFT :
11633               strEqual(value, "right")  ? POS_RIGHT :
11634               strEqual(value, "top")    ? POS_TOP :
11635               strEqual(value, "upper")  ? POS_UPPER :
11636               strEqual(value, "middle") ? POS_MIDDLE :
11637               strEqual(value, "lower")  ? POS_LOWER :
11638               strEqual(value, "bottom") ? POS_BOTTOM :
11639               strEqual(value, "any")    ? POS_ANY :
11640               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
11641   }
11642   else if (strEqual(suffix, ".align"))
11643   {
11644     result = (strEqual(value, "left")   ? ALIGN_LEFT :
11645               strEqual(value, "right")  ? ALIGN_RIGHT :
11646               strEqual(value, "center") ? ALIGN_CENTER :
11647               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11648   }
11649   else if (strEqual(suffix, ".valign"))
11650   {
11651     result = (strEqual(value, "top")    ? VALIGN_TOP :
11652               strEqual(value, "bottom") ? VALIGN_BOTTOM :
11653               strEqual(value, "middle") ? VALIGN_MIDDLE :
11654               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11655   }
11656   else if (strEqual(suffix, ".anim_mode"))
11657   {
11658     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
11659               string_has_parameter(value, "loop")       ? ANIM_LOOP :
11660               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
11661               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
11662               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
11663               string_has_parameter(value, "random")     ? ANIM_RANDOM :
11664               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11665               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
11666               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
11667               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
11668               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11669               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
11670               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
11671               string_has_parameter(value, "all")        ? ANIM_ALL :
11672               string_has_parameter(value, "tiled")      ? ANIM_TILED :
11673               ANIM_DEFAULT);
11674
11675     if (string_has_parameter(value, "once"))
11676       result |= ANIM_ONCE;
11677
11678     if (string_has_parameter(value, "reverse"))
11679       result |= ANIM_REVERSE;
11680
11681     if (string_has_parameter(value, "opaque_player"))
11682       result |= ANIM_OPAQUE_PLAYER;
11683
11684     if (string_has_parameter(value, "static_panel"))
11685       result |= ANIM_STATIC_PANEL;
11686   }
11687   else if (strEqual(suffix, ".init_event") ||
11688            strEqual(suffix, ".anim_event"))
11689   {
11690     result = get_anim_parameter_values(value);
11691   }
11692   else if (strEqual(suffix, ".init_delay_action") ||
11693            strEqual(suffix, ".anim_delay_action") ||
11694            strEqual(suffix, ".post_delay_action") ||
11695            strEqual(suffix, ".init_event_action") ||
11696            strEqual(suffix, ".anim_event_action"))
11697   {
11698     result = get_anim_action_parameter_value(value_raw);
11699   }
11700   else if (strEqual(suffix, ".class"))
11701   {
11702     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11703               get_hash_from_key(value));
11704   }
11705   else if (strEqual(suffix, ".style"))
11706   {
11707     result = STYLE_DEFAULT;
11708
11709     if (string_has_parameter(value, "accurate_borders"))
11710       result |= STYLE_ACCURATE_BORDERS;
11711
11712     if (string_has_parameter(value, "inner_corners"))
11713       result |= STYLE_INNER_CORNERS;
11714
11715     if (string_has_parameter(value, "reverse"))
11716       result |= STYLE_REVERSE;
11717
11718     if (string_has_parameter(value, "leftmost_position"))
11719       result |= STYLE_LEFTMOST_POSITION;
11720
11721     if (string_has_parameter(value, "block_clicks"))
11722       result |= STYLE_BLOCK;
11723
11724     if (string_has_parameter(value, "passthrough_clicks"))
11725       result |= STYLE_PASSTHROUGH;
11726
11727     if (string_has_parameter(value, "multiple_actions"))
11728       result |= STYLE_MULTIPLE_ACTIONS;
11729   }
11730   else if (strEqual(suffix, ".fade_mode"))
11731   {
11732     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
11733               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
11734               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
11735               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
11736               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
11737               FADE_MODE_DEFAULT);
11738   }
11739   else if (strEqual(suffix, ".auto_delay_unit"))
11740   {
11741     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
11742               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11743               AUTO_DELAY_UNIT_DEFAULT);
11744   }
11745   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
11746   {
11747     result = gfx.get_font_from_token_function(value);
11748   }
11749   else          // generic parameter of type integer or boolean
11750   {
11751     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11752               type == TYPE_INTEGER ? get_integer_from_string(value) :
11753               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11754               ARG_UNDEFINED_VALUE);
11755   }
11756
11757   free(value);
11758
11759   return result;
11760 }
11761
11762 static int get_token_parameter_value(char *token, char *value_raw)
11763 {
11764   char *suffix;
11765
11766   if (token == NULL || value_raw == NULL)
11767     return ARG_UNDEFINED_VALUE;
11768
11769   suffix = strrchr(token, '.');
11770   if (suffix == NULL)
11771     suffix = token;
11772
11773   if (strEqual(suffix, ".element"))
11774     return getElementFromToken(value_raw);
11775
11776   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
11777   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
11778 }
11779
11780 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
11781                                      boolean ignore_defaults)
11782 {
11783   int i;
11784
11785   for (i = 0; image_config_vars[i].token != NULL; i++)
11786   {
11787     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11788
11789     // (ignore definitions set to "[DEFAULT]" which are already initialized)
11790     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
11791       continue;
11792
11793     if (value != NULL)
11794       *image_config_vars[i].value =
11795         get_token_parameter_value(image_config_vars[i].token, value);
11796   }
11797 }
11798
11799 void InitMenuDesignSettings_Static(void)
11800 {
11801   // always start with reliable default values from static default config
11802   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
11803 }
11804
11805 static void InitMenuDesignSettings_SpecialPreProcessing(void)
11806 {
11807   int i;
11808
11809   // the following initializes hierarchical values from static configuration
11810
11811   // special case: initialize "ARG_DEFAULT" values in static default config
11812   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
11813   titlescreen_initial_first_default.fade_mode  =
11814     title_initial_first_default.fade_mode;
11815   titlescreen_initial_first_default.fade_delay =
11816     title_initial_first_default.fade_delay;
11817   titlescreen_initial_first_default.post_delay =
11818     title_initial_first_default.post_delay;
11819   titlescreen_initial_first_default.auto_delay =
11820     title_initial_first_default.auto_delay;
11821   titlescreen_initial_first_default.auto_delay_unit =
11822     title_initial_first_default.auto_delay_unit;
11823   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
11824   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
11825   titlescreen_first_default.post_delay = title_first_default.post_delay;
11826   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
11827   titlescreen_first_default.auto_delay_unit =
11828     title_first_default.auto_delay_unit;
11829   titlemessage_initial_first_default.fade_mode  =
11830     title_initial_first_default.fade_mode;
11831   titlemessage_initial_first_default.fade_delay =
11832     title_initial_first_default.fade_delay;
11833   titlemessage_initial_first_default.post_delay =
11834     title_initial_first_default.post_delay;
11835   titlemessage_initial_first_default.auto_delay =
11836     title_initial_first_default.auto_delay;
11837   titlemessage_initial_first_default.auto_delay_unit =
11838     title_initial_first_default.auto_delay_unit;
11839   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
11840   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
11841   titlemessage_first_default.post_delay = title_first_default.post_delay;
11842   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
11843   titlemessage_first_default.auto_delay_unit =
11844     title_first_default.auto_delay_unit;
11845
11846   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
11847   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
11848   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
11849   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
11850   titlescreen_initial_default.auto_delay_unit =
11851     title_initial_default.auto_delay_unit;
11852   titlescreen_default.fade_mode  = title_default.fade_mode;
11853   titlescreen_default.fade_delay = title_default.fade_delay;
11854   titlescreen_default.post_delay = title_default.post_delay;
11855   titlescreen_default.auto_delay = title_default.auto_delay;
11856   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
11857   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
11858   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
11859   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
11860   titlemessage_initial_default.auto_delay_unit =
11861     title_initial_default.auto_delay_unit;
11862   titlemessage_default.fade_mode  = title_default.fade_mode;
11863   titlemessage_default.fade_delay = title_default.fade_delay;
11864   titlemessage_default.post_delay = title_default.post_delay;
11865   titlemessage_default.auto_delay = title_default.auto_delay;
11866   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
11867
11868   // special case: initialize "ARG_DEFAULT" values in static default config
11869   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11870   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
11871   {
11872     titlescreen_initial_first[i] = titlescreen_initial_first_default;
11873     titlescreen_first[i] = titlescreen_first_default;
11874     titlemessage_initial_first[i] = titlemessage_initial_first_default;
11875     titlemessage_first[i] = titlemessage_first_default;
11876
11877     titlescreen_initial[i] = titlescreen_initial_default;
11878     titlescreen[i] = titlescreen_default;
11879     titlemessage_initial[i] = titlemessage_initial_default;
11880     titlemessage[i] = titlemessage_default;
11881   }
11882
11883   // special case: initialize "ARG_DEFAULT" values in static default config
11884   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11885   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11886   {
11887     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
11888       continue;
11889
11890     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
11891     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
11892     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
11893   }
11894
11895   // special case: initialize "ARG_DEFAULT" values in static default config
11896   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11897   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11898   {
11899     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
11900     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
11901     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
11902
11903     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
11904       continue;
11905
11906     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
11907   }
11908 }
11909
11910 static void InitMenuDesignSettings_SpecialPostProcessing(void)
11911 {
11912   static struct
11913   {
11914     struct XY *dst, *src;
11915   }
11916   game_buttons_xy[] =
11917   {
11918     { &game.button.save,        &game.button.stop       },
11919     { &game.button.pause2,      &game.button.pause      },
11920     { &game.button.load,        &game.button.play       },
11921     { &game.button.undo,        &game.button.stop       },
11922     { &game.button.redo,        &game.button.play       },
11923
11924     { NULL,                     NULL                    }
11925   };
11926   int i, j;
11927
11928   // special case: initialize later added SETUP list size from LEVELS value
11929   if (menu.list_size[GAME_MODE_SETUP] == -1)
11930     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
11931
11932   // set default position for snapshot buttons to stop/pause/play buttons
11933   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
11934     if ((*game_buttons_xy[i].dst).x == -1 &&
11935         (*game_buttons_xy[i].dst).y == -1)
11936       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
11937
11938   // --------------------------------------------------------------------------
11939   // dynamic viewports (including playfield margins, borders and alignments)
11940   // --------------------------------------------------------------------------
11941
11942   // dynamic viewports currently only supported for landscape mode
11943   int display_width  = MAX(video.display_width, video.display_height);
11944   int display_height = MIN(video.display_width, video.display_height);
11945
11946   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11947   {
11948     struct RectWithBorder *vp_window    = &viewport.window[i];
11949     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
11950     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
11951     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
11952     boolean dynamic_window_width     = (vp_window->min_width     != -1);
11953     boolean dynamic_window_height    = (vp_window->min_height    != -1);
11954     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
11955     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
11956
11957     // adjust window size if min/max width/height is specified
11958
11959     if (vp_window->min_width != -1)
11960     {
11961       int window_width = display_width;
11962
11963       // when using static window height, use aspect ratio of display
11964       if (vp_window->min_height == -1)
11965         window_width = vp_window->height * display_width / display_height;
11966
11967       vp_window->width = MAX(vp_window->min_width, window_width);
11968     }
11969
11970     if (vp_window->min_height != -1)
11971     {
11972       int window_height = display_height;
11973
11974       // when using static window width, use aspect ratio of display
11975       if (vp_window->min_width == -1)
11976         window_height = vp_window->width * display_height / display_width;
11977
11978       vp_window->height = MAX(vp_window->min_height, window_height);
11979     }
11980
11981     if (vp_window->max_width != -1)
11982       vp_window->width = MIN(vp_window->width, vp_window->max_width);
11983
11984     if (vp_window->max_height != -1)
11985       vp_window->height = MIN(vp_window->height, vp_window->max_height);
11986
11987     int playfield_width  = vp_window->width;
11988     int playfield_height = vp_window->height;
11989
11990     // adjust playfield size and position according to specified margins
11991
11992     playfield_width  -= vp_playfield->margin_left;
11993     playfield_width  -= vp_playfield->margin_right;
11994
11995     playfield_height -= vp_playfield->margin_top;
11996     playfield_height -= vp_playfield->margin_bottom;
11997
11998     // adjust playfield size if min/max width/height is specified
11999
12000     if (vp_playfield->min_width != -1)
12001       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12002
12003     if (vp_playfield->min_height != -1)
12004       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12005
12006     if (vp_playfield->max_width != -1)
12007       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12008
12009     if (vp_playfield->max_height != -1)
12010       vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12011
12012     // adjust playfield position according to specified alignment
12013
12014     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12015       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12016     else if (vp_playfield->align == ALIGN_CENTER)
12017       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12018     else if (vp_playfield->align == ALIGN_RIGHT)
12019       vp_playfield->x += playfield_width - vp_playfield->width;
12020
12021     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12022       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12023     else if (vp_playfield->valign == VALIGN_MIDDLE)
12024       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12025     else if (vp_playfield->valign == VALIGN_BOTTOM)
12026       vp_playfield->y += playfield_height - vp_playfield->height;
12027
12028     vp_playfield->x += vp_playfield->margin_left;
12029     vp_playfield->y += vp_playfield->margin_top;
12030
12031     // adjust individual playfield borders if only default border is specified
12032
12033     if (vp_playfield->border_left == -1)
12034       vp_playfield->border_left = vp_playfield->border_size;
12035     if (vp_playfield->border_right == -1)
12036       vp_playfield->border_right = vp_playfield->border_size;
12037     if (vp_playfield->border_top == -1)
12038       vp_playfield->border_top = vp_playfield->border_size;
12039     if (vp_playfield->border_bottom == -1)
12040       vp_playfield->border_bottom = vp_playfield->border_size;
12041
12042     // set dynamic playfield borders if borders are specified as undefined
12043     // (but only if window size was dynamic and playfield size was static)
12044
12045     if (dynamic_window_width && !dynamic_playfield_width)
12046     {
12047       if (vp_playfield->border_left == -1)
12048       {
12049         vp_playfield->border_left = (vp_playfield->x -
12050                                      vp_playfield->margin_left);
12051         vp_playfield->x     -= vp_playfield->border_left;
12052         vp_playfield->width += vp_playfield->border_left;
12053       }
12054
12055       if (vp_playfield->border_right == -1)
12056       {
12057         vp_playfield->border_right = (vp_window->width -
12058                                       vp_playfield->x -
12059                                       vp_playfield->width -
12060                                       vp_playfield->margin_right);
12061         vp_playfield->width += vp_playfield->border_right;
12062       }
12063     }
12064
12065     if (dynamic_window_height && !dynamic_playfield_height)
12066     {
12067       if (vp_playfield->border_top == -1)
12068       {
12069         vp_playfield->border_top = (vp_playfield->y -
12070                                     vp_playfield->margin_top);
12071         vp_playfield->y      -= vp_playfield->border_top;
12072         vp_playfield->height += vp_playfield->border_top;
12073       }
12074
12075       if (vp_playfield->border_bottom == -1)
12076       {
12077         vp_playfield->border_bottom = (vp_window->height -
12078                                        vp_playfield->y -
12079                                        vp_playfield->height -
12080                                        vp_playfield->margin_bottom);
12081         vp_playfield->height += vp_playfield->border_bottom;
12082       }
12083     }
12084
12085     // adjust playfield size to be a multiple of a defined alignment tile size
12086
12087     int align_size = vp_playfield->align_size;
12088     int playfield_xtiles = vp_playfield->width  / align_size;
12089     int playfield_ytiles = vp_playfield->height / align_size;
12090     int playfield_width_corrected  = playfield_xtiles * align_size;
12091     int playfield_height_corrected = playfield_ytiles * align_size;
12092     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12093                                  i == GFX_SPECIAL_ARG_EDITOR);
12094
12095     if (is_playfield_mode &&
12096         dynamic_playfield_width &&
12097         vp_playfield->width != playfield_width_corrected)
12098     {
12099       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12100
12101       vp_playfield->width = playfield_width_corrected;
12102
12103       if (vp_playfield->align == ALIGN_LEFT)
12104       {
12105         vp_playfield->border_left += playfield_xdiff;
12106       }
12107       else if (vp_playfield->align == ALIGN_RIGHT)
12108       {
12109         vp_playfield->border_right += playfield_xdiff;
12110       }
12111       else if (vp_playfield->align == ALIGN_CENTER)
12112       {
12113         int border_left_diff  = playfield_xdiff / 2;
12114         int border_right_diff = playfield_xdiff - border_left_diff;
12115
12116         vp_playfield->border_left  += border_left_diff;
12117         vp_playfield->border_right += border_right_diff;
12118       }
12119     }
12120
12121     if (is_playfield_mode &&
12122         dynamic_playfield_height &&
12123         vp_playfield->height != playfield_height_corrected)
12124     {
12125       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12126
12127       vp_playfield->height = playfield_height_corrected;
12128
12129       if (vp_playfield->valign == VALIGN_TOP)
12130       {
12131         vp_playfield->border_top += playfield_ydiff;
12132       }
12133       else if (vp_playfield->align == VALIGN_BOTTOM)
12134       {
12135         vp_playfield->border_right += playfield_ydiff;
12136       }
12137       else if (vp_playfield->align == VALIGN_MIDDLE)
12138       {
12139         int border_top_diff    = playfield_ydiff / 2;
12140         int border_bottom_diff = playfield_ydiff - border_top_diff;
12141
12142         vp_playfield->border_top    += border_top_diff;
12143         vp_playfield->border_bottom += border_bottom_diff;
12144       }
12145     }
12146
12147     // adjust door positions according to specified alignment
12148
12149     for (j = 0; j < 2; j++)
12150     {
12151       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12152
12153       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12154         vp_door->x = ALIGNED_VP_XPOS(vp_door);
12155       else if (vp_door->align == ALIGN_CENTER)
12156         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12157       else if (vp_door->align == ALIGN_RIGHT)
12158         vp_door->x += vp_window->width - vp_door->width;
12159
12160       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12161         vp_door->y = ALIGNED_VP_YPOS(vp_door);
12162       else if (vp_door->valign == VALIGN_MIDDLE)
12163         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12164       else if (vp_door->valign == VALIGN_BOTTOM)
12165         vp_door->y += vp_window->height - vp_door->height;
12166     }
12167   }
12168 }
12169
12170 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12171 {
12172   static struct
12173   {
12174     struct XYTileSize *dst, *src;
12175     int graphic;
12176   }
12177   editor_buttons_xy[] =
12178   {
12179     {
12180       &editor.button.element_left,      &editor.palette.element_left,
12181       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12182     },
12183     {
12184       &editor.button.element_middle,    &editor.palette.element_middle,
12185       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12186     },
12187     {
12188       &editor.button.element_right,     &editor.palette.element_right,
12189       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12190     },
12191
12192     { NULL,                     NULL                    }
12193   };
12194   int i;
12195
12196   // set default position for element buttons to element graphics
12197   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12198   {
12199     if ((*editor_buttons_xy[i].dst).x == -1 &&
12200         (*editor_buttons_xy[i].dst).y == -1)
12201     {
12202       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12203
12204       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12205
12206       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12207     }
12208   }
12209
12210   // adjust editor palette rows and columns if specified to be dynamic
12211
12212   if (editor.palette.cols == -1)
12213   {
12214     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12215     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12216     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12217
12218     editor.palette.cols = (vp_width - sc_width) / bt_width;
12219
12220     if (editor.palette.x == -1)
12221     {
12222       int palette_width = editor.palette.cols * bt_width + sc_width;
12223
12224       editor.palette.x = (vp_width - palette_width) / 2;
12225     }
12226   }
12227
12228   if (editor.palette.rows == -1)
12229   {
12230     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12231     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12232     int tx_height = getFontHeight(FONT_TEXT_2);
12233
12234     editor.palette.rows = (vp_height - tx_height) / bt_height;
12235
12236     if (editor.palette.y == -1)
12237     {
12238       int palette_height = editor.palette.rows * bt_height + tx_height;
12239
12240       editor.palette.y = (vp_height - palette_height) / 2;
12241     }
12242   }
12243 }
12244
12245 static void LoadMenuDesignSettingsFromFilename(char *filename)
12246 {
12247   static struct TitleFadingInfo tfi;
12248   static struct TitleMessageInfo tmi;
12249   static struct TokenInfo title_tokens[] =
12250   {
12251     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
12252     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
12253     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
12254     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
12255     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
12256
12257     { -1,               NULL,                   NULL                    }
12258   };
12259   static struct TokenInfo titlemessage_tokens[] =
12260   {
12261     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
12262     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
12263     { TYPE_INTEGER,     &tmi.width,             ".width"                },
12264     { TYPE_INTEGER,     &tmi.height,            ".height"               },
12265     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
12266     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
12267     { TYPE_INTEGER,     &tmi.align,             ".align"                },
12268     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
12269     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
12270     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
12271     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
12272     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
12273     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
12274     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
12275     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
12276     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
12277     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
12278     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
12279
12280     { -1,               NULL,                   NULL                    }
12281   };
12282   static struct
12283   {
12284     struct TitleFadingInfo *info;
12285     char *text;
12286   }
12287   title_info[] =
12288   {
12289     // initialize first titles from "enter screen" definitions, if defined
12290     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
12291     { &title_first_default,             "menu.enter_screen.TITLE"       },
12292
12293     // initialize title screens from "next screen" definitions, if defined
12294     { &title_initial_default,           "menu.next_screen.TITLE"        },
12295     { &title_default,                   "menu.next_screen.TITLE"        },
12296
12297     { NULL,                             NULL                            }
12298   };
12299   static struct
12300   {
12301     struct TitleMessageInfo *array;
12302     char *text;
12303   }
12304   titlemessage_arrays[] =
12305   {
12306     // initialize first titles from "enter screen" definitions, if defined
12307     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
12308     { titlescreen_first,                "menu.enter_screen.TITLE"       },
12309     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
12310     { titlemessage_first,               "menu.enter_screen.TITLE"       },
12311
12312     // initialize titles from "next screen" definitions, if defined
12313     { titlescreen_initial,              "menu.next_screen.TITLE"        },
12314     { titlescreen,                      "menu.next_screen.TITLE"        },
12315     { titlemessage_initial,             "menu.next_screen.TITLE"        },
12316     { titlemessage,                     "menu.next_screen.TITLE"        },
12317
12318     // overwrite titles with title definitions, if defined
12319     { titlescreen_initial_first,        "[title_initial]"               },
12320     { titlescreen_first,                "[title]"                       },
12321     { titlemessage_initial_first,       "[title_initial]"               },
12322     { titlemessage_first,               "[title]"                       },
12323
12324     { titlescreen_initial,              "[title_initial]"               },
12325     { titlescreen,                      "[title]"                       },
12326     { titlemessage_initial,             "[title_initial]"               },
12327     { titlemessage,                     "[title]"                       },
12328
12329     // overwrite titles with title screen/message definitions, if defined
12330     { titlescreen_initial_first,        "[titlescreen_initial]"         },
12331     { titlescreen_first,                "[titlescreen]"                 },
12332     { titlemessage_initial_first,       "[titlemessage_initial]"        },
12333     { titlemessage_first,               "[titlemessage]"                },
12334
12335     { titlescreen_initial,              "[titlescreen_initial]"         },
12336     { titlescreen,                      "[titlescreen]"                 },
12337     { titlemessage_initial,             "[titlemessage_initial]"        },
12338     { titlemessage,                     "[titlemessage]"                },
12339
12340     { NULL,                             NULL                            }
12341   };
12342   SetupFileHash *setup_file_hash;
12343   int i, j, k;
12344
12345   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12346     return;
12347
12348   // the following initializes hierarchical values from dynamic configuration
12349
12350   // special case: initialize with default values that may be overwritten
12351   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12352   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12353   {
12354     struct TokenIntPtrInfo menu_config[] =
12355     {
12356       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
12357       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
12358       { "menu.list_size",       &menu.list_size[i]      }
12359     };
12360
12361     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12362     {
12363       char *token = menu_config[j].token;
12364       char *value = getHashEntry(setup_file_hash, token);
12365
12366       if (value != NULL)
12367         *menu_config[j].value = get_integer_from_string(value);
12368     }
12369   }
12370
12371   // special case: initialize with default values that may be overwritten
12372   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12373   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12374   {
12375     struct TokenIntPtrInfo menu_config[] =
12376     {
12377       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
12378       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
12379       { "menu.list_size.INFO",          &menu.list_size_info[i]         }
12380     };
12381
12382     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12383     {
12384       char *token = menu_config[j].token;
12385       char *value = getHashEntry(setup_file_hash, token);
12386
12387       if (value != NULL)
12388         *menu_config[j].value = get_integer_from_string(value);
12389     }
12390   }
12391
12392   // special case: initialize with default values that may be overwritten
12393   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12394   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12395   {
12396     struct TokenIntPtrInfo menu_config[] =
12397     {
12398       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
12399       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
12400     };
12401
12402     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12403     {
12404       char *token = menu_config[j].token;
12405       char *value = getHashEntry(setup_file_hash, token);
12406
12407       if (value != NULL)
12408         *menu_config[j].value = get_integer_from_string(value);
12409     }
12410   }
12411
12412   // special case: initialize with default values that may be overwritten
12413   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12414   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12415   {
12416     struct TokenIntPtrInfo menu_config[] =
12417     {
12418       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
12419       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
12420       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
12421       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
12422       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
12423       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
12424       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
12425       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
12426       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
12427     };
12428
12429     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12430     {
12431       char *token = menu_config[j].token;
12432       char *value = getHashEntry(setup_file_hash, token);
12433
12434       if (value != NULL)
12435         *menu_config[j].value = get_integer_from_string(value);
12436     }
12437   }
12438
12439   // special case: initialize with default values that may be overwritten
12440   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12441   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12442   {
12443     struct TokenIntPtrInfo menu_config[] =
12444     {
12445       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
12446       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12447       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12448       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
12449       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12450       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12451       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
12452       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
12453       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
12454     };
12455
12456     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12457     {
12458       char *token = menu_config[j].token;
12459       char *value = getHashEntry(setup_file_hash, token);
12460
12461       if (value != NULL)
12462         *menu_config[j].value = get_token_parameter_value(token, value);
12463     }
12464   }
12465
12466   // special case: initialize with default values that may be overwritten
12467   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12468   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12469   {
12470     struct
12471     {
12472       char *token_prefix;
12473       struct RectWithBorder *struct_ptr;
12474     }
12475     vp_struct[] =
12476     {
12477       { "viewport.window",      &viewport.window[i]     },
12478       { "viewport.playfield",   &viewport.playfield[i]  },
12479       { "viewport.door_1",      &viewport.door_1[i]     },
12480       { "viewport.door_2",      &viewport.door_2[i]     }
12481     };
12482
12483     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12484     {
12485       struct TokenIntPtrInfo vp_config[] =
12486       {
12487         { ".x",                 &vp_struct[j].struct_ptr->x             },
12488         { ".y",                 &vp_struct[j].struct_ptr->y             },
12489         { ".width",             &vp_struct[j].struct_ptr->width         },
12490         { ".height",            &vp_struct[j].struct_ptr->height        },
12491         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
12492         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
12493         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
12494         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
12495         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
12496         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
12497         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
12498         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
12499         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
12500         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
12501         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
12502         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
12503         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
12504         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
12505         { ".align",             &vp_struct[j].struct_ptr->align         },
12506         { ".valign",            &vp_struct[j].struct_ptr->valign        }
12507       };
12508
12509       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12510       {
12511         char *token = getStringCat2(vp_struct[j].token_prefix,
12512                                     vp_config[k].token);
12513         char *value = getHashEntry(setup_file_hash, token);
12514
12515         if (value != NULL)
12516           *vp_config[k].value = get_token_parameter_value(token, value);
12517
12518         free(token);
12519       }
12520     }
12521   }
12522
12523   // special case: initialize with default values that may be overwritten
12524   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12525   for (i = 0; title_info[i].info != NULL; i++)
12526   {
12527     struct TitleFadingInfo *info = title_info[i].info;
12528     char *base_token = title_info[i].text;
12529
12530     for (j = 0; title_tokens[j].type != -1; j++)
12531     {
12532       char *token = getStringCat2(base_token, title_tokens[j].text);
12533       char *value = getHashEntry(setup_file_hash, token);
12534
12535       if (value != NULL)
12536       {
12537         int parameter_value = get_token_parameter_value(token, value);
12538
12539         tfi = *info;
12540
12541         *(int *)title_tokens[j].value = (int)parameter_value;
12542
12543         *info = tfi;
12544       }
12545
12546       free(token);
12547     }
12548   }
12549
12550   // special case: initialize with default values that may be overwritten
12551   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12552   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12553   {
12554     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12555     char *base_token = titlemessage_arrays[i].text;
12556
12557     for (j = 0; titlemessage_tokens[j].type != -1; j++)
12558     {
12559       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12560       char *value = getHashEntry(setup_file_hash, token);
12561
12562       if (value != NULL)
12563       {
12564         int parameter_value = get_token_parameter_value(token, value);
12565
12566         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12567         {
12568           tmi = array[k];
12569
12570           if (titlemessage_tokens[j].type == TYPE_INTEGER)
12571             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
12572           else
12573             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12574
12575           array[k] = tmi;
12576         }
12577       }
12578
12579       free(token);
12580     }
12581   }
12582
12583   // special case: check if network and preview player positions are redefined,
12584   // to compare this later against the main menu level preview being redefined
12585   struct TokenIntPtrInfo menu_config_players[] =
12586   {
12587     { "main.network_players.x", &menu.main.network_players.redefined    },
12588     { "main.network_players.y", &menu.main.network_players.redefined    },
12589     { "main.preview_players.x", &menu.main.preview_players.redefined    },
12590     { "main.preview_players.y", &menu.main.preview_players.redefined    },
12591     { "preview.x",              &preview.redefined                      },
12592     { "preview.y",              &preview.redefined                      }
12593   };
12594
12595   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12596     *menu_config_players[i].value = FALSE;
12597
12598   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12599     if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
12600       *menu_config_players[i].value = TRUE;
12601
12602   // read (and overwrite with) values that may be specified in config file
12603   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12604
12605   freeSetupFileHash(setup_file_hash);
12606 }
12607
12608 void LoadMenuDesignSettings(void)
12609 {
12610   char *filename_base = UNDEFINED_FILENAME, *filename_local;
12611
12612   InitMenuDesignSettings_Static();
12613   InitMenuDesignSettings_SpecialPreProcessing();
12614
12615   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12616   {
12617     // first look for special settings configured in level series config
12618     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12619
12620     if (fileExists(filename_base))
12621       LoadMenuDesignSettingsFromFilename(filename_base);
12622   }
12623
12624   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12625
12626   if (filename_local != NULL && !strEqual(filename_base, filename_local))
12627     LoadMenuDesignSettingsFromFilename(filename_local);
12628
12629   InitMenuDesignSettings_SpecialPostProcessing();
12630 }
12631
12632 void LoadMenuDesignSettings_AfterGraphics(void)
12633 {
12634   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12635 }
12636
12637 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12638 {
12639   char *filename = getEditorSetupFilename();
12640   SetupFileList *setup_file_list, *list;
12641   SetupFileHash *element_hash;
12642   int num_unknown_tokens = 0;
12643   int i;
12644
12645   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12646     return;
12647
12648   element_hash = newSetupFileHash();
12649
12650   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12651     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12652
12653   // determined size may be larger than needed (due to unknown elements)
12654   *num_elements = 0;
12655   for (list = setup_file_list; list != NULL; list = list->next)
12656     (*num_elements)++;
12657
12658   // add space for up to 3 more elements for padding that may be needed
12659   *num_elements += 3;
12660
12661   // free memory for old list of elements, if needed
12662   checked_free(*elements);
12663
12664   // allocate memory for new list of elements
12665   *elements = checked_malloc(*num_elements * sizeof(int));
12666
12667   *num_elements = 0;
12668   for (list = setup_file_list; list != NULL; list = list->next)
12669   {
12670     char *value = getHashEntry(element_hash, list->token);
12671
12672     if (value == NULL)          // try to find obsolete token mapping
12673     {
12674       char *mapped_token = get_mapped_token(list->token);
12675
12676       if (mapped_token != NULL)
12677       {
12678         value = getHashEntry(element_hash, mapped_token);
12679
12680         free(mapped_token);
12681       }
12682     }
12683
12684     if (value != NULL)
12685     {
12686       (*elements)[(*num_elements)++] = atoi(value);
12687     }
12688     else
12689     {
12690       if (num_unknown_tokens == 0)
12691       {
12692         Warn("---");
12693         Warn("unknown token(s) found in config file:");
12694         Warn("- config file: '%s'", filename);
12695
12696         num_unknown_tokens++;
12697       }
12698
12699       Warn("- token: '%s'", list->token);
12700     }
12701   }
12702
12703   if (num_unknown_tokens > 0)
12704     Warn("---");
12705
12706   while (*num_elements % 4)     // pad with empty elements, if needed
12707     (*elements)[(*num_elements)++] = EL_EMPTY;
12708
12709   freeSetupFileList(setup_file_list);
12710   freeSetupFileHash(element_hash);
12711
12712 #if 0
12713   for (i = 0; i < *num_elements; i++)
12714     Debug("editor", "element '%s' [%d]\n",
12715           element_info[(*elements)[i]].token_name, (*elements)[i]);
12716 #endif
12717 }
12718
12719 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12720                                                      boolean is_sound)
12721 {
12722   SetupFileHash *setup_file_hash = NULL;
12723   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12724   char *filename_music, *filename_prefix, *filename_info;
12725   struct
12726   {
12727     char *token;
12728     char **value_ptr;
12729   }
12730   token_to_value_ptr[] =
12731   {
12732     { "title_header",   &tmp_music_file_info.title_header       },
12733     { "artist_header",  &tmp_music_file_info.artist_header      },
12734     { "album_header",   &tmp_music_file_info.album_header       },
12735     { "year_header",    &tmp_music_file_info.year_header        },
12736
12737     { "title",          &tmp_music_file_info.title              },
12738     { "artist",         &tmp_music_file_info.artist             },
12739     { "album",          &tmp_music_file_info.album              },
12740     { "year",           &tmp_music_file_info.year               },
12741
12742     { NULL,             NULL                                    },
12743   };
12744   int i;
12745
12746   filename_music = (is_sound ? getCustomSoundFilename(basename) :
12747                     getCustomMusicFilename(basename));
12748
12749   if (filename_music == NULL)
12750     return NULL;
12751
12752   // ---------- try to replace file extension ----------
12753
12754   filename_prefix = getStringCopy(filename_music);
12755   if (strrchr(filename_prefix, '.') != NULL)
12756     *strrchr(filename_prefix, '.') = '\0';
12757   filename_info = getStringCat2(filename_prefix, ".txt");
12758
12759   if (fileExists(filename_info))
12760     setup_file_hash = loadSetupFileHash(filename_info);
12761
12762   free(filename_prefix);
12763   free(filename_info);
12764
12765   if (setup_file_hash == NULL)
12766   {
12767     // ---------- try to add file extension ----------
12768
12769     filename_prefix = getStringCopy(filename_music);
12770     filename_info = getStringCat2(filename_prefix, ".txt");
12771
12772     if (fileExists(filename_info))
12773       setup_file_hash = loadSetupFileHash(filename_info);
12774
12775     free(filename_prefix);
12776     free(filename_info);
12777   }
12778
12779   if (setup_file_hash == NULL)
12780     return NULL;
12781
12782   // ---------- music file info found ----------
12783
12784   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
12785
12786   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
12787   {
12788     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
12789
12790     *token_to_value_ptr[i].value_ptr =
12791       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
12792   }
12793
12794   tmp_music_file_info.basename = getStringCopy(basename);
12795   tmp_music_file_info.music = music;
12796   tmp_music_file_info.is_sound = is_sound;
12797
12798   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
12799   *new_music_file_info = tmp_music_file_info;
12800
12801   return new_music_file_info;
12802 }
12803
12804 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
12805 {
12806   return get_music_file_info_ext(basename, music, FALSE);
12807 }
12808
12809 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
12810 {
12811   return get_music_file_info_ext(basename, sound, TRUE);
12812 }
12813
12814 static boolean music_info_listed_ext(struct MusicFileInfo *list,
12815                                      char *basename, boolean is_sound)
12816 {
12817   for (; list != NULL; list = list->next)
12818     if (list->is_sound == is_sound && strEqual(list->basename, basename))
12819       return TRUE;
12820
12821   return FALSE;
12822 }
12823
12824 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
12825 {
12826   return music_info_listed_ext(list, basename, FALSE);
12827 }
12828
12829 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
12830 {
12831   return music_info_listed_ext(list, basename, TRUE);
12832 }
12833
12834 void LoadMusicInfo(void)
12835 {
12836   char *music_directory = getCustomMusicDirectory();
12837   int num_music = getMusicListSize();
12838   int num_music_noconf = 0;
12839   int num_sounds = getSoundListSize();
12840   Directory *dir;
12841   DirectoryEntry *dir_entry;
12842   struct FileInfo *music, *sound;
12843   struct MusicFileInfo *next, **new;
12844   int i;
12845
12846   while (music_file_info != NULL)
12847   {
12848     next = music_file_info->next;
12849
12850     checked_free(music_file_info->basename);
12851
12852     checked_free(music_file_info->title_header);
12853     checked_free(music_file_info->artist_header);
12854     checked_free(music_file_info->album_header);
12855     checked_free(music_file_info->year_header);
12856
12857     checked_free(music_file_info->title);
12858     checked_free(music_file_info->artist);
12859     checked_free(music_file_info->album);
12860     checked_free(music_file_info->year);
12861
12862     free(music_file_info);
12863
12864     music_file_info = next;
12865   }
12866
12867   new = &music_file_info;
12868
12869   for (i = 0; i < num_music; i++)
12870   {
12871     music = getMusicListEntry(i);
12872
12873     if (music->filename == NULL)
12874       continue;
12875
12876     if (strEqual(music->filename, UNDEFINED_FILENAME))
12877       continue;
12878
12879     // a configured file may be not recognized as music
12880     if (!FileIsMusic(music->filename))
12881       continue;
12882
12883     if (!music_info_listed(music_file_info, music->filename))
12884     {
12885       *new = get_music_file_info(music->filename, i);
12886
12887       if (*new != NULL)
12888         new = &(*new)->next;
12889     }
12890   }
12891
12892   if ((dir = openDirectory(music_directory)) == NULL)
12893   {
12894     Warn("cannot read music directory '%s'", music_directory);
12895
12896     return;
12897   }
12898
12899   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
12900   {
12901     char *basename = dir_entry->basename;
12902     boolean music_already_used = FALSE;
12903     int i;
12904
12905     // skip all music files that are configured in music config file
12906     for (i = 0; i < num_music; i++)
12907     {
12908       music = getMusicListEntry(i);
12909
12910       if (music->filename == NULL)
12911         continue;
12912
12913       if (strEqual(basename, music->filename))
12914       {
12915         music_already_used = TRUE;
12916         break;
12917       }
12918     }
12919
12920     if (music_already_used)
12921       continue;
12922
12923     if (!FileIsMusic(dir_entry->filename))
12924       continue;
12925
12926     if (!music_info_listed(music_file_info, basename))
12927     {
12928       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12929
12930       if (*new != NULL)
12931         new = &(*new)->next;
12932     }
12933
12934     num_music_noconf++;
12935   }
12936
12937   closeDirectory(dir);
12938
12939   for (i = 0; i < num_sounds; i++)
12940   {
12941     sound = getSoundListEntry(i);
12942
12943     if (sound->filename == NULL)
12944       continue;
12945
12946     if (strEqual(sound->filename, UNDEFINED_FILENAME))
12947       continue;
12948
12949     // a configured file may be not recognized as sound
12950     if (!FileIsSound(sound->filename))
12951       continue;
12952
12953     if (!sound_info_listed(music_file_info, sound->filename))
12954     {
12955       *new = get_sound_file_info(sound->filename, i);
12956       if (*new != NULL)
12957         new = &(*new)->next;
12958     }
12959   }
12960
12961   // add pointers to previous list nodes
12962
12963   struct MusicFileInfo *node = music_file_info;
12964
12965   while (node != NULL)
12966   {
12967     if (node->next)
12968       node->next->prev = node;
12969
12970     node = node->next;
12971   }
12972 }
12973
12974 static void add_helpanim_entry(int element, int action, int direction,
12975                                int delay, int *num_list_entries)
12976 {
12977   struct HelpAnimInfo *new_list_entry;
12978   (*num_list_entries)++;
12979
12980   helpanim_info =
12981     checked_realloc(helpanim_info,
12982                     *num_list_entries * sizeof(struct HelpAnimInfo));
12983   new_list_entry = &helpanim_info[*num_list_entries - 1];
12984
12985   new_list_entry->element = element;
12986   new_list_entry->action = action;
12987   new_list_entry->direction = direction;
12988   new_list_entry->delay = delay;
12989 }
12990
12991 static void print_unknown_token(char *filename, char *token, int token_nr)
12992 {
12993   if (token_nr == 0)
12994   {
12995     Warn("---");
12996     Warn("unknown token(s) found in config file:");
12997     Warn("- config file: '%s'", filename);
12998   }
12999
13000   Warn("- token: '%s'", token);
13001 }
13002
13003 static void print_unknown_token_end(int token_nr)
13004 {
13005   if (token_nr > 0)
13006     Warn("---");
13007 }
13008
13009 void LoadHelpAnimInfo(void)
13010 {
13011   char *filename = getHelpAnimFilename();
13012   SetupFileList *setup_file_list = NULL, *list;
13013   SetupFileHash *element_hash, *action_hash, *direction_hash;
13014   int num_list_entries = 0;
13015   int num_unknown_tokens = 0;
13016   int i;
13017
13018   if (fileExists(filename))
13019     setup_file_list = loadSetupFileList(filename);
13020
13021   if (setup_file_list == NULL)
13022   {
13023     // use reliable default values from static configuration
13024     SetupFileList *insert_ptr;
13025
13026     insert_ptr = setup_file_list =
13027       newSetupFileList(helpanim_config[0].token,
13028                        helpanim_config[0].value);
13029
13030     for (i = 1; helpanim_config[i].token; i++)
13031       insert_ptr = addListEntry(insert_ptr,
13032                                 helpanim_config[i].token,
13033                                 helpanim_config[i].value);
13034   }
13035
13036   element_hash   = newSetupFileHash();
13037   action_hash    = newSetupFileHash();
13038   direction_hash = newSetupFileHash();
13039
13040   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13041     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13042
13043   for (i = 0; i < NUM_ACTIONS; i++)
13044     setHashEntry(action_hash, element_action_info[i].suffix,
13045                  i_to_a(element_action_info[i].value));
13046
13047   // do not store direction index (bit) here, but direction value!
13048   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13049     setHashEntry(direction_hash, element_direction_info[i].suffix,
13050                  i_to_a(1 << element_direction_info[i].value));
13051
13052   for (list = setup_file_list; list != NULL; list = list->next)
13053   {
13054     char *element_token, *action_token, *direction_token;
13055     char *element_value, *action_value, *direction_value;
13056     int delay = atoi(list->value);
13057
13058     if (strEqual(list->token, "end"))
13059     {
13060       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13061
13062       continue;
13063     }
13064
13065     /* first try to break element into element/action/direction parts;
13066        if this does not work, also accept combined "element[.act][.dir]"
13067        elements (like "dynamite.active"), which are unique elements */
13068
13069     if (strchr(list->token, '.') == NULL)       // token contains no '.'
13070     {
13071       element_value = getHashEntry(element_hash, list->token);
13072       if (element_value != NULL)        // element found
13073         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13074                            &num_list_entries);
13075       else
13076       {
13077         // no further suffixes found -- this is not an element
13078         print_unknown_token(filename, list->token, num_unknown_tokens++);
13079       }
13080
13081       continue;
13082     }
13083
13084     // token has format "<prefix>.<something>"
13085
13086     action_token = strchr(list->token, '.');    // suffix may be action ...
13087     direction_token = action_token;             // ... or direction
13088
13089     element_token = getStringCopy(list->token);
13090     *strchr(element_token, '.') = '\0';
13091
13092     element_value = getHashEntry(element_hash, element_token);
13093
13094     if (element_value == NULL)          // this is no element
13095     {
13096       element_value = getHashEntry(element_hash, list->token);
13097       if (element_value != NULL)        // combined element found
13098         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13099                            &num_list_entries);
13100       else
13101         print_unknown_token(filename, list->token, num_unknown_tokens++);
13102
13103       free(element_token);
13104
13105       continue;
13106     }
13107
13108     action_value = getHashEntry(action_hash, action_token);
13109
13110     if (action_value != NULL)           // action found
13111     {
13112       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13113                     &num_list_entries);
13114
13115       free(element_token);
13116
13117       continue;
13118     }
13119
13120     direction_value = getHashEntry(direction_hash, direction_token);
13121
13122     if (direction_value != NULL)        // direction found
13123     {
13124       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13125                          &num_list_entries);
13126
13127       free(element_token);
13128
13129       continue;
13130     }
13131
13132     if (strchr(action_token + 1, '.') == NULL)
13133     {
13134       // no further suffixes found -- this is not an action nor direction
13135
13136       element_value = getHashEntry(element_hash, list->token);
13137       if (element_value != NULL)        // combined element found
13138         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13139                            &num_list_entries);
13140       else
13141         print_unknown_token(filename, list->token, num_unknown_tokens++);
13142
13143       free(element_token);
13144
13145       continue;
13146     }
13147
13148     // token has format "<prefix>.<suffix>.<something>"
13149
13150     direction_token = strchr(action_token + 1, '.');
13151
13152     action_token = getStringCopy(action_token);
13153     *strchr(action_token + 1, '.') = '\0';
13154
13155     action_value = getHashEntry(action_hash, action_token);
13156
13157     if (action_value == NULL)           // this is no action
13158     {
13159       element_value = getHashEntry(element_hash, list->token);
13160       if (element_value != NULL)        // combined element found
13161         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13162                            &num_list_entries);
13163       else
13164         print_unknown_token(filename, list->token, num_unknown_tokens++);
13165
13166       free(element_token);
13167       free(action_token);
13168
13169       continue;
13170     }
13171
13172     direction_value = getHashEntry(direction_hash, direction_token);
13173
13174     if (direction_value != NULL)        // direction found
13175     {
13176       add_helpanim_entry(atoi(element_value), atoi(action_value),
13177                          atoi(direction_value), delay, &num_list_entries);
13178
13179       free(element_token);
13180       free(action_token);
13181
13182       continue;
13183     }
13184
13185     // this is no direction
13186
13187     element_value = getHashEntry(element_hash, list->token);
13188     if (element_value != NULL)          // combined element found
13189       add_helpanim_entry(atoi(element_value), -1, -1, delay,
13190                          &num_list_entries);
13191     else
13192       print_unknown_token(filename, list->token, num_unknown_tokens++);
13193
13194     free(element_token);
13195     free(action_token);
13196   }
13197
13198   print_unknown_token_end(num_unknown_tokens);
13199
13200   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13201   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
13202
13203   freeSetupFileList(setup_file_list);
13204   freeSetupFileHash(element_hash);
13205   freeSetupFileHash(action_hash);
13206   freeSetupFileHash(direction_hash);
13207
13208 #if 0
13209   for (i = 0; i < num_list_entries; i++)
13210     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13211           EL_NAME(helpanim_info[i].element),
13212           helpanim_info[i].element,
13213           helpanim_info[i].action,
13214           helpanim_info[i].direction,
13215           helpanim_info[i].delay);
13216 #endif
13217 }
13218
13219 void LoadHelpTextInfo(void)
13220 {
13221   char *filename = getHelpTextFilename();
13222   int i;
13223
13224   if (helptext_info != NULL)
13225   {
13226     freeSetupFileHash(helptext_info);
13227     helptext_info = NULL;
13228   }
13229
13230   if (fileExists(filename))
13231     helptext_info = loadSetupFileHash(filename);
13232
13233   if (helptext_info == NULL)
13234   {
13235     // use reliable default values from static configuration
13236     helptext_info = newSetupFileHash();
13237
13238     for (i = 0; helptext_config[i].token; i++)
13239       setHashEntry(helptext_info,
13240                    helptext_config[i].token,
13241                    helptext_config[i].value);
13242   }
13243
13244 #if 0
13245   BEGIN_HASH_ITERATION(helptext_info, itr)
13246   {
13247     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13248           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13249   }
13250   END_HASH_ITERATION(hash, itr)
13251 #endif
13252 }
13253
13254
13255 // ----------------------------------------------------------------------------
13256 // convert levels
13257 // ----------------------------------------------------------------------------
13258
13259 #define MAX_NUM_CONVERT_LEVELS          1000
13260
13261 void ConvertLevels(void)
13262 {
13263   static LevelDirTree *convert_leveldir = NULL;
13264   static int convert_level_nr = -1;
13265   static int num_levels_handled = 0;
13266   static int num_levels_converted = 0;
13267   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13268   int i;
13269
13270   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13271                                                global.convert_leveldir);
13272
13273   if (convert_leveldir == NULL)
13274     Fail("no such level identifier: '%s'", global.convert_leveldir);
13275
13276   leveldir_current = convert_leveldir;
13277
13278   if (global.convert_level_nr != -1)
13279   {
13280     convert_leveldir->first_level = global.convert_level_nr;
13281     convert_leveldir->last_level  = global.convert_level_nr;
13282   }
13283
13284   convert_level_nr = convert_leveldir->first_level;
13285
13286   PrintLine("=", 79);
13287   Print("Converting levels\n");
13288   PrintLine("-", 79);
13289   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13290   Print("Level series name:       '%s'\n", convert_leveldir->name);
13291   Print("Level series author:     '%s'\n", convert_leveldir->author);
13292   Print("Number of levels:        %d\n",   convert_leveldir->levels);
13293   PrintLine("=", 79);
13294   Print("\n");
13295
13296   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13297     levels_failed[i] = FALSE;
13298
13299   while (convert_level_nr <= convert_leveldir->last_level)
13300   {
13301     char *level_filename;
13302     boolean new_level;
13303
13304     level_nr = convert_level_nr++;
13305
13306     Print("Level %03d: ", level_nr);
13307
13308     LoadLevel(level_nr);
13309     if (level.no_level_file || level.no_valid_file)
13310     {
13311       Print("(no level)\n");
13312       continue;
13313     }
13314
13315     Print("converting level ... ");
13316
13317 #if 0
13318     // special case: conversion of some EMC levels as requested by ACME
13319     level.game_engine_type = GAME_ENGINE_TYPE_RND;
13320 #endif
13321
13322     level_filename = getDefaultLevelFilename(level_nr);
13323     new_level = !fileExists(level_filename);
13324
13325     if (new_level)
13326     {
13327       SaveLevel(level_nr);
13328
13329       num_levels_converted++;
13330
13331       Print("converted.\n");
13332     }
13333     else
13334     {
13335       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13336         levels_failed[level_nr] = TRUE;
13337
13338       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13339     }
13340
13341     num_levels_handled++;
13342   }
13343
13344   Print("\n");
13345   PrintLine("=", 79);
13346   Print("Number of levels handled: %d\n", num_levels_handled);
13347   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13348          (num_levels_handled ?
13349           num_levels_converted * 100 / num_levels_handled : 0));
13350   PrintLine("-", 79);
13351   Print("Summary (for automatic parsing by scripts):\n");
13352   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13353          convert_leveldir->identifier, num_levels_converted,
13354          num_levels_handled,
13355          (num_levels_handled ?
13356           num_levels_converted * 100 / num_levels_handled : 0));
13357
13358   if (num_levels_handled != num_levels_converted)
13359   {
13360     Print(", FAILED:");
13361     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13362       if (levels_failed[i])
13363         Print(" %03d", i);
13364   }
13365
13366   Print("\n");
13367   PrintLine("=", 79);
13368
13369   CloseAllAndExit(0);
13370 }
13371
13372
13373 // ----------------------------------------------------------------------------
13374 // create and save images for use in level sketches (raw BMP format)
13375 // ----------------------------------------------------------------------------
13376
13377 void CreateLevelSketchImages(void)
13378 {
13379   Bitmap *bitmap1;
13380   Bitmap *bitmap2;
13381   int i;
13382
13383   InitElementPropertiesGfxElement();
13384
13385   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13386   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13387
13388   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13389   {
13390     int element = getMappedElement(i);
13391     char basename1[16];
13392     char basename2[16];
13393     char *filename1;
13394     char *filename2;
13395
13396     sprintf(basename1, "%04d.bmp", i);
13397     sprintf(basename2, "%04ds.bmp", i);
13398
13399     filename1 = getPath2(global.create_sketch_images_dir, basename1);
13400     filename2 = getPath2(global.create_sketch_images_dir, basename2);
13401
13402     DrawSizedElement(0, 0, element, TILESIZE);
13403     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13404
13405     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13406       Fail("cannot save level sketch image file '%s'", filename1);
13407
13408     DrawSizedElement(0, 0, element, MINI_TILESIZE);
13409     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13410
13411     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13412       Fail("cannot save level sketch image file '%s'", filename2);
13413
13414     free(filename1);
13415     free(filename2);
13416
13417     // create corresponding SQL statements (for normal and small images)
13418     if (i < 1000)
13419     {
13420       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13421       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13422     }
13423
13424     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13425     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13426
13427     // optional: create content for forum level sketch demonstration post
13428     if (options.debug)
13429       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13430   }
13431
13432   FreeBitmap(bitmap1);
13433   FreeBitmap(bitmap2);
13434
13435   if (options.debug)
13436     fprintf(stderr, "\n");
13437
13438   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13439
13440   CloseAllAndExit(0);
13441 }
13442
13443
13444 // ----------------------------------------------------------------------------
13445 // create and save images for element collecting animations (raw BMP format)
13446 // ----------------------------------------------------------------------------
13447
13448 static boolean createCollectImage(int element)
13449 {
13450   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13451 }
13452
13453 void CreateCollectElementImages(void)
13454 {
13455   int i, j;
13456   int num_steps = 8;
13457   int anim_frames = num_steps - 1;
13458   int tile_size = TILESIZE;
13459   int anim_width  = tile_size * anim_frames;
13460   int anim_height = tile_size;
13461   int num_collect_images = 0;
13462   int pos_collect_images = 0;
13463
13464   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13465     if (createCollectImage(i))
13466       num_collect_images++;
13467
13468   Info("Creating %d element collecting animation images ...",
13469        num_collect_images);
13470
13471   int dst_width  = anim_width * 2;
13472   int dst_height = anim_height * num_collect_images / 2;
13473   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13474   char *basename_bmp = "RocksCollect.bmp";
13475   char *basename_png = "RocksCollect.png";
13476   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13477   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13478   int len_filename_bmp = strlen(filename_bmp);
13479   int len_filename_png = strlen(filename_png);
13480   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13481   char cmd_convert[max_command_len];
13482
13483   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13484            filename_bmp,
13485            filename_png);
13486
13487   // force using RGBA surface for destination bitmap
13488   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13489                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13490
13491   dst_bitmap->surface =
13492     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13493
13494   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13495   {
13496     if (!createCollectImage(i))
13497       continue;
13498
13499     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13500     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13501     int graphic = el2img(i);
13502     char *token_name = element_info[i].token_name;
13503     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13504     Bitmap *src_bitmap;
13505     int src_x, src_y;
13506
13507     Info("- creating collecting image for '%s' ...", token_name);
13508
13509     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13510
13511     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13512                tile_size, tile_size, 0, 0);
13513
13514     // force using RGBA surface for temporary bitmap (using transparent black)
13515     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13516                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13517
13518     tmp_bitmap->surface =
13519       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13520
13521     tmp_bitmap->surface_masked = tmp_bitmap->surface;
13522
13523     for (j = 0; j < anim_frames; j++)
13524     {
13525       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13526       int frame_size = frame_size_final * num_steps;
13527       int offset = (tile_size - frame_size_final) / 2;
13528       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13529
13530       while (frame_size > frame_size_final)
13531       {
13532         frame_size /= 2;
13533
13534         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13535
13536         FreeBitmap(frame_bitmap);
13537
13538         frame_bitmap = half_bitmap;
13539       }
13540
13541       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13542                        frame_size_final, frame_size_final,
13543                        dst_x + j * tile_size + offset, dst_y + offset);
13544
13545       FreeBitmap(frame_bitmap);
13546     }
13547
13548     tmp_bitmap->surface_masked = NULL;
13549
13550     FreeBitmap(tmp_bitmap);
13551
13552     pos_collect_images++;
13553   }
13554
13555   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13556     Fail("cannot save element collecting image file '%s'", filename_bmp);
13557
13558   FreeBitmap(dst_bitmap);
13559
13560   Info("Converting image file from BMP to PNG ...");
13561
13562   if (system(cmd_convert) != 0)
13563     Fail("converting image file failed");
13564
13565   unlink(filename_bmp);
13566
13567   Info("Done.");
13568
13569   CloseAllAndExit(0);
13570 }
13571
13572
13573 // ----------------------------------------------------------------------------
13574 // create and save images for custom and group elements (raw BMP format)
13575 // ----------------------------------------------------------------------------
13576
13577 void CreateCustomElementImages(char *directory)
13578 {
13579   char *src_basename = "RocksCE-template.ilbm";
13580   char *dst_basename = "RocksCE.bmp";
13581   char *src_filename = getPath2(directory, src_basename);
13582   char *dst_filename = getPath2(directory, dst_basename);
13583   Bitmap *src_bitmap;
13584   Bitmap *bitmap;
13585   int yoffset_ce = 0;
13586   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13587   int i;
13588
13589   InitVideoDefaults();
13590
13591   ReCreateBitmap(&backbuffer, video.width, video.height);
13592
13593   src_bitmap = LoadImage(src_filename);
13594
13595   bitmap = CreateBitmap(TILEX * 16 * 2,
13596                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13597                         DEFAULT_DEPTH);
13598
13599   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13600   {
13601     int x = i % 16;
13602     int y = i / 16;
13603     int ii = i + 1;
13604     int j;
13605
13606     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13607                TILEX * x, TILEY * y + yoffset_ce);
13608
13609     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13610                TILEX, TILEY,
13611                TILEX * x + TILEX * 16,
13612                TILEY * y + yoffset_ce);
13613
13614     for (j = 2; j >= 0; j--)
13615     {
13616       int c = ii % 10;
13617
13618       BlitBitmap(src_bitmap, bitmap,
13619                  TILEX + c * 7, 0, 6, 10,
13620                  TILEX * x + 6 + j * 7,
13621                  TILEY * y + 11 + yoffset_ce);
13622
13623       BlitBitmap(src_bitmap, bitmap,
13624                  TILEX + c * 8, TILEY, 6, 10,
13625                  TILEX * 16 + TILEX * x + 6 + j * 8,
13626                  TILEY * y + 10 + yoffset_ce);
13627
13628       ii /= 10;
13629     }
13630   }
13631
13632   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13633   {
13634     int x = i % 16;
13635     int y = i / 16;
13636     int ii = i + 1;
13637     int j;
13638
13639     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13640                TILEX * x, TILEY * y + yoffset_ge);
13641
13642     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13643                TILEX, TILEY,
13644                TILEX * x + TILEX * 16,
13645                TILEY * y + yoffset_ge);
13646
13647     for (j = 1; j >= 0; j--)
13648     {
13649       int c = ii % 10;
13650
13651       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13652                  TILEX * x + 6 + j * 10,
13653                  TILEY * y + 11 + yoffset_ge);
13654
13655       BlitBitmap(src_bitmap, bitmap,
13656                  TILEX + c * 8, TILEY + 12, 6, 10,
13657                  TILEX * 16 + TILEX * x + 10 + j * 8,
13658                  TILEY * y + 10 + yoffset_ge);
13659
13660       ii /= 10;
13661     }
13662   }
13663
13664   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13665     Fail("cannot save CE graphics file '%s'", dst_filename);
13666
13667   FreeBitmap(bitmap);
13668
13669   CloseAllAndExit(0);
13670 }