307b78895dd8e13c290095e3402f1355a1d7df23
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "tools.h"
23 #include "tape.h"
24 #include "config.h"
25
26 #define ENABLE_UNUSED_CODE      0       // currently unused functions
27 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
28 #define ENABLE_RESERVED_CODE    0       // reserved for later use
29
30 #define CHUNK_ID_LEN            4       // IFF style chunk id length
31 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
32 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
33
34 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
35 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
36
37 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
38 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
39 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
40 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
41 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
42 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
43 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
44 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
45 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
46 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
47 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
48
49 // (element number, number of change pages, change page number)
50 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
51
52 // (element number only)
53 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
54 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
55 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
56
57 // (nothing at all if unchanged)
58 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
59
60 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
61 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
62 #define TAPE_CHUNK_HEAD_UNUSED  1       // unused tape header bytes
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     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3358
3359     xx_change = *change;        // copy change data into temporary buffer
3360
3361     resetEventBits();           // reset bits; change page might have changed
3362
3363     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3364                                             -1, element);
3365
3366     *change = xx_change;
3367
3368     setEventFlagsFromEventBits(change);
3369
3370     if (real_chunk_size >= chunk_size)
3371       break;
3372   }
3373
3374   level->file_has_custom_elements = TRUE;
3375
3376   return real_chunk_size;
3377 }
3378
3379 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3380 {
3381   int element = getMappedElement(getFile16BitBE(file));
3382   int real_chunk_size = 2;
3383   struct ElementInfo *ei = &element_info[element];
3384   struct ElementGroupInfo *group = ei->group;
3385
3386   xx_ei = *ei;          // copy element data into temporary buffer
3387   xx_group = *group;    // copy group data into temporary buffer
3388
3389   while (!checkEndOfFile(file))
3390   {
3391     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3392                                             -1, element);
3393
3394     if (real_chunk_size >= chunk_size)
3395       break;
3396   }
3397
3398   *ei = xx_ei;
3399   *group = xx_group;
3400
3401   level->file_has_custom_elements = TRUE;
3402
3403   return real_chunk_size;
3404 }
3405
3406 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3407 {
3408   int element = getMappedElement(getFile16BitBE(file));
3409   int real_chunk_size = 2;
3410   struct ElementInfo *ei = &element_info[element];
3411
3412   xx_ei = *ei;          // copy element data into temporary buffer
3413
3414   while (!checkEndOfFile(file))
3415   {
3416     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3417                                             -1, element);
3418
3419     if (real_chunk_size >= chunk_size)
3420       break;
3421   }
3422
3423   *ei = xx_ei;
3424
3425   level->file_has_custom_elements = TRUE;
3426
3427   return real_chunk_size;
3428 }
3429
3430 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3431                                       struct LevelFileInfo *level_file_info,
3432                                       boolean level_info_only)
3433 {
3434   char *filename = level_file_info->filename;
3435   char cookie[MAX_LINE_LEN];
3436   char chunk_name[CHUNK_ID_LEN + 1];
3437   int chunk_size;
3438   File *file;
3439
3440   if (!(file = openFile(filename, MODE_READ)))
3441   {
3442     level->no_valid_file = TRUE;
3443     level->no_level_file = TRUE;
3444
3445     if (level_info_only)
3446       return;
3447
3448     Warn("cannot read level '%s' -- using empty level", filename);
3449
3450     if (!setup.editor.use_template_for_new_levels)
3451       return;
3452
3453     // if level file not found, try to initialize level data from template
3454     filename = getGlobalLevelTemplateFilename();
3455
3456     if (!(file = openFile(filename, MODE_READ)))
3457       return;
3458
3459     // default: for empty levels, use level template for custom elements
3460     level->use_custom_template = TRUE;
3461
3462     level->no_valid_file = FALSE;
3463   }
3464
3465   getFileChunkBE(file, chunk_name, NULL);
3466   if (strEqual(chunk_name, "RND1"))
3467   {
3468     getFile32BitBE(file);               // not used
3469
3470     getFileChunkBE(file, chunk_name, NULL);
3471     if (!strEqual(chunk_name, "CAVE"))
3472     {
3473       level->no_valid_file = TRUE;
3474
3475       Warn("unknown format of level file '%s'", filename);
3476
3477       closeFile(file);
3478
3479       return;
3480     }
3481   }
3482   else  // check for pre-2.0 file format with cookie string
3483   {
3484     strcpy(cookie, chunk_name);
3485     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3486       cookie[4] = '\0';
3487     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3488       cookie[strlen(cookie) - 1] = '\0';
3489
3490     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3491     {
3492       level->no_valid_file = TRUE;
3493
3494       Warn("unknown format of level file '%s'", filename);
3495
3496       closeFile(file);
3497
3498       return;
3499     }
3500
3501     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3502     {
3503       level->no_valid_file = TRUE;
3504
3505       Warn("unsupported version of level file '%s'", filename);
3506
3507       closeFile(file);
3508
3509       return;
3510     }
3511
3512     // pre-2.0 level files have no game version, so use file version here
3513     level->game_version = level->file_version;
3514   }
3515
3516   if (level->file_version < FILE_VERSION_1_2)
3517   {
3518     // level files from versions before 1.2.0 without chunk structure
3519     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
3520     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3521   }
3522   else
3523   {
3524     static struct
3525     {
3526       char *name;
3527       int size;
3528       int (*loader)(File *, int, struct LevelInfo *);
3529     }
3530     chunk_info[] =
3531     {
3532       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
3533       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
3534       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
3535       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
3536       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
3537       { "INFO", -1,                     LoadLevel_INFO },
3538       { "BODY", -1,                     LoadLevel_BODY },
3539       { "CONT", -1,                     LoadLevel_CONT },
3540       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
3541       { "CNT3", -1,                     LoadLevel_CNT3 },
3542       { "CUS1", -1,                     LoadLevel_CUS1 },
3543       { "CUS2", -1,                     LoadLevel_CUS2 },
3544       { "CUS3", -1,                     LoadLevel_CUS3 },
3545       { "CUS4", -1,                     LoadLevel_CUS4 },
3546       { "GRP1", -1,                     LoadLevel_GRP1 },
3547       { "CONF", -1,                     LoadLevel_CONF },
3548       { "ELEM", -1,                     LoadLevel_ELEM },
3549       { "NOTE", -1,                     LoadLevel_NOTE },
3550       { "CUSX", -1,                     LoadLevel_CUSX },
3551       { "GRPX", -1,                     LoadLevel_GRPX },
3552       { "EMPX", -1,                     LoadLevel_EMPX },
3553
3554       {  NULL,  0,                      NULL }
3555     };
3556
3557     while (getFileChunkBE(file, chunk_name, &chunk_size))
3558     {
3559       int i = 0;
3560
3561       while (chunk_info[i].name != NULL &&
3562              !strEqual(chunk_name, chunk_info[i].name))
3563         i++;
3564
3565       if (chunk_info[i].name == NULL)
3566       {
3567         Warn("unknown chunk '%s' in level file '%s'",
3568              chunk_name, filename);
3569
3570         ReadUnusedBytesFromFile(file, chunk_size);
3571       }
3572       else if (chunk_info[i].size != -1 &&
3573                chunk_info[i].size != chunk_size)
3574       {
3575         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3576              chunk_size, chunk_name, filename);
3577
3578         ReadUnusedBytesFromFile(file, chunk_size);
3579       }
3580       else
3581       {
3582         // call function to load this level chunk
3583         int chunk_size_expected =
3584           (chunk_info[i].loader)(file, chunk_size, level);
3585
3586         // the size of some chunks cannot be checked before reading other
3587         // chunks first (like "HEAD" and "BODY") that contain some header
3588         // information, so check them here
3589         if (chunk_size_expected != chunk_size)
3590         {
3591           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3592                chunk_size, chunk_name, filename);
3593         }
3594       }
3595     }
3596   }
3597
3598   closeFile(file);
3599 }
3600
3601
3602 // ----------------------------------------------------------------------------
3603 // functions for loading EM level
3604 // ----------------------------------------------------------------------------
3605
3606 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3607 {
3608   static int ball_xy[8][2] =
3609   {
3610     { 0, 0 },
3611     { 1, 0 },
3612     { 2, 0 },
3613     { 0, 1 },
3614     { 2, 1 },
3615     { 0, 2 },
3616     { 1, 2 },
3617     { 2, 2 },
3618   };
3619   struct LevelInfo_EM *level_em = level->native_em_level;
3620   struct CAVE *cav = level_em->cav;
3621   int i, j, x, y;
3622
3623   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3624   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3625
3626   cav->time_seconds     = level->time;
3627   cav->gems_needed      = level->gems_needed;
3628
3629   cav->emerald_score    = level->score[SC_EMERALD];
3630   cav->diamond_score    = level->score[SC_DIAMOND];
3631   cav->alien_score      = level->score[SC_ROBOT];
3632   cav->tank_score       = level->score[SC_SPACESHIP];
3633   cav->bug_score        = level->score[SC_BUG];
3634   cav->eater_score      = level->score[SC_YAMYAM];
3635   cav->nut_score        = level->score[SC_NUT];
3636   cav->dynamite_score   = level->score[SC_DYNAMITE];
3637   cav->key_score        = level->score[SC_KEY];
3638   cav->exit_score       = level->score[SC_TIME_BONUS];
3639
3640   cav->num_eater_arrays = level->num_yamyam_contents;
3641
3642   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3643     for (y = 0; y < 3; y++)
3644       for (x = 0; x < 3; x++)
3645         cav->eater_array[i][y * 3 + x] =
3646           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3647
3648   cav->amoeba_time              = level->amoeba_speed;
3649   cav->wonderwall_time          = level->time_magic_wall;
3650   cav->wheel_time               = level->time_wheel;
3651
3652   cav->android_move_time        = level->android_move_time;
3653   cav->android_clone_time       = level->android_clone_time;
3654   cav->ball_random              = level->ball_random;
3655   cav->ball_active              = level->ball_active_initial;
3656   cav->ball_time                = level->ball_time;
3657   cav->num_ball_arrays          = level->num_ball_contents;
3658
3659   cav->lenses_score             = level->lenses_score;
3660   cav->magnify_score            = level->magnify_score;
3661   cav->slurp_score              = level->slurp_score;
3662
3663   cav->lenses_time              = level->lenses_time;
3664   cav->magnify_time             = level->magnify_time;
3665
3666   cav->wind_direction =
3667     map_direction_RND_to_EM(level->wind_direction_initial);
3668
3669   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3670     for (j = 0; j < 8; j++)
3671       cav->ball_array[i][j] =
3672         map_element_RND_to_EM_cave(level->ball_content[i].
3673                                    e[ball_xy[j][0]][ball_xy[j][1]]);
3674
3675   map_android_clone_elements_RND_to_EM(level);
3676
3677   // first fill the complete playfield with the empty space element
3678   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3679     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3680       cav->cave[x][y] = Cblank;
3681
3682   // then copy the real level contents from level file into the playfield
3683   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3684   {
3685     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3686
3687     if (level->field[x][y] == EL_AMOEBA_DEAD)
3688       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3689
3690     cav->cave[x][y] = new_element;
3691   }
3692
3693   for (i = 0; i < MAX_PLAYERS; i++)
3694   {
3695     cav->player_x[i] = -1;
3696     cav->player_y[i] = -1;
3697   }
3698
3699   // initialize player positions and delete players from the playfield
3700   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3701   {
3702     if (IS_PLAYER_ELEMENT(level->field[x][y]))
3703     {
3704       int player_nr = GET_PLAYER_NR(level->field[x][y]);
3705
3706       cav->player_x[player_nr] = x;
3707       cav->player_y[player_nr] = y;
3708
3709       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3710     }
3711   }
3712 }
3713
3714 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3715 {
3716   static int ball_xy[8][2] =
3717   {
3718     { 0, 0 },
3719     { 1, 0 },
3720     { 2, 0 },
3721     { 0, 1 },
3722     { 2, 1 },
3723     { 0, 2 },
3724     { 1, 2 },
3725     { 2, 2 },
3726   };
3727   struct LevelInfo_EM *level_em = level->native_em_level;
3728   struct CAVE *cav = level_em->cav;
3729   int i, j, x, y;
3730
3731   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
3732   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3733
3734   level->time        = cav->time_seconds;
3735   level->gems_needed = cav->gems_needed;
3736
3737   sprintf(level->name, "Level %d", level->file_info.nr);
3738
3739   level->score[SC_EMERALD]      = cav->emerald_score;
3740   level->score[SC_DIAMOND]      = cav->diamond_score;
3741   level->score[SC_ROBOT]        = cav->alien_score;
3742   level->score[SC_SPACESHIP]    = cav->tank_score;
3743   level->score[SC_BUG]          = cav->bug_score;
3744   level->score[SC_YAMYAM]       = cav->eater_score;
3745   level->score[SC_NUT]          = cav->nut_score;
3746   level->score[SC_DYNAMITE]     = cav->dynamite_score;
3747   level->score[SC_KEY]          = cav->key_score;
3748   level->score[SC_TIME_BONUS]   = cav->exit_score;
3749
3750   level->num_yamyam_contents    = cav->num_eater_arrays;
3751
3752   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3753     for (y = 0; y < 3; y++)
3754       for (x = 0; x < 3; x++)
3755         level->yamyam_content[i].e[x][y] =
3756           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3757
3758   level->amoeba_speed           = cav->amoeba_time;
3759   level->time_magic_wall        = cav->wonderwall_time;
3760   level->time_wheel             = cav->wheel_time;
3761
3762   level->android_move_time      = cav->android_move_time;
3763   level->android_clone_time     = cav->android_clone_time;
3764   level->ball_random            = cav->ball_random;
3765   level->ball_active_initial    = cav->ball_active;
3766   level->ball_time              = cav->ball_time;
3767   level->num_ball_contents      = cav->num_ball_arrays;
3768
3769   level->lenses_score           = cav->lenses_score;
3770   level->magnify_score          = cav->magnify_score;
3771   level->slurp_score            = cav->slurp_score;
3772
3773   level->lenses_time            = cav->lenses_time;
3774   level->magnify_time           = cav->magnify_time;
3775
3776   level->wind_direction_initial =
3777     map_direction_EM_to_RND(cav->wind_direction);
3778
3779   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3780     for (j = 0; j < 8; j++)
3781       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3782         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3783
3784   map_android_clone_elements_EM_to_RND(level);
3785
3786   // convert the playfield (some elements need special treatment)
3787   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3788   {
3789     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3790
3791     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3792       new_element = EL_AMOEBA_DEAD;
3793
3794     level->field[x][y] = new_element;
3795   }
3796
3797   for (i = 0; i < MAX_PLAYERS; i++)
3798   {
3799     // in case of all players set to the same field, use the first player
3800     int nr = MAX_PLAYERS - i - 1;
3801     int jx = cav->player_x[nr];
3802     int jy = cav->player_y[nr];
3803
3804     if (jx != -1 && jy != -1)
3805       level->field[jx][jy] = EL_PLAYER_1 + nr;
3806   }
3807
3808   // time score is counted for each 10 seconds left in Emerald Mine levels
3809   level->time_score_base = 10;
3810 }
3811
3812
3813 // ----------------------------------------------------------------------------
3814 // functions for loading SP level
3815 // ----------------------------------------------------------------------------
3816
3817 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3818 {
3819   struct LevelInfo_SP *level_sp = level->native_sp_level;
3820   LevelInfoType *header = &level_sp->header;
3821   int i, x, y;
3822
3823   level_sp->width  = level->fieldx;
3824   level_sp->height = level->fieldy;
3825
3826   for (x = 0; x < level->fieldx; x++)
3827     for (y = 0; y < level->fieldy; y++)
3828       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3829
3830   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3831
3832   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3833     header->LevelTitle[i] = level->name[i];
3834   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3835
3836   header->InfotronsNeeded = level->gems_needed;
3837
3838   header->SpecialPortCount = 0;
3839
3840   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3841   {
3842     boolean gravity_port_found = FALSE;
3843     boolean gravity_port_valid = FALSE;
3844     int gravity_port_flag;
3845     int gravity_port_base_element;
3846     int element = level->field[x][y];
3847
3848     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3849         element <= EL_SP_GRAVITY_ON_PORT_UP)
3850     {
3851       gravity_port_found = TRUE;
3852       gravity_port_valid = TRUE;
3853       gravity_port_flag = 1;
3854       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3855     }
3856     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3857              element <= EL_SP_GRAVITY_OFF_PORT_UP)
3858     {
3859       gravity_port_found = TRUE;
3860       gravity_port_valid = TRUE;
3861       gravity_port_flag = 0;
3862       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3863     }
3864     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3865              element <= EL_SP_GRAVITY_PORT_UP)
3866     {
3867       // change R'n'D style gravity inverting special port to normal port
3868       // (there are no gravity inverting ports in native Supaplex engine)
3869
3870       gravity_port_found = TRUE;
3871       gravity_port_valid = FALSE;
3872       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3873     }
3874
3875     if (gravity_port_found)
3876     {
3877       if (gravity_port_valid &&
3878           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3879       {
3880         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3881
3882         port->PortLocation = (y * level->fieldx + x) * 2;
3883         port->Gravity = gravity_port_flag;
3884
3885         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3886
3887         header->SpecialPortCount++;
3888       }
3889       else
3890       {
3891         // change special gravity port to normal port
3892
3893         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3894       }
3895
3896       level_sp->playfield[x][y] = element - EL_SP_START;
3897     }
3898   }
3899 }
3900
3901 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3902 {
3903   struct LevelInfo_SP *level_sp = level->native_sp_level;
3904   LevelInfoType *header = &level_sp->header;
3905   boolean num_invalid_elements = 0;
3906   int i, j, x, y;
3907
3908   level->fieldx = level_sp->width;
3909   level->fieldy = level_sp->height;
3910
3911   for (x = 0; x < level->fieldx; x++)
3912   {
3913     for (y = 0; y < level->fieldy; y++)
3914     {
3915       int element_old = level_sp->playfield[x][y];
3916       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3917
3918       if (element_new == EL_UNKNOWN)
3919       {
3920         num_invalid_elements++;
3921
3922         Debug("level:native:SP", "invalid element %d at position %d, %d",
3923               element_old, x, y);
3924       }
3925
3926       level->field[x][y] = element_new;
3927     }
3928   }
3929
3930   if (num_invalid_elements > 0)
3931     Warn("found %d invalid elements%s", num_invalid_elements,
3932          (!options.debug ? " (use '--debug' for more details)" : ""));
3933
3934   for (i = 0; i < MAX_PLAYERS; i++)
3935     level->initial_player_gravity[i] =
3936       (header->InitialGravity == 1 ? TRUE : FALSE);
3937
3938   // skip leading spaces
3939   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3940     if (header->LevelTitle[i] != ' ')
3941       break;
3942
3943   // copy level title
3944   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3945     level->name[j] = header->LevelTitle[i];
3946   level->name[j] = '\0';
3947
3948   // cut trailing spaces
3949   for (; j > 0; j--)
3950     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3951       level->name[j - 1] = '\0';
3952
3953   level->gems_needed = header->InfotronsNeeded;
3954
3955   for (i = 0; i < header->SpecialPortCount; i++)
3956   {
3957     SpecialPortType *port = &header->SpecialPort[i];
3958     int port_location = port->PortLocation;
3959     int gravity = port->Gravity;
3960     int port_x, port_y, port_element;
3961
3962     port_x = (port_location / 2) % level->fieldx;
3963     port_y = (port_location / 2) / level->fieldx;
3964
3965     if (port_x < 0 || port_x >= level->fieldx ||
3966         port_y < 0 || port_y >= level->fieldy)
3967     {
3968       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3969
3970       continue;
3971     }
3972
3973     port_element = level->field[port_x][port_y];
3974
3975     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3976         port_element > EL_SP_GRAVITY_PORT_UP)
3977     {
3978       Warn("no special port at position (%d, %d)", port_x, port_y);
3979
3980       continue;
3981     }
3982
3983     // change previous (wrong) gravity inverting special port to either
3984     // gravity enabling special port or gravity disabling special port
3985     level->field[port_x][port_y] +=
3986       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3987        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3988   }
3989
3990   // change special gravity ports without database entries to normal ports
3991   for (x = 0; x < level->fieldx; x++)
3992     for (y = 0; y < level->fieldy; y++)
3993       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3994           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3995         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3996
3997   level->time = 0;                      // no time limit
3998   level->amoeba_speed = 0;
3999   level->time_magic_wall = 0;
4000   level->time_wheel = 0;
4001   level->amoeba_content = EL_EMPTY;
4002
4003   // original Supaplex does not use score values -- rate by playing time
4004   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4005     level->score[i] = 0;
4006
4007   level->rate_time_over_score = TRUE;
4008
4009   // there are no yamyams in supaplex levels
4010   for (i = 0; i < level->num_yamyam_contents; i++)
4011     for (x = 0; x < 3; x++)
4012       for (y = 0; y < 3; y++)
4013         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4014 }
4015
4016 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4017 {
4018   struct LevelInfo_SP *level_sp = level->native_sp_level;
4019   struct DemoInfo_SP *demo = &level_sp->demo;
4020   int i, j;
4021
4022   // always start with reliable default values
4023   demo->is_available = FALSE;
4024   demo->length = 0;
4025
4026   if (TAPE_IS_EMPTY(tape))
4027     return;
4028
4029   demo->level_nr = tape.level_nr;       // (currently not used)
4030
4031   level_sp->header.DemoRandomSeed = tape.random_seed;
4032
4033   demo->length = 0;
4034
4035   for (i = 0; i < tape.length; i++)
4036   {
4037     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4038     int demo_repeat = tape.pos[i].delay;
4039     int demo_entries = (demo_repeat + 15) / 16;
4040
4041     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4042     {
4043       Warn("tape truncated: size exceeds maximum SP demo size %d",
4044            SP_MAX_TAPE_LEN);
4045
4046       break;
4047     }
4048
4049     for (j = 0; j < demo_repeat / 16; j++)
4050       demo->data[demo->length++] = 0xf0 | demo_action;
4051
4052     if (demo_repeat % 16)
4053       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4054   }
4055
4056   demo->is_available = TRUE;
4057 }
4058
4059 static void setTapeInfoToDefaults(void);
4060
4061 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4062 {
4063   struct LevelInfo_SP *level_sp = level->native_sp_level;
4064   struct DemoInfo_SP *demo = &level_sp->demo;
4065   char *filename = level->file_info.filename;
4066   int i;
4067
4068   // always start with reliable default values
4069   setTapeInfoToDefaults();
4070
4071   if (!demo->is_available)
4072     return;
4073
4074   tape.level_nr = demo->level_nr;       // (currently not used)
4075   tape.random_seed = level_sp->header.DemoRandomSeed;
4076
4077   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4078
4079   tape.counter = 0;
4080   tape.pos[tape.counter].delay = 0;
4081
4082   for (i = 0; i < demo->length; i++)
4083   {
4084     int demo_action = demo->data[i] & 0x0f;
4085     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4086     int tape_action = map_key_SP_to_RND(demo_action);
4087     int tape_repeat = demo_repeat + 1;
4088     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4089     boolean success = 0;
4090     int j;
4091
4092     for (j = 0; j < tape_repeat; j++)
4093       success = TapeAddAction(action);
4094
4095     if (!success)
4096     {
4097       Warn("SP demo truncated: size exceeds maximum tape size %d",
4098            MAX_TAPE_LEN);
4099
4100       break;
4101     }
4102   }
4103
4104   TapeHaltRecording();
4105 }
4106
4107
4108 // ----------------------------------------------------------------------------
4109 // functions for loading MM level
4110 // ----------------------------------------------------------------------------
4111
4112 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4113 {
4114   struct LevelInfo_MM *level_mm = level->native_mm_level;
4115   int x, y;
4116
4117   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4118   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4119
4120   level_mm->time = level->time;
4121   level_mm->kettles_needed = level->gems_needed;
4122   level_mm->auto_count_kettles = level->auto_count_gems;
4123
4124   level_mm->laser_red = level->mm_laser_red;
4125   level_mm->laser_green = level->mm_laser_green;
4126   level_mm->laser_blue = level->mm_laser_blue;
4127
4128   strcpy(level_mm->name, level->name);
4129   strcpy(level_mm->author, level->author);
4130
4131   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
4132   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
4133   level_mm->score[SC_KEY]        = level->score[SC_KEY];
4134   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4135   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4136
4137   level_mm->amoeba_speed = level->amoeba_speed;
4138   level_mm->time_fuse    = level->mm_time_fuse;
4139   level_mm->time_bomb    = level->mm_time_bomb;
4140   level_mm->time_ball    = level->mm_time_ball;
4141   level_mm->time_block   = level->mm_time_block;
4142
4143   for (x = 0; x < level->fieldx; x++)
4144     for (y = 0; y < level->fieldy; y++)
4145       Ur[x][y] =
4146         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4147 }
4148
4149 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4150 {
4151   struct LevelInfo_MM *level_mm = level->native_mm_level;
4152   int x, y;
4153
4154   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4155   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4156
4157   level->time = level_mm->time;
4158   level->gems_needed = level_mm->kettles_needed;
4159   level->auto_count_gems = level_mm->auto_count_kettles;
4160
4161   level->mm_laser_red = level_mm->laser_red;
4162   level->mm_laser_green = level_mm->laser_green;
4163   level->mm_laser_blue = level_mm->laser_blue;
4164
4165   strcpy(level->name, level_mm->name);
4166
4167   // only overwrite author from 'levelinfo.conf' if author defined in level
4168   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4169     strcpy(level->author, level_mm->author);
4170
4171   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
4172   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
4173   level->score[SC_KEY]        = level_mm->score[SC_KEY];
4174   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4175   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4176
4177   level->amoeba_speed  = level_mm->amoeba_speed;
4178   level->mm_time_fuse  = level_mm->time_fuse;
4179   level->mm_time_bomb  = level_mm->time_bomb;
4180   level->mm_time_ball  = level_mm->time_ball;
4181   level->mm_time_block = level_mm->time_block;
4182
4183   for (x = 0; x < level->fieldx; x++)
4184     for (y = 0; y < level->fieldy; y++)
4185       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4186 }
4187
4188
4189 // ----------------------------------------------------------------------------
4190 // functions for loading DC level
4191 // ----------------------------------------------------------------------------
4192
4193 #define DC_LEVEL_HEADER_SIZE            344
4194
4195 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4196                                         boolean init)
4197 {
4198   static int last_data_encoded;
4199   static int offset1;
4200   static int offset2;
4201   int diff;
4202   int diff_hi, diff_lo;
4203   int data_hi, data_lo;
4204   unsigned short data_decoded;
4205
4206   if (init)
4207   {
4208     last_data_encoded = 0;
4209     offset1 = -1;
4210     offset2 = 0;
4211
4212     return 0;
4213   }
4214
4215   diff = data_encoded - last_data_encoded;
4216   diff_hi = diff & ~0xff;
4217   diff_lo = diff &  0xff;
4218
4219   offset2 += diff_lo;
4220
4221   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4222   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4223   data_hi = data_hi & 0xff00;
4224
4225   data_decoded = data_hi | data_lo;
4226
4227   last_data_encoded = data_encoded;
4228
4229   offset1 = (offset1 + 1) % 31;
4230   offset2 = offset2 & 0xff;
4231
4232   return data_decoded;
4233 }
4234
4235 static int getMappedElement_DC(int element)
4236 {
4237   switch (element)
4238   {
4239     case 0x0000:
4240       element = EL_ROCK;
4241       break;
4242
4243       // 0x0117 - 0x036e: (?)
4244       // EL_DIAMOND
4245
4246       // 0x042d - 0x0684: (?)
4247       // EL_EMERALD
4248
4249     case 0x06f1:
4250       element = EL_NUT;
4251       break;
4252
4253     case 0x074c:
4254       element = EL_BOMB;
4255       break;
4256
4257     case 0x07a4:
4258       element = EL_PEARL;
4259       break;
4260
4261     case 0x0823:
4262       element = EL_CRYSTAL;
4263       break;
4264
4265     case 0x0e77:        // quicksand (boulder)
4266       element = EL_QUICKSAND_FAST_FULL;
4267       break;
4268
4269     case 0x0e99:        // slow quicksand (boulder)
4270       element = EL_QUICKSAND_FULL;
4271       break;
4272
4273     case 0x0ed2:
4274       element = EL_EM_EXIT_OPEN;
4275       break;
4276
4277     case 0x0ee3:
4278       element = EL_EM_EXIT_CLOSED;
4279       break;
4280
4281     case 0x0eeb:
4282       element = EL_EM_STEEL_EXIT_OPEN;
4283       break;
4284
4285     case 0x0efc:
4286       element = EL_EM_STEEL_EXIT_CLOSED;
4287       break;
4288
4289     case 0x0f4f:        // dynamite (lit 1)
4290       element = EL_EM_DYNAMITE_ACTIVE;
4291       break;
4292
4293     case 0x0f57:        // dynamite (lit 2)
4294       element = EL_EM_DYNAMITE_ACTIVE;
4295       break;
4296
4297     case 0x0f5f:        // dynamite (lit 3)
4298       element = EL_EM_DYNAMITE_ACTIVE;
4299       break;
4300
4301     case 0x0f67:        // dynamite (lit 4)
4302       element = EL_EM_DYNAMITE_ACTIVE;
4303       break;
4304
4305     case 0x0f81:
4306     case 0x0f82:
4307     case 0x0f83:
4308     case 0x0f84:
4309       element = EL_AMOEBA_WET;
4310       break;
4311
4312     case 0x0f85:
4313       element = EL_AMOEBA_DROP;
4314       break;
4315
4316     case 0x0fb9:
4317       element = EL_DC_MAGIC_WALL;
4318       break;
4319
4320     case 0x0fd0:
4321       element = EL_SPACESHIP_UP;
4322       break;
4323
4324     case 0x0fd9:
4325       element = EL_SPACESHIP_DOWN;
4326       break;
4327
4328     case 0x0ff1:
4329       element = EL_SPACESHIP_LEFT;
4330       break;
4331
4332     case 0x0ff9:
4333       element = EL_SPACESHIP_RIGHT;
4334       break;
4335
4336     case 0x1057:
4337       element = EL_BUG_UP;
4338       break;
4339
4340     case 0x1060:
4341       element = EL_BUG_DOWN;
4342       break;
4343
4344     case 0x1078:
4345       element = EL_BUG_LEFT;
4346       break;
4347
4348     case 0x1080:
4349       element = EL_BUG_RIGHT;
4350       break;
4351
4352     case 0x10de:
4353       element = EL_MOLE_UP;
4354       break;
4355
4356     case 0x10e7:
4357       element = EL_MOLE_DOWN;
4358       break;
4359
4360     case 0x10ff:
4361       element = EL_MOLE_LEFT;
4362       break;
4363
4364     case 0x1107:
4365       element = EL_MOLE_RIGHT;
4366       break;
4367
4368     case 0x11c0:
4369       element = EL_ROBOT;
4370       break;
4371
4372     case 0x13f5:
4373       element = EL_YAMYAM_UP;
4374       break;
4375
4376     case 0x1425:
4377       element = EL_SWITCHGATE_OPEN;
4378       break;
4379
4380     case 0x1426:
4381       element = EL_SWITCHGATE_CLOSED;
4382       break;
4383
4384     case 0x1437:
4385       element = EL_DC_SWITCHGATE_SWITCH_UP;
4386       break;
4387
4388     case 0x143a:
4389       element = EL_TIMEGATE_CLOSED;
4390       break;
4391
4392     case 0x144c:        // conveyor belt switch (green)
4393       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4394       break;
4395
4396     case 0x144f:        // conveyor belt switch (red)
4397       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4398       break;
4399
4400     case 0x1452:        // conveyor belt switch (blue)
4401       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4402       break;
4403
4404     case 0x145b:
4405       element = EL_CONVEYOR_BELT_3_MIDDLE;
4406       break;
4407
4408     case 0x1463:
4409       element = EL_CONVEYOR_BELT_3_LEFT;
4410       break;
4411
4412     case 0x146b:
4413       element = EL_CONVEYOR_BELT_3_RIGHT;
4414       break;
4415
4416     case 0x1473:
4417       element = EL_CONVEYOR_BELT_1_MIDDLE;
4418       break;
4419
4420     case 0x147b:
4421       element = EL_CONVEYOR_BELT_1_LEFT;
4422       break;
4423
4424     case 0x1483:
4425       element = EL_CONVEYOR_BELT_1_RIGHT;
4426       break;
4427
4428     case 0x148b:
4429       element = EL_CONVEYOR_BELT_4_MIDDLE;
4430       break;
4431
4432     case 0x1493:
4433       element = EL_CONVEYOR_BELT_4_LEFT;
4434       break;
4435
4436     case 0x149b:
4437       element = EL_CONVEYOR_BELT_4_RIGHT;
4438       break;
4439
4440     case 0x14ac:
4441       element = EL_EXPANDABLE_WALL_HORIZONTAL;
4442       break;
4443
4444     case 0x14bd:
4445       element = EL_EXPANDABLE_WALL_VERTICAL;
4446       break;
4447
4448     case 0x14c6:
4449       element = EL_EXPANDABLE_WALL_ANY;
4450       break;
4451
4452     case 0x14ce:        // growing steel wall (left/right)
4453       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4454       break;
4455
4456     case 0x14df:        // growing steel wall (up/down)
4457       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4458       break;
4459
4460     case 0x14e8:        // growing steel wall (up/down/left/right)
4461       element = EL_EXPANDABLE_STEELWALL_ANY;
4462       break;
4463
4464     case 0x14e9:
4465       element = EL_SHIELD_DEADLY;
4466       break;
4467
4468     case 0x1501:
4469       element = EL_EXTRA_TIME;
4470       break;
4471
4472     case 0x154f:
4473       element = EL_ACID;
4474       break;
4475
4476     case 0x1577:
4477       element = EL_EMPTY_SPACE;
4478       break;
4479
4480     case 0x1578:        // quicksand (empty)
4481       element = EL_QUICKSAND_FAST_EMPTY;
4482       break;
4483
4484     case 0x1579:        // slow quicksand (empty)
4485       element = EL_QUICKSAND_EMPTY;
4486       break;
4487
4488       // 0x157c - 0x158b:
4489       // EL_SAND
4490
4491       // 0x1590 - 0x159f:
4492       // EL_DC_LANDMINE
4493
4494     case 0x15a0:
4495       element = EL_EM_DYNAMITE;
4496       break;
4497
4498     case 0x15a1:        // key (red)
4499       element = EL_EM_KEY_1;
4500       break;
4501
4502     case 0x15a2:        // key (yellow)
4503       element = EL_EM_KEY_2;
4504       break;
4505
4506     case 0x15a3:        // key (blue)
4507       element = EL_EM_KEY_4;
4508       break;
4509
4510     case 0x15a4:        // key (green)
4511       element = EL_EM_KEY_3;
4512       break;
4513
4514     case 0x15a5:        // key (white)
4515       element = EL_DC_KEY_WHITE;
4516       break;
4517
4518     case 0x15a6:
4519       element = EL_WALL_SLIPPERY;
4520       break;
4521
4522     case 0x15a7:
4523       element = EL_WALL;
4524       break;
4525
4526     case 0x15a8:        // wall (not round)
4527       element = EL_WALL;
4528       break;
4529
4530     case 0x15a9:        // (blue)
4531       element = EL_CHAR_A;
4532       break;
4533
4534     case 0x15aa:        // (blue)
4535       element = EL_CHAR_B;
4536       break;
4537
4538     case 0x15ab:        // (blue)
4539       element = EL_CHAR_C;
4540       break;
4541
4542     case 0x15ac:        // (blue)
4543       element = EL_CHAR_D;
4544       break;
4545
4546     case 0x15ad:        // (blue)
4547       element = EL_CHAR_E;
4548       break;
4549
4550     case 0x15ae:        // (blue)
4551       element = EL_CHAR_F;
4552       break;
4553
4554     case 0x15af:        // (blue)
4555       element = EL_CHAR_G;
4556       break;
4557
4558     case 0x15b0:        // (blue)
4559       element = EL_CHAR_H;
4560       break;
4561
4562     case 0x15b1:        // (blue)
4563       element = EL_CHAR_I;
4564       break;
4565
4566     case 0x15b2:        // (blue)
4567       element = EL_CHAR_J;
4568       break;
4569
4570     case 0x15b3:        // (blue)
4571       element = EL_CHAR_K;
4572       break;
4573
4574     case 0x15b4:        // (blue)
4575       element = EL_CHAR_L;
4576       break;
4577
4578     case 0x15b5:        // (blue)
4579       element = EL_CHAR_M;
4580       break;
4581
4582     case 0x15b6:        // (blue)
4583       element = EL_CHAR_N;
4584       break;
4585
4586     case 0x15b7:        // (blue)
4587       element = EL_CHAR_O;
4588       break;
4589
4590     case 0x15b8:        // (blue)
4591       element = EL_CHAR_P;
4592       break;
4593
4594     case 0x15b9:        // (blue)
4595       element = EL_CHAR_Q;
4596       break;
4597
4598     case 0x15ba:        // (blue)
4599       element = EL_CHAR_R;
4600       break;
4601
4602     case 0x15bb:        // (blue)
4603       element = EL_CHAR_S;
4604       break;
4605
4606     case 0x15bc:        // (blue)
4607       element = EL_CHAR_T;
4608       break;
4609
4610     case 0x15bd:        // (blue)
4611       element = EL_CHAR_U;
4612       break;
4613
4614     case 0x15be:        // (blue)
4615       element = EL_CHAR_V;
4616       break;
4617
4618     case 0x15bf:        // (blue)
4619       element = EL_CHAR_W;
4620       break;
4621
4622     case 0x15c0:        // (blue)
4623       element = EL_CHAR_X;
4624       break;
4625
4626     case 0x15c1:        // (blue)
4627       element = EL_CHAR_Y;
4628       break;
4629
4630     case 0x15c2:        // (blue)
4631       element = EL_CHAR_Z;
4632       break;
4633
4634     case 0x15c3:        // (blue)
4635       element = EL_CHAR_AUMLAUT;
4636       break;
4637
4638     case 0x15c4:        // (blue)
4639       element = EL_CHAR_OUMLAUT;
4640       break;
4641
4642     case 0x15c5:        // (blue)
4643       element = EL_CHAR_UUMLAUT;
4644       break;
4645
4646     case 0x15c6:        // (blue)
4647       element = EL_CHAR_0;
4648       break;
4649
4650     case 0x15c7:        // (blue)
4651       element = EL_CHAR_1;
4652       break;
4653
4654     case 0x15c8:        // (blue)
4655       element = EL_CHAR_2;
4656       break;
4657
4658     case 0x15c9:        // (blue)
4659       element = EL_CHAR_3;
4660       break;
4661
4662     case 0x15ca:        // (blue)
4663       element = EL_CHAR_4;
4664       break;
4665
4666     case 0x15cb:        // (blue)
4667       element = EL_CHAR_5;
4668       break;
4669
4670     case 0x15cc:        // (blue)
4671       element = EL_CHAR_6;
4672       break;
4673
4674     case 0x15cd:        // (blue)
4675       element = EL_CHAR_7;
4676       break;
4677
4678     case 0x15ce:        // (blue)
4679       element = EL_CHAR_8;
4680       break;
4681
4682     case 0x15cf:        // (blue)
4683       element = EL_CHAR_9;
4684       break;
4685
4686     case 0x15d0:        // (blue)
4687       element = EL_CHAR_PERIOD;
4688       break;
4689
4690     case 0x15d1:        // (blue)
4691       element = EL_CHAR_EXCLAM;
4692       break;
4693
4694     case 0x15d2:        // (blue)
4695       element = EL_CHAR_COLON;
4696       break;
4697
4698     case 0x15d3:        // (blue)
4699       element = EL_CHAR_LESS;
4700       break;
4701
4702     case 0x15d4:        // (blue)
4703       element = EL_CHAR_GREATER;
4704       break;
4705
4706     case 0x15d5:        // (blue)
4707       element = EL_CHAR_QUESTION;
4708       break;
4709
4710     case 0x15d6:        // (blue)
4711       element = EL_CHAR_COPYRIGHT;
4712       break;
4713
4714     case 0x15d7:        // (blue)
4715       element = EL_CHAR_UP;
4716       break;
4717
4718     case 0x15d8:        // (blue)
4719       element = EL_CHAR_DOWN;
4720       break;
4721
4722     case 0x15d9:        // (blue)
4723       element = EL_CHAR_BUTTON;
4724       break;
4725
4726     case 0x15da:        // (blue)
4727       element = EL_CHAR_PLUS;
4728       break;
4729
4730     case 0x15db:        // (blue)
4731       element = EL_CHAR_MINUS;
4732       break;
4733
4734     case 0x15dc:        // (blue)
4735       element = EL_CHAR_APOSTROPHE;
4736       break;
4737
4738     case 0x15dd:        // (blue)
4739       element = EL_CHAR_PARENLEFT;
4740       break;
4741
4742     case 0x15de:        // (blue)
4743       element = EL_CHAR_PARENRIGHT;
4744       break;
4745
4746     case 0x15df:        // (green)
4747       element = EL_CHAR_A;
4748       break;
4749
4750     case 0x15e0:        // (green)
4751       element = EL_CHAR_B;
4752       break;
4753
4754     case 0x15e1:        // (green)
4755       element = EL_CHAR_C;
4756       break;
4757
4758     case 0x15e2:        // (green)
4759       element = EL_CHAR_D;
4760       break;
4761
4762     case 0x15e3:        // (green)
4763       element = EL_CHAR_E;
4764       break;
4765
4766     case 0x15e4:        // (green)
4767       element = EL_CHAR_F;
4768       break;
4769
4770     case 0x15e5:        // (green)
4771       element = EL_CHAR_G;
4772       break;
4773
4774     case 0x15e6:        // (green)
4775       element = EL_CHAR_H;
4776       break;
4777
4778     case 0x15e7:        // (green)
4779       element = EL_CHAR_I;
4780       break;
4781
4782     case 0x15e8:        // (green)
4783       element = EL_CHAR_J;
4784       break;
4785
4786     case 0x15e9:        // (green)
4787       element = EL_CHAR_K;
4788       break;
4789
4790     case 0x15ea:        // (green)
4791       element = EL_CHAR_L;
4792       break;
4793
4794     case 0x15eb:        // (green)
4795       element = EL_CHAR_M;
4796       break;
4797
4798     case 0x15ec:        // (green)
4799       element = EL_CHAR_N;
4800       break;
4801
4802     case 0x15ed:        // (green)
4803       element = EL_CHAR_O;
4804       break;
4805
4806     case 0x15ee:        // (green)
4807       element = EL_CHAR_P;
4808       break;
4809
4810     case 0x15ef:        // (green)
4811       element = EL_CHAR_Q;
4812       break;
4813
4814     case 0x15f0:        // (green)
4815       element = EL_CHAR_R;
4816       break;
4817
4818     case 0x15f1:        // (green)
4819       element = EL_CHAR_S;
4820       break;
4821
4822     case 0x15f2:        // (green)
4823       element = EL_CHAR_T;
4824       break;
4825
4826     case 0x15f3:        // (green)
4827       element = EL_CHAR_U;
4828       break;
4829
4830     case 0x15f4:        // (green)
4831       element = EL_CHAR_V;
4832       break;
4833
4834     case 0x15f5:        // (green)
4835       element = EL_CHAR_W;
4836       break;
4837
4838     case 0x15f6:        // (green)
4839       element = EL_CHAR_X;
4840       break;
4841
4842     case 0x15f7:        // (green)
4843       element = EL_CHAR_Y;
4844       break;
4845
4846     case 0x15f8:        // (green)
4847       element = EL_CHAR_Z;
4848       break;
4849
4850     case 0x15f9:        // (green)
4851       element = EL_CHAR_AUMLAUT;
4852       break;
4853
4854     case 0x15fa:        // (green)
4855       element = EL_CHAR_OUMLAUT;
4856       break;
4857
4858     case 0x15fb:        // (green)
4859       element = EL_CHAR_UUMLAUT;
4860       break;
4861
4862     case 0x15fc:        // (green)
4863       element = EL_CHAR_0;
4864       break;
4865
4866     case 0x15fd:        // (green)
4867       element = EL_CHAR_1;
4868       break;
4869
4870     case 0x15fe:        // (green)
4871       element = EL_CHAR_2;
4872       break;
4873
4874     case 0x15ff:        // (green)
4875       element = EL_CHAR_3;
4876       break;
4877
4878     case 0x1600:        // (green)
4879       element = EL_CHAR_4;
4880       break;
4881
4882     case 0x1601:        // (green)
4883       element = EL_CHAR_5;
4884       break;
4885
4886     case 0x1602:        // (green)
4887       element = EL_CHAR_6;
4888       break;
4889
4890     case 0x1603:        // (green)
4891       element = EL_CHAR_7;
4892       break;
4893
4894     case 0x1604:        // (green)
4895       element = EL_CHAR_8;
4896       break;
4897
4898     case 0x1605:        // (green)
4899       element = EL_CHAR_9;
4900       break;
4901
4902     case 0x1606:        // (green)
4903       element = EL_CHAR_PERIOD;
4904       break;
4905
4906     case 0x1607:        // (green)
4907       element = EL_CHAR_EXCLAM;
4908       break;
4909
4910     case 0x1608:        // (green)
4911       element = EL_CHAR_COLON;
4912       break;
4913
4914     case 0x1609:        // (green)
4915       element = EL_CHAR_LESS;
4916       break;
4917
4918     case 0x160a:        // (green)
4919       element = EL_CHAR_GREATER;
4920       break;
4921
4922     case 0x160b:        // (green)
4923       element = EL_CHAR_QUESTION;
4924       break;
4925
4926     case 0x160c:        // (green)
4927       element = EL_CHAR_COPYRIGHT;
4928       break;
4929
4930     case 0x160d:        // (green)
4931       element = EL_CHAR_UP;
4932       break;
4933
4934     case 0x160e:        // (green)
4935       element = EL_CHAR_DOWN;
4936       break;
4937
4938     case 0x160f:        // (green)
4939       element = EL_CHAR_BUTTON;
4940       break;
4941
4942     case 0x1610:        // (green)
4943       element = EL_CHAR_PLUS;
4944       break;
4945
4946     case 0x1611:        // (green)
4947       element = EL_CHAR_MINUS;
4948       break;
4949
4950     case 0x1612:        // (green)
4951       element = EL_CHAR_APOSTROPHE;
4952       break;
4953
4954     case 0x1613:        // (green)
4955       element = EL_CHAR_PARENLEFT;
4956       break;
4957
4958     case 0x1614:        // (green)
4959       element = EL_CHAR_PARENRIGHT;
4960       break;
4961
4962     case 0x1615:        // (blue steel)
4963       element = EL_STEEL_CHAR_A;
4964       break;
4965
4966     case 0x1616:        // (blue steel)
4967       element = EL_STEEL_CHAR_B;
4968       break;
4969
4970     case 0x1617:        // (blue steel)
4971       element = EL_STEEL_CHAR_C;
4972       break;
4973
4974     case 0x1618:        // (blue steel)
4975       element = EL_STEEL_CHAR_D;
4976       break;
4977
4978     case 0x1619:        // (blue steel)
4979       element = EL_STEEL_CHAR_E;
4980       break;
4981
4982     case 0x161a:        // (blue steel)
4983       element = EL_STEEL_CHAR_F;
4984       break;
4985
4986     case 0x161b:        // (blue steel)
4987       element = EL_STEEL_CHAR_G;
4988       break;
4989
4990     case 0x161c:        // (blue steel)
4991       element = EL_STEEL_CHAR_H;
4992       break;
4993
4994     case 0x161d:        // (blue steel)
4995       element = EL_STEEL_CHAR_I;
4996       break;
4997
4998     case 0x161e:        // (blue steel)
4999       element = EL_STEEL_CHAR_J;
5000       break;
5001
5002     case 0x161f:        // (blue steel)
5003       element = EL_STEEL_CHAR_K;
5004       break;
5005
5006     case 0x1620:        // (blue steel)
5007       element = EL_STEEL_CHAR_L;
5008       break;
5009
5010     case 0x1621:        // (blue steel)
5011       element = EL_STEEL_CHAR_M;
5012       break;
5013
5014     case 0x1622:        // (blue steel)
5015       element = EL_STEEL_CHAR_N;
5016       break;
5017
5018     case 0x1623:        // (blue steel)
5019       element = EL_STEEL_CHAR_O;
5020       break;
5021
5022     case 0x1624:        // (blue steel)
5023       element = EL_STEEL_CHAR_P;
5024       break;
5025
5026     case 0x1625:        // (blue steel)
5027       element = EL_STEEL_CHAR_Q;
5028       break;
5029
5030     case 0x1626:        // (blue steel)
5031       element = EL_STEEL_CHAR_R;
5032       break;
5033
5034     case 0x1627:        // (blue steel)
5035       element = EL_STEEL_CHAR_S;
5036       break;
5037
5038     case 0x1628:        // (blue steel)
5039       element = EL_STEEL_CHAR_T;
5040       break;
5041
5042     case 0x1629:        // (blue steel)
5043       element = EL_STEEL_CHAR_U;
5044       break;
5045
5046     case 0x162a:        // (blue steel)
5047       element = EL_STEEL_CHAR_V;
5048       break;
5049
5050     case 0x162b:        // (blue steel)
5051       element = EL_STEEL_CHAR_W;
5052       break;
5053
5054     case 0x162c:        // (blue steel)
5055       element = EL_STEEL_CHAR_X;
5056       break;
5057
5058     case 0x162d:        // (blue steel)
5059       element = EL_STEEL_CHAR_Y;
5060       break;
5061
5062     case 0x162e:        // (blue steel)
5063       element = EL_STEEL_CHAR_Z;
5064       break;
5065
5066     case 0x162f:        // (blue steel)
5067       element = EL_STEEL_CHAR_AUMLAUT;
5068       break;
5069
5070     case 0x1630:        // (blue steel)
5071       element = EL_STEEL_CHAR_OUMLAUT;
5072       break;
5073
5074     case 0x1631:        // (blue steel)
5075       element = EL_STEEL_CHAR_UUMLAUT;
5076       break;
5077
5078     case 0x1632:        // (blue steel)
5079       element = EL_STEEL_CHAR_0;
5080       break;
5081
5082     case 0x1633:        // (blue steel)
5083       element = EL_STEEL_CHAR_1;
5084       break;
5085
5086     case 0x1634:        // (blue steel)
5087       element = EL_STEEL_CHAR_2;
5088       break;
5089
5090     case 0x1635:        // (blue steel)
5091       element = EL_STEEL_CHAR_3;
5092       break;
5093
5094     case 0x1636:        // (blue steel)
5095       element = EL_STEEL_CHAR_4;
5096       break;
5097
5098     case 0x1637:        // (blue steel)
5099       element = EL_STEEL_CHAR_5;
5100       break;
5101
5102     case 0x1638:        // (blue steel)
5103       element = EL_STEEL_CHAR_6;
5104       break;
5105
5106     case 0x1639:        // (blue steel)
5107       element = EL_STEEL_CHAR_7;
5108       break;
5109
5110     case 0x163a:        // (blue steel)
5111       element = EL_STEEL_CHAR_8;
5112       break;
5113
5114     case 0x163b:        // (blue steel)
5115       element = EL_STEEL_CHAR_9;
5116       break;
5117
5118     case 0x163c:        // (blue steel)
5119       element = EL_STEEL_CHAR_PERIOD;
5120       break;
5121
5122     case 0x163d:        // (blue steel)
5123       element = EL_STEEL_CHAR_EXCLAM;
5124       break;
5125
5126     case 0x163e:        // (blue steel)
5127       element = EL_STEEL_CHAR_COLON;
5128       break;
5129
5130     case 0x163f:        // (blue steel)
5131       element = EL_STEEL_CHAR_LESS;
5132       break;
5133
5134     case 0x1640:        // (blue steel)
5135       element = EL_STEEL_CHAR_GREATER;
5136       break;
5137
5138     case 0x1641:        // (blue steel)
5139       element = EL_STEEL_CHAR_QUESTION;
5140       break;
5141
5142     case 0x1642:        // (blue steel)
5143       element = EL_STEEL_CHAR_COPYRIGHT;
5144       break;
5145
5146     case 0x1643:        // (blue steel)
5147       element = EL_STEEL_CHAR_UP;
5148       break;
5149
5150     case 0x1644:        // (blue steel)
5151       element = EL_STEEL_CHAR_DOWN;
5152       break;
5153
5154     case 0x1645:        // (blue steel)
5155       element = EL_STEEL_CHAR_BUTTON;
5156       break;
5157
5158     case 0x1646:        // (blue steel)
5159       element = EL_STEEL_CHAR_PLUS;
5160       break;
5161
5162     case 0x1647:        // (blue steel)
5163       element = EL_STEEL_CHAR_MINUS;
5164       break;
5165
5166     case 0x1648:        // (blue steel)
5167       element = EL_STEEL_CHAR_APOSTROPHE;
5168       break;
5169
5170     case 0x1649:        // (blue steel)
5171       element = EL_STEEL_CHAR_PARENLEFT;
5172       break;
5173
5174     case 0x164a:        // (blue steel)
5175       element = EL_STEEL_CHAR_PARENRIGHT;
5176       break;
5177
5178     case 0x164b:        // (green steel)
5179       element = EL_STEEL_CHAR_A;
5180       break;
5181
5182     case 0x164c:        // (green steel)
5183       element = EL_STEEL_CHAR_B;
5184       break;
5185
5186     case 0x164d:        // (green steel)
5187       element = EL_STEEL_CHAR_C;
5188       break;
5189
5190     case 0x164e:        // (green steel)
5191       element = EL_STEEL_CHAR_D;
5192       break;
5193
5194     case 0x164f:        // (green steel)
5195       element = EL_STEEL_CHAR_E;
5196       break;
5197
5198     case 0x1650:        // (green steel)
5199       element = EL_STEEL_CHAR_F;
5200       break;
5201
5202     case 0x1651:        // (green steel)
5203       element = EL_STEEL_CHAR_G;
5204       break;
5205
5206     case 0x1652:        // (green steel)
5207       element = EL_STEEL_CHAR_H;
5208       break;
5209
5210     case 0x1653:        // (green steel)
5211       element = EL_STEEL_CHAR_I;
5212       break;
5213
5214     case 0x1654:        // (green steel)
5215       element = EL_STEEL_CHAR_J;
5216       break;
5217
5218     case 0x1655:        // (green steel)
5219       element = EL_STEEL_CHAR_K;
5220       break;
5221
5222     case 0x1656:        // (green steel)
5223       element = EL_STEEL_CHAR_L;
5224       break;
5225
5226     case 0x1657:        // (green steel)
5227       element = EL_STEEL_CHAR_M;
5228       break;
5229
5230     case 0x1658:        // (green steel)
5231       element = EL_STEEL_CHAR_N;
5232       break;
5233
5234     case 0x1659:        // (green steel)
5235       element = EL_STEEL_CHAR_O;
5236       break;
5237
5238     case 0x165a:        // (green steel)
5239       element = EL_STEEL_CHAR_P;
5240       break;
5241
5242     case 0x165b:        // (green steel)
5243       element = EL_STEEL_CHAR_Q;
5244       break;
5245
5246     case 0x165c:        // (green steel)
5247       element = EL_STEEL_CHAR_R;
5248       break;
5249
5250     case 0x165d:        // (green steel)
5251       element = EL_STEEL_CHAR_S;
5252       break;
5253
5254     case 0x165e:        // (green steel)
5255       element = EL_STEEL_CHAR_T;
5256       break;
5257
5258     case 0x165f:        // (green steel)
5259       element = EL_STEEL_CHAR_U;
5260       break;
5261
5262     case 0x1660:        // (green steel)
5263       element = EL_STEEL_CHAR_V;
5264       break;
5265
5266     case 0x1661:        // (green steel)
5267       element = EL_STEEL_CHAR_W;
5268       break;
5269
5270     case 0x1662:        // (green steel)
5271       element = EL_STEEL_CHAR_X;
5272       break;
5273
5274     case 0x1663:        // (green steel)
5275       element = EL_STEEL_CHAR_Y;
5276       break;
5277
5278     case 0x1664:        // (green steel)
5279       element = EL_STEEL_CHAR_Z;
5280       break;
5281
5282     case 0x1665:        // (green steel)
5283       element = EL_STEEL_CHAR_AUMLAUT;
5284       break;
5285
5286     case 0x1666:        // (green steel)
5287       element = EL_STEEL_CHAR_OUMLAUT;
5288       break;
5289
5290     case 0x1667:        // (green steel)
5291       element = EL_STEEL_CHAR_UUMLAUT;
5292       break;
5293
5294     case 0x1668:        // (green steel)
5295       element = EL_STEEL_CHAR_0;
5296       break;
5297
5298     case 0x1669:        // (green steel)
5299       element = EL_STEEL_CHAR_1;
5300       break;
5301
5302     case 0x166a:        // (green steel)
5303       element = EL_STEEL_CHAR_2;
5304       break;
5305
5306     case 0x166b:        // (green steel)
5307       element = EL_STEEL_CHAR_3;
5308       break;
5309
5310     case 0x166c:        // (green steel)
5311       element = EL_STEEL_CHAR_4;
5312       break;
5313
5314     case 0x166d:        // (green steel)
5315       element = EL_STEEL_CHAR_5;
5316       break;
5317
5318     case 0x166e:        // (green steel)
5319       element = EL_STEEL_CHAR_6;
5320       break;
5321
5322     case 0x166f:        // (green steel)
5323       element = EL_STEEL_CHAR_7;
5324       break;
5325
5326     case 0x1670:        // (green steel)
5327       element = EL_STEEL_CHAR_8;
5328       break;
5329
5330     case 0x1671:        // (green steel)
5331       element = EL_STEEL_CHAR_9;
5332       break;
5333
5334     case 0x1672:        // (green steel)
5335       element = EL_STEEL_CHAR_PERIOD;
5336       break;
5337
5338     case 0x1673:        // (green steel)
5339       element = EL_STEEL_CHAR_EXCLAM;
5340       break;
5341
5342     case 0x1674:        // (green steel)
5343       element = EL_STEEL_CHAR_COLON;
5344       break;
5345
5346     case 0x1675:        // (green steel)
5347       element = EL_STEEL_CHAR_LESS;
5348       break;
5349
5350     case 0x1676:        // (green steel)
5351       element = EL_STEEL_CHAR_GREATER;
5352       break;
5353
5354     case 0x1677:        // (green steel)
5355       element = EL_STEEL_CHAR_QUESTION;
5356       break;
5357
5358     case 0x1678:        // (green steel)
5359       element = EL_STEEL_CHAR_COPYRIGHT;
5360       break;
5361
5362     case 0x1679:        // (green steel)
5363       element = EL_STEEL_CHAR_UP;
5364       break;
5365
5366     case 0x167a:        // (green steel)
5367       element = EL_STEEL_CHAR_DOWN;
5368       break;
5369
5370     case 0x167b:        // (green steel)
5371       element = EL_STEEL_CHAR_BUTTON;
5372       break;
5373
5374     case 0x167c:        // (green steel)
5375       element = EL_STEEL_CHAR_PLUS;
5376       break;
5377
5378     case 0x167d:        // (green steel)
5379       element = EL_STEEL_CHAR_MINUS;
5380       break;
5381
5382     case 0x167e:        // (green steel)
5383       element = EL_STEEL_CHAR_APOSTROPHE;
5384       break;
5385
5386     case 0x167f:        // (green steel)
5387       element = EL_STEEL_CHAR_PARENLEFT;
5388       break;
5389
5390     case 0x1680:        // (green steel)
5391       element = EL_STEEL_CHAR_PARENRIGHT;
5392       break;
5393
5394     case 0x1681:        // gate (red)
5395       element = EL_EM_GATE_1;
5396       break;
5397
5398     case 0x1682:        // secret gate (red)
5399       element = EL_EM_GATE_1_GRAY;
5400       break;
5401
5402     case 0x1683:        // gate (yellow)
5403       element = EL_EM_GATE_2;
5404       break;
5405
5406     case 0x1684:        // secret gate (yellow)
5407       element = EL_EM_GATE_2_GRAY;
5408       break;
5409
5410     case 0x1685:        // gate (blue)
5411       element = EL_EM_GATE_4;
5412       break;
5413
5414     case 0x1686:        // secret gate (blue)
5415       element = EL_EM_GATE_4_GRAY;
5416       break;
5417
5418     case 0x1687:        // gate (green)
5419       element = EL_EM_GATE_3;
5420       break;
5421
5422     case 0x1688:        // secret gate (green)
5423       element = EL_EM_GATE_3_GRAY;
5424       break;
5425
5426     case 0x1689:        // gate (white)
5427       element = EL_DC_GATE_WHITE;
5428       break;
5429
5430     case 0x168a:        // secret gate (white)
5431       element = EL_DC_GATE_WHITE_GRAY;
5432       break;
5433
5434     case 0x168b:        // secret gate (no key)
5435       element = EL_DC_GATE_FAKE_GRAY;
5436       break;
5437
5438     case 0x168c:
5439       element = EL_ROBOT_WHEEL;
5440       break;
5441
5442     case 0x168d:
5443       element = EL_DC_TIMEGATE_SWITCH;
5444       break;
5445
5446     case 0x168e:
5447       element = EL_ACID_POOL_BOTTOM;
5448       break;
5449
5450     case 0x168f:
5451       element = EL_ACID_POOL_TOPLEFT;
5452       break;
5453
5454     case 0x1690:
5455       element = EL_ACID_POOL_TOPRIGHT;
5456       break;
5457
5458     case 0x1691:
5459       element = EL_ACID_POOL_BOTTOMLEFT;
5460       break;
5461
5462     case 0x1692:
5463       element = EL_ACID_POOL_BOTTOMRIGHT;
5464       break;
5465
5466     case 0x1693:
5467       element = EL_STEELWALL;
5468       break;
5469
5470     case 0x1694:
5471       element = EL_STEELWALL_SLIPPERY;
5472       break;
5473
5474     case 0x1695:        // steel wall (not round)
5475       element = EL_STEELWALL;
5476       break;
5477
5478     case 0x1696:        // steel wall (left)
5479       element = EL_DC_STEELWALL_1_LEFT;
5480       break;
5481
5482     case 0x1697:        // steel wall (bottom)
5483       element = EL_DC_STEELWALL_1_BOTTOM;
5484       break;
5485
5486     case 0x1698:        // steel wall (right)
5487       element = EL_DC_STEELWALL_1_RIGHT;
5488       break;
5489
5490     case 0x1699:        // steel wall (top)
5491       element = EL_DC_STEELWALL_1_TOP;
5492       break;
5493
5494     case 0x169a:        // steel wall (left/bottom)
5495       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5496       break;
5497
5498     case 0x169b:        // steel wall (right/bottom)
5499       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5500       break;
5501
5502     case 0x169c:        // steel wall (right/top)
5503       element = EL_DC_STEELWALL_1_TOPRIGHT;
5504       break;
5505
5506     case 0x169d:        // steel wall (left/top)
5507       element = EL_DC_STEELWALL_1_TOPLEFT;
5508       break;
5509
5510     case 0x169e:        // steel wall (right/bottom small)
5511       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5512       break;
5513
5514     case 0x169f:        // steel wall (left/bottom small)
5515       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5516       break;
5517
5518     case 0x16a0:        // steel wall (right/top small)
5519       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5520       break;
5521
5522     case 0x16a1:        // steel wall (left/top small)
5523       element = EL_DC_STEELWALL_1_TOPLEFT_2;
5524       break;
5525
5526     case 0x16a2:        // steel wall (left/right)
5527       element = EL_DC_STEELWALL_1_VERTICAL;
5528       break;
5529
5530     case 0x16a3:        // steel wall (top/bottom)
5531       element = EL_DC_STEELWALL_1_HORIZONTAL;
5532       break;
5533
5534     case 0x16a4:        // steel wall 2 (left end)
5535       element = EL_DC_STEELWALL_2_LEFT;
5536       break;
5537
5538     case 0x16a5:        // steel wall 2 (right end)
5539       element = EL_DC_STEELWALL_2_RIGHT;
5540       break;
5541
5542     case 0x16a6:        // steel wall 2 (top end)
5543       element = EL_DC_STEELWALL_2_TOP;
5544       break;
5545
5546     case 0x16a7:        // steel wall 2 (bottom end)
5547       element = EL_DC_STEELWALL_2_BOTTOM;
5548       break;
5549
5550     case 0x16a8:        // steel wall 2 (left/right)
5551       element = EL_DC_STEELWALL_2_HORIZONTAL;
5552       break;
5553
5554     case 0x16a9:        // steel wall 2 (up/down)
5555       element = EL_DC_STEELWALL_2_VERTICAL;
5556       break;
5557
5558     case 0x16aa:        // steel wall 2 (mid)
5559       element = EL_DC_STEELWALL_2_MIDDLE;
5560       break;
5561
5562     case 0x16ab:
5563       element = EL_SIGN_EXCLAMATION;
5564       break;
5565
5566     case 0x16ac:
5567       element = EL_SIGN_RADIOACTIVITY;
5568       break;
5569
5570     case 0x16ad:
5571       element = EL_SIGN_STOP;
5572       break;
5573
5574     case 0x16ae:
5575       element = EL_SIGN_WHEELCHAIR;
5576       break;
5577
5578     case 0x16af:
5579       element = EL_SIGN_PARKING;
5580       break;
5581
5582     case 0x16b0:
5583       element = EL_SIGN_NO_ENTRY;
5584       break;
5585
5586     case 0x16b1:
5587       element = EL_SIGN_HEART;
5588       break;
5589
5590     case 0x16b2:
5591       element = EL_SIGN_GIVE_WAY;
5592       break;
5593
5594     case 0x16b3:
5595       element = EL_SIGN_ENTRY_FORBIDDEN;
5596       break;
5597
5598     case 0x16b4:
5599       element = EL_SIGN_EMERGENCY_EXIT;
5600       break;
5601
5602     case 0x16b5:
5603       element = EL_SIGN_YIN_YANG;
5604       break;
5605
5606     case 0x16b6:
5607       element = EL_WALL_EMERALD;
5608       break;
5609
5610     case 0x16b7:
5611       element = EL_WALL_DIAMOND;
5612       break;
5613
5614     case 0x16b8:
5615       element = EL_WALL_PEARL;
5616       break;
5617
5618     case 0x16b9:
5619       element = EL_WALL_CRYSTAL;
5620       break;
5621
5622     case 0x16ba:
5623       element = EL_INVISIBLE_WALL;
5624       break;
5625
5626     case 0x16bb:
5627       element = EL_INVISIBLE_STEELWALL;
5628       break;
5629
5630       // 0x16bc - 0x16cb:
5631       // EL_INVISIBLE_SAND
5632
5633     case 0x16cc:
5634       element = EL_LIGHT_SWITCH;
5635       break;
5636
5637     case 0x16cd:
5638       element = EL_ENVELOPE_1;
5639       break;
5640
5641     default:
5642       if (element >= 0x0117 && element <= 0x036e)       // (?)
5643         element = EL_DIAMOND;
5644       else if (element >= 0x042d && element <= 0x0684)  // (?)
5645         element = EL_EMERALD;
5646       else if (element >= 0x157c && element <= 0x158b)
5647         element = EL_SAND;
5648       else if (element >= 0x1590 && element <= 0x159f)
5649         element = EL_DC_LANDMINE;
5650       else if (element >= 0x16bc && element <= 0x16cb)
5651         element = EL_INVISIBLE_SAND;
5652       else
5653       {
5654         Warn("unknown Diamond Caves element 0x%04x", element);
5655
5656         element = EL_UNKNOWN;
5657       }
5658       break;
5659   }
5660
5661   return getMappedElement(element);
5662 }
5663
5664 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5665                                        int nr)
5666 {
5667   byte header[DC_LEVEL_HEADER_SIZE];
5668   int envelope_size;
5669   int envelope_header_pos = 62;
5670   int envelope_content_pos = 94;
5671   int level_name_pos = 251;
5672   int level_author_pos = 292;
5673   int envelope_header_len;
5674   int envelope_content_len;
5675   int level_name_len;
5676   int level_author_len;
5677   int fieldx, fieldy;
5678   int num_yamyam_contents;
5679   int i, x, y;
5680
5681   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
5682
5683   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5684   {
5685     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5686
5687     header[i * 2 + 0] = header_word >> 8;
5688     header[i * 2 + 1] = header_word & 0xff;
5689   }
5690
5691   // read some values from level header to check level decoding integrity
5692   fieldx = header[6] | (header[7] << 8);
5693   fieldy = header[8] | (header[9] << 8);
5694   num_yamyam_contents = header[60] | (header[61] << 8);
5695
5696   // do some simple sanity checks to ensure that level was correctly decoded
5697   if (fieldx < 1 || fieldx > 256 ||
5698       fieldy < 1 || fieldy > 256 ||
5699       num_yamyam_contents < 1 || num_yamyam_contents > 8)
5700   {
5701     level->no_valid_file = TRUE;
5702
5703     Warn("cannot decode level from stream -- using empty level");
5704
5705     return;
5706   }
5707
5708   // maximum envelope header size is 31 bytes
5709   envelope_header_len   = header[envelope_header_pos];
5710   // maximum envelope content size is 110 (156?) bytes
5711   envelope_content_len  = header[envelope_content_pos];
5712
5713   // maximum level title size is 40 bytes
5714   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
5715   // maximum level author size is 30 (51?) bytes
5716   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5717
5718   envelope_size = 0;
5719
5720   for (i = 0; i < envelope_header_len; i++)
5721     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5722       level->envelope[0].text[envelope_size++] =
5723         header[envelope_header_pos + 1 + i];
5724
5725   if (envelope_header_len > 0 && envelope_content_len > 0)
5726   {
5727     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5728       level->envelope[0].text[envelope_size++] = '\n';
5729     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5730       level->envelope[0].text[envelope_size++] = '\n';
5731   }
5732
5733   for (i = 0; i < envelope_content_len; i++)
5734     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5735       level->envelope[0].text[envelope_size++] =
5736         header[envelope_content_pos + 1 + i];
5737
5738   level->envelope[0].text[envelope_size] = '\0';
5739
5740   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5741   level->envelope[0].ysize = 10;
5742   level->envelope[0].autowrap = TRUE;
5743   level->envelope[0].centered = TRUE;
5744
5745   for (i = 0; i < level_name_len; i++)
5746     level->name[i] = header[level_name_pos + 1 + i];
5747   level->name[level_name_len] = '\0';
5748
5749   for (i = 0; i < level_author_len; i++)
5750     level->author[i] = header[level_author_pos + 1 + i];
5751   level->author[level_author_len] = '\0';
5752
5753   num_yamyam_contents = header[60] | (header[61] << 8);
5754   level->num_yamyam_contents =
5755     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5756
5757   for (i = 0; i < num_yamyam_contents; i++)
5758   {
5759     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5760     {
5761       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5762       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5763
5764       if (i < MAX_ELEMENT_CONTENTS)
5765         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5766     }
5767   }
5768
5769   fieldx = header[6] | (header[7] << 8);
5770   fieldy = header[8] | (header[9] << 8);
5771   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5772   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5773
5774   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5775   {
5776     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5777     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5778
5779     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5780       level->field[x][y] = getMappedElement_DC(element_dc);
5781   }
5782
5783   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5784   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5785   level->field[x][y] = EL_PLAYER_1;
5786
5787   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5788   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5789   level->field[x][y] = EL_PLAYER_2;
5790
5791   level->gems_needed            = header[18] | (header[19] << 8);
5792
5793   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
5794   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
5795   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
5796   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
5797   level->score[SC_NUT]          = header[28] | (header[29] << 8);
5798   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
5799   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
5800   level->score[SC_BUG]          = header[34] | (header[35] << 8);
5801   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
5802   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
5803   level->score[SC_KEY]          = header[40] | (header[41] << 8);
5804   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
5805
5806   level->time                   = header[44] | (header[45] << 8);
5807
5808   level->amoeba_speed           = header[46] | (header[47] << 8);
5809   level->time_light             = header[48] | (header[49] << 8);
5810   level->time_timegate          = header[50] | (header[51] << 8);
5811   level->time_wheel             = header[52] | (header[53] << 8);
5812   level->time_magic_wall        = header[54] | (header[55] << 8);
5813   level->extra_time             = header[56] | (header[57] << 8);
5814   level->shield_normal_time     = header[58] | (header[59] << 8);
5815
5816   // shield and extra time elements do not have a score
5817   level->score[SC_SHIELD]       = 0;
5818   level->extra_time_score       = 0;
5819
5820   // set time for normal and deadly shields to the same value
5821   level->shield_deadly_time     = level->shield_normal_time;
5822
5823   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5824   // can slip down from flat walls, like normal walls and steel walls
5825   level->em_slippery_gems = TRUE;
5826
5827   // time score is counted for each 10 seconds left in Diamond Caves levels
5828   level->time_score_base = 10;
5829 }
5830
5831 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5832                                      struct LevelFileInfo *level_file_info,
5833                                      boolean level_info_only)
5834 {
5835   char *filename = level_file_info->filename;
5836   File *file;
5837   int num_magic_bytes = 8;
5838   char magic_bytes[num_magic_bytes + 1];
5839   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5840
5841   if (!(file = openFile(filename, MODE_READ)))
5842   {
5843     level->no_valid_file = TRUE;
5844
5845     if (!level_info_only)
5846       Warn("cannot read level '%s' -- using empty level", filename);
5847
5848     return;
5849   }
5850
5851   // fseek(file, 0x0000, SEEK_SET);
5852
5853   if (level_file_info->packed)
5854   {
5855     // read "magic bytes" from start of file
5856     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5857       magic_bytes[0] = '\0';
5858
5859     // check "magic bytes" for correct file format
5860     if (!strPrefix(magic_bytes, "DC2"))
5861     {
5862       level->no_valid_file = TRUE;
5863
5864       Warn("unknown DC level file '%s' -- using empty level", filename);
5865
5866       return;
5867     }
5868
5869     if (strPrefix(magic_bytes, "DC2Win95") ||
5870         strPrefix(magic_bytes, "DC2Win98"))
5871     {
5872       int position_first_level = 0x00fa;
5873       int extra_bytes = 4;
5874       int skip_bytes;
5875
5876       // advance file stream to first level inside the level package
5877       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5878
5879       // each block of level data is followed by block of non-level data
5880       num_levels_to_skip *= 2;
5881
5882       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5883       while (num_levels_to_skip >= 0)
5884       {
5885         // advance file stream to next level inside the level package
5886         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5887         {
5888           level->no_valid_file = TRUE;
5889
5890           Warn("cannot fseek in file '%s' -- using empty level", filename);
5891
5892           return;
5893         }
5894
5895         // skip apparently unused extra bytes following each level
5896         ReadUnusedBytesFromFile(file, extra_bytes);
5897
5898         // read size of next level in level package
5899         skip_bytes = getFile32BitLE(file);
5900
5901         num_levels_to_skip--;
5902       }
5903     }
5904     else
5905     {
5906       level->no_valid_file = TRUE;
5907
5908       Warn("unknown DC2 level file '%s' -- using empty level", filename);
5909
5910       return;
5911     }
5912   }
5913
5914   LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5915
5916   closeFile(file);
5917 }
5918
5919
5920 // ----------------------------------------------------------------------------
5921 // functions for loading SB level
5922 // ----------------------------------------------------------------------------
5923
5924 int getMappedElement_SB(int element_ascii, boolean use_ces)
5925 {
5926   static struct
5927   {
5928     int ascii;
5929     int sb;
5930     int ce;
5931   }
5932   sb_element_mapping[] =
5933   {
5934     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
5935     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
5936     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
5937     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
5938     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
5939     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
5940     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
5941     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
5942
5943     { 0,   -1,                      -1          },
5944   };
5945
5946   int i;
5947
5948   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5949     if (element_ascii == sb_element_mapping[i].ascii)
5950       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5951
5952   return EL_UNDEFINED;
5953 }
5954
5955 static void SetLevelSettings_SB(struct LevelInfo *level)
5956 {
5957   // time settings
5958   level->time = 0;
5959   level->use_step_counter = TRUE;
5960
5961   // score settings
5962   level->score[SC_TIME_BONUS] = 0;
5963   level->time_score_base = 1;
5964   level->rate_time_over_score = TRUE;
5965
5966   // game settings
5967   level->auto_exit_sokoban = TRUE;
5968 }
5969
5970 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5971                                      struct LevelFileInfo *level_file_info,
5972                                      boolean level_info_only)
5973 {
5974   char *filename = level_file_info->filename;
5975   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5976   char last_comment[MAX_LINE_LEN];
5977   char level_name[MAX_LINE_LEN];
5978   char *line_ptr;
5979   File *file;
5980   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5981   boolean read_continued_line = FALSE;
5982   boolean reading_playfield = FALSE;
5983   boolean got_valid_playfield_line = FALSE;
5984   boolean invalid_playfield_char = FALSE;
5985   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5986   int file_level_nr = 0;
5987   int line_nr = 0;
5988   int x = 0, y = 0;             // initialized to make compilers happy
5989
5990   last_comment[0] = '\0';
5991   level_name[0] = '\0';
5992
5993   if (!(file = openFile(filename, MODE_READ)))
5994   {
5995     level->no_valid_file = TRUE;
5996
5997     if (!level_info_only)
5998       Warn("cannot read level '%s' -- using empty level", filename);
5999
6000     return;
6001   }
6002
6003   while (!checkEndOfFile(file))
6004   {
6005     // level successfully read, but next level may follow here
6006     if (!got_valid_playfield_line && reading_playfield)
6007     {
6008       // read playfield from single level file -- skip remaining file
6009       if (!level_file_info->packed)
6010         break;
6011
6012       if (file_level_nr >= num_levels_to_skip)
6013         break;
6014
6015       file_level_nr++;
6016
6017       last_comment[0] = '\0';
6018       level_name[0] = '\0';
6019
6020       reading_playfield = FALSE;
6021     }
6022
6023     got_valid_playfield_line = FALSE;
6024
6025     // read next line of input file
6026     if (!getStringFromFile(file, line, MAX_LINE_LEN))
6027       break;
6028
6029     // check if line was completely read and is terminated by line break
6030     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6031       line_nr++;
6032
6033     // cut trailing line break (this can be newline and/or carriage return)
6034     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6035       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6036         *line_ptr = '\0';
6037
6038     // copy raw input line for later use (mainly debugging output)
6039     strcpy(line_raw, line);
6040
6041     if (read_continued_line)
6042     {
6043       // append new line to existing line, if there is enough space
6044       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6045         strcat(previous_line, line_ptr);
6046
6047       strcpy(line, previous_line);      // copy storage buffer to line
6048
6049       read_continued_line = FALSE;
6050     }
6051
6052     // if the last character is '\', continue at next line
6053     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6054     {
6055       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
6056       strcpy(previous_line, line);      // copy line to storage buffer
6057
6058       read_continued_line = TRUE;
6059
6060       continue;
6061     }
6062
6063     // skip empty lines
6064     if (line[0] == '\0')
6065       continue;
6066
6067     // extract comment text from comment line
6068     if (line[0] == ';')
6069     {
6070       for (line_ptr = line; *line_ptr; line_ptr++)
6071         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6072           break;
6073
6074       strcpy(last_comment, line_ptr);
6075
6076       continue;
6077     }
6078
6079     // extract level title text from line containing level title
6080     if (line[0] == '\'')
6081     {
6082       strcpy(level_name, &line[1]);
6083
6084       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6085         level_name[strlen(level_name) - 1] = '\0';
6086
6087       continue;
6088     }
6089
6090     // skip lines containing only spaces (or empty lines)
6091     for (line_ptr = line; *line_ptr; line_ptr++)
6092       if (*line_ptr != ' ')
6093         break;
6094     if (*line_ptr == '\0')
6095       continue;
6096
6097     // at this point, we have found a line containing part of a playfield
6098
6099     got_valid_playfield_line = TRUE;
6100
6101     if (!reading_playfield)
6102     {
6103       reading_playfield = TRUE;
6104       invalid_playfield_char = FALSE;
6105
6106       for (x = 0; x < MAX_LEV_FIELDX; x++)
6107         for (y = 0; y < MAX_LEV_FIELDY; y++)
6108           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6109
6110       level->fieldx = 0;
6111       level->fieldy = 0;
6112
6113       // start with topmost tile row
6114       y = 0;
6115     }
6116
6117     // skip playfield line if larger row than allowed
6118     if (y >= MAX_LEV_FIELDY)
6119       continue;
6120
6121     // start with leftmost tile column
6122     x = 0;
6123
6124     // read playfield elements from line
6125     for (line_ptr = line; *line_ptr; line_ptr++)
6126     {
6127       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6128
6129       // stop parsing playfield line if larger column than allowed
6130       if (x >= MAX_LEV_FIELDX)
6131         break;
6132
6133       if (mapped_sb_element == EL_UNDEFINED)
6134       {
6135         invalid_playfield_char = TRUE;
6136
6137         break;
6138       }
6139
6140       level->field[x][y] = mapped_sb_element;
6141
6142       // continue with next tile column
6143       x++;
6144
6145       level->fieldx = MAX(x, level->fieldx);
6146     }
6147
6148     if (invalid_playfield_char)
6149     {
6150       // if first playfield line, treat invalid lines as comment lines
6151       if (y == 0)
6152         reading_playfield = FALSE;
6153
6154       continue;
6155     }
6156
6157     // continue with next tile row
6158     y++;
6159   }
6160
6161   closeFile(file);
6162
6163   level->fieldy = y;
6164
6165   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6166   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6167
6168   if (!reading_playfield)
6169   {
6170     level->no_valid_file = TRUE;
6171
6172     Warn("cannot read level '%s' -- using empty level", filename);
6173
6174     return;
6175   }
6176
6177   if (*level_name != '\0')
6178   {
6179     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6180     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6181   }
6182   else if (*last_comment != '\0')
6183   {
6184     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6185     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6186   }
6187   else
6188   {
6189     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6190   }
6191
6192   // set all empty fields beyond the border walls to invisible steel wall
6193   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6194   {
6195     if ((x == 0 || x == level->fieldx - 1 ||
6196          y == 0 || y == level->fieldy - 1) &&
6197         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6198       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6199                      level->field, level->fieldx, level->fieldy);
6200   }
6201
6202   // set special level settings for Sokoban levels
6203   SetLevelSettings_SB(level);
6204
6205   if (load_xsb_to_ces)
6206   {
6207     // special global settings can now be set in level template
6208     level->use_custom_template = TRUE;
6209   }
6210 }
6211
6212
6213 // -------------------------------------------------------------------------
6214 // functions for handling native levels
6215 // -------------------------------------------------------------------------
6216
6217 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6218                                      struct LevelFileInfo *level_file_info,
6219                                      boolean level_info_only)
6220 {
6221   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6222     level->no_valid_file = TRUE;
6223 }
6224
6225 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6226                                      struct LevelFileInfo *level_file_info,
6227                                      boolean level_info_only)
6228 {
6229   int pos = 0;
6230
6231   // determine position of requested level inside level package
6232   if (level_file_info->packed)
6233     pos = level_file_info->nr - leveldir_current->first_level;
6234
6235   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6236     level->no_valid_file = TRUE;
6237 }
6238
6239 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6240                                      struct LevelFileInfo *level_file_info,
6241                                      boolean level_info_only)
6242 {
6243   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6244     level->no_valid_file = TRUE;
6245 }
6246
6247 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6248 {
6249   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6250     CopyNativeLevel_RND_to_EM(level);
6251   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6252     CopyNativeLevel_RND_to_SP(level);
6253   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6254     CopyNativeLevel_RND_to_MM(level);
6255 }
6256
6257 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6258 {
6259   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6260     CopyNativeLevel_EM_to_RND(level);
6261   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6262     CopyNativeLevel_SP_to_RND(level);
6263   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6264     CopyNativeLevel_MM_to_RND(level);
6265 }
6266
6267 void SaveNativeLevel(struct LevelInfo *level)
6268 {
6269   if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6270   {
6271     char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6272     char *filename = getLevelFilenameFromBasename(basename);
6273
6274     CopyNativeLevel_RND_to_SP(level);
6275     CopyNativeTape_RND_to_SP(level);
6276
6277     SaveNativeLevel_SP(filename);
6278   }
6279 }
6280
6281
6282 // ----------------------------------------------------------------------------
6283 // functions for loading generic level
6284 // ----------------------------------------------------------------------------
6285
6286 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6287                                   struct LevelFileInfo *level_file_info,
6288                                   boolean level_info_only)
6289 {
6290   // always start with reliable default values
6291   setLevelInfoToDefaults(level, level_info_only, TRUE);
6292
6293   switch (level_file_info->type)
6294   {
6295     case LEVEL_FILE_TYPE_RND:
6296       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6297       break;
6298
6299     case LEVEL_FILE_TYPE_EM:
6300       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6301       level->game_engine_type = GAME_ENGINE_TYPE_EM;
6302       break;
6303
6304     case LEVEL_FILE_TYPE_SP:
6305       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6306       level->game_engine_type = GAME_ENGINE_TYPE_SP;
6307       break;
6308
6309     case LEVEL_FILE_TYPE_MM:
6310       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6311       level->game_engine_type = GAME_ENGINE_TYPE_MM;
6312       break;
6313
6314     case LEVEL_FILE_TYPE_DC:
6315       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6316       break;
6317
6318     case LEVEL_FILE_TYPE_SB:
6319       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6320       break;
6321
6322     default:
6323       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6324       break;
6325   }
6326
6327   // if level file is invalid, restore level structure to default values
6328   if (level->no_valid_file)
6329     setLevelInfoToDefaults(level, level_info_only, FALSE);
6330
6331   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6332     level->game_engine_type = GAME_ENGINE_TYPE_RND;
6333
6334   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6335     CopyNativeLevel_Native_to_RND(level);
6336 }
6337
6338 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6339 {
6340   static struct LevelFileInfo level_file_info;
6341
6342   // always start with reliable default values
6343   setFileInfoToDefaults(&level_file_info);
6344
6345   level_file_info.nr = 0;                       // unknown level number
6346   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
6347
6348   setString(&level_file_info.filename, filename);
6349
6350   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6351 }
6352
6353 static void LoadLevel_InitVersion(struct LevelInfo *level)
6354 {
6355   int i, j;
6356
6357   if (leveldir_current == NULL)         // only when dumping level
6358     return;
6359
6360   // all engine modifications also valid for levels which use latest engine
6361   if (level->game_version < VERSION_IDENT(3,2,0,5))
6362   {
6363     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6364     level->time_score_base = 10;
6365   }
6366
6367   if (leveldir_current->latest_engine)
6368   {
6369     // ---------- use latest game engine --------------------------------------
6370
6371     /* For all levels which are forced to use the latest game engine version
6372        (normally all but user contributed, private and undefined levels), set
6373        the game engine version to the actual version; this allows for actual
6374        corrections in the game engine to take effect for existing, converted
6375        levels (from "classic" or other existing games) to make the emulation
6376        of the corresponding game more accurate, while (hopefully) not breaking
6377        existing levels created from other players. */
6378
6379     level->game_version = GAME_VERSION_ACTUAL;
6380
6381     /* Set special EM style gems behaviour: EM style gems slip down from
6382        normal, steel and growing wall. As this is a more fundamental change,
6383        it seems better to set the default behaviour to "off" (as it is more
6384        natural) and make it configurable in the level editor (as a property
6385        of gem style elements). Already existing converted levels (neither
6386        private nor contributed levels) are changed to the new behaviour. */
6387
6388     if (level->file_version < FILE_VERSION_2_0)
6389       level->em_slippery_gems = TRUE;
6390
6391     return;
6392   }
6393
6394   // ---------- use game engine the level was created with --------------------
6395
6396   /* For all levels which are not forced to use the latest game engine
6397      version (normally user contributed, private and undefined levels),
6398      use the version of the game engine the levels were created for.
6399
6400      Since 2.0.1, the game engine version is now directly stored
6401      in the level file (chunk "VERS"), so there is no need anymore
6402      to set the game version from the file version (except for old,
6403      pre-2.0 levels, where the game version is still taken from the
6404      file format version used to store the level -- see above). */
6405
6406   // player was faster than enemies in 1.0.0 and before
6407   if (level->file_version == FILE_VERSION_1_0)
6408     for (i = 0; i < MAX_PLAYERS; i++)
6409       level->initial_player_stepsize[i] = STEPSIZE_FAST;
6410
6411   // default behaviour for EM style gems was "slippery" only in 2.0.1
6412   if (level->game_version == VERSION_IDENT(2,0,1,0))
6413     level->em_slippery_gems = TRUE;
6414
6415   // springs could be pushed over pits before (pre-release version) 2.2.0
6416   if (level->game_version < VERSION_IDENT(2,2,0,0))
6417     level->use_spring_bug = TRUE;
6418
6419   if (level->game_version < VERSION_IDENT(3,2,0,5))
6420   {
6421     // time orb caused limited time in endless time levels before 3.2.0-5
6422     level->use_time_orb_bug = TRUE;
6423
6424     // default behaviour for snapping was "no snap delay" before 3.2.0-5
6425     level->block_snap_field = FALSE;
6426
6427     // extra time score was same value as time left score before 3.2.0-5
6428     level->extra_time_score = level->score[SC_TIME_BONUS];
6429   }
6430
6431   if (level->game_version < VERSION_IDENT(3,2,0,7))
6432   {
6433     // default behaviour for snapping was "not continuous" before 3.2.0-7
6434     level->continuous_snapping = FALSE;
6435   }
6436
6437   // only few elements were able to actively move into acid before 3.1.0
6438   // trigger settings did not exist before 3.1.0; set to default "any"
6439   if (level->game_version < VERSION_IDENT(3,1,0,0))
6440   {
6441     // correct "can move into acid" settings (all zero in old levels)
6442
6443     level->can_move_into_acid_bits = 0; // nothing can move into acid
6444     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6445
6446     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
6447     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6448     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
6449     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
6450
6451     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6452       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6453
6454     // correct trigger settings (stored as zero == "none" in old levels)
6455
6456     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6457     {
6458       int element = EL_CUSTOM_START + i;
6459       struct ElementInfo *ei = &element_info[element];
6460
6461       for (j = 0; j < ei->num_change_pages; j++)
6462       {
6463         struct ElementChangeInfo *change = &ei->change_page[j];
6464
6465         change->trigger_player = CH_PLAYER_ANY;
6466         change->trigger_page = CH_PAGE_ANY;
6467       }
6468     }
6469   }
6470
6471   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6472   {
6473     int element = EL_CUSTOM_256;
6474     struct ElementInfo *ei = &element_info[element];
6475     struct ElementChangeInfo *change = &ei->change_page[0];
6476
6477     /* This is needed to fix a problem that was caused by a bugfix in function
6478        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6479        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6480        not replace walkable elements, but instead just placed the player on it,
6481        without placing the Sokoban field under the player). Unfortunately, this
6482        breaks "Snake Bite" style levels when the snake is halfway through a door
6483        that just closes (the snake head is still alive and can be moved in this
6484        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6485        player (without Sokoban element) which then gets killed as designed). */
6486
6487     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6488          strncmp(ei->description, "pause b4 death", 14) == 0) &&
6489         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6490       change->target_element = EL_PLAYER_1;
6491   }
6492
6493   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6494   if (level->game_version < VERSION_IDENT(3,2,5,0))
6495   {
6496     /* This is needed to fix a problem that was caused by a bugfix in function
6497        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6498        corrects the behaviour when a custom element changes to another custom
6499        element with a higher element number that has change actions defined.
6500        Normally, only one change per frame is allowed for custom elements.
6501        Therefore, it is checked if a custom element already changed in the
6502        current frame; if it did, subsequent changes are suppressed.
6503        Unfortunately, this is only checked for element changes, but not for
6504        change actions, which are still executed. As the function above loops
6505        through all custom elements from lower to higher, an element change
6506        resulting in a lower CE number won't be checked again, while a target
6507        element with a higher number will also be checked, and potential change
6508        actions will get executed for this CE, too (which is wrong), while
6509        further changes are ignored (which is correct). As this bugfix breaks
6510        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6511        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6512        behaviour for existing levels and tapes that make use of this bug */
6513
6514     level->use_action_after_change_bug = TRUE;
6515   }
6516
6517   // not centering level after relocating player was default only in 3.2.3
6518   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
6519     level->shifted_relocation = TRUE;
6520
6521   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6522   if (level->game_version < VERSION_IDENT(3,2,6,0))
6523     level->em_explodes_by_fire = TRUE;
6524
6525   // levels were solved by the first player entering an exit up to 4.1.0.0
6526   if (level->game_version <= VERSION_IDENT(4,1,0,0))
6527     level->solved_by_one_player = TRUE;
6528
6529   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6530   if (level->game_version < VERSION_IDENT(4,1,1,1))
6531     level->use_life_bugs = TRUE;
6532
6533   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6534   if (level->game_version < VERSION_IDENT(4,1,1,1))
6535     level->sb_objects_needed = FALSE;
6536
6537   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6538   if (level->game_version <= VERSION_IDENT(4,2,2,0))
6539     level->finish_dig_collect = FALSE;
6540
6541   // CE changing to player was kept under the player if walkable up to 4.2.3.1
6542   if (level->game_version <= VERSION_IDENT(4,2,3,1))
6543     level->keep_walkable_ce = TRUE;
6544 }
6545
6546 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6547 {
6548   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
6549   int x, y;
6550
6551   // check if this level is (not) a Sokoban level
6552   for (y = 0; y < level->fieldy; y++)
6553     for (x = 0; x < level->fieldx; x++)
6554       if (!IS_SB_ELEMENT(Tile[x][y]))
6555         is_sokoban_level = FALSE;
6556
6557   if (is_sokoban_level)
6558   {
6559     // set special level settings for Sokoban levels
6560     SetLevelSettings_SB(level);
6561   }
6562 }
6563
6564 static void LoadLevel_InitSettings(struct LevelInfo *level)
6565 {
6566   // adjust level settings for (non-native) Sokoban-style levels
6567   LoadLevel_InitSettings_SB(level);
6568 }
6569
6570 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6571 {
6572   int i, x, y;
6573
6574   // map elements that have changed in newer versions
6575   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6576                                                     level->game_version);
6577   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6578     for (x = 0; x < 3; x++)
6579       for (y = 0; y < 3; y++)
6580         level->yamyam_content[i].e[x][y] =
6581           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6582                                     level->game_version);
6583
6584 }
6585
6586 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6587 {
6588   int i, j;
6589
6590   // map custom element change events that have changed in newer versions
6591   // (these following values were accidentally changed in version 3.0.1)
6592   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6593   if (level->game_version <= VERSION_IDENT(3,0,0,0))
6594   {
6595     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6596     {
6597       int element = EL_CUSTOM_START + i;
6598
6599       // order of checking and copying events to be mapped is important
6600       // (do not change the start and end value -- they are constant)
6601       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6602       {
6603         if (HAS_CHANGE_EVENT(element, j - 2))
6604         {
6605           SET_CHANGE_EVENT(element, j - 2, FALSE);
6606           SET_CHANGE_EVENT(element, j, TRUE);
6607         }
6608       }
6609
6610       // order of checking and copying events to be mapped is important
6611       // (do not change the start and end value -- they are constant)
6612       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6613       {
6614         if (HAS_CHANGE_EVENT(element, j - 1))
6615         {
6616           SET_CHANGE_EVENT(element, j - 1, FALSE);
6617           SET_CHANGE_EVENT(element, j, TRUE);
6618         }
6619       }
6620     }
6621   }
6622
6623   // initialize "can_change" field for old levels with only one change page
6624   if (level->game_version <= VERSION_IDENT(3,0,2,0))
6625   {
6626     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6627     {
6628       int element = EL_CUSTOM_START + i;
6629
6630       if (CAN_CHANGE(element))
6631         element_info[element].change->can_change = TRUE;
6632     }
6633   }
6634
6635   // correct custom element values (for old levels without these options)
6636   if (level->game_version < VERSION_IDENT(3,1,1,0))
6637   {
6638     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6639     {
6640       int element = EL_CUSTOM_START + i;
6641       struct ElementInfo *ei = &element_info[element];
6642
6643       if (ei->access_direction == MV_NO_DIRECTION)
6644         ei->access_direction = MV_ALL_DIRECTIONS;
6645     }
6646   }
6647
6648   // correct custom element values (fix invalid values for all versions)
6649   if (1)
6650   {
6651     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6652     {
6653       int element = EL_CUSTOM_START + i;
6654       struct ElementInfo *ei = &element_info[element];
6655
6656       for (j = 0; j < ei->num_change_pages; j++)
6657       {
6658         struct ElementChangeInfo *change = &ei->change_page[j];
6659
6660         if (change->trigger_player == CH_PLAYER_NONE)
6661           change->trigger_player = CH_PLAYER_ANY;
6662
6663         if (change->trigger_side == CH_SIDE_NONE)
6664           change->trigger_side = CH_SIDE_ANY;
6665       }
6666     }
6667   }
6668
6669   // initialize "can_explode" field for old levels which did not store this
6670   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6671   if (level->game_version <= VERSION_IDENT(3,1,0,0))
6672   {
6673     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6674     {
6675       int element = EL_CUSTOM_START + i;
6676
6677       if (EXPLODES_1X1_OLD(element))
6678         element_info[element].explosion_type = EXPLODES_1X1;
6679
6680       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6681                                              EXPLODES_SMASHED(element) ||
6682                                              EXPLODES_IMPACT(element)));
6683     }
6684   }
6685
6686   // correct previously hard-coded move delay values for maze runner style
6687   if (level->game_version < VERSION_IDENT(3,1,1,0))
6688   {
6689     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6690     {
6691       int element = EL_CUSTOM_START + i;
6692
6693       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6694       {
6695         // previously hard-coded and therefore ignored
6696         element_info[element].move_delay_fixed = 9;
6697         element_info[element].move_delay_random = 0;
6698       }
6699     }
6700   }
6701
6702   // set some other uninitialized values of custom elements in older levels
6703   if (level->game_version < VERSION_IDENT(3,1,0,0))
6704   {
6705     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6706     {
6707       int element = EL_CUSTOM_START + i;
6708
6709       element_info[element].access_direction = MV_ALL_DIRECTIONS;
6710
6711       element_info[element].explosion_delay = 17;
6712       element_info[element].ignition_delay = 8;
6713     }
6714   }
6715
6716   // set mouse click change events to work for left/middle/right mouse button
6717   if (level->game_version < VERSION_IDENT(4,2,3,0))
6718   {
6719     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6720     {
6721       int element = EL_CUSTOM_START + i;
6722       struct ElementInfo *ei = &element_info[element];
6723
6724       for (j = 0; j < ei->num_change_pages; j++)
6725       {
6726         struct ElementChangeInfo *change = &ei->change_page[j];
6727
6728         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6729             change->has_event[CE_PRESSED_BY_MOUSE] ||
6730             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6731             change->has_event[CE_MOUSE_PRESSED_ON_X])
6732           change->trigger_side = CH_SIDE_ANY;
6733       }
6734     }
6735   }
6736 }
6737
6738 static void LoadLevel_InitElements(struct LevelInfo *level)
6739 {
6740   LoadLevel_InitStandardElements(level);
6741
6742   if (level->file_has_custom_elements)
6743     LoadLevel_InitCustomElements(level);
6744
6745   // initialize element properties for level editor etc.
6746   InitElementPropertiesEngine(level->game_version);
6747   InitElementPropertiesGfxElement();
6748 }
6749
6750 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6751 {
6752   int x, y;
6753
6754   // map elements that have changed in newer versions
6755   for (y = 0; y < level->fieldy; y++)
6756     for (x = 0; x < level->fieldx; x++)
6757       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6758                                                      level->game_version);
6759
6760   // clear unused playfield data (nicer if level gets resized in editor)
6761   for (x = 0; x < MAX_LEV_FIELDX; x++)
6762     for (y = 0; y < MAX_LEV_FIELDY; y++)
6763       if (x >= level->fieldx || y >= level->fieldy)
6764         level->field[x][y] = EL_EMPTY;
6765
6766   // copy elements to runtime playfield array
6767   for (x = 0; x < MAX_LEV_FIELDX; x++)
6768     for (y = 0; y < MAX_LEV_FIELDY; y++)
6769       Tile[x][y] = level->field[x][y];
6770
6771   // initialize level size variables for faster access
6772   lev_fieldx = level->fieldx;
6773   lev_fieldy = level->fieldy;
6774
6775   // determine border element for this level
6776   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6777     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
6778   else
6779     SetBorderElement();
6780 }
6781
6782 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6783 {
6784   struct LevelFileInfo *level_file_info = &level->file_info;
6785
6786   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6787     CopyNativeLevel_RND_to_Native(level);
6788 }
6789
6790 static void LoadLevelTemplate_LoadAndInit(void)
6791 {
6792   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6793
6794   LoadLevel_InitVersion(&level_template);
6795   LoadLevel_InitElements(&level_template);
6796   LoadLevel_InitSettings(&level_template);
6797
6798   ActivateLevelTemplate();
6799 }
6800
6801 void LoadLevelTemplate(int nr)
6802 {
6803   if (!fileExists(getGlobalLevelTemplateFilename()))
6804   {
6805     Warn("no level template found for this level");
6806
6807     return;
6808   }
6809
6810   setLevelFileInfo(&level_template.file_info, nr);
6811
6812   LoadLevelTemplate_LoadAndInit();
6813 }
6814
6815 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6816 {
6817   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6818
6819   LoadLevelTemplate_LoadAndInit();
6820 }
6821
6822 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6823 {
6824   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6825
6826   if (level.use_custom_template)
6827   {
6828     if (network_level != NULL)
6829       LoadNetworkLevelTemplate(network_level);
6830     else
6831       LoadLevelTemplate(-1);
6832   }
6833
6834   LoadLevel_InitVersion(&level);
6835   LoadLevel_InitElements(&level);
6836   LoadLevel_InitPlayfield(&level);
6837   LoadLevel_InitSettings(&level);
6838
6839   LoadLevel_InitNativeEngines(&level);
6840 }
6841
6842 void LoadLevel(int nr)
6843 {
6844   SetLevelSetInfo(leveldir_current->identifier, nr);
6845
6846   setLevelFileInfo(&level.file_info, nr);
6847
6848   LoadLevel_LoadAndInit(NULL);
6849 }
6850
6851 void LoadLevelInfoOnly(int nr)
6852 {
6853   setLevelFileInfo(&level.file_info, nr);
6854
6855   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6856 }
6857
6858 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6859 {
6860   SetLevelSetInfo(network_level->leveldir_identifier,
6861                   network_level->file_info.nr);
6862
6863   copyLevelFileInfo(&network_level->file_info, &level.file_info);
6864
6865   LoadLevel_LoadAndInit(network_level);
6866 }
6867
6868 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6869 {
6870   int chunk_size = 0;
6871
6872   chunk_size += putFileVersion(file, level->file_version);
6873   chunk_size += putFileVersion(file, level->game_version);
6874
6875   return chunk_size;
6876 }
6877
6878 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6879 {
6880   int chunk_size = 0;
6881
6882   chunk_size += putFile16BitBE(file, level->creation_date.year);
6883   chunk_size += putFile8Bit(file,    level->creation_date.month);
6884   chunk_size += putFile8Bit(file,    level->creation_date.day);
6885
6886   return chunk_size;
6887 }
6888
6889 #if ENABLE_HISTORIC_CHUNKS
6890 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6891 {
6892   int i, x, y;
6893
6894   putFile8Bit(file, level->fieldx);
6895   putFile8Bit(file, level->fieldy);
6896
6897   putFile16BitBE(file, level->time);
6898   putFile16BitBE(file, level->gems_needed);
6899
6900   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6901     putFile8Bit(file, level->name[i]);
6902
6903   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6904     putFile8Bit(file, level->score[i]);
6905
6906   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6907     for (y = 0; y < 3; y++)
6908       for (x = 0; x < 3; x++)
6909         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6910                            level->yamyam_content[i].e[x][y]));
6911   putFile8Bit(file, level->amoeba_speed);
6912   putFile8Bit(file, level->time_magic_wall);
6913   putFile8Bit(file, level->time_wheel);
6914   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6915                      level->amoeba_content));
6916   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6917   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6918   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6919   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6920
6921   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6922
6923   putFile8Bit(file, (level->block_last_field ? 1 : 0));
6924   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6925   putFile32BitBE(file, level->can_move_into_acid_bits);
6926   putFile8Bit(file, level->dont_collide_with_bits);
6927
6928   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6929   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6930
6931   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6932   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6933   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6934
6935   putFile8Bit(file, level->game_engine_type);
6936
6937   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6938 }
6939 #endif
6940
6941 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6942 {
6943   int chunk_size = 0;
6944   int i;
6945
6946   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6947     chunk_size += putFile8Bit(file, level->name[i]);
6948
6949   return chunk_size;
6950 }
6951
6952 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6953 {
6954   int chunk_size = 0;
6955   int i;
6956
6957   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6958     chunk_size += putFile8Bit(file, level->author[i]);
6959
6960   return chunk_size;
6961 }
6962
6963 #if ENABLE_HISTORIC_CHUNKS
6964 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6965 {
6966   int chunk_size = 0;
6967   int x, y;
6968
6969   for (y = 0; y < level->fieldy; y++)
6970     for (x = 0; x < level->fieldx; x++)
6971       if (level->encoding_16bit_field)
6972         chunk_size += putFile16BitBE(file, level->field[x][y]);
6973       else
6974         chunk_size += putFile8Bit(file, level->field[x][y]);
6975
6976   return chunk_size;
6977 }
6978 #endif
6979
6980 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6981 {
6982   int chunk_size = 0;
6983   int x, y;
6984
6985   for (y = 0; y < level->fieldy; y++) 
6986     for (x = 0; x < level->fieldx; x++) 
6987       chunk_size += putFile16BitBE(file, level->field[x][y]);
6988
6989   return chunk_size;
6990 }
6991
6992 #if ENABLE_HISTORIC_CHUNKS
6993 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6994 {
6995   int i, x, y;
6996
6997   putFile8Bit(file, EL_YAMYAM);
6998   putFile8Bit(file, level->num_yamyam_contents);
6999   putFile8Bit(file, 0);
7000   putFile8Bit(file, 0);
7001
7002   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7003     for (y = 0; y < 3; y++)
7004       for (x = 0; x < 3; x++)
7005         if (level->encoding_16bit_field)
7006           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7007         else
7008           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7009 }
7010 #endif
7011
7012 #if ENABLE_HISTORIC_CHUNKS
7013 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7014 {
7015   int i, x, y;
7016   int num_contents, content_xsize, content_ysize;
7017   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7018
7019   if (element == EL_YAMYAM)
7020   {
7021     num_contents = level->num_yamyam_contents;
7022     content_xsize = 3;
7023     content_ysize = 3;
7024
7025     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7026       for (y = 0; y < 3; y++)
7027         for (x = 0; x < 3; x++)
7028           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7029   }
7030   else if (element == EL_BD_AMOEBA)
7031   {
7032     num_contents = 1;
7033     content_xsize = 1;
7034     content_ysize = 1;
7035
7036     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7037       for (y = 0; y < 3; y++)
7038         for (x = 0; x < 3; x++)
7039           content_array[i][x][y] = EL_EMPTY;
7040     content_array[0][0][0] = level->amoeba_content;
7041   }
7042   else
7043   {
7044     // chunk header already written -- write empty chunk data
7045     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7046
7047     Warn("cannot save content for element '%d'", element);
7048
7049     return;
7050   }
7051
7052   putFile16BitBE(file, element);
7053   putFile8Bit(file, num_contents);
7054   putFile8Bit(file, content_xsize);
7055   putFile8Bit(file, content_ysize);
7056
7057   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7058
7059   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7060     for (y = 0; y < 3; y++)
7061       for (x = 0; x < 3; x++)
7062         putFile16BitBE(file, content_array[i][x][y]);
7063 }
7064 #endif
7065
7066 #if ENABLE_HISTORIC_CHUNKS
7067 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7068 {
7069   int envelope_nr = element - EL_ENVELOPE_1;
7070   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7071   int chunk_size = 0;
7072   int i;
7073
7074   chunk_size += putFile16BitBE(file, element);
7075   chunk_size += putFile16BitBE(file, envelope_len);
7076   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7077   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7078
7079   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7080   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7081
7082   for (i = 0; i < envelope_len; i++)
7083     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7084
7085   return chunk_size;
7086 }
7087 #endif
7088
7089 #if ENABLE_HISTORIC_CHUNKS
7090 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7091                            int num_changed_custom_elements)
7092 {
7093   int i, check = 0;
7094
7095   putFile16BitBE(file, num_changed_custom_elements);
7096
7097   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7098   {
7099     int element = EL_CUSTOM_START + i;
7100
7101     struct ElementInfo *ei = &element_info[element];
7102
7103     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7104     {
7105       if (check < num_changed_custom_elements)
7106       {
7107         putFile16BitBE(file, element);
7108         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7109       }
7110
7111       check++;
7112     }
7113   }
7114
7115   if (check != num_changed_custom_elements)     // should not happen
7116     Warn("inconsistent number of custom element properties");
7117 }
7118 #endif
7119
7120 #if ENABLE_HISTORIC_CHUNKS
7121 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7122                            int num_changed_custom_elements)
7123 {
7124   int i, check = 0;
7125
7126   putFile16BitBE(file, num_changed_custom_elements);
7127
7128   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7129   {
7130     int element = EL_CUSTOM_START + i;
7131
7132     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7133     {
7134       if (check < num_changed_custom_elements)
7135       {
7136         putFile16BitBE(file, element);
7137         putFile16BitBE(file, element_info[element].change->target_element);
7138       }
7139
7140       check++;
7141     }
7142   }
7143
7144   if (check != num_changed_custom_elements)     // should not happen
7145     Warn("inconsistent number of custom target elements");
7146 }
7147 #endif
7148
7149 #if ENABLE_HISTORIC_CHUNKS
7150 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7151                            int num_changed_custom_elements)
7152 {
7153   int i, j, x, y, check = 0;
7154
7155   putFile16BitBE(file, num_changed_custom_elements);
7156
7157   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7158   {
7159     int element = EL_CUSTOM_START + i;
7160     struct ElementInfo *ei = &element_info[element];
7161
7162     if (ei->modified_settings)
7163     {
7164       if (check < num_changed_custom_elements)
7165       {
7166         putFile16BitBE(file, element);
7167
7168         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7169           putFile8Bit(file, ei->description[j]);
7170
7171         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7172
7173         // some free bytes for future properties and padding
7174         WriteUnusedBytesToFile(file, 7);
7175
7176         putFile8Bit(file, ei->use_gfx_element);
7177         putFile16BitBE(file, ei->gfx_element_initial);
7178
7179         putFile8Bit(file, ei->collect_score_initial);
7180         putFile8Bit(file, ei->collect_count_initial);
7181
7182         putFile16BitBE(file, ei->push_delay_fixed);
7183         putFile16BitBE(file, ei->push_delay_random);
7184         putFile16BitBE(file, ei->move_delay_fixed);
7185         putFile16BitBE(file, ei->move_delay_random);
7186
7187         putFile16BitBE(file, ei->move_pattern);
7188         putFile8Bit(file, ei->move_direction_initial);
7189         putFile8Bit(file, ei->move_stepsize);
7190
7191         for (y = 0; y < 3; y++)
7192           for (x = 0; x < 3; x++)
7193             putFile16BitBE(file, ei->content.e[x][y]);
7194
7195         putFile32BitBE(file, ei->change->events);
7196
7197         putFile16BitBE(file, ei->change->target_element);
7198
7199         putFile16BitBE(file, ei->change->delay_fixed);
7200         putFile16BitBE(file, ei->change->delay_random);
7201         putFile16BitBE(file, ei->change->delay_frames);
7202
7203         putFile16BitBE(file, ei->change->initial_trigger_element);
7204
7205         putFile8Bit(file, ei->change->explode);
7206         putFile8Bit(file, ei->change->use_target_content);
7207         putFile8Bit(file, ei->change->only_if_complete);
7208         putFile8Bit(file, ei->change->use_random_replace);
7209
7210         putFile8Bit(file, ei->change->random_percentage);
7211         putFile8Bit(file, ei->change->replace_when);
7212
7213         for (y = 0; y < 3; y++)
7214           for (x = 0; x < 3; x++)
7215             putFile16BitBE(file, ei->change->content.e[x][y]);
7216
7217         putFile8Bit(file, ei->slippery_type);
7218
7219         // some free bytes for future properties and padding
7220         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7221       }
7222
7223       check++;
7224     }
7225   }
7226
7227   if (check != num_changed_custom_elements)     // should not happen
7228     Warn("inconsistent number of custom element properties");
7229 }
7230 #endif
7231
7232 #if ENABLE_HISTORIC_CHUNKS
7233 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7234 {
7235   struct ElementInfo *ei = &element_info[element];
7236   int i, j, x, y;
7237
7238   // ---------- custom element base property values (96 bytes) ----------------
7239
7240   putFile16BitBE(file, element);
7241
7242   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7243     putFile8Bit(file, ei->description[i]);
7244
7245   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7246
7247   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
7248
7249   putFile8Bit(file, ei->num_change_pages);
7250
7251   putFile16BitBE(file, ei->ce_value_fixed_initial);
7252   putFile16BitBE(file, ei->ce_value_random_initial);
7253   putFile8Bit(file, ei->use_last_ce_value);
7254
7255   putFile8Bit(file, ei->use_gfx_element);
7256   putFile16BitBE(file, ei->gfx_element_initial);
7257
7258   putFile8Bit(file, ei->collect_score_initial);
7259   putFile8Bit(file, ei->collect_count_initial);
7260
7261   putFile8Bit(file, ei->drop_delay_fixed);
7262   putFile8Bit(file, ei->push_delay_fixed);
7263   putFile8Bit(file, ei->drop_delay_random);
7264   putFile8Bit(file, ei->push_delay_random);
7265   putFile16BitBE(file, ei->move_delay_fixed);
7266   putFile16BitBE(file, ei->move_delay_random);
7267
7268   // bits 0 - 15 of "move_pattern" ...
7269   putFile16BitBE(file, ei->move_pattern & 0xffff);
7270   putFile8Bit(file, ei->move_direction_initial);
7271   putFile8Bit(file, ei->move_stepsize);
7272
7273   putFile8Bit(file, ei->slippery_type);
7274
7275   for (y = 0; y < 3; y++)
7276     for (x = 0; x < 3; x++)
7277       putFile16BitBE(file, ei->content.e[x][y]);
7278
7279   putFile16BitBE(file, ei->move_enter_element);
7280   putFile16BitBE(file, ei->move_leave_element);
7281   putFile8Bit(file, ei->move_leave_type);
7282
7283   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7284   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7285
7286   putFile8Bit(file, ei->access_direction);
7287
7288   putFile8Bit(file, ei->explosion_delay);
7289   putFile8Bit(file, ei->ignition_delay);
7290   putFile8Bit(file, ei->explosion_type);
7291
7292   // some free bytes for future custom property values and padding
7293   WriteUnusedBytesToFile(file, 1);
7294
7295   // ---------- change page property values (48 bytes) ------------------------
7296
7297   for (i = 0; i < ei->num_change_pages; i++)
7298   {
7299     struct ElementChangeInfo *change = &ei->change_page[i];
7300     unsigned int event_bits;
7301
7302     // bits 0 - 31 of "has_event[]" ...
7303     event_bits = 0;
7304     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7305       if (change->has_event[j])
7306         event_bits |= (1 << j);
7307     putFile32BitBE(file, event_bits);
7308
7309     putFile16BitBE(file, change->target_element);
7310
7311     putFile16BitBE(file, change->delay_fixed);
7312     putFile16BitBE(file, change->delay_random);
7313     putFile16BitBE(file, change->delay_frames);
7314
7315     putFile16BitBE(file, change->initial_trigger_element);
7316
7317     putFile8Bit(file, change->explode);
7318     putFile8Bit(file, change->use_target_content);
7319     putFile8Bit(file, change->only_if_complete);
7320     putFile8Bit(file, change->use_random_replace);
7321
7322     putFile8Bit(file, change->random_percentage);
7323     putFile8Bit(file, change->replace_when);
7324
7325     for (y = 0; y < 3; y++)
7326       for (x = 0; x < 3; x++)
7327         putFile16BitBE(file, change->target_content.e[x][y]);
7328
7329     putFile8Bit(file, change->can_change);
7330
7331     putFile8Bit(file, change->trigger_side);
7332
7333     putFile8Bit(file, change->trigger_player);
7334     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7335                        log_2(change->trigger_page)));
7336
7337     putFile8Bit(file, change->has_action);
7338     putFile8Bit(file, change->action_type);
7339     putFile8Bit(file, change->action_mode);
7340     putFile16BitBE(file, change->action_arg);
7341
7342     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7343     event_bits = 0;
7344     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7345       if (change->has_event[j])
7346         event_bits |= (1 << (j - 32));
7347     putFile8Bit(file, event_bits);
7348   }
7349 }
7350 #endif
7351
7352 #if ENABLE_HISTORIC_CHUNKS
7353 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7354 {
7355   struct ElementInfo *ei = &element_info[element];
7356   struct ElementGroupInfo *group = ei->group;
7357   int i;
7358
7359   putFile16BitBE(file, element);
7360
7361   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7362     putFile8Bit(file, ei->description[i]);
7363
7364   putFile8Bit(file, group->num_elements);
7365
7366   putFile8Bit(file, ei->use_gfx_element);
7367   putFile16BitBE(file, ei->gfx_element_initial);
7368
7369   putFile8Bit(file, group->choice_mode);
7370
7371   // some free bytes for future values and padding
7372   WriteUnusedBytesToFile(file, 3);
7373
7374   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7375     putFile16BitBE(file, group->element[i]);
7376 }
7377 #endif
7378
7379 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7380                                 boolean write_element)
7381 {
7382   int save_type = entry->save_type;
7383   int data_type = entry->data_type;
7384   int conf_type = entry->conf_type;
7385   int byte_mask = conf_type & CONF_MASK_BYTES;
7386   int element = entry->element;
7387   int default_value = entry->default_value;
7388   int num_bytes = 0;
7389   boolean modified = FALSE;
7390
7391   if (byte_mask != CONF_MASK_MULTI_BYTES)
7392   {
7393     void *value_ptr = entry->value;
7394     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7395                  *(int *)value_ptr);
7396
7397     // check if any settings have been modified before saving them
7398     if (value != default_value)
7399       modified = TRUE;
7400
7401     // do not save if explicitly told or if unmodified default settings
7402     if ((save_type == SAVE_CONF_NEVER) ||
7403         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7404       return 0;
7405
7406     if (write_element)
7407       num_bytes += putFile16BitBE(file, element);
7408
7409     num_bytes += putFile8Bit(file, conf_type);
7410     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
7411                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7412                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7413                   0);
7414   }
7415   else if (data_type == TYPE_STRING)
7416   {
7417     char *default_string = entry->default_string;
7418     char *string = (char *)(entry->value);
7419     int string_length = strlen(string);
7420     int i;
7421
7422     // check if any settings have been modified before saving them
7423     if (!strEqual(string, default_string))
7424       modified = TRUE;
7425
7426     // do not save if explicitly told or if unmodified default settings
7427     if ((save_type == SAVE_CONF_NEVER) ||
7428         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7429       return 0;
7430
7431     if (write_element)
7432       num_bytes += putFile16BitBE(file, element);
7433
7434     num_bytes += putFile8Bit(file, conf_type);
7435     num_bytes += putFile16BitBE(file, string_length);
7436
7437     for (i = 0; i < string_length; i++)
7438       num_bytes += putFile8Bit(file, string[i]);
7439   }
7440   else if (data_type == TYPE_ELEMENT_LIST)
7441   {
7442     int *element_array = (int *)(entry->value);
7443     int num_elements = *(int *)(entry->num_entities);
7444     int i;
7445
7446     // check if any settings have been modified before saving them
7447     for (i = 0; i < num_elements; i++)
7448       if (element_array[i] != default_value)
7449         modified = TRUE;
7450
7451     // do not save if explicitly told or if unmodified default settings
7452     if ((save_type == SAVE_CONF_NEVER) ||
7453         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7454       return 0;
7455
7456     if (write_element)
7457       num_bytes += putFile16BitBE(file, element);
7458
7459     num_bytes += putFile8Bit(file, conf_type);
7460     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7461
7462     for (i = 0; i < num_elements; i++)
7463       num_bytes += putFile16BitBE(file, element_array[i]);
7464   }
7465   else if (data_type == TYPE_CONTENT_LIST)
7466   {
7467     struct Content *content = (struct Content *)(entry->value);
7468     int num_contents = *(int *)(entry->num_entities);
7469     int i, x, y;
7470
7471     // check if any settings have been modified before saving them
7472     for (i = 0; i < num_contents; i++)
7473       for (y = 0; y < 3; y++)
7474         for (x = 0; x < 3; x++)
7475           if (content[i].e[x][y] != default_value)
7476             modified = TRUE;
7477
7478     // do not save if explicitly told or if unmodified default settings
7479     if ((save_type == SAVE_CONF_NEVER) ||
7480         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7481       return 0;
7482
7483     if (write_element)
7484       num_bytes += putFile16BitBE(file, element);
7485
7486     num_bytes += putFile8Bit(file, conf_type);
7487     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7488
7489     for (i = 0; i < num_contents; i++)
7490       for (y = 0; y < 3; y++)
7491         for (x = 0; x < 3; x++)
7492           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7493   }
7494
7495   return num_bytes;
7496 }
7497
7498 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7499 {
7500   int chunk_size = 0;
7501   int i;
7502
7503   li = *level;          // copy level data into temporary buffer
7504
7505   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7506     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7507
7508   return chunk_size;
7509 }
7510
7511 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7512 {
7513   int chunk_size = 0;
7514   int i;
7515
7516   li = *level;          // copy level data into temporary buffer
7517
7518   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7519     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7520
7521   return chunk_size;
7522 }
7523
7524 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7525 {
7526   int envelope_nr = element - EL_ENVELOPE_1;
7527   int chunk_size = 0;
7528   int i;
7529
7530   chunk_size += putFile16BitBE(file, element);
7531
7532   // copy envelope data into temporary buffer
7533   xx_envelope = level->envelope[envelope_nr];
7534
7535   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7536     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7537
7538   return chunk_size;
7539 }
7540
7541 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7542 {
7543   struct ElementInfo *ei = &element_info[element];
7544   int chunk_size = 0;
7545   int i, j;
7546
7547   chunk_size += putFile16BitBE(file, element);
7548
7549   xx_ei = *ei;          // copy element data into temporary buffer
7550
7551   // set default description string for this specific element
7552   strcpy(xx_default_description, getDefaultElementDescription(ei));
7553
7554   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7555     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7556
7557   for (i = 0; i < ei->num_change_pages; i++)
7558   {
7559     struct ElementChangeInfo *change = &ei->change_page[i];
7560
7561     xx_current_change_page = i;
7562
7563     xx_change = *change;        // copy change data into temporary buffer
7564
7565     resetEventBits();
7566     setEventBitsFromEventFlags(change);
7567
7568     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7569       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7570                                          FALSE);
7571   }
7572
7573   return chunk_size;
7574 }
7575
7576 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7577 {
7578   struct ElementInfo *ei = &element_info[element];
7579   struct ElementGroupInfo *group = ei->group;
7580   int chunk_size = 0;
7581   int i;
7582
7583   chunk_size += putFile16BitBE(file, element);
7584
7585   xx_ei = *ei;          // copy element data into temporary buffer
7586   xx_group = *group;    // copy group data into temporary buffer
7587
7588   // set default description string for this specific element
7589   strcpy(xx_default_description, getDefaultElementDescription(ei));
7590
7591   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7592     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7593
7594   return chunk_size;
7595 }
7596
7597 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7598 {
7599   struct ElementInfo *ei = &element_info[element];
7600   int chunk_size = 0;
7601   int i;
7602
7603   chunk_size += putFile16BitBE(file, element);
7604
7605   xx_ei = *ei;          // copy element data into temporary buffer
7606
7607   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7608     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7609
7610   return chunk_size;
7611 }
7612
7613 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7614                                   boolean save_as_template)
7615 {
7616   int chunk_size;
7617   int i;
7618   FILE *file;
7619
7620   if (!(file = fopen(filename, MODE_WRITE)))
7621   {
7622     Warn("cannot save level file '%s'", filename);
7623
7624     return;
7625   }
7626
7627   level->file_version = FILE_VERSION_ACTUAL;
7628   level->game_version = GAME_VERSION_ACTUAL;
7629
7630   level->creation_date = getCurrentDate();
7631
7632   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7633   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7634
7635   chunk_size = SaveLevel_VERS(NULL, level);
7636   putFileChunkBE(file, "VERS", chunk_size);
7637   SaveLevel_VERS(file, level);
7638
7639   chunk_size = SaveLevel_DATE(NULL, level);
7640   putFileChunkBE(file, "DATE", chunk_size);
7641   SaveLevel_DATE(file, level);
7642
7643   chunk_size = SaveLevel_NAME(NULL, level);
7644   putFileChunkBE(file, "NAME", chunk_size);
7645   SaveLevel_NAME(file, level);
7646
7647   chunk_size = SaveLevel_AUTH(NULL, level);
7648   putFileChunkBE(file, "AUTH", chunk_size);
7649   SaveLevel_AUTH(file, level);
7650
7651   chunk_size = SaveLevel_INFO(NULL, level);
7652   putFileChunkBE(file, "INFO", chunk_size);
7653   SaveLevel_INFO(file, level);
7654
7655   chunk_size = SaveLevel_BODY(NULL, level);
7656   putFileChunkBE(file, "BODY", chunk_size);
7657   SaveLevel_BODY(file, level);
7658
7659   chunk_size = SaveLevel_ELEM(NULL, level);
7660   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
7661   {
7662     putFileChunkBE(file, "ELEM", chunk_size);
7663     SaveLevel_ELEM(file, level);
7664   }
7665
7666   for (i = 0; i < NUM_ENVELOPES; i++)
7667   {
7668     int element = EL_ENVELOPE_1 + i;
7669
7670     chunk_size = SaveLevel_NOTE(NULL, level, element);
7671     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
7672     {
7673       putFileChunkBE(file, "NOTE", chunk_size);
7674       SaveLevel_NOTE(file, level, element);
7675     }
7676   }
7677
7678   // if not using template level, check for non-default custom/group elements
7679   if (!level->use_custom_template || save_as_template)
7680   {
7681     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7682     {
7683       int element = EL_CUSTOM_START + i;
7684
7685       chunk_size = SaveLevel_CUSX(NULL, level, element);
7686       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
7687       {
7688         putFileChunkBE(file, "CUSX", chunk_size);
7689         SaveLevel_CUSX(file, level, element);
7690       }
7691     }
7692
7693     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7694     {
7695       int element = EL_GROUP_START + i;
7696
7697       chunk_size = SaveLevel_GRPX(NULL, level, element);
7698       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
7699       {
7700         putFileChunkBE(file, "GRPX", chunk_size);
7701         SaveLevel_GRPX(file, level, element);
7702       }
7703     }
7704
7705     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7706     {
7707       int element = GET_EMPTY_ELEMENT(i);
7708
7709       chunk_size = SaveLevel_EMPX(NULL, level, element);
7710       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
7711       {
7712         putFileChunkBE(file, "EMPX", chunk_size);
7713         SaveLevel_EMPX(file, level, element);
7714       }
7715     }
7716   }
7717
7718   fclose(file);
7719
7720   SetFilePermissions(filename, PERMS_PRIVATE);
7721 }
7722
7723 void SaveLevel(int nr)
7724 {
7725   char *filename = getDefaultLevelFilename(nr);
7726
7727   SaveLevelFromFilename(&level, filename, FALSE);
7728 }
7729
7730 void SaveLevelTemplate(void)
7731 {
7732   char *filename = getLocalLevelTemplateFilename();
7733
7734   SaveLevelFromFilename(&level, filename, TRUE);
7735 }
7736
7737 boolean SaveLevelChecked(int nr)
7738 {
7739   char *filename = getDefaultLevelFilename(nr);
7740   boolean new_level = !fileExists(filename);
7741   boolean level_saved = FALSE;
7742
7743   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7744   {
7745     SaveLevel(nr);
7746
7747     if (new_level)
7748       Request("Level saved!", REQ_CONFIRM);
7749
7750     level_saved = TRUE;
7751   }
7752
7753   return level_saved;
7754 }
7755
7756 void DumpLevel(struct LevelInfo *level)
7757 {
7758   if (level->no_level_file || level->no_valid_file)
7759   {
7760     Warn("cannot dump -- no valid level file found");
7761
7762     return;
7763   }
7764
7765   PrintLine("-", 79);
7766   Print("Level xxx (file version %08d, game version %08d)\n",
7767         level->file_version, level->game_version);
7768   PrintLine("-", 79);
7769
7770   Print("Level author: '%s'\n", level->author);
7771   Print("Level title:  '%s'\n", level->name);
7772   Print("\n");
7773   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7774   Print("\n");
7775   Print("Level time:  %d seconds\n", level->time);
7776   Print("Gems needed: %d\n", level->gems_needed);
7777   Print("\n");
7778   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7779   Print("Time for wheel:      %d seconds\n", level->time_wheel);
7780   Print("Time for light:      %d seconds\n", level->time_light);
7781   Print("Time for timegate:   %d seconds\n", level->time_timegate);
7782   Print("\n");
7783   Print("Amoeba speed: %d\n", level->amoeba_speed);
7784   Print("\n");
7785
7786   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
7787   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
7788   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7789   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7790   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7791   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7792
7793   PrintLine("-", 79);
7794 }
7795
7796 void DumpLevels(void)
7797 {
7798   static LevelDirTree *dumplevel_leveldir = NULL;
7799
7800   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7801                                                  global.dumplevel_leveldir);
7802
7803   if (dumplevel_leveldir == NULL)
7804     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7805
7806   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7807       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7808     Fail("no such level number: %d", global.dumplevel_level_nr);
7809
7810   leveldir_current = dumplevel_leveldir;
7811
7812   LoadLevel(global.dumplevel_level_nr);
7813   DumpLevel(&level);
7814
7815   CloseAllAndExit(0);
7816 }
7817
7818
7819 // ============================================================================
7820 // tape file functions
7821 // ============================================================================
7822
7823 static void setTapeInfoToDefaults(void)
7824 {
7825   int i;
7826
7827   // always start with reliable default values (empty tape)
7828   TapeErase();
7829
7830   // default values (also for pre-1.2 tapes) with only the first player
7831   tape.player_participates[0] = TRUE;
7832   for (i = 1; i < MAX_PLAYERS; i++)
7833     tape.player_participates[i] = FALSE;
7834
7835   // at least one (default: the first) player participates in every tape
7836   tape.num_participating_players = 1;
7837
7838   tape.property_bits = TAPE_PROPERTY_NONE;
7839
7840   tape.level_nr = level_nr;
7841   tape.counter = 0;
7842   tape.changed = FALSE;
7843
7844   tape.recording = FALSE;
7845   tape.playing = FALSE;
7846   tape.pausing = FALSE;
7847
7848   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7849   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7850
7851   tape.no_info_chunk = TRUE;
7852   tape.no_valid_file = FALSE;
7853 }
7854
7855 static int getTapePosSize(struct TapeInfo *tape)
7856 {
7857   int tape_pos_size = 0;
7858
7859   if (tape->use_key_actions)
7860     tape_pos_size += tape->num_participating_players;
7861
7862   if (tape->use_mouse_actions)
7863     tape_pos_size += 3;         // x and y position and mouse button mask
7864
7865   tape_pos_size += 1;           // tape action delay value
7866
7867   return tape_pos_size;
7868 }
7869
7870 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7871 {
7872   tape->use_key_actions = FALSE;
7873   tape->use_mouse_actions = FALSE;
7874
7875   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7876     tape->use_key_actions = TRUE;
7877
7878   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7879     tape->use_mouse_actions = TRUE;
7880 }
7881
7882 static int getTapeActionValue(struct TapeInfo *tape)
7883 {
7884   return (tape->use_key_actions &&
7885           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7886           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
7887           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7888           TAPE_ACTIONS_DEFAULT);
7889 }
7890
7891 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7892 {
7893   tape->file_version = getFileVersion(file);
7894   tape->game_version = getFileVersion(file);
7895
7896   return chunk_size;
7897 }
7898
7899 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7900 {
7901   int i;
7902
7903   tape->random_seed = getFile32BitBE(file);
7904   tape->date        = getFile32BitBE(file);
7905   tape->length      = getFile32BitBE(file);
7906
7907   // read header fields that are new since version 1.2
7908   if (tape->file_version >= FILE_VERSION_1_2)
7909   {
7910     byte store_participating_players = getFile8Bit(file);
7911     int engine_version;
7912
7913     // since version 1.2, tapes store which players participate in the tape
7914     tape->num_participating_players = 0;
7915     for (i = 0; i < MAX_PLAYERS; i++)
7916     {
7917       tape->player_participates[i] = FALSE;
7918
7919       if (store_participating_players & (1 << i))
7920       {
7921         tape->player_participates[i] = TRUE;
7922         tape->num_participating_players++;
7923       }
7924     }
7925
7926     setTapeActionFlags(tape, getFile8Bit(file));
7927
7928     tape->property_bits = getFile8Bit(file);
7929
7930     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7931
7932     engine_version = getFileVersion(file);
7933     if (engine_version > 0)
7934       tape->engine_version = engine_version;
7935     else
7936       tape->engine_version = tape->game_version;
7937   }
7938
7939   return chunk_size;
7940 }
7941
7942 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7943 {
7944   tape->scr_fieldx = getFile8Bit(file);
7945   tape->scr_fieldy = getFile8Bit(file);
7946
7947   return chunk_size;
7948 }
7949
7950 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7951 {
7952   char *level_identifier = NULL;
7953   int level_identifier_size;
7954   int i;
7955
7956   tape->no_info_chunk = FALSE;
7957
7958   level_identifier_size = getFile16BitBE(file);
7959
7960   level_identifier = checked_malloc(level_identifier_size);
7961
7962   for (i = 0; i < level_identifier_size; i++)
7963     level_identifier[i] = getFile8Bit(file);
7964
7965   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
7966   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
7967
7968   checked_free(level_identifier);
7969
7970   tape->level_nr = getFile16BitBE(file);
7971
7972   chunk_size = 2 + level_identifier_size + 2;
7973
7974   return chunk_size;
7975 }
7976
7977 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7978 {
7979   int i, j;
7980   int tape_pos_size = getTapePosSize(tape);
7981   int chunk_size_expected = tape_pos_size * tape->length;
7982
7983   if (chunk_size_expected != chunk_size)
7984   {
7985     ReadUnusedBytesFromFile(file, chunk_size);
7986     return chunk_size_expected;
7987   }
7988
7989   for (i = 0; i < tape->length; i++)
7990   {
7991     if (i >= MAX_TAPE_LEN)
7992     {
7993       Warn("tape truncated -- size exceeds maximum tape size %d",
7994             MAX_TAPE_LEN);
7995
7996       // tape too large; read and ignore remaining tape data from this chunk
7997       for (;i < tape->length; i++)
7998         ReadUnusedBytesFromFile(file, tape_pos_size);
7999
8000       break;
8001     }
8002
8003     if (tape->use_key_actions)
8004     {
8005       for (j = 0; j < MAX_PLAYERS; j++)
8006       {
8007         tape->pos[i].action[j] = MV_NONE;
8008
8009         if (tape->player_participates[j])
8010           tape->pos[i].action[j] = getFile8Bit(file);
8011       }
8012     }
8013
8014     if (tape->use_mouse_actions)
8015     {
8016       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
8017       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
8018       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8019     }
8020
8021     tape->pos[i].delay = getFile8Bit(file);
8022
8023     if (tape->file_version == FILE_VERSION_1_0)
8024     {
8025       // eliminate possible diagonal moves in old tapes
8026       // this is only for backward compatibility
8027
8028       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8029       byte action = tape->pos[i].action[0];
8030       int k, num_moves = 0;
8031
8032       for (k = 0; k<4; k++)
8033       {
8034         if (action & joy_dir[k])
8035         {
8036           tape->pos[i + num_moves].action[0] = joy_dir[k];
8037           if (num_moves > 0)
8038             tape->pos[i + num_moves].delay = 0;
8039           num_moves++;
8040         }
8041       }
8042
8043       if (num_moves > 1)
8044       {
8045         num_moves--;
8046         i += num_moves;
8047         tape->length += num_moves;
8048       }
8049     }
8050     else if (tape->file_version < FILE_VERSION_2_0)
8051     {
8052       // convert pre-2.0 tapes to new tape format
8053
8054       if (tape->pos[i].delay > 1)
8055       {
8056         // action part
8057         tape->pos[i + 1] = tape->pos[i];
8058         tape->pos[i + 1].delay = 1;
8059
8060         // delay part
8061         for (j = 0; j < MAX_PLAYERS; j++)
8062           tape->pos[i].action[j] = MV_NONE;
8063         tape->pos[i].delay--;
8064
8065         i++;
8066         tape->length++;
8067       }
8068     }
8069
8070     if (checkEndOfFile(file))
8071       break;
8072   }
8073
8074   if (i != tape->length)
8075     chunk_size = tape_pos_size * i;
8076
8077   return chunk_size;
8078 }
8079
8080 static void LoadTape_SokobanSolution(char *filename)
8081 {
8082   File *file;
8083   int move_delay = TILESIZE / level.initial_player_stepsize[0];
8084
8085   if (!(file = openFile(filename, MODE_READ)))
8086   {
8087     tape.no_valid_file = TRUE;
8088
8089     return;
8090   }
8091
8092   while (!checkEndOfFile(file))
8093   {
8094     unsigned char c = getByteFromFile(file);
8095
8096     if (checkEndOfFile(file))
8097       break;
8098
8099     switch (c)
8100     {
8101       case 'u':
8102       case 'U':
8103         tape.pos[tape.length].action[0] = MV_UP;
8104         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8105         tape.length++;
8106         break;
8107
8108       case 'd':
8109       case 'D':
8110         tape.pos[tape.length].action[0] = MV_DOWN;
8111         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8112         tape.length++;
8113         break;
8114
8115       case 'l':
8116       case 'L':
8117         tape.pos[tape.length].action[0] = MV_LEFT;
8118         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8119         tape.length++;
8120         break;
8121
8122       case 'r':
8123       case 'R':
8124         tape.pos[tape.length].action[0] = MV_RIGHT;
8125         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8126         tape.length++;
8127         break;
8128
8129       case '\n':
8130       case '\r':
8131       case '\t':
8132       case ' ':
8133         // ignore white-space characters
8134         break;
8135
8136       default:
8137         tape.no_valid_file = TRUE;
8138
8139         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8140
8141         break;
8142     }
8143   }
8144
8145   closeFile(file);
8146
8147   if (tape.no_valid_file)
8148     return;
8149
8150   tape.length_frames  = GetTapeLengthFrames();
8151   tape.length_seconds = GetTapeLengthSeconds();
8152 }
8153
8154 void LoadTapeFromFilename(char *filename)
8155 {
8156   char cookie[MAX_LINE_LEN];
8157   char chunk_name[CHUNK_ID_LEN + 1];
8158   File *file;
8159   int chunk_size;
8160
8161   // always start with reliable default values
8162   setTapeInfoToDefaults();
8163
8164   if (strSuffix(filename, ".sln"))
8165   {
8166     LoadTape_SokobanSolution(filename);
8167
8168     return;
8169   }
8170
8171   if (!(file = openFile(filename, MODE_READ)))
8172   {
8173     tape.no_valid_file = TRUE;
8174
8175     return;
8176   }
8177
8178   getFileChunkBE(file, chunk_name, NULL);
8179   if (strEqual(chunk_name, "RND1"))
8180   {
8181     getFile32BitBE(file);               // not used
8182
8183     getFileChunkBE(file, chunk_name, NULL);
8184     if (!strEqual(chunk_name, "TAPE"))
8185     {
8186       tape.no_valid_file = TRUE;
8187
8188       Warn("unknown format of tape file '%s'", filename);
8189
8190       closeFile(file);
8191
8192       return;
8193     }
8194   }
8195   else  // check for pre-2.0 file format with cookie string
8196   {
8197     strcpy(cookie, chunk_name);
8198     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8199       cookie[4] = '\0';
8200     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8201       cookie[strlen(cookie) - 1] = '\0';
8202
8203     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8204     {
8205       tape.no_valid_file = TRUE;
8206
8207       Warn("unknown format of tape file '%s'", filename);
8208
8209       closeFile(file);
8210
8211       return;
8212     }
8213
8214     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8215     {
8216       tape.no_valid_file = TRUE;
8217
8218       Warn("unsupported version of tape file '%s'", filename);
8219
8220       closeFile(file);
8221
8222       return;
8223     }
8224
8225     // pre-2.0 tape files have no game version, so use file version here
8226     tape.game_version = tape.file_version;
8227   }
8228
8229   if (tape.file_version < FILE_VERSION_1_2)
8230   {
8231     // tape files from versions before 1.2.0 without chunk structure
8232     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8233     LoadTape_BODY(file, 2 * tape.length,      &tape);
8234   }
8235   else
8236   {
8237     static struct
8238     {
8239       char *name;
8240       int size;
8241       int (*loader)(File *, int, struct TapeInfo *);
8242     }
8243     chunk_info[] =
8244     {
8245       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
8246       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
8247       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
8248       { "INFO", -1,                     LoadTape_INFO },
8249       { "BODY", -1,                     LoadTape_BODY },
8250       {  NULL,  0,                      NULL }
8251     };
8252
8253     while (getFileChunkBE(file, chunk_name, &chunk_size))
8254     {
8255       int i = 0;
8256
8257       while (chunk_info[i].name != NULL &&
8258              !strEqual(chunk_name, chunk_info[i].name))
8259         i++;
8260
8261       if (chunk_info[i].name == NULL)
8262       {
8263         Warn("unknown chunk '%s' in tape file '%s'",
8264               chunk_name, filename);
8265
8266         ReadUnusedBytesFromFile(file, chunk_size);
8267       }
8268       else if (chunk_info[i].size != -1 &&
8269                chunk_info[i].size != chunk_size)
8270       {
8271         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8272               chunk_size, chunk_name, filename);
8273
8274         ReadUnusedBytesFromFile(file, chunk_size);
8275       }
8276       else
8277       {
8278         // call function to load this tape chunk
8279         int chunk_size_expected =
8280           (chunk_info[i].loader)(file, chunk_size, &tape);
8281
8282         // the size of some chunks cannot be checked before reading other
8283         // chunks first (like "HEAD" and "BODY") that contain some header
8284         // information, so check them here
8285         if (chunk_size_expected != chunk_size)
8286         {
8287           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8288                 chunk_size, chunk_name, filename);
8289         }
8290       }
8291     }
8292   }
8293
8294   closeFile(file);
8295
8296   tape.length_frames  = GetTapeLengthFrames();
8297   tape.length_seconds = GetTapeLengthSeconds();
8298
8299 #if 0
8300   Debug("files:LoadTapeFromFilename", "tape file version: %d",
8301         tape.file_version);
8302   Debug("files:LoadTapeFromFilename", "tape game version: %d",
8303         tape.game_version);
8304   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8305         tape.engine_version);
8306 #endif
8307 }
8308
8309 void LoadTape(int nr)
8310 {
8311   char *filename = getTapeFilename(nr);
8312
8313   LoadTapeFromFilename(filename);
8314 }
8315
8316 void LoadSolutionTape(int nr)
8317 {
8318   char *filename = getSolutionTapeFilename(nr);
8319
8320   LoadTapeFromFilename(filename);
8321
8322   if (TAPE_IS_EMPTY(tape) &&
8323       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8324       level.native_sp_level->demo.is_available)
8325     CopyNativeTape_SP_to_RND(&level);
8326 }
8327
8328 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8329 {
8330   // chunk required for team mode tapes with non-default screen size
8331   return (tape->num_participating_players > 1 &&
8332           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8333            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8334 }
8335
8336 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8337 {
8338   putFileVersion(file, tape->file_version);
8339   putFileVersion(file, tape->game_version);
8340 }
8341
8342 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8343 {
8344   int i;
8345   byte store_participating_players = 0;
8346
8347   // set bits for participating players for compact storage
8348   for (i = 0; i < MAX_PLAYERS; i++)
8349     if (tape->player_participates[i])
8350       store_participating_players |= (1 << i);
8351
8352   putFile32BitBE(file, tape->random_seed);
8353   putFile32BitBE(file, tape->date);
8354   putFile32BitBE(file, tape->length);
8355
8356   putFile8Bit(file, store_participating_players);
8357
8358   putFile8Bit(file, getTapeActionValue(tape));
8359
8360   putFile8Bit(file, tape->property_bits);
8361
8362   // unused bytes not at the end here for 4-byte alignment of engine_version
8363   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8364
8365   putFileVersion(file, tape->engine_version);
8366 }
8367
8368 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8369 {
8370   putFile8Bit(file, tape->scr_fieldx);
8371   putFile8Bit(file, tape->scr_fieldy);
8372 }
8373
8374 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8375 {
8376   int level_identifier_size = strlen(tape->level_identifier) + 1;
8377   int i;
8378
8379   putFile16BitBE(file, level_identifier_size);
8380
8381   for (i = 0; i < level_identifier_size; i++)
8382     putFile8Bit(file, tape->level_identifier[i]);
8383
8384   putFile16BitBE(file, tape->level_nr);
8385 }
8386
8387 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8388 {
8389   int i, j;
8390
8391   for (i = 0; i < tape->length; i++)
8392   {
8393     if (tape->use_key_actions)
8394     {
8395       for (j = 0; j < MAX_PLAYERS; j++)
8396         if (tape->player_participates[j])
8397           putFile8Bit(file, tape->pos[i].action[j]);
8398     }
8399
8400     if (tape->use_mouse_actions)
8401     {
8402       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8403       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8404       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8405     }
8406
8407     putFile8Bit(file, tape->pos[i].delay);
8408   }
8409 }
8410
8411 void SaveTapeToFilename(char *filename)
8412 {
8413   FILE *file;
8414   int tape_pos_size;
8415   int info_chunk_size;
8416   int body_chunk_size;
8417
8418   if (!(file = fopen(filename, MODE_WRITE)))
8419   {
8420     Warn("cannot save level recording file '%s'", filename);
8421
8422     return;
8423   }
8424
8425   tape_pos_size = getTapePosSize(&tape);
8426
8427   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8428   body_chunk_size = tape_pos_size * tape.length;
8429
8430   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8431   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8432
8433   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8434   SaveTape_VERS(file, &tape);
8435
8436   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8437   SaveTape_HEAD(file, &tape);
8438
8439   if (checkSaveTape_SCRN(&tape))
8440   {
8441     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8442     SaveTape_SCRN(file, &tape);
8443   }
8444
8445   putFileChunkBE(file, "INFO", info_chunk_size);
8446   SaveTape_INFO(file, &tape);
8447
8448   putFileChunkBE(file, "BODY", body_chunk_size);
8449   SaveTape_BODY(file, &tape);
8450
8451   fclose(file);
8452
8453   SetFilePermissions(filename, PERMS_PRIVATE);
8454 }
8455
8456 static void SaveTapeExt(char *filename)
8457 {
8458   int i;
8459
8460   tape.file_version = FILE_VERSION_ACTUAL;
8461   tape.game_version = GAME_VERSION_ACTUAL;
8462
8463   tape.num_participating_players = 0;
8464
8465   // count number of participating players
8466   for (i = 0; i < MAX_PLAYERS; i++)
8467     if (tape.player_participates[i])
8468       tape.num_participating_players++;
8469
8470   SaveTapeToFilename(filename);
8471
8472   tape.changed = FALSE;
8473 }
8474
8475 void SaveTape(int nr)
8476 {
8477   char *filename = getTapeFilename(nr);
8478
8479   InitTapeDirectory(leveldir_current->subdir);
8480
8481   SaveTapeExt(filename);
8482 }
8483
8484 void SaveScoreTape(int nr)
8485 {
8486   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8487
8488   // used instead of "leveldir_current->subdir" (for network games)
8489   InitScoreTapeDirectory(levelset.identifier, nr);
8490
8491   SaveTapeExt(filename);
8492 }
8493
8494 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8495                                   unsigned int req_state_added)
8496 {
8497   char *filename = getTapeFilename(nr);
8498   boolean new_tape = !fileExists(filename);
8499   boolean tape_saved = FALSE;
8500
8501   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8502   {
8503     SaveTape(nr);
8504
8505     if (new_tape)
8506       Request(msg_saved, REQ_CONFIRM | req_state_added);
8507
8508     tape_saved = TRUE;
8509   }
8510
8511   return tape_saved;
8512 }
8513
8514 boolean SaveTapeChecked(int nr)
8515 {
8516   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8517 }
8518
8519 boolean SaveTapeChecked_LevelSolved(int nr)
8520 {
8521   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8522                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
8523 }
8524
8525 void DumpTape(struct TapeInfo *tape)
8526 {
8527   int tape_frame_counter;
8528   int i, j;
8529
8530   if (tape->no_valid_file)
8531   {
8532     Warn("cannot dump -- no valid tape file found");
8533
8534     return;
8535   }
8536
8537   PrintLine("-", 79);
8538
8539   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8540         tape->level_nr, tape->file_version, tape->game_version);
8541   Print("                  (effective engine version %08d)\n",
8542         tape->engine_version);
8543   Print("Level series identifier: '%s'\n", tape->level_identifier);
8544
8545   Print("Special tape properties: ");
8546   if (tape->property_bits == TAPE_PROPERTY_NONE)
8547     Print("[none]");
8548   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8549     Print("[em_random_bug]");
8550   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8551     Print("[game_speed]");
8552   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8553     Print("[pause]");
8554   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8555     Print("[single_step]");
8556   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8557     Print("[snapshot]");
8558   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8559     Print("[replayed]");
8560   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8561     Print("[tas_keys]");
8562   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8563     Print("[small_graphics]");
8564   Print("\n");
8565
8566   int year2 = tape->date / 10000;
8567   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8568   int month_index_raw = (tape->date / 100) % 100;
8569   int month_index = month_index_raw % 12;       // prevent invalid index
8570   int month = month_index + 1;
8571   int day = tape->date % 100;
8572
8573   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8574
8575   PrintLine("-", 79);
8576
8577   tape_frame_counter = 0;
8578
8579   for (i = 0; i < tape->length; i++)
8580   {
8581     if (i >= MAX_TAPE_LEN)
8582       break;
8583
8584     Print("%04d: ", i);
8585
8586     for (j = 0; j < MAX_PLAYERS; j++)
8587     {
8588       if (tape->player_participates[j])
8589       {
8590         int action = tape->pos[i].action[j];
8591
8592         Print("%d:%02x ", j, action);
8593         Print("[%c%c%c%c|%c%c] - ",
8594               (action & JOY_LEFT ? '<' : ' '),
8595               (action & JOY_RIGHT ? '>' : ' '),
8596               (action & JOY_UP ? '^' : ' '),
8597               (action & JOY_DOWN ? 'v' : ' '),
8598               (action & JOY_BUTTON_1 ? '1' : ' '),
8599               (action & JOY_BUTTON_2 ? '2' : ' '));
8600       }
8601     }
8602
8603     Print("(%03d) ", tape->pos[i].delay);
8604     Print("[%05d]\n", tape_frame_counter);
8605
8606     tape_frame_counter += tape->pos[i].delay;
8607   }
8608
8609   PrintLine("-", 79);
8610 }
8611
8612 void DumpTapes(void)
8613 {
8614   static LevelDirTree *dumptape_leveldir = NULL;
8615
8616   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8617                                                 global.dumptape_leveldir);
8618
8619   if (dumptape_leveldir == NULL)
8620     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8621
8622   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8623       global.dumptape_level_nr > dumptape_leveldir->last_level)
8624     Fail("no such level number: %d", global.dumptape_level_nr);
8625
8626   leveldir_current = dumptape_leveldir;
8627
8628   if (options.mytapes)
8629     LoadTape(global.dumptape_level_nr);
8630   else
8631     LoadSolutionTape(global.dumptape_level_nr);
8632
8633   DumpTape(&tape);
8634
8635   CloseAllAndExit(0);
8636 }
8637
8638
8639 // ============================================================================
8640 // score file functions
8641 // ============================================================================
8642
8643 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8644 {
8645   int i;
8646
8647   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8648   {
8649     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8650     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8651     scores->entry[i].score = 0;
8652     scores->entry[i].time = 0;
8653
8654     scores->entry[i].id = -1;
8655     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
8656     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
8657     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
8658     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8659     strcpy(scores->entry[i].country_code, "??");
8660   }
8661
8662   scores->num_entries = 0;
8663   scores->last_added = -1;
8664   scores->last_added_local = -1;
8665
8666   scores->updated = FALSE;
8667   scores->uploaded = FALSE;
8668   scores->force_last_added = FALSE;
8669 }
8670
8671 static void setScoreInfoToDefaults(void)
8672 {
8673   setScoreInfoToDefaultsExt(&scores);
8674 }
8675
8676 static void setServerScoreInfoToDefaults(void)
8677 {
8678   setScoreInfoToDefaultsExt(&server_scores);
8679 }
8680
8681 static void LoadScore_OLD(int nr)
8682 {
8683   int i;
8684   char *filename = getScoreFilename(nr);
8685   char cookie[MAX_LINE_LEN];
8686   char line[MAX_LINE_LEN];
8687   char *line_ptr;
8688   FILE *file;
8689
8690   if (!(file = fopen(filename, MODE_READ)))
8691     return;
8692
8693   // check file identifier
8694   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8695     cookie[0] = '\0';
8696   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8697     cookie[strlen(cookie) - 1] = '\0';
8698
8699   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8700   {
8701     Warn("unknown format of score file '%s'", filename);
8702
8703     fclose(file);
8704
8705     return;
8706   }
8707
8708   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8709   {
8710     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8711       Warn("fscanf() failed; %s", strerror(errno));
8712
8713     if (fgets(line, MAX_LINE_LEN, file) == NULL)
8714       line[0] = '\0';
8715
8716     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8717       line[strlen(line) - 1] = '\0';
8718
8719     for (line_ptr = line; *line_ptr; line_ptr++)
8720     {
8721       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8722       {
8723         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8724         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8725         break;
8726       }
8727     }
8728   }
8729
8730   fclose(file);
8731 }
8732
8733 static void ConvertScore_OLD(void)
8734 {
8735   // only convert score to time for levels that rate playing time over score
8736   if (!level.rate_time_over_score)
8737     return;
8738
8739   // convert old score to playing time for score-less levels (like Supaplex)
8740   int time_final_max = 999;
8741   int i;
8742
8743   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8744   {
8745     int score = scores.entry[i].score;
8746
8747     if (score > 0 && score < time_final_max)
8748       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8749   }
8750 }
8751
8752 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8753 {
8754   scores->file_version = getFileVersion(file);
8755   scores->game_version = getFileVersion(file);
8756
8757   return chunk_size;
8758 }
8759
8760 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8761 {
8762   char *level_identifier = NULL;
8763   int level_identifier_size;
8764   int i;
8765
8766   level_identifier_size = getFile16BitBE(file);
8767
8768   level_identifier = checked_malloc(level_identifier_size);
8769
8770   for (i = 0; i < level_identifier_size; i++)
8771     level_identifier[i] = getFile8Bit(file);
8772
8773   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8774   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8775
8776   checked_free(level_identifier);
8777
8778   scores->level_nr = getFile16BitBE(file);
8779   scores->num_entries = getFile16BitBE(file);
8780
8781   chunk_size = 2 + level_identifier_size + 2 + 2;
8782
8783   return chunk_size;
8784 }
8785
8786 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8787 {
8788   int i, j;
8789
8790   for (i = 0; i < scores->num_entries; i++)
8791   {
8792     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8793       scores->entry[i].name[j] = getFile8Bit(file);
8794
8795     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8796   }
8797
8798   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8799
8800   return chunk_size;
8801 }
8802
8803 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8804 {
8805   int i;
8806
8807   for (i = 0; i < scores->num_entries; i++)
8808     scores->entry[i].score = getFile16BitBE(file);
8809
8810   chunk_size = scores->num_entries * 2;
8811
8812   return chunk_size;
8813 }
8814
8815 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8816 {
8817   int i;
8818
8819   for (i = 0; i < scores->num_entries; i++)
8820     scores->entry[i].score = getFile32BitBE(file);
8821
8822   chunk_size = scores->num_entries * 4;
8823
8824   return chunk_size;
8825 }
8826
8827 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8828 {
8829   int i;
8830
8831   for (i = 0; i < scores->num_entries; i++)
8832     scores->entry[i].time = getFile32BitBE(file);
8833
8834   chunk_size = scores->num_entries * 4;
8835
8836   return chunk_size;
8837 }
8838
8839 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8840 {
8841   int i, j;
8842
8843   for (i = 0; i < scores->num_entries; i++)
8844   {
8845     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8846       scores->entry[i].tape_basename[j] = getFile8Bit(file);
8847
8848     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8849   }
8850
8851   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8852
8853   return chunk_size;
8854 }
8855
8856 void LoadScore(int nr)
8857 {
8858   char *filename = getScoreFilename(nr);
8859   char cookie[MAX_LINE_LEN];
8860   char chunk_name[CHUNK_ID_LEN + 1];
8861   int chunk_size;
8862   boolean old_score_file_format = FALSE;
8863   File *file;
8864
8865   // always start with reliable default values
8866   setScoreInfoToDefaults();
8867
8868   if (!(file = openFile(filename, MODE_READ)))
8869     return;
8870
8871   getFileChunkBE(file, chunk_name, NULL);
8872   if (strEqual(chunk_name, "RND1"))
8873   {
8874     getFile32BitBE(file);               // not used
8875
8876     getFileChunkBE(file, chunk_name, NULL);
8877     if (!strEqual(chunk_name, "SCOR"))
8878     {
8879       Warn("unknown format of score file '%s'", filename);
8880
8881       closeFile(file);
8882
8883       return;
8884     }
8885   }
8886   else  // check for old file format with cookie string
8887   {
8888     strcpy(cookie, chunk_name);
8889     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8890       cookie[4] = '\0';
8891     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8892       cookie[strlen(cookie) - 1] = '\0';
8893
8894     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8895     {
8896       Warn("unknown format of score file '%s'", filename);
8897
8898       closeFile(file);
8899
8900       return;
8901     }
8902
8903     old_score_file_format = TRUE;
8904   }
8905
8906   if (old_score_file_format)
8907   {
8908     // score files from versions before 4.2.4.0 without chunk structure
8909     LoadScore_OLD(nr);
8910
8911     // convert score to time, if possible (mainly for Supaplex levels)
8912     ConvertScore_OLD();
8913   }
8914   else
8915   {
8916     static struct
8917     {
8918       char *name;
8919       int size;
8920       int (*loader)(File *, int, struct ScoreInfo *);
8921     }
8922     chunk_info[] =
8923     {
8924       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
8925       { "INFO", -1,                     LoadScore_INFO },
8926       { "NAME", -1,                     LoadScore_NAME },
8927       { "SCOR", -1,                     LoadScore_SCOR },
8928       { "SC4R", -1,                     LoadScore_SC4R },
8929       { "TIME", -1,                     LoadScore_TIME },
8930       { "TAPE", -1,                     LoadScore_TAPE },
8931
8932       {  NULL,  0,                      NULL }
8933     };
8934
8935     while (getFileChunkBE(file, chunk_name, &chunk_size))
8936     {
8937       int i = 0;
8938
8939       while (chunk_info[i].name != NULL &&
8940              !strEqual(chunk_name, chunk_info[i].name))
8941         i++;
8942
8943       if (chunk_info[i].name == NULL)
8944       {
8945         Warn("unknown chunk '%s' in score file '%s'",
8946               chunk_name, filename);
8947
8948         ReadUnusedBytesFromFile(file, chunk_size);
8949       }
8950       else if (chunk_info[i].size != -1 &&
8951                chunk_info[i].size != chunk_size)
8952       {
8953         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8954               chunk_size, chunk_name, filename);
8955
8956         ReadUnusedBytesFromFile(file, chunk_size);
8957       }
8958       else
8959       {
8960         // call function to load this score chunk
8961         int chunk_size_expected =
8962           (chunk_info[i].loader)(file, chunk_size, &scores);
8963
8964         // the size of some chunks cannot be checked before reading other
8965         // chunks first (like "HEAD" and "BODY") that contain some header
8966         // information, so check them here
8967         if (chunk_size_expected != chunk_size)
8968         {
8969           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8970                 chunk_size, chunk_name, filename);
8971         }
8972       }
8973     }
8974   }
8975
8976   closeFile(file);
8977 }
8978
8979 #if ENABLE_HISTORIC_CHUNKS
8980 void SaveScore_OLD(int nr)
8981 {
8982   int i;
8983   char *filename = getScoreFilename(nr);
8984   FILE *file;
8985
8986   // used instead of "leveldir_current->subdir" (for network games)
8987   InitScoreDirectory(levelset.identifier);
8988
8989   if (!(file = fopen(filename, MODE_WRITE)))
8990   {
8991     Warn("cannot save score for level %d", nr);
8992
8993     return;
8994   }
8995
8996   fprintf(file, "%s\n\n", SCORE_COOKIE);
8997
8998   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8999     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9000
9001   fclose(file);
9002
9003   SetFilePermissions(filename, PERMS_PRIVATE);
9004 }
9005 #endif
9006
9007 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9008 {
9009   putFileVersion(file, scores->file_version);
9010   putFileVersion(file, scores->game_version);
9011 }
9012
9013 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9014 {
9015   int level_identifier_size = strlen(scores->level_identifier) + 1;
9016   int i;
9017
9018   putFile16BitBE(file, level_identifier_size);
9019
9020   for (i = 0; i < level_identifier_size; i++)
9021     putFile8Bit(file, scores->level_identifier[i]);
9022
9023   putFile16BitBE(file, scores->level_nr);
9024   putFile16BitBE(file, scores->num_entries);
9025 }
9026
9027 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9028 {
9029   int i, j;
9030
9031   for (i = 0; i < scores->num_entries; i++)
9032   {
9033     int name_size = strlen(scores->entry[i].name);
9034
9035     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9036       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9037   }
9038 }
9039
9040 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9041 {
9042   int i;
9043
9044   for (i = 0; i < scores->num_entries; i++)
9045     putFile16BitBE(file, scores->entry[i].score);
9046 }
9047
9048 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9049 {
9050   int i;
9051
9052   for (i = 0; i < scores->num_entries; i++)
9053     putFile32BitBE(file, scores->entry[i].score);
9054 }
9055
9056 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9057 {
9058   int i;
9059
9060   for (i = 0; i < scores->num_entries; i++)
9061     putFile32BitBE(file, scores->entry[i].time);
9062 }
9063
9064 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9065 {
9066   int i, j;
9067
9068   for (i = 0; i < scores->num_entries; i++)
9069   {
9070     int size = strlen(scores->entry[i].tape_basename);
9071
9072     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9073       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9074   }
9075 }
9076
9077 static void SaveScoreToFilename(char *filename)
9078 {
9079   FILE *file;
9080   int info_chunk_size;
9081   int name_chunk_size;
9082   int scor_chunk_size;
9083   int sc4r_chunk_size;
9084   int time_chunk_size;
9085   int tape_chunk_size;
9086   boolean has_large_score_values;
9087   int i;
9088
9089   if (!(file = fopen(filename, MODE_WRITE)))
9090   {
9091     Warn("cannot save score file '%s'", filename);
9092
9093     return;
9094   }
9095
9096   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9097   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9098   scor_chunk_size = scores.num_entries * 2;
9099   sc4r_chunk_size = scores.num_entries * 4;
9100   time_chunk_size = scores.num_entries * 4;
9101   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9102
9103   has_large_score_values = FALSE;
9104   for (i = 0; i < scores.num_entries; i++)
9105     if (scores.entry[i].score > 0xffff)
9106       has_large_score_values = TRUE;
9107
9108   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9109   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9110
9111   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9112   SaveScore_VERS(file, &scores);
9113
9114   putFileChunkBE(file, "INFO", info_chunk_size);
9115   SaveScore_INFO(file, &scores);
9116
9117   putFileChunkBE(file, "NAME", name_chunk_size);
9118   SaveScore_NAME(file, &scores);
9119
9120   if (has_large_score_values)
9121   {
9122     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9123     SaveScore_SC4R(file, &scores);
9124   }
9125   else
9126   {
9127     putFileChunkBE(file, "SCOR", scor_chunk_size);
9128     SaveScore_SCOR(file, &scores);
9129   }
9130
9131   putFileChunkBE(file, "TIME", time_chunk_size);
9132   SaveScore_TIME(file, &scores);
9133
9134   putFileChunkBE(file, "TAPE", tape_chunk_size);
9135   SaveScore_TAPE(file, &scores);
9136
9137   fclose(file);
9138
9139   SetFilePermissions(filename, PERMS_PRIVATE);
9140 }
9141
9142 void SaveScore(int nr)
9143 {
9144   char *filename = getScoreFilename(nr);
9145   int i;
9146
9147   // used instead of "leveldir_current->subdir" (for network games)
9148   InitScoreDirectory(levelset.identifier);
9149
9150   scores.file_version = FILE_VERSION_ACTUAL;
9151   scores.game_version = GAME_VERSION_ACTUAL;
9152
9153   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9154   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9155   scores.level_nr = level_nr;
9156
9157   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9158     if (scores.entry[i].score == 0 &&
9159         scores.entry[i].time == 0 &&
9160         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9161       break;
9162
9163   scores.num_entries = i;
9164
9165   if (scores.num_entries == 0)
9166     return;
9167
9168   SaveScoreToFilename(filename);
9169 }
9170
9171 void ExecuteAsThread(SDL_ThreadFunction function, char *name, void *data,
9172                      char *error)
9173 {
9174 #if defined(PLATFORM_EMSCRIPTEN)
9175   // threads currently not fully supported by Emscripten/SDL and some browsers
9176   function(data);
9177 #else
9178   SDL_Thread *thread = SDL_CreateThread(function, name, data);
9179
9180   if (thread != NULL)
9181     SDL_DetachThread(thread);
9182   else
9183     Error("Cannot create thread to %s!", error);
9184
9185   // nasty kludge to lower probability of intermingled thread error messages
9186   Delay(1);
9187 #endif
9188 }
9189
9190 char *getPasswordJSON(char *password)
9191 {
9192   static char password_json[MAX_FILENAME_LEN] = "";
9193   static boolean initialized = FALSE;
9194
9195   if (!initialized)
9196   {
9197     if (password != NULL &&
9198         !strEqual(password, "") &&
9199         !strEqual(password, UNDEFINED_PASSWORD))
9200       snprintf(password_json, MAX_FILENAME_LEN,
9201                "  \"password\":             \"%s\",\n",
9202                setup.api_server_password);
9203
9204     initialized = TRUE;
9205   }
9206
9207   return password_json;
9208 }
9209
9210 struct ApiGetScoreThreadData
9211 {
9212   int level_nr;
9213   char *score_cache_filename;
9214 };
9215
9216 static void *CreateThreadData_ApiGetScore(int nr)
9217 {
9218   struct ApiGetScoreThreadData *data =
9219     checked_malloc(sizeof(struct ApiGetScoreThreadData));
9220   char *score_cache_filename = getScoreCacheFilename(nr);
9221
9222   data->level_nr = nr;
9223   data->score_cache_filename = getStringCopy(score_cache_filename);
9224
9225   return data;
9226 }
9227
9228 static void FreeThreadData_ApiGetScore(void *data_raw)
9229 {
9230   struct ApiGetScoreThreadData *data = data_raw;
9231
9232   checked_free(data->score_cache_filename);
9233   checked_free(data);
9234 }
9235
9236 static boolean SetRequest_ApiGetScore(struct HttpRequest *request,
9237                                       void *data_raw)
9238 {
9239   struct ApiGetScoreThreadData *data = data_raw;
9240   int level_nr = data->level_nr;
9241
9242   request->hostname = setup.api_server_hostname;
9243   request->port     = API_SERVER_PORT;
9244   request->method   = API_SERVER_METHOD;
9245   request->uri      = API_SERVER_URI_GET;
9246
9247   char *levelset_identifier = getEscapedJSON(leveldir_current->identifier);
9248   char *levelset_name       = getEscapedJSON(leveldir_current->name);
9249
9250   snprintf(request->body, MAX_HTTP_BODY_SIZE,
9251            "{\n"
9252            "%s"
9253            "  \"game_version\":         \"%s\",\n"
9254            "  \"game_platform\":        \"%s\",\n"
9255            "  \"levelset_identifier\":  \"%s\",\n"
9256            "  \"levelset_name\":        \"%s\",\n"
9257            "  \"level_nr\":             \"%d\"\n"
9258            "}\n",
9259            getPasswordJSON(setup.api_server_password),
9260            getProgramRealVersionString(),
9261            getProgramPlatformString(),
9262            levelset_identifier,
9263            levelset_name,
9264            level_nr);
9265
9266   checked_free(levelset_identifier);
9267   checked_free(levelset_name);
9268
9269   ConvertHttpRequestBodyToServerEncoding(request);
9270
9271   return TRUE;
9272 }
9273
9274 static void HandleResponse_ApiGetScore(struct HttpResponse *response,
9275                                        void *data_raw)
9276 {
9277   struct ApiGetScoreThreadData *data = data_raw;
9278
9279   if (response->body_size == 0)
9280   {
9281     // no scores available for this level
9282
9283     return;
9284   }
9285
9286   ConvertHttpResponseBodyToClientEncoding(response);
9287
9288   char *filename = data->score_cache_filename;
9289   FILE *file;
9290   int i;
9291
9292   // used instead of "leveldir_current->subdir" (for network games)
9293   InitScoreCacheDirectory(levelset.identifier);
9294
9295   if (!(file = fopen(filename, MODE_WRITE)))
9296   {
9297     Warn("cannot save score cache file '%s'", filename);
9298
9299     return;
9300   }
9301
9302   for (i = 0; i < response->body_size; i++)
9303     fputc(response->body[i], file);
9304
9305   fclose(file);
9306
9307   SetFilePermissions(filename, PERMS_PRIVATE);
9308
9309   server_scores.updated = TRUE;
9310 }
9311
9312 #if defined(PLATFORM_EMSCRIPTEN)
9313 static void Emscripten_ApiGetScore_Loaded(unsigned handle, void *data_raw,
9314                                           void *buffer, unsigned int size)
9315 {
9316   struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
9317
9318   if (response != NULL)
9319   {
9320     HandleResponse_ApiGetScore(response, data_raw);
9321
9322     checked_free(response);
9323   }
9324   else
9325   {
9326     Error("server response too large to handle (%d bytes)", size);
9327   }
9328
9329   FreeThreadData_ApiGetScore(data_raw);
9330 }
9331
9332 static void Emscripten_ApiGetScore_Failed(unsigned handle, void *data_raw,
9333                                           int code, const char *status)
9334 {
9335   Error("server failed to handle request: %d %s", code, status);
9336
9337   FreeThreadData_ApiGetScore(data_raw);
9338 }
9339
9340 static void Emscripten_ApiGetScore_Progress(unsigned handle, void *data_raw,
9341                                             int bytes, int size)
9342 {
9343   // nothing to do here
9344 }
9345
9346 static void Emscripten_ApiGetScore_HttpRequest(struct HttpRequest *request,
9347                                                void *data_raw)
9348 {
9349   if (!SetRequest_ApiGetScore(request, data_raw))
9350   {
9351     FreeThreadData_ApiGetScore(data_raw);
9352
9353     return;
9354   }
9355
9356   emscripten_async_wget2_data(request->uri,
9357                               request->method,
9358                               request->body,
9359                               data_raw,
9360                               TRUE,
9361                               Emscripten_ApiGetScore_Loaded,
9362                               Emscripten_ApiGetScore_Failed,
9363                               Emscripten_ApiGetScore_Progress);
9364 }
9365
9366 #else
9367
9368 static void ApiGetScore_HttpRequestExt(struct HttpRequest *request,
9369                                        struct HttpResponse *response,
9370                                        void *data_raw)
9371 {
9372   if (!SetRequest_ApiGetScore(request, data_raw))
9373     return;
9374
9375   if (!DoHttpRequest(request, response))
9376   {
9377     Error("HTTP request failed: %s", GetHttpError());
9378
9379     return;
9380   }
9381
9382   if (!HTTP_SUCCESS(response->status_code))
9383   {
9384     // do not show error message if no scores found for this level set
9385     if (response->status_code == 404)
9386       return;
9387
9388     Error("server failed to handle request: %d %s",
9389           response->status_code,
9390           response->status_text);
9391
9392     return;
9393   }
9394
9395   HandleResponse_ApiGetScore(response, data_raw);
9396 }
9397
9398 static void ApiGetScore_HttpRequest(struct HttpRequest *request,
9399                                     struct HttpResponse *response,
9400                                     void *data_raw)
9401 {
9402   ApiGetScore_HttpRequestExt(request, response, data_raw);
9403
9404   FreeThreadData_ApiGetScore(data_raw);
9405 }
9406 #endif
9407
9408 static int ApiGetScoreThread(void *data_raw)
9409 {
9410   struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
9411   struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
9412
9413   program.api_thread_count++;
9414
9415 #if defined(PLATFORM_EMSCRIPTEN)
9416   Emscripten_ApiGetScore_HttpRequest(request, data_raw);
9417 #else
9418   ApiGetScore_HttpRequest(request, response, data_raw);
9419 #endif
9420
9421   program.api_thread_count--;
9422
9423   checked_free(request);
9424   checked_free(response);
9425
9426   return 0;
9427 }
9428
9429 static void ApiGetScoreAsThread(int nr)
9430 {
9431   struct ApiGetScoreThreadData *data = CreateThreadData_ApiGetScore(nr);
9432
9433   ExecuteAsThread(ApiGetScoreThread,
9434                   "ApiGetScore", data,
9435                   "download scores from server");
9436 }
9437
9438 static void LoadServerScoreFromCache(int nr)
9439 {
9440   struct ScoreEntry score_entry;
9441   struct
9442   {
9443     void *value;
9444     boolean is_string;
9445     int string_size;
9446   }
9447   score_mapping[] =
9448   {
9449     { &score_entry.score,               FALSE,  0                       },
9450     { &score_entry.time,                FALSE,  0                       },
9451     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
9452     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
9453     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
9454     { &score_entry.id,                  FALSE,  0                       },
9455     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
9456     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
9457     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
9458     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
9459
9460     { NULL,                             FALSE,  0                       }
9461   };
9462   char *filename = getScoreCacheFilename(nr);
9463   SetupFileHash *score_hash = loadSetupFileHash(filename);
9464   int i, j;
9465
9466   server_scores.num_entries = 0;
9467
9468   if (score_hash == NULL)
9469     return;
9470
9471   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9472   {
9473     score_entry = server_scores.entry[i];
9474
9475     for (j = 0; score_mapping[j].value != NULL; j++)
9476     {
9477       char token[10];
9478
9479       sprintf(token, "%02d.%d", i, j);
9480
9481       char *value = getHashEntry(score_hash, token);
9482
9483       if (value == NULL)
9484         continue;
9485
9486       if (score_mapping[j].is_string)
9487       {
9488         char *score_value = (char *)score_mapping[j].value;
9489         int value_size = score_mapping[j].string_size;
9490
9491         strncpy(score_value, value, value_size);
9492         score_value[value_size] = '\0';
9493       }
9494       else
9495       {
9496         int *score_value = (int *)score_mapping[j].value;
9497
9498         *score_value = atoi(value);
9499       }
9500
9501       server_scores.num_entries = i + 1;
9502     }
9503
9504     server_scores.entry[i] = score_entry;
9505   }
9506
9507   freeSetupFileHash(score_hash);
9508 }
9509
9510 void LoadServerScore(int nr, boolean download_score)
9511 {
9512   if (!setup.use_api_server)
9513     return;
9514
9515   // always start with reliable default values
9516   setServerScoreInfoToDefaults();
9517
9518   // 1st step: load server scores from cache file (which may not exist)
9519   // (this should prevent reading it while the thread is writing to it)
9520   LoadServerScoreFromCache(nr);
9521
9522   if (download_score && runtime.use_api_server)
9523   {
9524     // 2nd step: download server scores from score server to cache file
9525     // (as thread, as it might time out if the server is not reachable)
9526     ApiGetScoreAsThread(nr);
9527   }
9528 }
9529
9530 static char *get_file_base64(char *filename)
9531 {
9532   struct stat file_status;
9533
9534   if (stat(filename, &file_status) != 0)
9535   {
9536     Error("cannot stat file '%s'", filename);
9537
9538     return NULL;
9539   }
9540
9541   int buffer_size = file_status.st_size;
9542   byte *buffer = checked_malloc(buffer_size);
9543   FILE *file;
9544   int i;
9545
9546   if (!(file = fopen(filename, MODE_READ)))
9547   {
9548     Error("cannot open file '%s'", filename);
9549
9550     checked_free(buffer);
9551
9552     return NULL;
9553   }
9554
9555   for (i = 0; i < buffer_size; i++)
9556   {
9557     int c = fgetc(file);
9558
9559     if (c == EOF)
9560     {
9561       Error("cannot read from input file '%s'", filename);
9562
9563       fclose(file);
9564       checked_free(buffer);
9565
9566       return NULL;
9567     }
9568
9569     buffer[i] = (byte)c;
9570   }
9571
9572   fclose(file);
9573
9574   int buffer_encoded_size = base64_encoded_size(buffer_size);
9575   char *buffer_encoded = checked_malloc(buffer_encoded_size);
9576
9577   base64_encode(buffer_encoded, buffer, buffer_size);
9578
9579   checked_free(buffer);
9580
9581   return buffer_encoded;
9582 }
9583
9584 static void PrepareScoreTapesForUpload(char *leveldir_subdir)
9585 {
9586   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9587
9588   // if score tape not uploaded, ask for uploading missing tapes later
9589   if (!setup.has_remaining_tapes)
9590     setup.ask_for_remaining_tapes = TRUE;
9591
9592   setup.provide_uploading_tapes = TRUE;
9593   setup.has_remaining_tapes = TRUE;
9594
9595   SaveSetup_ServerSetup();
9596 }
9597
9598 struct ApiAddScoreThreadData
9599 {
9600   int level_nr;
9601   boolean tape_saved;
9602   char *leveldir_subdir;
9603   char *score_tape_filename;
9604   struct ScoreEntry score_entry;
9605 };
9606
9607 static void *CreateThreadData_ApiAddScore(int nr, boolean tape_saved,
9608                                           char *score_tape_filename)
9609 {
9610   struct ApiAddScoreThreadData *data =
9611     checked_malloc(sizeof(struct ApiAddScoreThreadData));
9612   struct ScoreEntry *score_entry = &scores.entry[scores.last_added];
9613
9614   if (score_tape_filename == NULL)
9615     score_tape_filename = getScoreTapeFilename(score_entry->tape_basename, nr);
9616
9617   data->level_nr = nr;
9618   data->tape_saved = tape_saved;
9619   data->leveldir_subdir = getStringCopy(leveldir_current->subdir);
9620   data->score_tape_filename = getStringCopy(score_tape_filename);
9621   data->score_entry = *score_entry;
9622
9623   return data;
9624 }
9625
9626 static void FreeThreadData_ApiAddScore(void *data_raw)
9627 {
9628   struct ApiAddScoreThreadData *data = data_raw;
9629
9630   checked_free(data->leveldir_subdir);
9631   checked_free(data->score_tape_filename);
9632   checked_free(data);
9633 }
9634
9635 static boolean SetRequest_ApiAddScore(struct HttpRequest *request,
9636                                       void *data_raw)
9637 {
9638   struct ApiAddScoreThreadData *data = data_raw;
9639   struct ScoreEntry *score_entry = &data->score_entry;
9640   char *score_tape_filename = data->score_tape_filename;
9641   boolean tape_saved = data->tape_saved;
9642   int level_nr = data->level_nr;
9643
9644   request->hostname = setup.api_server_hostname;
9645   request->port     = API_SERVER_PORT;
9646   request->method   = API_SERVER_METHOD;
9647   request->uri      = API_SERVER_URI_ADD;
9648
9649   char *tape_base64 = get_file_base64(score_tape_filename);
9650
9651   if (tape_base64 == NULL)
9652   {
9653     Error("loading and base64 encoding score tape file failed");
9654
9655     return FALSE;
9656   }
9657
9658   char *player_name_raw = score_entry->name;
9659   char *player_uuid_raw = setup.player_uuid;
9660
9661   if (options.player_name != NULL && global.autoplay_leveldir != NULL)
9662   {
9663     player_name_raw = options.player_name;
9664     player_uuid_raw = "";
9665   }
9666
9667   char *levelset_identifier = getEscapedJSON(leveldir_current->identifier);
9668   char *levelset_name       = getEscapedJSON(leveldir_current->name);
9669   char *levelset_author     = getEscapedJSON(leveldir_current->author);
9670   char *level_name          = getEscapedJSON(level.name);
9671   char *level_author        = getEscapedJSON(level.author);
9672   char *player_name         = getEscapedJSON(player_name_raw);
9673   char *player_uuid         = getEscapedJSON(player_uuid_raw);
9674
9675   snprintf(request->body, MAX_HTTP_BODY_SIZE,
9676            "{\n"
9677            "%s"
9678            "  \"game_version\":         \"%s\",\n"
9679            "  \"game_platform\":        \"%s\",\n"
9680            "  \"batch_time\":           \"%d\",\n"
9681            "  \"levelset_identifier\":  \"%s\",\n"
9682            "  \"levelset_name\":        \"%s\",\n"
9683            "  \"levelset_author\":      \"%s\",\n"
9684            "  \"levelset_num_levels\":  \"%d\",\n"
9685            "  \"levelset_first_level\": \"%d\",\n"
9686            "  \"level_nr\":             \"%d\",\n"
9687            "  \"level_name\":           \"%s\",\n"
9688            "  \"level_author\":         \"%s\",\n"
9689            "  \"use_step_counter\":     \"%d\",\n"
9690            "  \"rate_time_over_score\": \"%d\",\n"
9691            "  \"player_name\":          \"%s\",\n"
9692            "  \"player_uuid\":          \"%s\",\n"
9693            "  \"score\":                \"%d\",\n"
9694            "  \"time\":                 \"%d\",\n"
9695            "  \"tape_basename\":        \"%s\",\n"
9696            "  \"tape_saved\":           \"%d\",\n"
9697            "  \"tape\":                 \"%s\"\n"
9698            "}\n",
9699            getPasswordJSON(setup.api_server_password),
9700            getProgramRealVersionString(),
9701            getProgramPlatformString(),
9702            (int)global.autoplay_time,
9703            levelset_identifier,
9704            levelset_name,
9705            levelset_author,
9706            leveldir_current->levels,
9707            leveldir_current->first_level,
9708            level_nr,
9709            level_name,
9710            level_author,
9711            level.use_step_counter,
9712            level.rate_time_over_score,
9713            player_name,
9714            player_uuid,
9715            score_entry->score,
9716            score_entry->time,
9717            score_entry->tape_basename,
9718            tape_saved,
9719            tape_base64);
9720
9721   checked_free(tape_base64);
9722
9723   checked_free(levelset_identifier);
9724   checked_free(levelset_name);
9725   checked_free(levelset_author);
9726   checked_free(level_name);
9727   checked_free(level_author);
9728   checked_free(player_name);
9729   checked_free(player_uuid);
9730
9731   ConvertHttpRequestBodyToServerEncoding(request);
9732
9733   return TRUE;
9734 }
9735
9736 static void HandleResponse_ApiAddScore(struct HttpResponse *response,
9737                                        void *data_raw)
9738 {
9739   server_scores.uploaded = TRUE;
9740 }
9741
9742 static void HandleFailure_ApiAddScore(void *data_raw)
9743 {
9744   struct ApiAddScoreThreadData *data = data_raw;
9745
9746   PrepareScoreTapesForUpload(data->leveldir_subdir);
9747 }
9748
9749 #if defined(PLATFORM_EMSCRIPTEN)
9750 static void Emscripten_ApiAddScore_Loaded(unsigned handle, void *data_raw,
9751                                           void *buffer, unsigned int size)
9752 {
9753   struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
9754
9755   if (response != NULL)
9756   {
9757     HandleResponse_ApiAddScore(response, data_raw);
9758
9759     checked_free(response);
9760   }
9761   else
9762   {
9763     Error("server response too large to handle (%d bytes)", size);
9764
9765     HandleFailure_ApiAddScore(data_raw);
9766   }
9767
9768   FreeThreadData_ApiAddScore(data_raw);
9769 }
9770
9771 static void Emscripten_ApiAddScore_Failed(unsigned handle, void *data_raw,
9772                                           int code, const char *status)
9773 {
9774   Error("server failed to handle request: %d %s", code, status);
9775
9776   HandleFailure_ApiAddScore(data_raw);
9777
9778   FreeThreadData_ApiAddScore(data_raw);
9779 }
9780
9781 static void Emscripten_ApiAddScore_Progress(unsigned handle, void *data_raw,
9782                                             int bytes, int size)
9783 {
9784   // nothing to do here
9785 }
9786
9787 static void Emscripten_ApiAddScore_HttpRequest(struct HttpRequest *request,
9788                                                void *data_raw)
9789 {
9790   if (!SetRequest_ApiAddScore(request, data_raw))
9791   {
9792     FreeThreadData_ApiAddScore(data_raw);
9793
9794     return;
9795   }
9796
9797   emscripten_async_wget2_data(request->uri,
9798                               request->method,
9799                               request->body,
9800                               data_raw,
9801                               TRUE,
9802                               Emscripten_ApiAddScore_Loaded,
9803                               Emscripten_ApiAddScore_Failed,
9804                               Emscripten_ApiAddScore_Progress);
9805 }
9806
9807 #else
9808
9809 static void ApiAddScore_HttpRequestExt(struct HttpRequest *request,
9810                                        struct HttpResponse *response,
9811                                        void *data_raw)
9812 {
9813   if (!SetRequest_ApiAddScore(request, data_raw))
9814     return;
9815
9816   if (!DoHttpRequest(request, response))
9817   {
9818     Error("HTTP request failed: %s", GetHttpError());
9819
9820     HandleFailure_ApiAddScore(data_raw);
9821
9822     return;
9823   }
9824
9825   if (!HTTP_SUCCESS(response->status_code))
9826   {
9827     Error("server failed to handle request: %d %s",
9828           response->status_code,
9829           response->status_text);
9830
9831     HandleFailure_ApiAddScore(data_raw);
9832
9833     return;
9834   }
9835
9836   HandleResponse_ApiAddScore(response, data_raw);
9837 }
9838
9839 static void ApiAddScore_HttpRequest(struct HttpRequest *request,
9840                                     struct HttpResponse *response,
9841                                     void *data_raw)
9842 {
9843   ApiAddScore_HttpRequestExt(request, response, data_raw);
9844
9845   FreeThreadData_ApiAddScore(data_raw);
9846 }
9847 #endif
9848
9849 static int ApiAddScoreThread(void *data_raw)
9850 {
9851   struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
9852   struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
9853
9854   program.api_thread_count++;
9855
9856 #if defined(PLATFORM_EMSCRIPTEN)
9857   Emscripten_ApiAddScore_HttpRequest(request, data_raw);
9858 #else
9859   ApiAddScore_HttpRequest(request, response, data_raw);
9860 #endif
9861
9862   program.api_thread_count--;
9863
9864   checked_free(request);
9865   checked_free(response);
9866
9867   return 0;
9868 }
9869
9870 static void ApiAddScoreAsThread(int nr, boolean tape_saved,
9871                                 char *score_tape_filename)
9872 {
9873   struct ApiAddScoreThreadData *data =
9874     CreateThreadData_ApiAddScore(nr, tape_saved, score_tape_filename);
9875
9876   ExecuteAsThread(ApiAddScoreThread,
9877                   "ApiAddScore", data,
9878                   "upload score to server");
9879 }
9880
9881 void SaveServerScore(int nr, boolean tape_saved)
9882 {
9883   if (!runtime.use_api_server)
9884   {
9885     PrepareScoreTapesForUpload(leveldir_current->subdir);
9886
9887     return;
9888   }
9889
9890   ApiAddScoreAsThread(nr, tape_saved, NULL);
9891 }
9892
9893 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9894                              char *score_tape_filename)
9895 {
9896   if (!runtime.use_api_server)
9897     return;
9898
9899   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9900 }
9901
9902 void LoadLocalAndServerScore(int nr, boolean download_score)
9903 {
9904   int last_added_local = scores.last_added_local;
9905
9906   // needed if only showing server scores
9907   setScoreInfoToDefaults();
9908
9909   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9910     LoadScore(nr);
9911
9912   // restore last added local score entry (before merging server scores)
9913   scores.last_added = scores.last_added_local = last_added_local;
9914
9915   if (setup.use_api_server &&
9916       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9917   {
9918     // load server scores from cache file and trigger update from server
9919     LoadServerScore(nr, download_score);
9920
9921     // merge local scores with scores from server
9922     MergeServerScore();
9923   }
9924 }
9925
9926
9927 // ============================================================================
9928 // setup file functions
9929 // ============================================================================
9930
9931 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
9932
9933
9934 static struct TokenInfo global_setup_tokens[] =
9935 {
9936   {
9937     TYPE_STRING,
9938     &setup.player_name,                         "player_name"
9939   },
9940   {
9941     TYPE_SWITCH,
9942     &setup.multiple_users,                      "multiple_users"
9943   },
9944   {
9945     TYPE_SWITCH,
9946     &setup.sound,                               "sound"
9947   },
9948   {
9949     TYPE_SWITCH,
9950     &setup.sound_loops,                         "repeating_sound_loops"
9951   },
9952   {
9953     TYPE_SWITCH,
9954     &setup.sound_music,                         "background_music"
9955   },
9956   {
9957     TYPE_SWITCH,
9958     &setup.sound_simple,                        "simple_sound_effects"
9959   },
9960   {
9961     TYPE_SWITCH,
9962     &setup.toons,                               "toons"
9963   },
9964   {
9965     TYPE_SWITCH,
9966     &setup.scroll_delay,                        "scroll_delay"
9967   },
9968   {
9969     TYPE_SWITCH,
9970     &setup.forced_scroll_delay,                 "forced_scroll_delay"
9971   },
9972   {
9973     TYPE_INTEGER,
9974     &setup.scroll_delay_value,                  "scroll_delay_value"
9975   },
9976   {
9977     TYPE_STRING,
9978     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
9979   },
9980   {
9981     TYPE_INTEGER,
9982     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
9983   },
9984   {
9985     TYPE_SWITCH,
9986     &setup.fade_screens,                        "fade_screens"
9987   },
9988   {
9989     TYPE_SWITCH,
9990     &setup.autorecord,                          "automatic_tape_recording"
9991   },
9992   {
9993     TYPE_SWITCH,
9994     &setup.auto_pause_on_start,                 "auto_pause_on_start"
9995   },
9996   {
9997     TYPE_SWITCH,
9998     &setup.show_titlescreen,                    "show_titlescreen"
9999   },
10000   {
10001     TYPE_SWITCH,
10002     &setup.quick_doors,                         "quick_doors"
10003   },
10004   {
10005     TYPE_SWITCH,
10006     &setup.team_mode,                           "team_mode"
10007   },
10008   {
10009     TYPE_SWITCH,
10010     &setup.handicap,                            "handicap"
10011   },
10012   {
10013     TYPE_SWITCH,
10014     &setup.skip_levels,                         "skip_levels"
10015   },
10016   {
10017     TYPE_SWITCH,
10018     &setup.increment_levels,                    "increment_levels"
10019   },
10020   {
10021     TYPE_SWITCH,
10022     &setup.auto_play_next_level,                "auto_play_next_level"
10023   },
10024   {
10025     TYPE_SWITCH,
10026     &setup.count_score_after_game,              "count_score_after_game"
10027   },
10028   {
10029     TYPE_SWITCH,
10030     &setup.show_scores_after_game,              "show_scores_after_game"
10031   },
10032   {
10033     TYPE_SWITCH,
10034     &setup.time_limit,                          "time_limit"
10035   },
10036   {
10037     TYPE_SWITCH,
10038     &setup.fullscreen,                          "fullscreen"
10039   },
10040   {
10041     TYPE_INTEGER,
10042     &setup.window_scaling_percent,              "window_scaling_percent"
10043   },
10044   {
10045     TYPE_STRING,
10046     &setup.window_scaling_quality,              "window_scaling_quality"
10047   },
10048   {
10049     TYPE_STRING,
10050     &setup.screen_rendering_mode,               "screen_rendering_mode"
10051   },
10052   {
10053     TYPE_STRING,
10054     &setup.vsync_mode,                          "vsync_mode"
10055   },
10056   {
10057     TYPE_SWITCH,
10058     &setup.ask_on_escape,                       "ask_on_escape"
10059   },
10060   {
10061     TYPE_SWITCH,
10062     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10063   },
10064   {
10065     TYPE_SWITCH,
10066     &setup.ask_on_game_over,                    "ask_on_game_over"
10067   },
10068   {
10069     TYPE_SWITCH,
10070     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10071   },
10072   {
10073     TYPE_SWITCH,
10074     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10075   },
10076   {
10077     TYPE_SWITCH,
10078     &setup.quick_switch,                        "quick_player_switch"
10079   },
10080   {
10081     TYPE_SWITCH,
10082     &setup.input_on_focus,                      "input_on_focus"
10083   },
10084   {
10085     TYPE_SWITCH,
10086     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10087   },
10088   {
10089     TYPE_SWITCH,
10090     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10091   },
10092   {
10093     TYPE_SWITCH,
10094     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10095   },
10096   {
10097     TYPE_SWITCH,
10098     &setup.game_speed_extended,                 "game_speed_extended"
10099   },
10100   {
10101     TYPE_INTEGER,
10102     &setup.game_frame_delay,                    "game_frame_delay"
10103   },
10104   {
10105     TYPE_SWITCH,
10106     &setup.sp_show_border_elements,             "sp_show_border_elements"
10107   },
10108   {
10109     TYPE_SWITCH,
10110     &setup.small_game_graphics,                 "small_game_graphics"
10111   },
10112   {
10113     TYPE_SWITCH,
10114     &setup.show_load_save_buttons,              "show_load_save_buttons"
10115   },
10116   {
10117     TYPE_SWITCH,
10118     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10119   },
10120   {
10121     TYPE_STRING,
10122     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10123   },
10124   {
10125     TYPE_STRING,
10126     &setup.graphics_set,                        "graphics_set"
10127   },
10128   {
10129     TYPE_STRING,
10130     &setup.sounds_set,                          "sounds_set"
10131   },
10132   {
10133     TYPE_STRING,
10134     &setup.music_set,                           "music_set"
10135   },
10136   {
10137     TYPE_SWITCH3,
10138     &setup.override_level_graphics,             "override_level_graphics"
10139   },
10140   {
10141     TYPE_SWITCH3,
10142     &setup.override_level_sounds,               "override_level_sounds"
10143   },
10144   {
10145     TYPE_SWITCH3,
10146     &setup.override_level_music,                "override_level_music"
10147   },
10148   {
10149     TYPE_INTEGER,
10150     &setup.volume_simple,                       "volume_simple"
10151   },
10152   {
10153     TYPE_INTEGER,
10154     &setup.volume_loops,                        "volume_loops"
10155   },
10156   {
10157     TYPE_INTEGER,
10158     &setup.volume_music,                        "volume_music"
10159   },
10160   {
10161     TYPE_SWITCH,
10162     &setup.network_mode,                        "network_mode"
10163   },
10164   {
10165     TYPE_PLAYER,
10166     &setup.network_player_nr,                   "network_player"
10167   },
10168   {
10169     TYPE_STRING,
10170     &setup.network_server_hostname,             "network_server_hostname"
10171   },
10172   {
10173     TYPE_STRING,
10174     &setup.touch.control_type,                  "touch.control_type"
10175   },
10176   {
10177     TYPE_INTEGER,
10178     &setup.touch.move_distance,                 "touch.move_distance"
10179   },
10180   {
10181     TYPE_INTEGER,
10182     &setup.touch.drop_distance,                 "touch.drop_distance"
10183   },
10184   {
10185     TYPE_INTEGER,
10186     &setup.touch.transparency,                  "touch.transparency"
10187   },
10188   {
10189     TYPE_INTEGER,
10190     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10191   },
10192   {
10193     TYPE_INTEGER,
10194     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10195   },
10196   {
10197     TYPE_INTEGER,
10198     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10199   },
10200   {
10201     TYPE_INTEGER,
10202     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10203   },
10204   {
10205     TYPE_INTEGER,
10206     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10207   },
10208   {
10209     TYPE_INTEGER,
10210     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10211   },
10212 };
10213
10214 static struct TokenInfo auto_setup_tokens[] =
10215 {
10216   {
10217     TYPE_INTEGER,
10218     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10219   },
10220 };
10221
10222 static struct TokenInfo server_setup_tokens[] =
10223 {
10224   {
10225     TYPE_STRING,
10226     &setup.player_uuid,                         "player_uuid"
10227   },
10228   {
10229     TYPE_INTEGER,
10230     &setup.player_version,                      "player_version"
10231   },
10232   {
10233     TYPE_SWITCH,
10234     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10235   },
10236   {
10237     TYPE_STRING,
10238     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10239   },
10240   {
10241     TYPE_STRING,
10242     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10243   },
10244   {
10245     TYPE_SWITCH,
10246     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10247   },
10248   {
10249     TYPE_SWITCH,
10250     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10251   },
10252   {
10253     TYPE_SWITCH,
10254     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10255   },
10256   {
10257     TYPE_SWITCH,
10258     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10259   },
10260   {
10261     TYPE_SWITCH,
10262     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10263   },
10264 };
10265
10266 static struct TokenInfo editor_setup_tokens[] =
10267 {
10268   {
10269     TYPE_SWITCH,
10270     &setup.editor.el_classic,                   "editor.el_classic"
10271   },
10272   {
10273     TYPE_SWITCH,
10274     &setup.editor.el_custom,                    "editor.el_custom"
10275   },
10276   {
10277     TYPE_SWITCH,
10278     &setup.editor.el_user_defined,              "editor.el_user_defined"
10279   },
10280   {
10281     TYPE_SWITCH,
10282     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10283   },
10284   {
10285     TYPE_SWITCH,
10286     &setup.editor.el_headlines,                 "editor.el_headlines"
10287   },
10288   {
10289     TYPE_SWITCH,
10290     &setup.editor.show_element_token,           "editor.show_element_token"
10291   },
10292   {
10293     TYPE_SWITCH,
10294     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10295   },
10296 };
10297
10298 static struct TokenInfo editor_cascade_setup_tokens[] =
10299 {
10300   {
10301     TYPE_SWITCH,
10302     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10303   },
10304   {
10305     TYPE_SWITCH,
10306     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10307   },
10308   {
10309     TYPE_SWITCH,
10310     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10311   },
10312   {
10313     TYPE_SWITCH,
10314     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10315   },
10316   {
10317     TYPE_SWITCH,
10318     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10319   },
10320   {
10321     TYPE_SWITCH,
10322     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10323   },
10324   {
10325     TYPE_SWITCH,
10326     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10327   },
10328   {
10329     TYPE_SWITCH,
10330     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10331   },
10332   {
10333     TYPE_SWITCH,
10334     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10335   },
10336   {
10337     TYPE_SWITCH,
10338     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10339   },
10340   {
10341     TYPE_SWITCH,
10342     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10343   },
10344   {
10345     TYPE_SWITCH,
10346     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10347   },
10348   {
10349     TYPE_SWITCH,
10350     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10351   },
10352   {
10353     TYPE_SWITCH,
10354     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10355   },
10356   {
10357     TYPE_SWITCH,
10358     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10359   },
10360   {
10361     TYPE_SWITCH,
10362     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10363   },
10364   {
10365     TYPE_SWITCH,
10366     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10367   },
10368   {
10369     TYPE_SWITCH,
10370     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10371   },
10372 };
10373
10374 static struct TokenInfo shortcut_setup_tokens[] =
10375 {
10376   {
10377     TYPE_KEY_X11,
10378     &setup.shortcut.save_game,                  "shortcut.save_game"
10379   },
10380   {
10381     TYPE_KEY_X11,
10382     &setup.shortcut.load_game,                  "shortcut.load_game"
10383   },
10384   {
10385     TYPE_KEY_X11,
10386     &setup.shortcut.restart_game,               "shortcut.restart_game"
10387   },
10388   {
10389     TYPE_KEY_X11,
10390     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10391   },
10392   {
10393     TYPE_KEY_X11,
10394     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10395   },
10396   {
10397     TYPE_KEY_X11,
10398     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10399   },
10400   {
10401     TYPE_KEY_X11,
10402     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10403   },
10404   {
10405     TYPE_KEY_X11,
10406     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10407   },
10408   {
10409     TYPE_KEY_X11,
10410     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10411   },
10412   {
10413     TYPE_KEY_X11,
10414     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10415   },
10416   {
10417     TYPE_KEY_X11,
10418     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10419   },
10420   {
10421     TYPE_KEY_X11,
10422     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10423   },
10424   {
10425     TYPE_KEY_X11,
10426     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10427   },
10428   {
10429     TYPE_KEY_X11,
10430     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10431   },
10432   {
10433     TYPE_KEY_X11,
10434     &setup.shortcut.tape_record,                "shortcut.tape_record"
10435   },
10436   {
10437     TYPE_KEY_X11,
10438     &setup.shortcut.tape_play,                  "shortcut.tape_play"
10439   },
10440   {
10441     TYPE_KEY_X11,
10442     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
10443   },
10444   {
10445     TYPE_KEY_X11,
10446     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
10447   },
10448   {
10449     TYPE_KEY_X11,
10450     &setup.shortcut.sound_music,                "shortcut.sound_music"
10451   },
10452   {
10453     TYPE_KEY_X11,
10454     &setup.shortcut.snap_left,                  "shortcut.snap_left"
10455   },
10456   {
10457     TYPE_KEY_X11,
10458     &setup.shortcut.snap_right,                 "shortcut.snap_right"
10459   },
10460   {
10461     TYPE_KEY_X11,
10462     &setup.shortcut.snap_up,                    "shortcut.snap_up"
10463   },
10464   {
10465     TYPE_KEY_X11,
10466     &setup.shortcut.snap_down,                  "shortcut.snap_down"
10467   },
10468 };
10469
10470 static struct SetupInputInfo setup_input;
10471 static struct TokenInfo player_setup_tokens[] =
10472 {
10473   {
10474     TYPE_BOOLEAN,
10475     &setup_input.use_joystick,                  ".use_joystick"
10476   },
10477   {
10478     TYPE_STRING,
10479     &setup_input.joy.device_name,               ".joy.device_name"
10480   },
10481   {
10482     TYPE_INTEGER,
10483     &setup_input.joy.xleft,                     ".joy.xleft"
10484   },
10485   {
10486     TYPE_INTEGER,
10487     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
10488   },
10489   {
10490     TYPE_INTEGER,
10491     &setup_input.joy.xright,                    ".joy.xright"
10492   },
10493   {
10494     TYPE_INTEGER,
10495     &setup_input.joy.yupper,                    ".joy.yupper"
10496   },
10497   {
10498     TYPE_INTEGER,
10499     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
10500   },
10501   {
10502     TYPE_INTEGER,
10503     &setup_input.joy.ylower,                    ".joy.ylower"
10504   },
10505   {
10506     TYPE_INTEGER,
10507     &setup_input.joy.snap,                      ".joy.snap_field"
10508   },
10509   {
10510     TYPE_INTEGER,
10511     &setup_input.joy.drop,                      ".joy.place_bomb"
10512   },
10513   {
10514     TYPE_KEY_X11,
10515     &setup_input.key.left,                      ".key.move_left"
10516   },
10517   {
10518     TYPE_KEY_X11,
10519     &setup_input.key.right,                     ".key.move_right"
10520   },
10521   {
10522     TYPE_KEY_X11,
10523     &setup_input.key.up,                        ".key.move_up"
10524   },
10525   {
10526     TYPE_KEY_X11,
10527     &setup_input.key.down,                      ".key.move_down"
10528   },
10529   {
10530     TYPE_KEY_X11,
10531     &setup_input.key.snap,                      ".key.snap_field"
10532   },
10533   {
10534     TYPE_KEY_X11,
10535     &setup_input.key.drop,                      ".key.place_bomb"
10536   },
10537 };
10538
10539 static struct TokenInfo system_setup_tokens[] =
10540 {
10541   {
10542     TYPE_STRING,
10543     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10544   },
10545   {
10546     TYPE_STRING,
10547     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10548   },
10549   {
10550     TYPE_STRING,
10551     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10552   },
10553   {
10554     TYPE_INTEGER,
10555     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10556   },
10557 };
10558
10559 static struct TokenInfo internal_setup_tokens[] =
10560 {
10561   {
10562     TYPE_STRING,
10563     &setup.internal.program_title,              "program_title"
10564   },
10565   {
10566     TYPE_STRING,
10567     &setup.internal.program_version,            "program_version"
10568   },
10569   {
10570     TYPE_STRING,
10571     &setup.internal.program_author,             "program_author"
10572   },
10573   {
10574     TYPE_STRING,
10575     &setup.internal.program_email,              "program_email"
10576   },
10577   {
10578     TYPE_STRING,
10579     &setup.internal.program_website,            "program_website"
10580   },
10581   {
10582     TYPE_STRING,
10583     &setup.internal.program_copyright,          "program_copyright"
10584   },
10585   {
10586     TYPE_STRING,
10587     &setup.internal.program_company,            "program_company"
10588   },
10589   {
10590     TYPE_STRING,
10591     &setup.internal.program_icon_file,          "program_icon_file"
10592   },
10593   {
10594     TYPE_STRING,
10595     &setup.internal.default_graphics_set,       "default_graphics_set"
10596   },
10597   {
10598     TYPE_STRING,
10599     &setup.internal.default_sounds_set,         "default_sounds_set"
10600   },
10601   {
10602     TYPE_STRING,
10603     &setup.internal.default_music_set,          "default_music_set"
10604   },
10605   {
10606     TYPE_STRING,
10607     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
10608   },
10609   {
10610     TYPE_STRING,
10611     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
10612   },
10613   {
10614     TYPE_STRING,
10615     &setup.internal.fallback_music_file,        "fallback_music_file"
10616   },
10617   {
10618     TYPE_STRING,
10619     &setup.internal.default_level_series,       "default_level_series"
10620   },
10621   {
10622     TYPE_INTEGER,
10623     &setup.internal.default_window_width,       "default_window_width"
10624   },
10625   {
10626     TYPE_INTEGER,
10627     &setup.internal.default_window_height,      "default_window_height"
10628   },
10629   {
10630     TYPE_BOOLEAN,
10631     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
10632   },
10633   {
10634     TYPE_BOOLEAN,
10635     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
10636   },
10637   {
10638     TYPE_BOOLEAN,
10639     &setup.internal.create_user_levelset,       "create_user_levelset"
10640   },
10641   {
10642     TYPE_BOOLEAN,
10643     &setup.internal.menu_game,                  "menu_game"
10644   },
10645   {
10646     TYPE_BOOLEAN,
10647     &setup.internal.menu_editor,                "menu_editor"
10648   },
10649   {
10650     TYPE_BOOLEAN,
10651     &setup.internal.menu_graphics,              "menu_graphics"
10652   },
10653   {
10654     TYPE_BOOLEAN,
10655     &setup.internal.menu_sound,                 "menu_sound"
10656   },
10657   {
10658     TYPE_BOOLEAN,
10659     &setup.internal.menu_artwork,               "menu_artwork"
10660   },
10661   {
10662     TYPE_BOOLEAN,
10663     &setup.internal.menu_input,                 "menu_input"
10664   },
10665   {
10666     TYPE_BOOLEAN,
10667     &setup.internal.menu_touch,                 "menu_touch"
10668   },
10669   {
10670     TYPE_BOOLEAN,
10671     &setup.internal.menu_shortcuts,             "menu_shortcuts"
10672   },
10673   {
10674     TYPE_BOOLEAN,
10675     &setup.internal.menu_exit,                  "menu_exit"
10676   },
10677   {
10678     TYPE_BOOLEAN,
10679     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
10680   },
10681 };
10682
10683 static struct TokenInfo debug_setup_tokens[] =
10684 {
10685   {
10686     TYPE_INTEGER,
10687     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
10688   },
10689   {
10690     TYPE_INTEGER,
10691     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
10692   },
10693   {
10694     TYPE_INTEGER,
10695     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
10696   },
10697   {
10698     TYPE_INTEGER,
10699     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
10700   },
10701   {
10702     TYPE_INTEGER,
10703     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
10704   },
10705   {
10706     TYPE_INTEGER,
10707     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
10708   },
10709   {
10710     TYPE_INTEGER,
10711     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
10712   },
10713   {
10714     TYPE_INTEGER,
10715     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
10716   },
10717   {
10718     TYPE_INTEGER,
10719     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
10720   },
10721   {
10722     TYPE_INTEGER,
10723     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
10724   },
10725   {
10726     TYPE_KEY_X11,
10727     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
10728   },
10729   {
10730     TYPE_KEY_X11,
10731     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
10732   },
10733   {
10734     TYPE_KEY_X11,
10735     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
10736   },
10737   {
10738     TYPE_KEY_X11,
10739     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
10740   },
10741   {
10742     TYPE_KEY_X11,
10743     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
10744   },
10745   {
10746     TYPE_KEY_X11,
10747     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
10748   },
10749   {
10750     TYPE_KEY_X11,
10751     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
10752   },
10753   {
10754     TYPE_KEY_X11,
10755     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
10756   },
10757   {
10758     TYPE_KEY_X11,
10759     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
10760   },
10761   {
10762     TYPE_KEY_X11,
10763     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
10764   },
10765   {
10766     TYPE_BOOLEAN,
10767     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
10768   {
10769     TYPE_BOOLEAN,
10770     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
10771   },
10772   {
10773     TYPE_BOOLEAN,
10774     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
10775   },
10776   {
10777     TYPE_SWITCH3,
10778     &setup.debug.xsn_mode,                      "debug.xsn_mode"
10779   },
10780   {
10781     TYPE_INTEGER,
10782     &setup.debug.xsn_percent,                   "debug.xsn_percent"
10783   },
10784 };
10785
10786 static struct TokenInfo options_setup_tokens[] =
10787 {
10788   {
10789     TYPE_BOOLEAN,
10790     &setup.options.verbose,                     "options.verbose"
10791   },
10792 };
10793
10794 static void setSetupInfoToDefaults(struct SetupInfo *si)
10795 {
10796   int i;
10797
10798   si->player_name = getStringCopy(getDefaultUserName(user.nr));
10799
10800   si->multiple_users = TRUE;
10801
10802   si->sound = TRUE;
10803   si->sound_loops = TRUE;
10804   si->sound_music = TRUE;
10805   si->sound_simple = TRUE;
10806   si->toons = TRUE;
10807   si->scroll_delay = TRUE;
10808   si->forced_scroll_delay = FALSE;
10809   si->scroll_delay_value = STD_SCROLL_DELAY;
10810   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10811   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10812   si->fade_screens = TRUE;
10813   si->autorecord = TRUE;
10814   si->auto_pause_on_start = FALSE;
10815   si->show_titlescreen = TRUE;
10816   si->quick_doors = FALSE;
10817   si->team_mode = FALSE;
10818   si->handicap = TRUE;
10819   si->skip_levels = TRUE;
10820   si->increment_levels = TRUE;
10821   si->auto_play_next_level = TRUE;
10822   si->count_score_after_game = TRUE;
10823   si->show_scores_after_game = TRUE;
10824   si->time_limit = TRUE;
10825   si->fullscreen = FALSE;
10826   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10827   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10828   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10829   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10830   si->ask_on_escape = TRUE;
10831   si->ask_on_escape_editor = TRUE;
10832   si->ask_on_game_over = TRUE;
10833   si->ask_on_quit_game = TRUE;
10834   si->ask_on_quit_program = TRUE;
10835   si->quick_switch = FALSE;
10836   si->input_on_focus = FALSE;
10837   si->prefer_aga_graphics = TRUE;
10838   si->prefer_lowpass_sounds = FALSE;
10839   si->prefer_extra_panel_items = TRUE;
10840   si->game_speed_extended = FALSE;
10841   si->game_frame_delay = GAME_FRAME_DELAY;
10842   si->sp_show_border_elements = FALSE;
10843   si->small_game_graphics = FALSE;
10844   si->show_load_save_buttons = FALSE;
10845   si->show_undo_redo_buttons = FALSE;
10846   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10847
10848   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10849   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
10850   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
10851
10852   si->override_level_graphics = FALSE;
10853   si->override_level_sounds = FALSE;
10854   si->override_level_music = FALSE;
10855
10856   si->volume_simple = 100;              // percent
10857   si->volume_loops = 100;               // percent
10858   si->volume_music = 100;               // percent
10859
10860   si->network_mode = FALSE;
10861   si->network_player_nr = 0;            // first player
10862   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10863
10864   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10865   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
10866   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
10867   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
10868   si->touch.draw_outlined = TRUE;
10869   si->touch.draw_pressed = TRUE;
10870
10871   for (i = 0; i < 2; i++)
10872   {
10873     char *default_grid_button[6][2] =
10874     {
10875       { "      ", "  ^^  " },
10876       { "      ", "  ^^  " },
10877       { "      ", "<<  >>" },
10878       { "      ", "<<  >>" },
10879       { "111222", "  vv  " },
10880       { "111222", "  vv  " }
10881     };
10882     int grid_xsize = DEFAULT_GRID_XSIZE(i);
10883     int grid_ysize = DEFAULT_GRID_YSIZE(i);
10884     int min_xsize = MIN(6, grid_xsize);
10885     int min_ysize = MIN(6, grid_ysize);
10886     int startx = grid_xsize - min_xsize;
10887     int starty = grid_ysize - min_ysize;
10888     int x, y;
10889
10890     // virtual buttons grid can only be set to defaults if video is initialized
10891     // (this will be repeated if virtual buttons are not loaded from setup file)
10892     if (video.initialized)
10893     {
10894       si->touch.grid_xsize[i] = grid_xsize;
10895       si->touch.grid_ysize[i] = grid_ysize;
10896     }
10897     else
10898     {
10899       si->touch.grid_xsize[i] = -1;
10900       si->touch.grid_ysize[i] = -1;
10901     }
10902
10903     for (x = 0; x < MAX_GRID_XSIZE; x++)
10904       for (y = 0; y < MAX_GRID_YSIZE; y++)
10905         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10906
10907     for (x = 0; x < min_xsize; x++)
10908       for (y = 0; y < min_ysize; y++)
10909         si->touch.grid_button[i][x][starty + y] =
10910           default_grid_button[y][0][x];
10911
10912     for (x = 0; x < min_xsize; x++)
10913       for (y = 0; y < min_ysize; y++)
10914         si->touch.grid_button[i][startx + x][starty + y] =
10915           default_grid_button[y][1][x];
10916   }
10917
10918   si->touch.grid_initialized            = video.initialized;
10919
10920   si->editor.el_boulderdash             = TRUE;
10921   si->editor.el_emerald_mine            = TRUE;
10922   si->editor.el_emerald_mine_club       = TRUE;
10923   si->editor.el_more                    = TRUE;
10924   si->editor.el_sokoban                 = TRUE;
10925   si->editor.el_supaplex                = TRUE;
10926   si->editor.el_diamond_caves           = TRUE;
10927   si->editor.el_dx_boulderdash          = TRUE;
10928
10929   si->editor.el_mirror_magic            = TRUE;
10930   si->editor.el_deflektor               = TRUE;
10931
10932   si->editor.el_chars                   = TRUE;
10933   si->editor.el_steel_chars             = TRUE;
10934
10935   si->editor.el_classic                 = TRUE;
10936   si->editor.el_custom                  = TRUE;
10937
10938   si->editor.el_user_defined            = FALSE;
10939   si->editor.el_dynamic                 = TRUE;
10940
10941   si->editor.el_headlines               = TRUE;
10942
10943   si->editor.show_element_token         = FALSE;
10944
10945   si->editor.show_read_only_warning     = TRUE;
10946
10947   si->editor.use_template_for_new_levels = TRUE;
10948
10949   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
10950   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
10951   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
10952   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10953   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
10954
10955   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
10956   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
10957   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
10958   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
10959   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10960
10961   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
10962   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
10963   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
10964   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
10965   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
10966   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
10967
10968   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
10969   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
10970   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
10971
10972   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
10973   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
10974   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
10975   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
10976
10977   for (i = 0; i < MAX_PLAYERS; i++)
10978   {
10979     si->input[i].use_joystick = FALSE;
10980     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10981     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
10982     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10983     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
10984     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
10985     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10986     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
10987     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
10988     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
10989     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
10990     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10991     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
10992     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
10993     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
10994     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
10995   }
10996
10997   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10998   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10999   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11000   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11001
11002   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11003   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11004   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11005   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11006   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11007   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11008   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11009
11010   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11011
11012   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11013   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11014   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11015
11016   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11017   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11018   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11019
11020   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11021   si->internal.choose_from_top_leveldir = FALSE;
11022   si->internal.show_scaling_in_title = TRUE;
11023   si->internal.create_user_levelset = TRUE;
11024
11025   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11026   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11027
11028   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11029   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11030   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11031   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11032   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11033   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11034   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11035   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11036   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11037   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11038
11039   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11040   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11041   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11042   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11043   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11044   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11045   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11046   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11047   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11048   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11049
11050   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11051   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11052
11053   si->debug.show_frames_per_second = FALSE;
11054
11055   si->debug.xsn_mode = AUTO;
11056   si->debug.xsn_percent = 0;
11057
11058   si->options.verbose = FALSE;
11059
11060 #if defined(PLATFORM_ANDROID)
11061   si->fullscreen = TRUE;
11062 #endif
11063
11064   setHideSetupEntry(&setup.debug.xsn_mode);
11065 }
11066
11067 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11068 {
11069   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11070 }
11071
11072 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11073 {
11074   si->player_uuid = NULL;       // (will be set later)
11075   si->player_version = 1;       // (will be set later)
11076
11077   si->use_api_server = TRUE;
11078   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11079   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11080   si->ask_for_uploading_tapes = TRUE;
11081   si->ask_for_remaining_tapes = FALSE;
11082   si->provide_uploading_tapes = TRUE;
11083   si->ask_for_using_api_server = TRUE;
11084   si->has_remaining_tapes = FALSE;
11085 }
11086
11087 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11088 {
11089   si->editor_cascade.el_bd              = TRUE;
11090   si->editor_cascade.el_em              = TRUE;
11091   si->editor_cascade.el_emc             = TRUE;
11092   si->editor_cascade.el_rnd             = TRUE;
11093   si->editor_cascade.el_sb              = TRUE;
11094   si->editor_cascade.el_sp              = TRUE;
11095   si->editor_cascade.el_dc              = TRUE;
11096   si->editor_cascade.el_dx              = TRUE;
11097
11098   si->editor_cascade.el_mm              = TRUE;
11099   si->editor_cascade.el_df              = TRUE;
11100
11101   si->editor_cascade.el_chars           = FALSE;
11102   si->editor_cascade.el_steel_chars     = FALSE;
11103   si->editor_cascade.el_ce              = FALSE;
11104   si->editor_cascade.el_ge              = FALSE;
11105   si->editor_cascade.el_es              = FALSE;
11106   si->editor_cascade.el_ref             = FALSE;
11107   si->editor_cascade.el_user            = FALSE;
11108   si->editor_cascade.el_dynamic         = FALSE;
11109 }
11110
11111 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11112
11113 static char *getHideSetupToken(void *setup_value)
11114 {
11115   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11116
11117   if (setup_value != NULL)
11118     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11119
11120   return hide_setup_token;
11121 }
11122
11123 void setHideSetupEntry(void *setup_value)
11124 {
11125   char *hide_setup_token = getHideSetupToken(setup_value);
11126
11127   if (hide_setup_hash == NULL)
11128     hide_setup_hash = newSetupFileHash();
11129
11130   if (setup_value != NULL)
11131     setHashEntry(hide_setup_hash, hide_setup_token, "");
11132 }
11133
11134 void removeHideSetupEntry(void *setup_value)
11135 {
11136   char *hide_setup_token = getHideSetupToken(setup_value);
11137
11138   if (setup_value != NULL)
11139     removeHashEntry(hide_setup_hash, hide_setup_token);
11140 }
11141
11142 boolean hideSetupEntry(void *setup_value)
11143 {
11144   char *hide_setup_token = getHideSetupToken(setup_value);
11145
11146   return (setup_value != NULL &&
11147           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11148 }
11149
11150 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11151                                       struct TokenInfo *token_info,
11152                                       int token_nr, char *token_text)
11153 {
11154   char *token_hide_text = getStringCat2(token_text, ".hide");
11155   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11156
11157   // set the value of this setup option in the setup option structure
11158   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11159
11160   // check if this setup option should be hidden in the setup menu
11161   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11162     setHideSetupEntry(token_info[token_nr].value);
11163
11164   free(token_hide_text);
11165 }
11166
11167 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11168                                       struct TokenInfo *token_info,
11169                                       int token_nr)
11170 {
11171   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11172                             token_info[token_nr].text);
11173 }
11174
11175 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11176 {
11177   int i, pnr;
11178
11179   if (!setup_file_hash)
11180     return;
11181
11182   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11183     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11184
11185   setup.touch.grid_initialized = TRUE;
11186   for (i = 0; i < 2; i++)
11187   {
11188     int grid_xsize = setup.touch.grid_xsize[i];
11189     int grid_ysize = setup.touch.grid_ysize[i];
11190     int x, y;
11191
11192     // if virtual buttons are not loaded from setup file, repeat initializing
11193     // virtual buttons grid with default values later when video is initialized
11194     if (grid_xsize == -1 ||
11195         grid_ysize == -1)
11196     {
11197       setup.touch.grid_initialized = FALSE;
11198
11199       continue;
11200     }
11201
11202     for (y = 0; y < grid_ysize; y++)
11203     {
11204       char token_string[MAX_LINE_LEN];
11205
11206       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11207
11208       char *value_string = getHashEntry(setup_file_hash, token_string);
11209
11210       if (value_string == NULL)
11211         continue;
11212
11213       for (x = 0; x < grid_xsize; x++)
11214       {
11215         char c = value_string[x];
11216
11217         setup.touch.grid_button[i][x][y] =
11218           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11219       }
11220     }
11221   }
11222
11223   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11224     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11225
11226   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11227     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11228
11229   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11230   {
11231     char prefix[30];
11232
11233     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11234
11235     setup_input = setup.input[pnr];
11236     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11237     {
11238       char full_token[100];
11239
11240       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11241       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11242                                 full_token);
11243     }
11244     setup.input[pnr] = setup_input;
11245   }
11246
11247   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11248     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11249
11250   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11251     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11252
11253   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11254     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11255
11256   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11257     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11258
11259   setHideRelatedSetupEntries();
11260 }
11261
11262 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11263 {
11264   int i;
11265
11266   if (!setup_file_hash)
11267     return;
11268
11269   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11270     setSetupInfo(auto_setup_tokens, i,
11271                  getHashEntry(setup_file_hash,
11272                               auto_setup_tokens[i].text));
11273 }
11274
11275 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11276 {
11277   int i;
11278
11279   if (!setup_file_hash)
11280     return;
11281
11282   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11283     setSetupInfo(server_setup_tokens, i,
11284                  getHashEntry(setup_file_hash,
11285                               server_setup_tokens[i].text));
11286 }
11287
11288 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11289 {
11290   int i;
11291
11292   if (!setup_file_hash)
11293     return;
11294
11295   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11296     setSetupInfo(editor_cascade_setup_tokens, i,
11297                  getHashEntry(setup_file_hash,
11298                               editor_cascade_setup_tokens[i].text));
11299 }
11300
11301 void LoadUserNames(void)
11302 {
11303   int last_user_nr = user.nr;
11304   int i;
11305
11306   if (global.user_names != NULL)
11307   {
11308     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11309       checked_free(global.user_names[i]);
11310
11311     checked_free(global.user_names);
11312   }
11313
11314   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11315
11316   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11317   {
11318     user.nr = i;
11319
11320     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11321
11322     if (setup_file_hash)
11323     {
11324       char *player_name = getHashEntry(setup_file_hash, "player_name");
11325
11326       global.user_names[i] = getFixedUserName(player_name);
11327
11328       freeSetupFileHash(setup_file_hash);
11329     }
11330
11331     if (global.user_names[i] == NULL)
11332       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11333   }
11334
11335   user.nr = last_user_nr;
11336 }
11337
11338 void LoadSetupFromFilename(char *filename)
11339 {
11340   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11341
11342   if (setup_file_hash)
11343   {
11344     decodeSetupFileHash_Default(setup_file_hash);
11345
11346     freeSetupFileHash(setup_file_hash);
11347   }
11348   else
11349   {
11350     Debug("setup", "using default setup values");
11351   }
11352 }
11353
11354 static void LoadSetup_SpecialPostProcessing(void)
11355 {
11356   char *player_name_new;
11357
11358   // needed to work around problems with fixed length strings
11359   player_name_new = getFixedUserName(setup.player_name);
11360   free(setup.player_name);
11361   setup.player_name = player_name_new;
11362
11363   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11364   if (setup.scroll_delay == FALSE)
11365   {
11366     setup.scroll_delay_value = MIN_SCROLL_DELAY;
11367     setup.scroll_delay = TRUE;                  // now always "on"
11368   }
11369
11370   // make sure that scroll delay value stays inside valid range
11371   setup.scroll_delay_value =
11372     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11373 }
11374
11375 void LoadSetup_Default(void)
11376 {
11377   char *filename;
11378
11379   // always start with reliable default values
11380   setSetupInfoToDefaults(&setup);
11381
11382   // try to load setup values from default setup file
11383   filename = getDefaultSetupFilename();
11384
11385   if (fileExists(filename))
11386     LoadSetupFromFilename(filename);
11387
11388   // try to load setup values from user setup file
11389   filename = getSetupFilename();
11390
11391   LoadSetupFromFilename(filename);
11392
11393   LoadSetup_SpecialPostProcessing();
11394 }
11395
11396 void LoadSetup_AutoSetup(void)
11397 {
11398   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11399   SetupFileHash *setup_file_hash = NULL;
11400
11401   // always start with reliable default values
11402   setSetupInfoToDefaults_AutoSetup(&setup);
11403
11404   setup_file_hash = loadSetupFileHash(filename);
11405
11406   if (setup_file_hash)
11407   {
11408     decodeSetupFileHash_AutoSetup(setup_file_hash);
11409
11410     freeSetupFileHash(setup_file_hash);
11411   }
11412
11413   free(filename);
11414 }
11415
11416 void LoadSetup_ServerSetup(void)
11417 {
11418   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11419   SetupFileHash *setup_file_hash = NULL;
11420
11421   // always start with reliable default values
11422   setSetupInfoToDefaults_ServerSetup(&setup);
11423
11424   setup_file_hash = loadSetupFileHash(filename);
11425
11426   if (setup_file_hash)
11427   {
11428     decodeSetupFileHash_ServerSetup(setup_file_hash);
11429
11430     freeSetupFileHash(setup_file_hash);
11431   }
11432
11433   free(filename);
11434
11435   if (setup.player_uuid == NULL)
11436   {
11437     // player UUID does not yet exist in setup file
11438     setup.player_uuid = getStringCopy(getUUID());
11439     setup.player_version = 2;
11440
11441     SaveSetup_ServerSetup();
11442   }
11443 }
11444
11445 void LoadSetup_EditorCascade(void)
11446 {
11447   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11448   SetupFileHash *setup_file_hash = NULL;
11449
11450   // always start with reliable default values
11451   setSetupInfoToDefaults_EditorCascade(&setup);
11452
11453   setup_file_hash = loadSetupFileHash(filename);
11454
11455   if (setup_file_hash)
11456   {
11457     decodeSetupFileHash_EditorCascade(setup_file_hash);
11458
11459     freeSetupFileHash(setup_file_hash);
11460   }
11461
11462   free(filename);
11463 }
11464
11465 void LoadSetup(void)
11466 {
11467   LoadSetup_Default();
11468   LoadSetup_AutoSetup();
11469   LoadSetup_ServerSetup();
11470   LoadSetup_EditorCascade();
11471 }
11472
11473 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11474                                            char *mapping_line)
11475 {
11476   char mapping_guid[MAX_LINE_LEN];
11477   char *mapping_start, *mapping_end;
11478
11479   // get GUID from game controller mapping line: copy complete line
11480   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11481   mapping_guid[MAX_LINE_LEN - 1] = '\0';
11482
11483   // get GUID from game controller mapping line: cut after GUID part
11484   mapping_start = strchr(mapping_guid, ',');
11485   if (mapping_start != NULL)
11486     *mapping_start = '\0';
11487
11488   // cut newline from game controller mapping line
11489   mapping_end = strchr(mapping_line, '\n');
11490   if (mapping_end != NULL)
11491     *mapping_end = '\0';
11492
11493   // add mapping entry to game controller mappings hash
11494   setHashEntry(mappings_hash, mapping_guid, mapping_line);
11495 }
11496
11497 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11498                                                  char *filename)
11499 {
11500   FILE *file;
11501
11502   if (!(file = fopen(filename, MODE_READ)))
11503   {
11504     Warn("cannot read game controller mappings file '%s'", filename);
11505
11506     return;
11507   }
11508
11509   while (!feof(file))
11510   {
11511     char line[MAX_LINE_LEN];
11512
11513     if (!fgets(line, MAX_LINE_LEN, file))
11514       break;
11515
11516     addGameControllerMappingToHash(mappings_hash, line);
11517   }
11518
11519   fclose(file);
11520 }
11521
11522 void SaveSetup_Default(void)
11523 {
11524   char *filename = getSetupFilename();
11525   FILE *file;
11526   int i, pnr;
11527
11528   InitUserDataDirectory();
11529
11530   if (!(file = fopen(filename, MODE_WRITE)))
11531   {
11532     Warn("cannot write setup file '%s'", filename);
11533
11534     return;
11535   }
11536
11537   fprintFileHeader(file, SETUP_FILENAME);
11538
11539   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11540   {
11541     // just to make things nicer :)
11542     if (global_setup_tokens[i].value == &setup.multiple_users           ||
11543         global_setup_tokens[i].value == &setup.sound                    ||
11544         global_setup_tokens[i].value == &setup.graphics_set             ||
11545         global_setup_tokens[i].value == &setup.volume_simple            ||
11546         global_setup_tokens[i].value == &setup.network_mode             ||
11547         global_setup_tokens[i].value == &setup.touch.control_type       ||
11548         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
11549         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11550       fprintf(file, "\n");
11551
11552     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11553   }
11554
11555   for (i = 0; i < 2; i++)
11556   {
11557     int grid_xsize = setup.touch.grid_xsize[i];
11558     int grid_ysize = setup.touch.grid_ysize[i];
11559     int x, y;
11560
11561     fprintf(file, "\n");
11562
11563     for (y = 0; y < grid_ysize; y++)
11564     {
11565       char token_string[MAX_LINE_LEN];
11566       char value_string[MAX_LINE_LEN];
11567
11568       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11569
11570       for (x = 0; x < grid_xsize; x++)
11571       {
11572         char c = setup.touch.grid_button[i][x][y];
11573
11574         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11575       }
11576
11577       value_string[grid_xsize] = '\0';
11578
11579       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11580     }
11581   }
11582
11583   fprintf(file, "\n");
11584   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11585     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11586
11587   fprintf(file, "\n");
11588   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11589     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11590
11591   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11592   {
11593     char prefix[30];
11594
11595     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11596     fprintf(file, "\n");
11597
11598     setup_input = setup.input[pnr];
11599     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11600       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11601   }
11602
11603   fprintf(file, "\n");
11604   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11605     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11606
11607   // (internal setup values not saved to user setup file)
11608
11609   fprintf(file, "\n");
11610   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11611     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11612         setup.debug.xsn_mode != AUTO)
11613       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11614
11615   fprintf(file, "\n");
11616   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11617     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11618
11619   fclose(file);
11620
11621   SetFilePermissions(filename, PERMS_PRIVATE);
11622 }
11623
11624 void SaveSetup_AutoSetup(void)
11625 {
11626   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11627   FILE *file;
11628   int i;
11629
11630   InitUserDataDirectory();
11631
11632   if (!(file = fopen(filename, MODE_WRITE)))
11633   {
11634     Warn("cannot write auto setup file '%s'", filename);
11635
11636     free(filename);
11637
11638     return;
11639   }
11640
11641   fprintFileHeader(file, AUTOSETUP_FILENAME);
11642
11643   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11644     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11645
11646   fclose(file);
11647
11648   SetFilePermissions(filename, PERMS_PRIVATE);
11649
11650   free(filename);
11651 }
11652
11653 void SaveSetup_ServerSetup(void)
11654 {
11655   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11656   FILE *file;
11657   int i;
11658
11659   InitUserDataDirectory();
11660
11661   if (!(file = fopen(filename, MODE_WRITE)))
11662   {
11663     Warn("cannot write server setup file '%s'", filename);
11664
11665     free(filename);
11666
11667     return;
11668   }
11669
11670   fprintFileHeader(file, SERVERSETUP_FILENAME);
11671
11672   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11673   {
11674     // just to make things nicer :)
11675     if (server_setup_tokens[i].value == &setup.use_api_server)
11676       fprintf(file, "\n");
11677
11678     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11679   }
11680
11681   fclose(file);
11682
11683   SetFilePermissions(filename, PERMS_PRIVATE);
11684
11685   free(filename);
11686 }
11687
11688 void SaveSetup_EditorCascade(void)
11689 {
11690   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11691   FILE *file;
11692   int i;
11693
11694   InitUserDataDirectory();
11695
11696   if (!(file = fopen(filename, MODE_WRITE)))
11697   {
11698     Warn("cannot write editor cascade state file '%s'", filename);
11699
11700     free(filename);
11701
11702     return;
11703   }
11704
11705   fprintFileHeader(file, EDITORCASCADE_FILENAME);
11706
11707   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11708     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11709
11710   fclose(file);
11711
11712   SetFilePermissions(filename, PERMS_PRIVATE);
11713
11714   free(filename);
11715 }
11716
11717 void SaveSetup(void)
11718 {
11719   SaveSetup_Default();
11720   SaveSetup_AutoSetup();
11721   SaveSetup_ServerSetup();
11722   SaveSetup_EditorCascade();
11723 }
11724
11725 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11726                                                   char *filename)
11727 {
11728   FILE *file;
11729
11730   if (!(file = fopen(filename, MODE_WRITE)))
11731   {
11732     Warn("cannot write game controller mappings file '%s'", filename);
11733
11734     return;
11735   }
11736
11737   BEGIN_HASH_ITERATION(mappings_hash, itr)
11738   {
11739     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11740   }
11741   END_HASH_ITERATION(mappings_hash, itr)
11742
11743   fclose(file);
11744 }
11745
11746 void SaveSetup_AddGameControllerMapping(char *mapping)
11747 {
11748   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11749   SetupFileHash *mappings_hash = newSetupFileHash();
11750
11751   InitUserDataDirectory();
11752
11753   // load existing personal game controller mappings
11754   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11755
11756   // add new mapping to personal game controller mappings
11757   addGameControllerMappingToHash(mappings_hash, mapping);
11758
11759   // save updated personal game controller mappings
11760   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11761
11762   freeSetupFileHash(mappings_hash);
11763   free(filename);
11764 }
11765
11766 void LoadCustomElementDescriptions(void)
11767 {
11768   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11769   SetupFileHash *setup_file_hash;
11770   int i;
11771
11772   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11773   {
11774     if (element_info[i].custom_description != NULL)
11775     {
11776       free(element_info[i].custom_description);
11777       element_info[i].custom_description = NULL;
11778     }
11779   }
11780
11781   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11782     return;
11783
11784   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11785   {
11786     char *token = getStringCat2(element_info[i].token_name, ".name");
11787     char *value = getHashEntry(setup_file_hash, token);
11788
11789     if (value != NULL)
11790       element_info[i].custom_description = getStringCopy(value);
11791
11792     free(token);
11793   }
11794
11795   freeSetupFileHash(setup_file_hash);
11796 }
11797
11798 static int getElementFromToken(char *token)
11799 {
11800   char *value = getHashEntry(element_token_hash, token);
11801
11802   if (value != NULL)
11803     return atoi(value);
11804
11805   Warn("unknown element token '%s'", token);
11806
11807   return EL_UNDEFINED;
11808 }
11809
11810 void FreeGlobalAnimEventInfo(void)
11811 {
11812   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11813
11814   if (gaei->event_list == NULL)
11815     return;
11816
11817   int i;
11818
11819   for (i = 0; i < gaei->num_event_lists; i++)
11820   {
11821     checked_free(gaei->event_list[i]->event_value);
11822     checked_free(gaei->event_list[i]);
11823   }
11824
11825   checked_free(gaei->event_list);
11826
11827   gaei->event_list = NULL;
11828   gaei->num_event_lists = 0;
11829 }
11830
11831 static int AddGlobalAnimEventList(void)
11832 {
11833   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11834   int list_pos = gaei->num_event_lists++;
11835
11836   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11837                                      sizeof(struct GlobalAnimEventListInfo *));
11838
11839   gaei->event_list[list_pos] =
11840     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11841
11842   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11843
11844   gaeli->event_value = NULL;
11845   gaeli->num_event_values = 0;
11846
11847   return list_pos;
11848 }
11849
11850 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11851 {
11852   // do not add empty global animation events
11853   if (event_value == ANIM_EVENT_NONE)
11854     return list_pos;
11855
11856   // if list position is undefined, create new list
11857   if (list_pos == ANIM_EVENT_UNDEFINED)
11858     list_pos = AddGlobalAnimEventList();
11859
11860   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11861   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11862   int value_pos = gaeli->num_event_values++;
11863
11864   gaeli->event_value = checked_realloc(gaeli->event_value,
11865                                        gaeli->num_event_values * sizeof(int *));
11866
11867   gaeli->event_value[value_pos] = event_value;
11868
11869   return list_pos;
11870 }
11871
11872 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11873 {
11874   if (list_pos == ANIM_EVENT_UNDEFINED)
11875     return ANIM_EVENT_NONE;
11876
11877   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11878   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11879
11880   return gaeli->event_value[value_pos];
11881 }
11882
11883 int GetGlobalAnimEventValueCount(int list_pos)
11884 {
11885   if (list_pos == ANIM_EVENT_UNDEFINED)
11886     return 0;
11887
11888   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11889   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11890
11891   return gaeli->num_event_values;
11892 }
11893
11894 // This function checks if a string <s> of the format "string1, string2, ..."
11895 // exactly contains a string <s_contained>.
11896
11897 static boolean string_has_parameter(char *s, char *s_contained)
11898 {
11899   char *substring;
11900
11901   if (s == NULL || s_contained == NULL)
11902     return FALSE;
11903
11904   if (strlen(s_contained) > strlen(s))
11905     return FALSE;
11906
11907   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11908   {
11909     char next_char = s[strlen(s_contained)];
11910
11911     // check if next character is delimiter or whitespace
11912     return (next_char == ',' || next_char == '\0' ||
11913             next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11914   }
11915
11916   // check if string contains another parameter string after a comma
11917   substring = strchr(s, ',');
11918   if (substring == NULL)        // string does not contain a comma
11919     return FALSE;
11920
11921   // advance string pointer to next character after the comma
11922   substring++;
11923
11924   // skip potential whitespaces after the comma
11925   while (*substring == ' ' || *substring == '\t')
11926     substring++;
11927
11928   return string_has_parameter(substring, s_contained);
11929 }
11930
11931 static int get_anim_parameter_value(char *s)
11932 {
11933   int event_value[] =
11934   {
11935     ANIM_EVENT_CLICK,
11936     ANIM_EVENT_INIT,
11937     ANIM_EVENT_START,
11938     ANIM_EVENT_END,
11939     ANIM_EVENT_POST
11940   };
11941   char *pattern_1[] =
11942   {
11943     "click:anim_",
11944     "init:anim_",
11945     "start:anim_",
11946     "end:anim_",
11947     "post:anim_"
11948   };
11949   char *pattern_2 = ".part_";
11950   char *matching_char = NULL;
11951   char *s_ptr = s;
11952   int pattern_1_len = 0;
11953   int result = ANIM_EVENT_NONE;
11954   int i;
11955
11956   for (i = 0; i < ARRAY_SIZE(event_value); i++)
11957   {
11958     matching_char = strstr(s_ptr, pattern_1[i]);
11959     pattern_1_len = strlen(pattern_1[i]);
11960     result = event_value[i];
11961
11962     if (matching_char != NULL)
11963       break;
11964   }
11965
11966   if (matching_char == NULL)
11967     return ANIM_EVENT_NONE;
11968
11969   s_ptr = matching_char + pattern_1_len;
11970
11971   // check for main animation number ("anim_X" or "anim_XX")
11972   if (*s_ptr >= '0' && *s_ptr <= '9')
11973   {
11974     int gic_anim_nr = (*s_ptr++ - '0');
11975
11976     if (*s_ptr >= '0' && *s_ptr <= '9')
11977       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11978
11979     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11980       return ANIM_EVENT_NONE;
11981
11982     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11983   }
11984   else
11985   {
11986     // invalid main animation number specified
11987
11988     return ANIM_EVENT_NONE;
11989   }
11990
11991   // check for animation part number ("part_X" or "part_XX") (optional)
11992   if (strPrefix(s_ptr, pattern_2))
11993   {
11994     s_ptr += strlen(pattern_2);
11995
11996     if (*s_ptr >= '0' && *s_ptr <= '9')
11997     {
11998       int gic_part_nr = (*s_ptr++ - '0');
11999
12000       if (*s_ptr >= '0' && *s_ptr <= '9')
12001         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12002
12003       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12004         return ANIM_EVENT_NONE;
12005
12006       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12007     }
12008     else
12009     {
12010       // invalid animation part number specified
12011
12012       return ANIM_EVENT_NONE;
12013     }
12014   }
12015
12016   // discard result if next character is neither delimiter nor whitespace
12017   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12018         *s_ptr == ' ' || *s_ptr == '\t'))
12019     return ANIM_EVENT_NONE;
12020
12021   return result;
12022 }
12023
12024 static int get_anim_parameter_values(char *s)
12025 {
12026   int list_pos = ANIM_EVENT_UNDEFINED;
12027   int event_value = ANIM_EVENT_DEFAULT;
12028
12029   if (string_has_parameter(s, "any"))
12030     event_value |= ANIM_EVENT_ANY;
12031
12032   if (string_has_parameter(s, "click:self") ||
12033       string_has_parameter(s, "click") ||
12034       string_has_parameter(s, "self"))
12035     event_value |= ANIM_EVENT_SELF;
12036
12037   if (string_has_parameter(s, "unclick:any"))
12038     event_value |= ANIM_EVENT_UNCLICK_ANY;
12039
12040   // if animation event found, add it to global animation event list
12041   if (event_value != ANIM_EVENT_NONE)
12042     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12043
12044   while (s != NULL)
12045   {
12046     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12047     event_value = get_anim_parameter_value(s);
12048
12049     // if animation event found, add it to global animation event list
12050     if (event_value != ANIM_EVENT_NONE)
12051       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12052
12053     // continue with next part of the string, starting with next comma
12054     s = strchr(s + 1, ',');
12055   }
12056
12057   return list_pos;
12058 }
12059
12060 static int get_anim_action_parameter_value(char *token)
12061 {
12062   // check most common default case first to massively speed things up
12063   if (strEqual(token, ARG_UNDEFINED))
12064     return ANIM_EVENT_ACTION_NONE;
12065
12066   int result = getImageIDFromToken(token);
12067
12068   if (result == -1)
12069   {
12070     char *gfx_token = getStringCat2("gfx.", token);
12071
12072     result = getImageIDFromToken(gfx_token);
12073
12074     checked_free(gfx_token);
12075   }
12076
12077   if (result == -1)
12078   {
12079     Key key = getKeyFromX11KeyName(token);
12080
12081     if (key != KSYM_UNDEFINED)
12082       result = -(int)key;
12083   }
12084
12085   if (result == -1)
12086     result = ANIM_EVENT_ACTION_NONE;
12087
12088   return result;
12089 }
12090
12091 int get_parameter_value(char *value_raw, char *suffix, int type)
12092 {
12093   char *value = getStringToLower(value_raw);
12094   int result = 0;       // probably a save default value
12095
12096   if (strEqual(suffix, ".direction"))
12097   {
12098     result = (strEqual(value, "left")  ? MV_LEFT :
12099               strEqual(value, "right") ? MV_RIGHT :
12100               strEqual(value, "up")    ? MV_UP :
12101               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12102   }
12103   else if (strEqual(suffix, ".position"))
12104   {
12105     result = (strEqual(value, "left")   ? POS_LEFT :
12106               strEqual(value, "right")  ? POS_RIGHT :
12107               strEqual(value, "top")    ? POS_TOP :
12108               strEqual(value, "upper")  ? POS_UPPER :
12109               strEqual(value, "middle") ? POS_MIDDLE :
12110               strEqual(value, "lower")  ? POS_LOWER :
12111               strEqual(value, "bottom") ? POS_BOTTOM :
12112               strEqual(value, "any")    ? POS_ANY :
12113               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12114   }
12115   else if (strEqual(suffix, ".align"))
12116   {
12117     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12118               strEqual(value, "right")  ? ALIGN_RIGHT :
12119               strEqual(value, "center") ? ALIGN_CENTER :
12120               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12121   }
12122   else if (strEqual(suffix, ".valign"))
12123   {
12124     result = (strEqual(value, "top")    ? VALIGN_TOP :
12125               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12126               strEqual(value, "middle") ? VALIGN_MIDDLE :
12127               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12128   }
12129   else if (strEqual(suffix, ".anim_mode"))
12130   {
12131     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12132               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12133               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12134               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12135               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12136               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12137               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12138               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12139               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12140               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12141               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12142               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12143               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12144               string_has_parameter(value, "all")        ? ANIM_ALL :
12145               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12146               ANIM_DEFAULT);
12147
12148     if (string_has_parameter(value, "once"))
12149       result |= ANIM_ONCE;
12150
12151     if (string_has_parameter(value, "reverse"))
12152       result |= ANIM_REVERSE;
12153
12154     if (string_has_parameter(value, "opaque_player"))
12155       result |= ANIM_OPAQUE_PLAYER;
12156
12157     if (string_has_parameter(value, "static_panel"))
12158       result |= ANIM_STATIC_PANEL;
12159   }
12160   else if (strEqual(suffix, ".init_event") ||
12161            strEqual(suffix, ".anim_event"))
12162   {
12163     result = get_anim_parameter_values(value);
12164   }
12165   else if (strEqual(suffix, ".init_delay_action") ||
12166            strEqual(suffix, ".anim_delay_action") ||
12167            strEqual(suffix, ".post_delay_action") ||
12168            strEqual(suffix, ".init_event_action") ||
12169            strEqual(suffix, ".anim_event_action"))
12170   {
12171     result = get_anim_action_parameter_value(value_raw);
12172   }
12173   else if (strEqual(suffix, ".class"))
12174   {
12175     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12176               get_hash_from_key(value));
12177   }
12178   else if (strEqual(suffix, ".style"))
12179   {
12180     result = STYLE_DEFAULT;
12181
12182     if (string_has_parameter(value, "accurate_borders"))
12183       result |= STYLE_ACCURATE_BORDERS;
12184
12185     if (string_has_parameter(value, "inner_corners"))
12186       result |= STYLE_INNER_CORNERS;
12187
12188     if (string_has_parameter(value, "reverse"))
12189       result |= STYLE_REVERSE;
12190
12191     if (string_has_parameter(value, "leftmost_position"))
12192       result |= STYLE_LEFTMOST_POSITION;
12193
12194     if (string_has_parameter(value, "block_clicks"))
12195       result |= STYLE_BLOCK;
12196
12197     if (string_has_parameter(value, "passthrough_clicks"))
12198       result |= STYLE_PASSTHROUGH;
12199
12200     if (string_has_parameter(value, "multiple_actions"))
12201       result |= STYLE_MULTIPLE_ACTIONS;
12202   }
12203   else if (strEqual(suffix, ".fade_mode"))
12204   {
12205     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12206               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12207               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12208               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12209               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12210               FADE_MODE_DEFAULT);
12211   }
12212   else if (strEqual(suffix, ".auto_delay_unit"))
12213   {
12214     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12215               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12216               AUTO_DELAY_UNIT_DEFAULT);
12217   }
12218   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12219   {
12220     result = gfx.get_font_from_token_function(value);
12221   }
12222   else          // generic parameter of type integer or boolean
12223   {
12224     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12225               type == TYPE_INTEGER ? get_integer_from_string(value) :
12226               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12227               ARG_UNDEFINED_VALUE);
12228   }
12229
12230   free(value);
12231
12232   return result;
12233 }
12234
12235 static int get_token_parameter_value(char *token, char *value_raw)
12236 {
12237   char *suffix;
12238
12239   if (token == NULL || value_raw == NULL)
12240     return ARG_UNDEFINED_VALUE;
12241
12242   suffix = strrchr(token, '.');
12243   if (suffix == NULL)
12244     suffix = token;
12245
12246   if (strEqual(suffix, ".element"))
12247     return getElementFromToken(value_raw);
12248
12249   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12250   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12251 }
12252
12253 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12254                                      boolean ignore_defaults)
12255 {
12256   int i;
12257
12258   for (i = 0; image_config_vars[i].token != NULL; i++)
12259   {
12260     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12261
12262     // (ignore definitions set to "[DEFAULT]" which are already initialized)
12263     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12264       continue;
12265
12266     if (value != NULL)
12267       *image_config_vars[i].value =
12268         get_token_parameter_value(image_config_vars[i].token, value);
12269   }
12270 }
12271
12272 void InitMenuDesignSettings_Static(void)
12273 {
12274   // always start with reliable default values from static default config
12275   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12276 }
12277
12278 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12279 {
12280   int i;
12281
12282   // the following initializes hierarchical values from static configuration
12283
12284   // special case: initialize "ARG_DEFAULT" values in static default config
12285   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12286   titlescreen_initial_first_default.fade_mode  =
12287     title_initial_first_default.fade_mode;
12288   titlescreen_initial_first_default.fade_delay =
12289     title_initial_first_default.fade_delay;
12290   titlescreen_initial_first_default.post_delay =
12291     title_initial_first_default.post_delay;
12292   titlescreen_initial_first_default.auto_delay =
12293     title_initial_first_default.auto_delay;
12294   titlescreen_initial_first_default.auto_delay_unit =
12295     title_initial_first_default.auto_delay_unit;
12296   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
12297   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12298   titlescreen_first_default.post_delay = title_first_default.post_delay;
12299   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12300   titlescreen_first_default.auto_delay_unit =
12301     title_first_default.auto_delay_unit;
12302   titlemessage_initial_first_default.fade_mode  =
12303     title_initial_first_default.fade_mode;
12304   titlemessage_initial_first_default.fade_delay =
12305     title_initial_first_default.fade_delay;
12306   titlemessage_initial_first_default.post_delay =
12307     title_initial_first_default.post_delay;
12308   titlemessage_initial_first_default.auto_delay =
12309     title_initial_first_default.auto_delay;
12310   titlemessage_initial_first_default.auto_delay_unit =
12311     title_initial_first_default.auto_delay_unit;
12312   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
12313   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12314   titlemessage_first_default.post_delay = title_first_default.post_delay;
12315   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12316   titlemessage_first_default.auto_delay_unit =
12317     title_first_default.auto_delay_unit;
12318
12319   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
12320   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12321   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12322   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12323   titlescreen_initial_default.auto_delay_unit =
12324     title_initial_default.auto_delay_unit;
12325   titlescreen_default.fade_mode  = title_default.fade_mode;
12326   titlescreen_default.fade_delay = title_default.fade_delay;
12327   titlescreen_default.post_delay = title_default.post_delay;
12328   titlescreen_default.auto_delay = title_default.auto_delay;
12329   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12330   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
12331   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12332   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12333   titlemessage_initial_default.auto_delay_unit =
12334     title_initial_default.auto_delay_unit;
12335   titlemessage_default.fade_mode  = title_default.fade_mode;
12336   titlemessage_default.fade_delay = title_default.fade_delay;
12337   titlemessage_default.post_delay = title_default.post_delay;
12338   titlemessage_default.auto_delay = title_default.auto_delay;
12339   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12340
12341   // special case: initialize "ARG_DEFAULT" values in static default config
12342   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12343   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12344   {
12345     titlescreen_initial_first[i] = titlescreen_initial_first_default;
12346     titlescreen_first[i] = titlescreen_first_default;
12347     titlemessage_initial_first[i] = titlemessage_initial_first_default;
12348     titlemessage_first[i] = titlemessage_first_default;
12349
12350     titlescreen_initial[i] = titlescreen_initial_default;
12351     titlescreen[i] = titlescreen_default;
12352     titlemessage_initial[i] = titlemessage_initial_default;
12353     titlemessage[i] = titlemessage_default;
12354   }
12355
12356   // special case: initialize "ARG_DEFAULT" values in static default config
12357   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12358   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12359   {
12360     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
12361       continue;
12362
12363     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12364     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12365     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12366   }
12367
12368   // special case: initialize "ARG_DEFAULT" values in static default config
12369   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12370   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12371   {
12372     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12373     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12374     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12375
12376     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
12377       continue;
12378
12379     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12380   }
12381 }
12382
12383 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12384 {
12385   static struct
12386   {
12387     struct XY *dst, *src;
12388   }
12389   game_buttons_xy[] =
12390   {
12391     { &game.button.save,        &game.button.stop       },
12392     { &game.button.pause2,      &game.button.pause      },
12393     { &game.button.load,        &game.button.play       },
12394     { &game.button.undo,        &game.button.stop       },
12395     { &game.button.redo,        &game.button.play       },
12396
12397     { NULL,                     NULL                    }
12398   };
12399   int i, j;
12400
12401   // special case: initialize later added SETUP list size from LEVELS value
12402   if (menu.list_size[GAME_MODE_SETUP] == -1)
12403     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12404
12405   // set default position for snapshot buttons to stop/pause/play buttons
12406   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12407     if ((*game_buttons_xy[i].dst).x == -1 &&
12408         (*game_buttons_xy[i].dst).y == -1)
12409       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12410
12411   // --------------------------------------------------------------------------
12412   // dynamic viewports (including playfield margins, borders and alignments)
12413   // --------------------------------------------------------------------------
12414
12415   // dynamic viewports currently only supported for landscape mode
12416   int display_width  = MAX(video.display_width, video.display_height);
12417   int display_height = MIN(video.display_width, video.display_height);
12418
12419   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12420   {
12421     struct RectWithBorder *vp_window    = &viewport.window[i];
12422     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12423     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
12424     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
12425     boolean dynamic_window_width     = (vp_window->min_width     != -1);
12426     boolean dynamic_window_height    = (vp_window->min_height    != -1);
12427     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
12428     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12429
12430     // adjust window size if min/max width/height is specified
12431
12432     if (vp_window->min_width != -1)
12433     {
12434       int window_width = display_width;
12435
12436       // when using static window height, use aspect ratio of display
12437       if (vp_window->min_height == -1)
12438         window_width = vp_window->height * display_width / display_height;
12439
12440       vp_window->width = MAX(vp_window->min_width, window_width);
12441     }
12442
12443     if (vp_window->min_height != -1)
12444     {
12445       int window_height = display_height;
12446
12447       // when using static window width, use aspect ratio of display
12448       if (vp_window->min_width == -1)
12449         window_height = vp_window->width * display_height / display_width;
12450
12451       vp_window->height = MAX(vp_window->min_height, window_height);
12452     }
12453
12454     if (vp_window->max_width != -1)
12455       vp_window->width = MIN(vp_window->width, vp_window->max_width);
12456
12457     if (vp_window->max_height != -1)
12458       vp_window->height = MIN(vp_window->height, vp_window->max_height);
12459
12460     int playfield_width  = vp_window->width;
12461     int playfield_height = vp_window->height;
12462
12463     // adjust playfield size and position according to specified margins
12464
12465     playfield_width  -= vp_playfield->margin_left;
12466     playfield_width  -= vp_playfield->margin_right;
12467
12468     playfield_height -= vp_playfield->margin_top;
12469     playfield_height -= vp_playfield->margin_bottom;
12470
12471     // adjust playfield size if min/max width/height is specified
12472
12473     if (vp_playfield->min_width != -1)
12474       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12475
12476     if (vp_playfield->min_height != -1)
12477       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12478
12479     if (vp_playfield->max_width != -1)
12480       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12481
12482     if (vp_playfield->max_height != -1)
12483       vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12484
12485     // adjust playfield position according to specified alignment
12486
12487     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12488       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12489     else if (vp_playfield->align == ALIGN_CENTER)
12490       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12491     else if (vp_playfield->align == ALIGN_RIGHT)
12492       vp_playfield->x += playfield_width - vp_playfield->width;
12493
12494     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12495       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12496     else if (vp_playfield->valign == VALIGN_MIDDLE)
12497       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12498     else if (vp_playfield->valign == VALIGN_BOTTOM)
12499       vp_playfield->y += playfield_height - vp_playfield->height;
12500
12501     vp_playfield->x += vp_playfield->margin_left;
12502     vp_playfield->y += vp_playfield->margin_top;
12503
12504     // adjust individual playfield borders if only default border is specified
12505
12506     if (vp_playfield->border_left == -1)
12507       vp_playfield->border_left = vp_playfield->border_size;
12508     if (vp_playfield->border_right == -1)
12509       vp_playfield->border_right = vp_playfield->border_size;
12510     if (vp_playfield->border_top == -1)
12511       vp_playfield->border_top = vp_playfield->border_size;
12512     if (vp_playfield->border_bottom == -1)
12513       vp_playfield->border_bottom = vp_playfield->border_size;
12514
12515     // set dynamic playfield borders if borders are specified as undefined
12516     // (but only if window size was dynamic and playfield size was static)
12517
12518     if (dynamic_window_width && !dynamic_playfield_width)
12519     {
12520       if (vp_playfield->border_left == -1)
12521       {
12522         vp_playfield->border_left = (vp_playfield->x -
12523                                      vp_playfield->margin_left);
12524         vp_playfield->x     -= vp_playfield->border_left;
12525         vp_playfield->width += vp_playfield->border_left;
12526       }
12527
12528       if (vp_playfield->border_right == -1)
12529       {
12530         vp_playfield->border_right = (vp_window->width -
12531                                       vp_playfield->x -
12532                                       vp_playfield->width -
12533                                       vp_playfield->margin_right);
12534         vp_playfield->width += vp_playfield->border_right;
12535       }
12536     }
12537
12538     if (dynamic_window_height && !dynamic_playfield_height)
12539     {
12540       if (vp_playfield->border_top == -1)
12541       {
12542         vp_playfield->border_top = (vp_playfield->y -
12543                                     vp_playfield->margin_top);
12544         vp_playfield->y      -= vp_playfield->border_top;
12545         vp_playfield->height += vp_playfield->border_top;
12546       }
12547
12548       if (vp_playfield->border_bottom == -1)
12549       {
12550         vp_playfield->border_bottom = (vp_window->height -
12551                                        vp_playfield->y -
12552                                        vp_playfield->height -
12553                                        vp_playfield->margin_bottom);
12554         vp_playfield->height += vp_playfield->border_bottom;
12555       }
12556     }
12557
12558     // adjust playfield size to be a multiple of a defined alignment tile size
12559
12560     int align_size = vp_playfield->align_size;
12561     int playfield_xtiles = vp_playfield->width  / align_size;
12562     int playfield_ytiles = vp_playfield->height / align_size;
12563     int playfield_width_corrected  = playfield_xtiles * align_size;
12564     int playfield_height_corrected = playfield_ytiles * align_size;
12565     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12566                                  i == GFX_SPECIAL_ARG_EDITOR);
12567
12568     if (is_playfield_mode &&
12569         dynamic_playfield_width &&
12570         vp_playfield->width != playfield_width_corrected)
12571     {
12572       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12573
12574       vp_playfield->width = playfield_width_corrected;
12575
12576       if (vp_playfield->align == ALIGN_LEFT)
12577       {
12578         vp_playfield->border_left += playfield_xdiff;
12579       }
12580       else if (vp_playfield->align == ALIGN_RIGHT)
12581       {
12582         vp_playfield->border_right += playfield_xdiff;
12583       }
12584       else if (vp_playfield->align == ALIGN_CENTER)
12585       {
12586         int border_left_diff  = playfield_xdiff / 2;
12587         int border_right_diff = playfield_xdiff - border_left_diff;
12588
12589         vp_playfield->border_left  += border_left_diff;
12590         vp_playfield->border_right += border_right_diff;
12591       }
12592     }
12593
12594     if (is_playfield_mode &&
12595         dynamic_playfield_height &&
12596         vp_playfield->height != playfield_height_corrected)
12597     {
12598       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12599
12600       vp_playfield->height = playfield_height_corrected;
12601
12602       if (vp_playfield->valign == VALIGN_TOP)
12603       {
12604         vp_playfield->border_top += playfield_ydiff;
12605       }
12606       else if (vp_playfield->align == VALIGN_BOTTOM)
12607       {
12608         vp_playfield->border_right += playfield_ydiff;
12609       }
12610       else if (vp_playfield->align == VALIGN_MIDDLE)
12611       {
12612         int border_top_diff    = playfield_ydiff / 2;
12613         int border_bottom_diff = playfield_ydiff - border_top_diff;
12614
12615         vp_playfield->border_top    += border_top_diff;
12616         vp_playfield->border_bottom += border_bottom_diff;
12617       }
12618     }
12619
12620     // adjust door positions according to specified alignment
12621
12622     for (j = 0; j < 2; j++)
12623     {
12624       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12625
12626       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12627         vp_door->x = ALIGNED_VP_XPOS(vp_door);
12628       else if (vp_door->align == ALIGN_CENTER)
12629         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12630       else if (vp_door->align == ALIGN_RIGHT)
12631         vp_door->x += vp_window->width - vp_door->width;
12632
12633       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12634         vp_door->y = ALIGNED_VP_YPOS(vp_door);
12635       else if (vp_door->valign == VALIGN_MIDDLE)
12636         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12637       else if (vp_door->valign == VALIGN_BOTTOM)
12638         vp_door->y += vp_window->height - vp_door->height;
12639     }
12640   }
12641 }
12642
12643 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12644 {
12645   static struct
12646   {
12647     struct XYTileSize *dst, *src;
12648     int graphic;
12649   }
12650   editor_buttons_xy[] =
12651   {
12652     {
12653       &editor.button.element_left,      &editor.palette.element_left,
12654       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12655     },
12656     {
12657       &editor.button.element_middle,    &editor.palette.element_middle,
12658       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12659     },
12660     {
12661       &editor.button.element_right,     &editor.palette.element_right,
12662       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12663     },
12664
12665     { NULL,                     NULL                    }
12666   };
12667   int i;
12668
12669   // set default position for element buttons to element graphics
12670   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12671   {
12672     if ((*editor_buttons_xy[i].dst).x == -1 &&
12673         (*editor_buttons_xy[i].dst).y == -1)
12674     {
12675       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12676
12677       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12678
12679       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12680     }
12681   }
12682
12683   // adjust editor palette rows and columns if specified to be dynamic
12684
12685   if (editor.palette.cols == -1)
12686   {
12687     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12688     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12689     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12690
12691     editor.palette.cols = (vp_width - sc_width) / bt_width;
12692
12693     if (editor.palette.x == -1)
12694     {
12695       int palette_width = editor.palette.cols * bt_width + sc_width;
12696
12697       editor.palette.x = (vp_width - palette_width) / 2;
12698     }
12699   }
12700
12701   if (editor.palette.rows == -1)
12702   {
12703     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12704     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12705     int tx_height = getFontHeight(FONT_TEXT_2);
12706
12707     editor.palette.rows = (vp_height - tx_height) / bt_height;
12708
12709     if (editor.palette.y == -1)
12710     {
12711       int palette_height = editor.palette.rows * bt_height + tx_height;
12712
12713       editor.palette.y = (vp_height - palette_height) / 2;
12714     }
12715   }
12716 }
12717
12718 static void LoadMenuDesignSettingsFromFilename(char *filename)
12719 {
12720   static struct TitleFadingInfo tfi;
12721   static struct TitleMessageInfo tmi;
12722   static struct TokenInfo title_tokens[] =
12723   {
12724     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
12725     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
12726     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
12727     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
12728     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
12729
12730     { -1,               NULL,                   NULL                    }
12731   };
12732   static struct TokenInfo titlemessage_tokens[] =
12733   {
12734     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
12735     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
12736     { TYPE_INTEGER,     &tmi.width,             ".width"                },
12737     { TYPE_INTEGER,     &tmi.height,            ".height"               },
12738     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
12739     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
12740     { TYPE_INTEGER,     &tmi.align,             ".align"                },
12741     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
12742     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
12743     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
12744     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
12745     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
12746     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
12747     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
12748     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
12749     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
12750     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
12751     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
12752
12753     { -1,               NULL,                   NULL                    }
12754   };
12755   static struct
12756   {
12757     struct TitleFadingInfo *info;
12758     char *text;
12759   }
12760   title_info[] =
12761   {
12762     // initialize first titles from "enter screen" definitions, if defined
12763     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
12764     { &title_first_default,             "menu.enter_screen.TITLE"       },
12765
12766     // initialize title screens from "next screen" definitions, if defined
12767     { &title_initial_default,           "menu.next_screen.TITLE"        },
12768     { &title_default,                   "menu.next_screen.TITLE"        },
12769
12770     { NULL,                             NULL                            }
12771   };
12772   static struct
12773   {
12774     struct TitleMessageInfo *array;
12775     char *text;
12776   }
12777   titlemessage_arrays[] =
12778   {
12779     // initialize first titles from "enter screen" definitions, if defined
12780     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
12781     { titlescreen_first,                "menu.enter_screen.TITLE"       },
12782     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
12783     { titlemessage_first,               "menu.enter_screen.TITLE"       },
12784
12785     // initialize titles from "next screen" definitions, if defined
12786     { titlescreen_initial,              "menu.next_screen.TITLE"        },
12787     { titlescreen,                      "menu.next_screen.TITLE"        },
12788     { titlemessage_initial,             "menu.next_screen.TITLE"        },
12789     { titlemessage,                     "menu.next_screen.TITLE"        },
12790
12791     // overwrite titles with title definitions, if defined
12792     { titlescreen_initial_first,        "[title_initial]"               },
12793     { titlescreen_first,                "[title]"                       },
12794     { titlemessage_initial_first,       "[title_initial]"               },
12795     { titlemessage_first,               "[title]"                       },
12796
12797     { titlescreen_initial,              "[title_initial]"               },
12798     { titlescreen,                      "[title]"                       },
12799     { titlemessage_initial,             "[title_initial]"               },
12800     { titlemessage,                     "[title]"                       },
12801
12802     // overwrite titles with title screen/message definitions, if defined
12803     { titlescreen_initial_first,        "[titlescreen_initial]"         },
12804     { titlescreen_first,                "[titlescreen]"                 },
12805     { titlemessage_initial_first,       "[titlemessage_initial]"        },
12806     { titlemessage_first,               "[titlemessage]"                },
12807
12808     { titlescreen_initial,              "[titlescreen_initial]"         },
12809     { titlescreen,                      "[titlescreen]"                 },
12810     { titlemessage_initial,             "[titlemessage_initial]"        },
12811     { titlemessage,                     "[titlemessage]"                },
12812
12813     { NULL,                             NULL                            }
12814   };
12815   SetupFileHash *setup_file_hash;
12816   int i, j, k;
12817
12818   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12819     return;
12820
12821   // the following initializes hierarchical values from dynamic configuration
12822
12823   // special case: initialize with default values that may be overwritten
12824   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12825   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12826   {
12827     struct TokenIntPtrInfo menu_config[] =
12828     {
12829       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
12830       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
12831       { "menu.list_size",       &menu.list_size[i]      }
12832     };
12833
12834     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12835     {
12836       char *token = menu_config[j].token;
12837       char *value = getHashEntry(setup_file_hash, token);
12838
12839       if (value != NULL)
12840         *menu_config[j].value = get_integer_from_string(value);
12841     }
12842   }
12843
12844   // special case: initialize with default values that may be overwritten
12845   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12846   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12847   {
12848     struct TokenIntPtrInfo menu_config[] =
12849     {
12850       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
12851       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
12852       { "menu.list_size.INFO",          &menu.list_size_info[i]         }
12853     };
12854
12855     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12856     {
12857       char *token = menu_config[j].token;
12858       char *value = getHashEntry(setup_file_hash, token);
12859
12860       if (value != NULL)
12861         *menu_config[j].value = get_integer_from_string(value);
12862     }
12863   }
12864
12865   // special case: initialize with default values that may be overwritten
12866   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12867   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12868   {
12869     struct TokenIntPtrInfo menu_config[] =
12870     {
12871       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
12872       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
12873     };
12874
12875     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12876     {
12877       char *token = menu_config[j].token;
12878       char *value = getHashEntry(setup_file_hash, token);
12879
12880       if (value != NULL)
12881         *menu_config[j].value = get_integer_from_string(value);
12882     }
12883   }
12884
12885   // special case: initialize with default values that may be overwritten
12886   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12887   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12888   {
12889     struct TokenIntPtrInfo menu_config[] =
12890     {
12891       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
12892       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
12893       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
12894       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
12895       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
12896       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
12897       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
12898       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
12899       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
12900     };
12901
12902     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12903     {
12904       char *token = menu_config[j].token;
12905       char *value = getHashEntry(setup_file_hash, token);
12906
12907       if (value != NULL)
12908         *menu_config[j].value = get_integer_from_string(value);
12909     }
12910   }
12911
12912   // special case: initialize with default values that may be overwritten
12913   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12914   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12915   {
12916     struct TokenIntPtrInfo menu_config[] =
12917     {
12918       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
12919       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12920       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12921       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
12922       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12923       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12924       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
12925       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
12926       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
12927     };
12928
12929     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12930     {
12931       char *token = menu_config[j].token;
12932       char *value = getHashEntry(setup_file_hash, token);
12933
12934       if (value != NULL)
12935         *menu_config[j].value = get_token_parameter_value(token, value);
12936     }
12937   }
12938
12939   // special case: initialize with default values that may be overwritten
12940   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12941   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12942   {
12943     struct
12944     {
12945       char *token_prefix;
12946       struct RectWithBorder *struct_ptr;
12947     }
12948     vp_struct[] =
12949     {
12950       { "viewport.window",      &viewport.window[i]     },
12951       { "viewport.playfield",   &viewport.playfield[i]  },
12952       { "viewport.door_1",      &viewport.door_1[i]     },
12953       { "viewport.door_2",      &viewport.door_2[i]     }
12954     };
12955
12956     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12957     {
12958       struct TokenIntPtrInfo vp_config[] =
12959       {
12960         { ".x",                 &vp_struct[j].struct_ptr->x             },
12961         { ".y",                 &vp_struct[j].struct_ptr->y             },
12962         { ".width",             &vp_struct[j].struct_ptr->width         },
12963         { ".height",            &vp_struct[j].struct_ptr->height        },
12964         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
12965         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
12966         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
12967         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
12968         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
12969         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
12970         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
12971         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
12972         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
12973         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
12974         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
12975         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
12976         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
12977         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
12978         { ".align",             &vp_struct[j].struct_ptr->align         },
12979         { ".valign",            &vp_struct[j].struct_ptr->valign        }
12980       };
12981
12982       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12983       {
12984         char *token = getStringCat2(vp_struct[j].token_prefix,
12985                                     vp_config[k].token);
12986         char *value = getHashEntry(setup_file_hash, token);
12987
12988         if (value != NULL)
12989           *vp_config[k].value = get_token_parameter_value(token, value);
12990
12991         free(token);
12992       }
12993     }
12994   }
12995
12996   // special case: initialize with default values that may be overwritten
12997   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12998   for (i = 0; title_info[i].info != NULL; i++)
12999   {
13000     struct TitleFadingInfo *info = title_info[i].info;
13001     char *base_token = title_info[i].text;
13002
13003     for (j = 0; title_tokens[j].type != -1; j++)
13004     {
13005       char *token = getStringCat2(base_token, title_tokens[j].text);
13006       char *value = getHashEntry(setup_file_hash, token);
13007
13008       if (value != NULL)
13009       {
13010         int parameter_value = get_token_parameter_value(token, value);
13011
13012         tfi = *info;
13013
13014         *(int *)title_tokens[j].value = (int)parameter_value;
13015
13016         *info = tfi;
13017       }
13018
13019       free(token);
13020     }
13021   }
13022
13023   // special case: initialize with default values that may be overwritten
13024   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13025   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13026   {
13027     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13028     char *base_token = titlemessage_arrays[i].text;
13029
13030     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13031     {
13032       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13033       char *value = getHashEntry(setup_file_hash, token);
13034
13035       if (value != NULL)
13036       {
13037         int parameter_value = get_token_parameter_value(token, value);
13038
13039         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13040         {
13041           tmi = array[k];
13042
13043           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13044             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13045           else
13046             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13047
13048           array[k] = tmi;
13049         }
13050       }
13051
13052       free(token);
13053     }
13054   }
13055
13056   // special case: check if network and preview player positions are redefined,
13057   // to compare this later against the main menu level preview being redefined
13058   struct TokenIntPtrInfo menu_config_players[] =
13059   {
13060     { "main.network_players.x", &menu.main.network_players.redefined    },
13061     { "main.network_players.y", &menu.main.network_players.redefined    },
13062     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13063     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13064     { "preview.x",              &preview.redefined                      },
13065     { "preview.y",              &preview.redefined                      }
13066   };
13067
13068   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13069     *menu_config_players[i].value = FALSE;
13070
13071   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13072     if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
13073       *menu_config_players[i].value = TRUE;
13074
13075   // read (and overwrite with) values that may be specified in config file
13076   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13077
13078   freeSetupFileHash(setup_file_hash);
13079 }
13080
13081 void LoadMenuDesignSettings(void)
13082 {
13083   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13084
13085   InitMenuDesignSettings_Static();
13086   InitMenuDesignSettings_SpecialPreProcessing();
13087
13088   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13089   {
13090     // first look for special settings configured in level series config
13091     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13092
13093     if (fileExists(filename_base))
13094       LoadMenuDesignSettingsFromFilename(filename_base);
13095   }
13096
13097   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13098
13099   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13100     LoadMenuDesignSettingsFromFilename(filename_local);
13101
13102   InitMenuDesignSettings_SpecialPostProcessing();
13103 }
13104
13105 void LoadMenuDesignSettings_AfterGraphics(void)
13106 {
13107   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13108 }
13109
13110 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13111 {
13112   char *filename = getEditorSetupFilename();
13113   SetupFileList *setup_file_list, *list;
13114   SetupFileHash *element_hash;
13115   int num_unknown_tokens = 0;
13116   int i;
13117
13118   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13119     return;
13120
13121   element_hash = newSetupFileHash();
13122
13123   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13124     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13125
13126   // determined size may be larger than needed (due to unknown elements)
13127   *num_elements = 0;
13128   for (list = setup_file_list; list != NULL; list = list->next)
13129     (*num_elements)++;
13130
13131   // add space for up to 3 more elements for padding that may be needed
13132   *num_elements += 3;
13133
13134   // free memory for old list of elements, if needed
13135   checked_free(*elements);
13136
13137   // allocate memory for new list of elements
13138   *elements = checked_malloc(*num_elements * sizeof(int));
13139
13140   *num_elements = 0;
13141   for (list = setup_file_list; list != NULL; list = list->next)
13142   {
13143     char *value = getHashEntry(element_hash, list->token);
13144
13145     if (value == NULL)          // try to find obsolete token mapping
13146     {
13147       char *mapped_token = get_mapped_token(list->token);
13148
13149       if (mapped_token != NULL)
13150       {
13151         value = getHashEntry(element_hash, mapped_token);
13152
13153         free(mapped_token);
13154       }
13155     }
13156
13157     if (value != NULL)
13158     {
13159       (*elements)[(*num_elements)++] = atoi(value);
13160     }
13161     else
13162     {
13163       if (num_unknown_tokens == 0)
13164       {
13165         Warn("---");
13166         Warn("unknown token(s) found in config file:");
13167         Warn("- config file: '%s'", filename);
13168
13169         num_unknown_tokens++;
13170       }
13171
13172       Warn("- token: '%s'", list->token);
13173     }
13174   }
13175
13176   if (num_unknown_tokens > 0)
13177     Warn("---");
13178
13179   while (*num_elements % 4)     // pad with empty elements, if needed
13180     (*elements)[(*num_elements)++] = EL_EMPTY;
13181
13182   freeSetupFileList(setup_file_list);
13183   freeSetupFileHash(element_hash);
13184
13185 #if 0
13186   for (i = 0; i < *num_elements; i++)
13187     Debug("editor", "element '%s' [%d]\n",
13188           element_info[(*elements)[i]].token_name, (*elements)[i]);
13189 #endif
13190 }
13191
13192 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13193                                                      boolean is_sound)
13194 {
13195   SetupFileHash *setup_file_hash = NULL;
13196   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13197   char *filename_music, *filename_prefix, *filename_info;
13198   struct
13199   {
13200     char *token;
13201     char **value_ptr;
13202   }
13203   token_to_value_ptr[] =
13204   {
13205     { "title_header",   &tmp_music_file_info.title_header       },
13206     { "artist_header",  &tmp_music_file_info.artist_header      },
13207     { "album_header",   &tmp_music_file_info.album_header       },
13208     { "year_header",    &tmp_music_file_info.year_header        },
13209
13210     { "title",          &tmp_music_file_info.title              },
13211     { "artist",         &tmp_music_file_info.artist             },
13212     { "album",          &tmp_music_file_info.album              },
13213     { "year",           &tmp_music_file_info.year               },
13214
13215     { NULL,             NULL                                    },
13216   };
13217   int i;
13218
13219   filename_music = (is_sound ? getCustomSoundFilename(basename) :
13220                     getCustomMusicFilename(basename));
13221
13222   if (filename_music == NULL)
13223     return NULL;
13224
13225   // ---------- try to replace file extension ----------
13226
13227   filename_prefix = getStringCopy(filename_music);
13228   if (strrchr(filename_prefix, '.') != NULL)
13229     *strrchr(filename_prefix, '.') = '\0';
13230   filename_info = getStringCat2(filename_prefix, ".txt");
13231
13232   if (fileExists(filename_info))
13233     setup_file_hash = loadSetupFileHash(filename_info);
13234
13235   free(filename_prefix);
13236   free(filename_info);
13237
13238   if (setup_file_hash == NULL)
13239   {
13240     // ---------- try to add file extension ----------
13241
13242     filename_prefix = getStringCopy(filename_music);
13243     filename_info = getStringCat2(filename_prefix, ".txt");
13244
13245     if (fileExists(filename_info))
13246       setup_file_hash = loadSetupFileHash(filename_info);
13247
13248     free(filename_prefix);
13249     free(filename_info);
13250   }
13251
13252   if (setup_file_hash == NULL)
13253     return NULL;
13254
13255   // ---------- music file info found ----------
13256
13257   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13258
13259   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13260   {
13261     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13262
13263     *token_to_value_ptr[i].value_ptr =
13264       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13265   }
13266
13267   tmp_music_file_info.basename = getStringCopy(basename);
13268   tmp_music_file_info.music = music;
13269   tmp_music_file_info.is_sound = is_sound;
13270
13271   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13272   *new_music_file_info = tmp_music_file_info;
13273
13274   return new_music_file_info;
13275 }
13276
13277 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13278 {
13279   return get_music_file_info_ext(basename, music, FALSE);
13280 }
13281
13282 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13283 {
13284   return get_music_file_info_ext(basename, sound, TRUE);
13285 }
13286
13287 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13288                                      char *basename, boolean is_sound)
13289 {
13290   for (; list != NULL; list = list->next)
13291     if (list->is_sound == is_sound && strEqual(list->basename, basename))
13292       return TRUE;
13293
13294   return FALSE;
13295 }
13296
13297 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13298 {
13299   return music_info_listed_ext(list, basename, FALSE);
13300 }
13301
13302 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13303 {
13304   return music_info_listed_ext(list, basename, TRUE);
13305 }
13306
13307 void LoadMusicInfo(void)
13308 {
13309   char *music_directory = getCustomMusicDirectory();
13310   int num_music = getMusicListSize();
13311   int num_music_noconf = 0;
13312   int num_sounds = getSoundListSize();
13313   Directory *dir;
13314   DirectoryEntry *dir_entry;
13315   struct FileInfo *music, *sound;
13316   struct MusicFileInfo *next, **new;
13317   int i;
13318
13319   while (music_file_info != NULL)
13320   {
13321     next = music_file_info->next;
13322
13323     checked_free(music_file_info->basename);
13324
13325     checked_free(music_file_info->title_header);
13326     checked_free(music_file_info->artist_header);
13327     checked_free(music_file_info->album_header);
13328     checked_free(music_file_info->year_header);
13329
13330     checked_free(music_file_info->title);
13331     checked_free(music_file_info->artist);
13332     checked_free(music_file_info->album);
13333     checked_free(music_file_info->year);
13334
13335     free(music_file_info);
13336
13337     music_file_info = next;
13338   }
13339
13340   new = &music_file_info;
13341
13342   for (i = 0; i < num_music; i++)
13343   {
13344     music = getMusicListEntry(i);
13345
13346     if (music->filename == NULL)
13347       continue;
13348
13349     if (strEqual(music->filename, UNDEFINED_FILENAME))
13350       continue;
13351
13352     // a configured file may be not recognized as music
13353     if (!FileIsMusic(music->filename))
13354       continue;
13355
13356     if (!music_info_listed(music_file_info, music->filename))
13357     {
13358       *new = get_music_file_info(music->filename, i);
13359
13360       if (*new != NULL)
13361         new = &(*new)->next;
13362     }
13363   }
13364
13365   if ((dir = openDirectory(music_directory)) == NULL)
13366   {
13367     Warn("cannot read music directory '%s'", music_directory);
13368
13369     return;
13370   }
13371
13372   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
13373   {
13374     char *basename = dir_entry->basename;
13375     boolean music_already_used = FALSE;
13376     int i;
13377
13378     // skip all music files that are configured in music config file
13379     for (i = 0; i < num_music; i++)
13380     {
13381       music = getMusicListEntry(i);
13382
13383       if (music->filename == NULL)
13384         continue;
13385
13386       if (strEqual(basename, music->filename))
13387       {
13388         music_already_used = TRUE;
13389         break;
13390       }
13391     }
13392
13393     if (music_already_used)
13394       continue;
13395
13396     if (!FileIsMusic(dir_entry->filename))
13397       continue;
13398
13399     if (!music_info_listed(music_file_info, basename))
13400     {
13401       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
13402
13403       if (*new != NULL)
13404         new = &(*new)->next;
13405     }
13406
13407     num_music_noconf++;
13408   }
13409
13410   closeDirectory(dir);
13411
13412   for (i = 0; i < num_sounds; i++)
13413   {
13414     sound = getSoundListEntry(i);
13415
13416     if (sound->filename == NULL)
13417       continue;
13418
13419     if (strEqual(sound->filename, UNDEFINED_FILENAME))
13420       continue;
13421
13422     // a configured file may be not recognized as sound
13423     if (!FileIsSound(sound->filename))
13424       continue;
13425
13426     if (!sound_info_listed(music_file_info, sound->filename))
13427     {
13428       *new = get_sound_file_info(sound->filename, i);
13429       if (*new != NULL)
13430         new = &(*new)->next;
13431     }
13432   }
13433
13434   // add pointers to previous list nodes
13435
13436   struct MusicFileInfo *node = music_file_info;
13437
13438   while (node != NULL)
13439   {
13440     if (node->next)
13441       node->next->prev = node;
13442
13443     node = node->next;
13444   }
13445 }
13446
13447 static void add_helpanim_entry(int element, int action, int direction,
13448                                int delay, int *num_list_entries)
13449 {
13450   struct HelpAnimInfo *new_list_entry;
13451   (*num_list_entries)++;
13452
13453   helpanim_info =
13454     checked_realloc(helpanim_info,
13455                     *num_list_entries * sizeof(struct HelpAnimInfo));
13456   new_list_entry = &helpanim_info[*num_list_entries - 1];
13457
13458   new_list_entry->element = element;
13459   new_list_entry->action = action;
13460   new_list_entry->direction = direction;
13461   new_list_entry->delay = delay;
13462 }
13463
13464 static void print_unknown_token(char *filename, char *token, int token_nr)
13465 {
13466   if (token_nr == 0)
13467   {
13468     Warn("---");
13469     Warn("unknown token(s) found in config file:");
13470     Warn("- config file: '%s'", filename);
13471   }
13472
13473   Warn("- token: '%s'", token);
13474 }
13475
13476 static void print_unknown_token_end(int token_nr)
13477 {
13478   if (token_nr > 0)
13479     Warn("---");
13480 }
13481
13482 void LoadHelpAnimInfo(void)
13483 {
13484   char *filename = getHelpAnimFilename();
13485   SetupFileList *setup_file_list = NULL, *list;
13486   SetupFileHash *element_hash, *action_hash, *direction_hash;
13487   int num_list_entries = 0;
13488   int num_unknown_tokens = 0;
13489   int i;
13490
13491   if (fileExists(filename))
13492     setup_file_list = loadSetupFileList(filename);
13493
13494   if (setup_file_list == NULL)
13495   {
13496     // use reliable default values from static configuration
13497     SetupFileList *insert_ptr;
13498
13499     insert_ptr = setup_file_list =
13500       newSetupFileList(helpanim_config[0].token,
13501                        helpanim_config[0].value);
13502
13503     for (i = 1; helpanim_config[i].token; i++)
13504       insert_ptr = addListEntry(insert_ptr,
13505                                 helpanim_config[i].token,
13506                                 helpanim_config[i].value);
13507   }
13508
13509   element_hash   = newSetupFileHash();
13510   action_hash    = newSetupFileHash();
13511   direction_hash = newSetupFileHash();
13512
13513   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13514     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13515
13516   for (i = 0; i < NUM_ACTIONS; i++)
13517     setHashEntry(action_hash, element_action_info[i].suffix,
13518                  i_to_a(element_action_info[i].value));
13519
13520   // do not store direction index (bit) here, but direction value!
13521   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13522     setHashEntry(direction_hash, element_direction_info[i].suffix,
13523                  i_to_a(1 << element_direction_info[i].value));
13524
13525   for (list = setup_file_list; list != NULL; list = list->next)
13526   {
13527     char *element_token, *action_token, *direction_token;
13528     char *element_value, *action_value, *direction_value;
13529     int delay = atoi(list->value);
13530
13531     if (strEqual(list->token, "end"))
13532     {
13533       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13534
13535       continue;
13536     }
13537
13538     /* first try to break element into element/action/direction parts;
13539        if this does not work, also accept combined "element[.act][.dir]"
13540        elements (like "dynamite.active"), which are unique elements */
13541
13542     if (strchr(list->token, '.') == NULL)       // token contains no '.'
13543     {
13544       element_value = getHashEntry(element_hash, list->token);
13545       if (element_value != NULL)        // element found
13546         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13547                            &num_list_entries);
13548       else
13549       {
13550         // no further suffixes found -- this is not an element
13551         print_unknown_token(filename, list->token, num_unknown_tokens++);
13552       }
13553
13554       continue;
13555     }
13556
13557     // token has format "<prefix>.<something>"
13558
13559     action_token = strchr(list->token, '.');    // suffix may be action ...
13560     direction_token = action_token;             // ... or direction
13561
13562     element_token = getStringCopy(list->token);
13563     *strchr(element_token, '.') = '\0';
13564
13565     element_value = getHashEntry(element_hash, element_token);
13566
13567     if (element_value == NULL)          // this is no element
13568     {
13569       element_value = getHashEntry(element_hash, list->token);
13570       if (element_value != NULL)        // combined element found
13571         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13572                            &num_list_entries);
13573       else
13574         print_unknown_token(filename, list->token, num_unknown_tokens++);
13575
13576       free(element_token);
13577
13578       continue;
13579     }
13580
13581     action_value = getHashEntry(action_hash, action_token);
13582
13583     if (action_value != NULL)           // action found
13584     {
13585       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13586                     &num_list_entries);
13587
13588       free(element_token);
13589
13590       continue;
13591     }
13592
13593     direction_value = getHashEntry(direction_hash, direction_token);
13594
13595     if (direction_value != NULL)        // direction found
13596     {
13597       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13598                          &num_list_entries);
13599
13600       free(element_token);
13601
13602       continue;
13603     }
13604
13605     if (strchr(action_token + 1, '.') == NULL)
13606     {
13607       // no further suffixes found -- this is not an action nor direction
13608
13609       element_value = getHashEntry(element_hash, list->token);
13610       if (element_value != NULL)        // combined element found
13611         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13612                            &num_list_entries);
13613       else
13614         print_unknown_token(filename, list->token, num_unknown_tokens++);
13615
13616       free(element_token);
13617
13618       continue;
13619     }
13620
13621     // token has format "<prefix>.<suffix>.<something>"
13622
13623     direction_token = strchr(action_token + 1, '.');
13624
13625     action_token = getStringCopy(action_token);
13626     *strchr(action_token + 1, '.') = '\0';
13627
13628     action_value = getHashEntry(action_hash, action_token);
13629
13630     if (action_value == NULL)           // this is no action
13631     {
13632       element_value = getHashEntry(element_hash, list->token);
13633       if (element_value != NULL)        // combined element found
13634         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13635                            &num_list_entries);
13636       else
13637         print_unknown_token(filename, list->token, num_unknown_tokens++);
13638
13639       free(element_token);
13640       free(action_token);
13641
13642       continue;
13643     }
13644
13645     direction_value = getHashEntry(direction_hash, direction_token);
13646
13647     if (direction_value != NULL)        // direction found
13648     {
13649       add_helpanim_entry(atoi(element_value), atoi(action_value),
13650                          atoi(direction_value), delay, &num_list_entries);
13651
13652       free(element_token);
13653       free(action_token);
13654
13655       continue;
13656     }
13657
13658     // this is no direction
13659
13660     element_value = getHashEntry(element_hash, list->token);
13661     if (element_value != NULL)          // combined element found
13662       add_helpanim_entry(atoi(element_value), -1, -1, delay,
13663                          &num_list_entries);
13664     else
13665       print_unknown_token(filename, list->token, num_unknown_tokens++);
13666
13667     free(element_token);
13668     free(action_token);
13669   }
13670
13671   print_unknown_token_end(num_unknown_tokens);
13672
13673   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13674   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
13675
13676   freeSetupFileList(setup_file_list);
13677   freeSetupFileHash(element_hash);
13678   freeSetupFileHash(action_hash);
13679   freeSetupFileHash(direction_hash);
13680
13681 #if 0
13682   for (i = 0; i < num_list_entries; i++)
13683     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13684           EL_NAME(helpanim_info[i].element),
13685           helpanim_info[i].element,
13686           helpanim_info[i].action,
13687           helpanim_info[i].direction,
13688           helpanim_info[i].delay);
13689 #endif
13690 }
13691
13692 void LoadHelpTextInfo(void)
13693 {
13694   char *filename = getHelpTextFilename();
13695   int i;
13696
13697   if (helptext_info != NULL)
13698   {
13699     freeSetupFileHash(helptext_info);
13700     helptext_info = NULL;
13701   }
13702
13703   if (fileExists(filename))
13704     helptext_info = loadSetupFileHash(filename);
13705
13706   if (helptext_info == NULL)
13707   {
13708     // use reliable default values from static configuration
13709     helptext_info = newSetupFileHash();
13710
13711     for (i = 0; helptext_config[i].token; i++)
13712       setHashEntry(helptext_info,
13713                    helptext_config[i].token,
13714                    helptext_config[i].value);
13715   }
13716
13717 #if 0
13718   BEGIN_HASH_ITERATION(helptext_info, itr)
13719   {
13720     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13721           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13722   }
13723   END_HASH_ITERATION(hash, itr)
13724 #endif
13725 }
13726
13727
13728 // ----------------------------------------------------------------------------
13729 // convert levels
13730 // ----------------------------------------------------------------------------
13731
13732 #define MAX_NUM_CONVERT_LEVELS          1000
13733
13734 void ConvertLevels(void)
13735 {
13736   static LevelDirTree *convert_leveldir = NULL;
13737   static int convert_level_nr = -1;
13738   static int num_levels_handled = 0;
13739   static int num_levels_converted = 0;
13740   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13741   int i;
13742
13743   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13744                                                global.convert_leveldir);
13745
13746   if (convert_leveldir == NULL)
13747     Fail("no such level identifier: '%s'", global.convert_leveldir);
13748
13749   leveldir_current = convert_leveldir;
13750
13751   if (global.convert_level_nr != -1)
13752   {
13753     convert_leveldir->first_level = global.convert_level_nr;
13754     convert_leveldir->last_level  = global.convert_level_nr;
13755   }
13756
13757   convert_level_nr = convert_leveldir->first_level;
13758
13759   PrintLine("=", 79);
13760   Print("Converting levels\n");
13761   PrintLine("-", 79);
13762   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13763   Print("Level series name:       '%s'\n", convert_leveldir->name);
13764   Print("Level series author:     '%s'\n", convert_leveldir->author);
13765   Print("Number of levels:        %d\n",   convert_leveldir->levels);
13766   PrintLine("=", 79);
13767   Print("\n");
13768
13769   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13770     levels_failed[i] = FALSE;
13771
13772   while (convert_level_nr <= convert_leveldir->last_level)
13773   {
13774     char *level_filename;
13775     boolean new_level;
13776
13777     level_nr = convert_level_nr++;
13778
13779     Print("Level %03d: ", level_nr);
13780
13781     LoadLevel(level_nr);
13782     if (level.no_level_file || level.no_valid_file)
13783     {
13784       Print("(no level)\n");
13785       continue;
13786     }
13787
13788     Print("converting level ... ");
13789
13790 #if 0
13791     // special case: conversion of some EMC levels as requested by ACME
13792     level.game_engine_type = GAME_ENGINE_TYPE_RND;
13793 #endif
13794
13795     level_filename = getDefaultLevelFilename(level_nr);
13796     new_level = !fileExists(level_filename);
13797
13798     if (new_level)
13799     {
13800       SaveLevel(level_nr);
13801
13802       num_levels_converted++;
13803
13804       Print("converted.\n");
13805     }
13806     else
13807     {
13808       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13809         levels_failed[level_nr] = TRUE;
13810
13811       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13812     }
13813
13814     num_levels_handled++;
13815   }
13816
13817   Print("\n");
13818   PrintLine("=", 79);
13819   Print("Number of levels handled: %d\n", num_levels_handled);
13820   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13821          (num_levels_handled ?
13822           num_levels_converted * 100 / num_levels_handled : 0));
13823   PrintLine("-", 79);
13824   Print("Summary (for automatic parsing by scripts):\n");
13825   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13826          convert_leveldir->identifier, num_levels_converted,
13827          num_levels_handled,
13828          (num_levels_handled ?
13829           num_levels_converted * 100 / num_levels_handled : 0));
13830
13831   if (num_levels_handled != num_levels_converted)
13832   {
13833     Print(", FAILED:");
13834     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13835       if (levels_failed[i])
13836         Print(" %03d", i);
13837   }
13838
13839   Print("\n");
13840   PrintLine("=", 79);
13841
13842   CloseAllAndExit(0);
13843 }
13844
13845
13846 // ----------------------------------------------------------------------------
13847 // create and save images for use in level sketches (raw BMP format)
13848 // ----------------------------------------------------------------------------
13849
13850 void CreateLevelSketchImages(void)
13851 {
13852   Bitmap *bitmap1;
13853   Bitmap *bitmap2;
13854   int i;
13855
13856   InitElementPropertiesGfxElement();
13857
13858   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13859   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13860
13861   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13862   {
13863     int element = getMappedElement(i);
13864     char basename1[16];
13865     char basename2[16];
13866     char *filename1;
13867     char *filename2;
13868
13869     sprintf(basename1, "%04d.bmp", i);
13870     sprintf(basename2, "%04ds.bmp", i);
13871
13872     filename1 = getPath2(global.create_sketch_images_dir, basename1);
13873     filename2 = getPath2(global.create_sketch_images_dir, basename2);
13874
13875     DrawSizedElement(0, 0, element, TILESIZE);
13876     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13877
13878     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13879       Fail("cannot save level sketch image file '%s'", filename1);
13880
13881     DrawSizedElement(0, 0, element, MINI_TILESIZE);
13882     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13883
13884     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13885       Fail("cannot save level sketch image file '%s'", filename2);
13886
13887     free(filename1);
13888     free(filename2);
13889
13890     // create corresponding SQL statements (for normal and small images)
13891     if (i < 1000)
13892     {
13893       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13894       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13895     }
13896
13897     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13898     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13899
13900     // optional: create content for forum level sketch demonstration post
13901     if (options.debug)
13902       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13903   }
13904
13905   FreeBitmap(bitmap1);
13906   FreeBitmap(bitmap2);
13907
13908   if (options.debug)
13909     fprintf(stderr, "\n");
13910
13911   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13912
13913   CloseAllAndExit(0);
13914 }
13915
13916
13917 // ----------------------------------------------------------------------------
13918 // create and save images for element collecting animations (raw BMP format)
13919 // ----------------------------------------------------------------------------
13920
13921 static boolean createCollectImage(int element)
13922 {
13923   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13924 }
13925
13926 void CreateCollectElementImages(void)
13927 {
13928   int i, j;
13929   int num_steps = 8;
13930   int anim_frames = num_steps - 1;
13931   int tile_size = TILESIZE;
13932   int anim_width  = tile_size * anim_frames;
13933   int anim_height = tile_size;
13934   int num_collect_images = 0;
13935   int pos_collect_images = 0;
13936
13937   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13938     if (createCollectImage(i))
13939       num_collect_images++;
13940
13941   Info("Creating %d element collecting animation images ...",
13942        num_collect_images);
13943
13944   int dst_width  = anim_width * 2;
13945   int dst_height = anim_height * num_collect_images / 2;
13946   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13947   char *basename = "RocksCollect.bmp";
13948   char *filename = getPath2(global.create_collect_images_dir, basename);
13949
13950   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13951   {
13952     if (!createCollectImage(i))
13953       continue;
13954
13955     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13956     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13957     int graphic = el2img(i);
13958     char *token_name = element_info[i].token_name;
13959     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13960     Bitmap *src_bitmap;
13961     int src_x, src_y;
13962
13963     Info("- creating collecting image for '%s' ...", token_name);
13964
13965     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13966
13967     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13968                tile_size, tile_size, 0, 0);
13969
13970     tmp_bitmap->surface_masked = tmp_bitmap->surface;
13971
13972     for (j = 0; j < anim_frames; j++)
13973     {
13974       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13975       int frame_size = frame_size_final * num_steps;
13976       int offset = (tile_size - frame_size_final) / 2;
13977       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13978
13979       while (frame_size > frame_size_final)
13980       {
13981         frame_size /= 2;
13982
13983         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13984
13985         FreeBitmap(frame_bitmap);
13986
13987         frame_bitmap = half_bitmap;
13988       }
13989
13990       BlitBitmap(frame_bitmap, dst_bitmap, 0, 0,
13991                  frame_size_final, frame_size_final,
13992                  dst_x + j * tile_size + offset, dst_y + offset);
13993
13994       FreeBitmap(frame_bitmap);
13995     }
13996
13997     tmp_bitmap->surface_masked = NULL;
13998
13999     FreeBitmap(tmp_bitmap);
14000
14001     pos_collect_images++;
14002   }
14003
14004   if (SDL_SaveBMP(dst_bitmap->surface, filename) != 0)
14005     Fail("cannot save element collecting image file '%s'", filename);
14006
14007   FreeBitmap(dst_bitmap);
14008
14009   Info("Done.");
14010
14011   CloseAllAndExit(0);
14012 }
14013
14014
14015 // ----------------------------------------------------------------------------
14016 // create and save images for custom and group elements (raw BMP format)
14017 // ----------------------------------------------------------------------------
14018
14019 void CreateCustomElementImages(char *directory)
14020 {
14021   char *src_basename = "RocksCE-template.ilbm";
14022   char *dst_basename = "RocksCE.bmp";
14023   char *src_filename = getPath2(directory, src_basename);
14024   char *dst_filename = getPath2(directory, dst_basename);
14025   Bitmap *src_bitmap;
14026   Bitmap *bitmap;
14027   int yoffset_ce = 0;
14028   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14029   int i;
14030
14031   InitVideoDefaults();
14032
14033   ReCreateBitmap(&backbuffer, video.width, video.height);
14034
14035   src_bitmap = LoadImage(src_filename);
14036
14037   bitmap = CreateBitmap(TILEX * 16 * 2,
14038                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14039                         DEFAULT_DEPTH);
14040
14041   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14042   {
14043     int x = i % 16;
14044     int y = i / 16;
14045     int ii = i + 1;
14046     int j;
14047
14048     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14049                TILEX * x, TILEY * y + yoffset_ce);
14050
14051     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14052                TILEX, TILEY,
14053                TILEX * x + TILEX * 16,
14054                TILEY * y + yoffset_ce);
14055
14056     for (j = 2; j >= 0; j--)
14057     {
14058       int c = ii % 10;
14059
14060       BlitBitmap(src_bitmap, bitmap,
14061                  TILEX + c * 7, 0, 6, 10,
14062                  TILEX * x + 6 + j * 7,
14063                  TILEY * y + 11 + yoffset_ce);
14064
14065       BlitBitmap(src_bitmap, bitmap,
14066                  TILEX + c * 8, TILEY, 6, 10,
14067                  TILEX * 16 + TILEX * x + 6 + j * 8,
14068                  TILEY * y + 10 + yoffset_ce);
14069
14070       ii /= 10;
14071     }
14072   }
14073
14074   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14075   {
14076     int x = i % 16;
14077     int y = i / 16;
14078     int ii = i + 1;
14079     int j;
14080
14081     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14082                TILEX * x, TILEY * y + yoffset_ge);
14083
14084     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14085                TILEX, TILEY,
14086                TILEX * x + TILEX * 16,
14087                TILEY * y + yoffset_ge);
14088
14089     for (j = 1; j >= 0; j--)
14090     {
14091       int c = ii % 10;
14092
14093       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14094                  TILEX * x + 6 + j * 10,
14095                  TILEY * y + 11 + yoffset_ge);
14096
14097       BlitBitmap(src_bitmap, bitmap,
14098                  TILEX + c * 8, TILEY + 12, 6, 10,
14099                  TILEX * 16 + TILEX * x + 10 + j * 8,
14100                  TILEY * y + 10 + yoffset_ge);
14101
14102       ii /= 10;
14103     }
14104   }
14105
14106   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14107     Fail("cannot save CE graphics file '%s'", dst_filename);
14108
14109   FreeBitmap(bitmap);
14110
14111   CloseAllAndExit(0);
14112 }