7902221e0c9d4e3513cb9587826c989fff307092
[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
8655   scores->num_entries = 0;
8656   scores->last_added = -1;
8657   scores->last_added_local = -1;
8658
8659   scores->updated = FALSE;
8660   scores->uploaded = FALSE;
8661   scores->force_last_added = FALSE;
8662 }
8663
8664 static void setScoreInfoToDefaults(void)
8665 {
8666   setScoreInfoToDefaultsExt(&scores);
8667 }
8668
8669 static void setServerScoreInfoToDefaults(void)
8670 {
8671   setScoreInfoToDefaultsExt(&server_scores);
8672 }
8673
8674 static void LoadScore_OLD(int nr)
8675 {
8676   int i;
8677   char *filename = getScoreFilename(nr);
8678   char cookie[MAX_LINE_LEN];
8679   char line[MAX_LINE_LEN];
8680   char *line_ptr;
8681   FILE *file;
8682
8683   if (!(file = fopen(filename, MODE_READ)))
8684     return;
8685
8686   // check file identifier
8687   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8688     cookie[0] = '\0';
8689   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8690     cookie[strlen(cookie) - 1] = '\0';
8691
8692   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8693   {
8694     Warn("unknown format of score file '%s'", filename);
8695
8696     fclose(file);
8697
8698     return;
8699   }
8700
8701   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8702   {
8703     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8704       Warn("fscanf() failed; %s", strerror(errno));
8705
8706     if (fgets(line, MAX_LINE_LEN, file) == NULL)
8707       line[0] = '\0';
8708
8709     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8710       line[strlen(line) - 1] = '\0';
8711
8712     for (line_ptr = line; *line_ptr; line_ptr++)
8713     {
8714       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8715       {
8716         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8717         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8718         break;
8719       }
8720     }
8721   }
8722
8723   fclose(file);
8724 }
8725
8726 static void ConvertScore_OLD(void)
8727 {
8728   // only convert score to time for levels that rate playing time over score
8729   if (!level.rate_time_over_score)
8730     return;
8731
8732   // convert old score to playing time for score-less levels (like Supaplex)
8733   int time_final_max = 999;
8734   int i;
8735
8736   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8737   {
8738     int score = scores.entry[i].score;
8739
8740     if (score > 0 && score < time_final_max)
8741       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8742   }
8743 }
8744
8745 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8746 {
8747   scores->file_version = getFileVersion(file);
8748   scores->game_version = getFileVersion(file);
8749
8750   return chunk_size;
8751 }
8752
8753 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8754 {
8755   char *level_identifier = NULL;
8756   int level_identifier_size;
8757   int i;
8758
8759   level_identifier_size = getFile16BitBE(file);
8760
8761   level_identifier = checked_malloc(level_identifier_size);
8762
8763   for (i = 0; i < level_identifier_size; i++)
8764     level_identifier[i] = getFile8Bit(file);
8765
8766   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8767   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8768
8769   checked_free(level_identifier);
8770
8771   scores->level_nr = getFile16BitBE(file);
8772   scores->num_entries = getFile16BitBE(file);
8773
8774   chunk_size = 2 + level_identifier_size + 2 + 2;
8775
8776   return chunk_size;
8777 }
8778
8779 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8780 {
8781   int i, j;
8782
8783   for (i = 0; i < scores->num_entries; i++)
8784   {
8785     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8786       scores->entry[i].name[j] = getFile8Bit(file);
8787
8788     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8789   }
8790
8791   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8792
8793   return chunk_size;
8794 }
8795
8796 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8797 {
8798   int i;
8799
8800   for (i = 0; i < scores->num_entries; i++)
8801     scores->entry[i].score = getFile16BitBE(file);
8802
8803   chunk_size = scores->num_entries * 2;
8804
8805   return chunk_size;
8806 }
8807
8808 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8809 {
8810   int i;
8811
8812   for (i = 0; i < scores->num_entries; i++)
8813     scores->entry[i].score = getFile32BitBE(file);
8814
8815   chunk_size = scores->num_entries * 4;
8816
8817   return chunk_size;
8818 }
8819
8820 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8821 {
8822   int i;
8823
8824   for (i = 0; i < scores->num_entries; i++)
8825     scores->entry[i].time = getFile32BitBE(file);
8826
8827   chunk_size = scores->num_entries * 4;
8828
8829   return chunk_size;
8830 }
8831
8832 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8833 {
8834   int i, j;
8835
8836   for (i = 0; i < scores->num_entries; i++)
8837   {
8838     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8839       scores->entry[i].tape_basename[j] = getFile8Bit(file);
8840
8841     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8842   }
8843
8844   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8845
8846   return chunk_size;
8847 }
8848
8849 void LoadScore(int nr)
8850 {
8851   char *filename = getScoreFilename(nr);
8852   char cookie[MAX_LINE_LEN];
8853   char chunk_name[CHUNK_ID_LEN + 1];
8854   int chunk_size;
8855   boolean old_score_file_format = FALSE;
8856   File *file;
8857
8858   // always start with reliable default values
8859   setScoreInfoToDefaults();
8860
8861   if (!(file = openFile(filename, MODE_READ)))
8862     return;
8863
8864   getFileChunkBE(file, chunk_name, NULL);
8865   if (strEqual(chunk_name, "RND1"))
8866   {
8867     getFile32BitBE(file);               // not used
8868
8869     getFileChunkBE(file, chunk_name, NULL);
8870     if (!strEqual(chunk_name, "SCOR"))
8871     {
8872       Warn("unknown format of score file '%s'", filename);
8873
8874       closeFile(file);
8875
8876       return;
8877     }
8878   }
8879   else  // check for old file format with cookie string
8880   {
8881     strcpy(cookie, chunk_name);
8882     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8883       cookie[4] = '\0';
8884     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8885       cookie[strlen(cookie) - 1] = '\0';
8886
8887     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8888     {
8889       Warn("unknown format of score file '%s'", filename);
8890
8891       closeFile(file);
8892
8893       return;
8894     }
8895
8896     old_score_file_format = TRUE;
8897   }
8898
8899   if (old_score_file_format)
8900   {
8901     // score files from versions before 4.2.4.0 without chunk structure
8902     LoadScore_OLD(nr);
8903
8904     // convert score to time, if possible (mainly for Supaplex levels)
8905     ConvertScore_OLD();
8906   }
8907   else
8908   {
8909     static struct
8910     {
8911       char *name;
8912       int size;
8913       int (*loader)(File *, int, struct ScoreInfo *);
8914     }
8915     chunk_info[] =
8916     {
8917       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
8918       { "INFO", -1,                     LoadScore_INFO },
8919       { "NAME", -1,                     LoadScore_NAME },
8920       { "SCOR", -1,                     LoadScore_SCOR },
8921       { "SC4R", -1,                     LoadScore_SC4R },
8922       { "TIME", -1,                     LoadScore_TIME },
8923       { "TAPE", -1,                     LoadScore_TAPE },
8924
8925       {  NULL,  0,                      NULL }
8926     };
8927
8928     while (getFileChunkBE(file, chunk_name, &chunk_size))
8929     {
8930       int i = 0;
8931
8932       while (chunk_info[i].name != NULL &&
8933              !strEqual(chunk_name, chunk_info[i].name))
8934         i++;
8935
8936       if (chunk_info[i].name == NULL)
8937       {
8938         Warn("unknown chunk '%s' in score file '%s'",
8939               chunk_name, filename);
8940
8941         ReadUnusedBytesFromFile(file, chunk_size);
8942       }
8943       else if (chunk_info[i].size != -1 &&
8944                chunk_info[i].size != chunk_size)
8945       {
8946         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8947               chunk_size, chunk_name, filename);
8948
8949         ReadUnusedBytesFromFile(file, chunk_size);
8950       }
8951       else
8952       {
8953         // call function to load this score chunk
8954         int chunk_size_expected =
8955           (chunk_info[i].loader)(file, chunk_size, &scores);
8956
8957         // the size of some chunks cannot be checked before reading other
8958         // chunks first (like "HEAD" and "BODY") that contain some header
8959         // information, so check them here
8960         if (chunk_size_expected != chunk_size)
8961         {
8962           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8963                 chunk_size, chunk_name, filename);
8964         }
8965       }
8966     }
8967   }
8968
8969   closeFile(file);
8970 }
8971
8972 #if ENABLE_HISTORIC_CHUNKS
8973 void SaveScore_OLD(int nr)
8974 {
8975   int i;
8976   char *filename = getScoreFilename(nr);
8977   FILE *file;
8978
8979   // used instead of "leveldir_current->subdir" (for network games)
8980   InitScoreDirectory(levelset.identifier);
8981
8982   if (!(file = fopen(filename, MODE_WRITE)))
8983   {
8984     Warn("cannot save score for level %d", nr);
8985
8986     return;
8987   }
8988
8989   fprintf(file, "%s\n\n", SCORE_COOKIE);
8990
8991   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8992     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
8993
8994   fclose(file);
8995
8996   SetFilePermissions(filename, PERMS_PRIVATE);
8997 }
8998 #endif
8999
9000 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9001 {
9002   putFileVersion(file, scores->file_version);
9003   putFileVersion(file, scores->game_version);
9004 }
9005
9006 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9007 {
9008   int level_identifier_size = strlen(scores->level_identifier) + 1;
9009   int i;
9010
9011   putFile16BitBE(file, level_identifier_size);
9012
9013   for (i = 0; i < level_identifier_size; i++)
9014     putFile8Bit(file, scores->level_identifier[i]);
9015
9016   putFile16BitBE(file, scores->level_nr);
9017   putFile16BitBE(file, scores->num_entries);
9018 }
9019
9020 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9021 {
9022   int i, j;
9023
9024   for (i = 0; i < scores->num_entries; i++)
9025   {
9026     int name_size = strlen(scores->entry[i].name);
9027
9028     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9029       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9030   }
9031 }
9032
9033 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9034 {
9035   int i;
9036
9037   for (i = 0; i < scores->num_entries; i++)
9038     putFile16BitBE(file, scores->entry[i].score);
9039 }
9040
9041 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9042 {
9043   int i;
9044
9045   for (i = 0; i < scores->num_entries; i++)
9046     putFile32BitBE(file, scores->entry[i].score);
9047 }
9048
9049 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9050 {
9051   int i;
9052
9053   for (i = 0; i < scores->num_entries; i++)
9054     putFile32BitBE(file, scores->entry[i].time);
9055 }
9056
9057 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9058 {
9059   int i, j;
9060
9061   for (i = 0; i < scores->num_entries; i++)
9062   {
9063     int size = strlen(scores->entry[i].tape_basename);
9064
9065     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9066       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9067   }
9068 }
9069
9070 static void SaveScoreToFilename(char *filename)
9071 {
9072   FILE *file;
9073   int info_chunk_size;
9074   int name_chunk_size;
9075   int scor_chunk_size;
9076   int sc4r_chunk_size;
9077   int time_chunk_size;
9078   int tape_chunk_size;
9079   boolean has_large_score_values;
9080   int i;
9081
9082   if (!(file = fopen(filename, MODE_WRITE)))
9083   {
9084     Warn("cannot save score file '%s'", filename);
9085
9086     return;
9087   }
9088
9089   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9090   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9091   scor_chunk_size = scores.num_entries * 2;
9092   sc4r_chunk_size = scores.num_entries * 4;
9093   time_chunk_size = scores.num_entries * 4;
9094   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9095
9096   has_large_score_values = FALSE;
9097   for (i = 0; i < scores.num_entries; i++)
9098     if (scores.entry[i].score > 0xffff)
9099       has_large_score_values = TRUE;
9100
9101   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9102   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9103
9104   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9105   SaveScore_VERS(file, &scores);
9106
9107   putFileChunkBE(file, "INFO", info_chunk_size);
9108   SaveScore_INFO(file, &scores);
9109
9110   putFileChunkBE(file, "NAME", name_chunk_size);
9111   SaveScore_NAME(file, &scores);
9112
9113   if (has_large_score_values)
9114   {
9115     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9116     SaveScore_SC4R(file, &scores);
9117   }
9118   else
9119   {
9120     putFileChunkBE(file, "SCOR", scor_chunk_size);
9121     SaveScore_SCOR(file, &scores);
9122   }
9123
9124   putFileChunkBE(file, "TIME", time_chunk_size);
9125   SaveScore_TIME(file, &scores);
9126
9127   putFileChunkBE(file, "TAPE", tape_chunk_size);
9128   SaveScore_TAPE(file, &scores);
9129
9130   fclose(file);
9131
9132   SetFilePermissions(filename, PERMS_PRIVATE);
9133 }
9134
9135 void SaveScore(int nr)
9136 {
9137   char *filename = getScoreFilename(nr);
9138   int i;
9139
9140   // used instead of "leveldir_current->subdir" (for network games)
9141   InitScoreDirectory(levelset.identifier);
9142
9143   scores.file_version = FILE_VERSION_ACTUAL;
9144   scores.game_version = GAME_VERSION_ACTUAL;
9145
9146   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9147   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9148   scores.level_nr = level_nr;
9149
9150   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9151     if (scores.entry[i].score == 0 &&
9152         scores.entry[i].time == 0 &&
9153         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9154       break;
9155
9156   scores.num_entries = i;
9157
9158   if (scores.num_entries == 0)
9159     return;
9160
9161   SaveScoreToFilename(filename);
9162 }
9163
9164 void ExecuteAsThread(SDL_ThreadFunction function, char *name, void *data,
9165                      char *error)
9166 {
9167 #if defined(PLATFORM_EMSCRIPTEN)
9168   // threads currently not fully supported by Emscripten/SDL and some browsers
9169   function(data);
9170 #else
9171   SDL_Thread *thread = SDL_CreateThread(function, name, data);
9172
9173   if (thread != NULL)
9174     SDL_DetachThread(thread);
9175   else
9176     Error("Cannot create thread to %s!", error);
9177
9178   // nasty kludge to lower probability of intermingled thread error messages
9179   Delay(1);
9180 #endif
9181 }
9182
9183 char *getPasswordJSON(char *password)
9184 {
9185   static char password_json[MAX_FILENAME_LEN] = "";
9186   static boolean initialized = FALSE;
9187
9188   if (!initialized)
9189   {
9190     if (password != NULL &&
9191         !strEqual(password, "") &&
9192         !strEqual(password, UNDEFINED_PASSWORD))
9193       snprintf(password_json, MAX_FILENAME_LEN,
9194                "  \"password\":             \"%s\",\n",
9195                setup.api_server_password);
9196
9197     initialized = TRUE;
9198   }
9199
9200   return password_json;
9201 }
9202
9203 struct ApiGetScoreThreadData
9204 {
9205   int level_nr;
9206   char *score_cache_filename;
9207 };
9208
9209 static void *CreateThreadData_ApiGetScore(int nr)
9210 {
9211   struct ApiGetScoreThreadData *data =
9212     checked_malloc(sizeof(struct ApiGetScoreThreadData));
9213   char *score_cache_filename = getScoreCacheFilename(nr);
9214
9215   data->level_nr = nr;
9216   data->score_cache_filename = getStringCopy(score_cache_filename);
9217
9218   return data;
9219 }
9220
9221 static void FreeThreadData_ApiGetScore(void *data_raw)
9222 {
9223   struct ApiGetScoreThreadData *data = data_raw;
9224
9225   checked_free(data->score_cache_filename);
9226   checked_free(data);
9227 }
9228
9229 static boolean SetRequest_ApiGetScore(struct HttpRequest *request,
9230                                       void *data_raw)
9231 {
9232   struct ApiGetScoreThreadData *data = data_raw;
9233   int level_nr = data->level_nr;
9234
9235   request->hostname = setup.api_server_hostname;
9236   request->port     = API_SERVER_PORT;
9237   request->method   = API_SERVER_METHOD;
9238   request->uri      = API_SERVER_URI_GET;
9239
9240   char *levelset_identifier = getEscapedJSON(leveldir_current->identifier);
9241   char *levelset_name       = getEscapedJSON(leveldir_current->name);
9242
9243   snprintf(request->body, MAX_HTTP_BODY_SIZE,
9244            "{\n"
9245            "%s"
9246            "  \"game_version\":         \"%s\",\n"
9247            "  \"game_platform\":        \"%s\",\n"
9248            "  \"levelset_identifier\":  \"%s\",\n"
9249            "  \"levelset_name\":        \"%s\",\n"
9250            "  \"level_nr\":             \"%d\"\n"
9251            "}\n",
9252            getPasswordJSON(setup.api_server_password),
9253            getProgramRealVersionString(),
9254            getProgramPlatformString(),
9255            levelset_identifier,
9256            levelset_name,
9257            level_nr);
9258
9259   checked_free(levelset_identifier);
9260   checked_free(levelset_name);
9261
9262   ConvertHttpRequestBodyToServerEncoding(request);
9263
9264   return TRUE;
9265 }
9266
9267 static void HandleResponse_ApiGetScore(struct HttpResponse *response,
9268                                        void *data_raw)
9269 {
9270   struct ApiGetScoreThreadData *data = data_raw;
9271
9272   if (response->body_size == 0)
9273   {
9274     // no scores available for this level
9275
9276     return;
9277   }
9278
9279   ConvertHttpResponseBodyToClientEncoding(response);
9280
9281   char *filename = data->score_cache_filename;
9282   FILE *file;
9283   int i;
9284
9285   // used instead of "leveldir_current->subdir" (for network games)
9286   InitScoreCacheDirectory(levelset.identifier);
9287
9288   if (!(file = fopen(filename, MODE_WRITE)))
9289   {
9290     Warn("cannot save score cache file '%s'", filename);
9291
9292     return;
9293   }
9294
9295   for (i = 0; i < response->body_size; i++)
9296     fputc(response->body[i], file);
9297
9298   fclose(file);
9299
9300   SetFilePermissions(filename, PERMS_PRIVATE);
9301
9302   server_scores.updated = TRUE;
9303 }
9304
9305 #if defined(PLATFORM_EMSCRIPTEN)
9306 static void Emscripten_ApiGetScore_Loaded(unsigned handle, void *data_raw,
9307                                           void *buffer, unsigned int size)
9308 {
9309   struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
9310
9311   if (response != NULL)
9312   {
9313     HandleResponse_ApiGetScore(response, data_raw);
9314
9315     checked_free(response);
9316   }
9317   else
9318   {
9319     Error("server response too large to handle (%d bytes)", size);
9320   }
9321
9322   FreeThreadData_ApiGetScore(data_raw);
9323 }
9324
9325 static void Emscripten_ApiGetScore_Failed(unsigned handle, void *data_raw,
9326                                           int code, const char *status)
9327 {
9328   Error("server failed to handle request: %d %s", code, status);
9329
9330   FreeThreadData_ApiGetScore(data_raw);
9331 }
9332
9333 static void Emscripten_ApiGetScore_Progress(unsigned handle, void *data_raw,
9334                                             int bytes, int size)
9335 {
9336   // nothing to do here
9337 }
9338
9339 static void Emscripten_ApiGetScore_HttpRequest(struct HttpRequest *request,
9340                                                void *data_raw)
9341 {
9342   if (!SetRequest_ApiGetScore(request, data_raw))
9343   {
9344     FreeThreadData_ApiGetScore(data_raw);
9345
9346     return;
9347   }
9348
9349   emscripten_async_wget2_data(request->uri,
9350                               request->method,
9351                               request->body,
9352                               data_raw,
9353                               TRUE,
9354                               Emscripten_ApiGetScore_Loaded,
9355                               Emscripten_ApiGetScore_Failed,
9356                               Emscripten_ApiGetScore_Progress);
9357 }
9358
9359 #else
9360
9361 static void ApiGetScore_HttpRequestExt(struct HttpRequest *request,
9362                                        struct HttpResponse *response,
9363                                        void *data_raw)
9364 {
9365   if (!SetRequest_ApiGetScore(request, data_raw))
9366     return;
9367
9368   if (!DoHttpRequest(request, response))
9369   {
9370     Error("HTTP request failed: %s", GetHttpError());
9371
9372     return;
9373   }
9374
9375   if (!HTTP_SUCCESS(response->status_code))
9376   {
9377     // do not show error message if no scores found for this level set
9378     if (response->status_code == 404)
9379       return;
9380
9381     Error("server failed to handle request: %d %s",
9382           response->status_code,
9383           response->status_text);
9384
9385     return;
9386   }
9387
9388   HandleResponse_ApiGetScore(response, data_raw);
9389 }
9390
9391 static void ApiGetScore_HttpRequest(struct HttpRequest *request,
9392                                     struct HttpResponse *response,
9393                                     void *data_raw)
9394 {
9395   ApiGetScore_HttpRequestExt(request, response, data_raw);
9396
9397   FreeThreadData_ApiGetScore(data_raw);
9398 }
9399 #endif
9400
9401 static int ApiGetScoreThread(void *data_raw)
9402 {
9403   struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
9404   struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
9405
9406   program.api_thread_count++;
9407
9408 #if defined(PLATFORM_EMSCRIPTEN)
9409   Emscripten_ApiGetScore_HttpRequest(request, data_raw);
9410 #else
9411   ApiGetScore_HttpRequest(request, response, data_raw);
9412 #endif
9413
9414   program.api_thread_count--;
9415
9416   checked_free(request);
9417   checked_free(response);
9418
9419   return 0;
9420 }
9421
9422 static void ApiGetScoreAsThread(int nr)
9423 {
9424   struct ApiGetScoreThreadData *data = CreateThreadData_ApiGetScore(nr);
9425
9426   ExecuteAsThread(ApiGetScoreThread,
9427                   "ApiGetScore", data,
9428                   "download scores from server");
9429 }
9430
9431 static void LoadServerScoreFromCache(int nr)
9432 {
9433   struct ScoreEntry score_entry;
9434   struct
9435   {
9436     void *value;
9437     boolean is_string;
9438     int string_size;
9439   }
9440   score_mapping[] =
9441   {
9442     { &score_entry.score,               FALSE,  0                       },
9443     { &score_entry.time,                FALSE,  0                       },
9444     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
9445     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
9446     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
9447     { &score_entry.id,                  FALSE,  0                       },
9448     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
9449     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
9450     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
9451     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
9452
9453     { NULL,                             FALSE,  0                       }
9454   };
9455   char *filename = getScoreCacheFilename(nr);
9456   SetupFileHash *score_hash = loadSetupFileHash(filename);
9457   int i, j;
9458
9459   server_scores.num_entries = 0;
9460
9461   if (score_hash == NULL)
9462     return;
9463
9464   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9465   {
9466     score_entry = server_scores.entry[i];
9467
9468     for (j = 0; score_mapping[j].value != NULL; j++)
9469     {
9470       char token[10];
9471
9472       sprintf(token, "%02d.%d", i, j);
9473
9474       char *value = getHashEntry(score_hash, token);
9475
9476       if (value == NULL)
9477         continue;
9478
9479       if (score_mapping[j].is_string)
9480       {
9481         char *score_value = (char *)score_mapping[j].value;
9482         int value_size = score_mapping[j].string_size;
9483
9484         strncpy(score_value, value, value_size);
9485         score_value[value_size] = '\0';
9486       }
9487       else
9488       {
9489         int *score_value = (int *)score_mapping[j].value;
9490
9491         *score_value = atoi(value);
9492       }
9493
9494       server_scores.num_entries = i + 1;
9495     }
9496
9497     server_scores.entry[i] = score_entry;
9498   }
9499
9500   freeSetupFileHash(score_hash);
9501 }
9502
9503 void LoadServerScore(int nr, boolean download_score)
9504 {
9505   if (!setup.use_api_server)
9506     return;
9507
9508   // always start with reliable default values
9509   setServerScoreInfoToDefaults();
9510
9511   // 1st step: load server scores from cache file (which may not exist)
9512   // (this should prevent reading it while the thread is writing to it)
9513   LoadServerScoreFromCache(nr);
9514
9515   if (download_score && runtime.use_api_server)
9516   {
9517     // 2nd step: download server scores from score server to cache file
9518     // (as thread, as it might time out if the server is not reachable)
9519     ApiGetScoreAsThread(nr);
9520   }
9521 }
9522
9523 static char *get_file_base64(char *filename)
9524 {
9525   struct stat file_status;
9526
9527   if (stat(filename, &file_status) != 0)
9528   {
9529     Error("cannot stat file '%s'", filename);
9530
9531     return NULL;
9532   }
9533
9534   int buffer_size = file_status.st_size;
9535   byte *buffer = checked_malloc(buffer_size);
9536   FILE *file;
9537   int i;
9538
9539   if (!(file = fopen(filename, MODE_READ)))
9540   {
9541     Error("cannot open file '%s'", filename);
9542
9543     checked_free(buffer);
9544
9545     return NULL;
9546   }
9547
9548   for (i = 0; i < buffer_size; i++)
9549   {
9550     int c = fgetc(file);
9551
9552     if (c == EOF)
9553     {
9554       Error("cannot read from input file '%s'", filename);
9555
9556       fclose(file);
9557       checked_free(buffer);
9558
9559       return NULL;
9560     }
9561
9562     buffer[i] = (byte)c;
9563   }
9564
9565   fclose(file);
9566
9567   int buffer_encoded_size = base64_encoded_size(buffer_size);
9568   char *buffer_encoded = checked_malloc(buffer_encoded_size);
9569
9570   base64_encode(buffer_encoded, buffer, buffer_size);
9571
9572   checked_free(buffer);
9573
9574   return buffer_encoded;
9575 }
9576
9577 static void PrepareScoreTapesForUpload(char *leveldir_subdir)
9578 {
9579   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9580
9581   // if score tape not uploaded, ask for uploading missing tapes later
9582   if (!setup.has_remaining_tapes)
9583     setup.ask_for_remaining_tapes = TRUE;
9584
9585   setup.provide_uploading_tapes = TRUE;
9586   setup.has_remaining_tapes = TRUE;
9587
9588   SaveSetup_ServerSetup();
9589 }
9590
9591 struct ApiAddScoreThreadData
9592 {
9593   int level_nr;
9594   boolean tape_saved;
9595   char *leveldir_subdir;
9596   char *score_tape_filename;
9597   struct ScoreEntry score_entry;
9598 };
9599
9600 static void *CreateThreadData_ApiAddScore(int nr, boolean tape_saved,
9601                                           char *score_tape_filename)
9602 {
9603   struct ApiAddScoreThreadData *data =
9604     checked_malloc(sizeof(struct ApiAddScoreThreadData));
9605   struct ScoreEntry *score_entry = &scores.entry[scores.last_added];
9606
9607   if (score_tape_filename == NULL)
9608     score_tape_filename = getScoreTapeFilename(score_entry->tape_basename, nr);
9609
9610   data->level_nr = nr;
9611   data->tape_saved = tape_saved;
9612   data->leveldir_subdir = getStringCopy(leveldir_current->subdir);
9613   data->score_tape_filename = getStringCopy(score_tape_filename);
9614   data->score_entry = *score_entry;
9615
9616   return data;
9617 }
9618
9619 static void FreeThreadData_ApiAddScore(void *data_raw)
9620 {
9621   struct ApiAddScoreThreadData *data = data_raw;
9622
9623   checked_free(data->leveldir_subdir);
9624   checked_free(data->score_tape_filename);
9625   checked_free(data);
9626 }
9627
9628 static boolean SetRequest_ApiAddScore(struct HttpRequest *request,
9629                                       void *data_raw)
9630 {
9631   struct ApiAddScoreThreadData *data = data_raw;
9632   struct ScoreEntry *score_entry = &data->score_entry;
9633   char *score_tape_filename = data->score_tape_filename;
9634   boolean tape_saved = data->tape_saved;
9635   int level_nr = data->level_nr;
9636
9637   request->hostname = setup.api_server_hostname;
9638   request->port     = API_SERVER_PORT;
9639   request->method   = API_SERVER_METHOD;
9640   request->uri      = API_SERVER_URI_ADD;
9641
9642   char *tape_base64 = get_file_base64(score_tape_filename);
9643
9644   if (tape_base64 == NULL)
9645   {
9646     Error("loading and base64 encoding score tape file failed");
9647
9648     return FALSE;
9649   }
9650
9651   char *player_name_raw = score_entry->name;
9652   char *player_uuid_raw = setup.player_uuid;
9653
9654   if (options.player_name != NULL && global.autoplay_leveldir != NULL)
9655   {
9656     player_name_raw = options.player_name;
9657     player_uuid_raw = "";
9658   }
9659
9660   char *levelset_identifier = getEscapedJSON(leveldir_current->identifier);
9661   char *levelset_name       = getEscapedJSON(leveldir_current->name);
9662   char *levelset_author     = getEscapedJSON(leveldir_current->author);
9663   char *level_name          = getEscapedJSON(level.name);
9664   char *level_author        = getEscapedJSON(level.author);
9665   char *player_name         = getEscapedJSON(player_name_raw);
9666   char *player_uuid         = getEscapedJSON(player_uuid_raw);
9667
9668   snprintf(request->body, MAX_HTTP_BODY_SIZE,
9669            "{\n"
9670            "%s"
9671            "  \"game_version\":         \"%s\",\n"
9672            "  \"game_platform\":        \"%s\",\n"
9673            "  \"batch_time\":           \"%d\",\n"
9674            "  \"levelset_identifier\":  \"%s\",\n"
9675            "  \"levelset_name\":        \"%s\",\n"
9676            "  \"levelset_author\":      \"%s\",\n"
9677            "  \"levelset_num_levels\":  \"%d\",\n"
9678            "  \"levelset_first_level\": \"%d\",\n"
9679            "  \"level_nr\":             \"%d\",\n"
9680            "  \"level_name\":           \"%s\",\n"
9681            "  \"level_author\":         \"%s\",\n"
9682            "  \"use_step_counter\":     \"%d\",\n"
9683            "  \"rate_time_over_score\": \"%d\",\n"
9684            "  \"player_name\":          \"%s\",\n"
9685            "  \"player_uuid\":          \"%s\",\n"
9686            "  \"score\":                \"%d\",\n"
9687            "  \"time\":                 \"%d\",\n"
9688            "  \"tape_basename\":        \"%s\",\n"
9689            "  \"tape_saved\":           \"%d\",\n"
9690            "  \"tape\":                 \"%s\"\n"
9691            "}\n",
9692            getPasswordJSON(setup.api_server_password),
9693            getProgramRealVersionString(),
9694            getProgramPlatformString(),
9695            (int)global.autoplay_time,
9696            levelset_identifier,
9697            levelset_name,
9698            levelset_author,
9699            leveldir_current->levels,
9700            leveldir_current->first_level,
9701            level_nr,
9702            level_name,
9703            level_author,
9704            level.use_step_counter,
9705            level.rate_time_over_score,
9706            player_name,
9707            player_uuid,
9708            score_entry->score,
9709            score_entry->time,
9710            score_entry->tape_basename,
9711            tape_saved,
9712            tape_base64);
9713
9714   checked_free(tape_base64);
9715
9716   checked_free(levelset_identifier);
9717   checked_free(levelset_name);
9718   checked_free(levelset_author);
9719   checked_free(level_name);
9720   checked_free(level_author);
9721   checked_free(player_name);
9722   checked_free(player_uuid);
9723
9724   ConvertHttpRequestBodyToServerEncoding(request);
9725
9726   return TRUE;
9727 }
9728
9729 static void HandleResponse_ApiAddScore(struct HttpResponse *response,
9730                                        void *data_raw)
9731 {
9732   server_scores.uploaded = TRUE;
9733 }
9734
9735 static void HandleFailure_ApiAddScore(void *data_raw)
9736 {
9737   struct ApiAddScoreThreadData *data = data_raw;
9738
9739   PrepareScoreTapesForUpload(data->leveldir_subdir);
9740 }
9741
9742 #if defined(PLATFORM_EMSCRIPTEN)
9743 static void Emscripten_ApiAddScore_Loaded(unsigned handle, void *data_raw,
9744                                           void *buffer, unsigned int size)
9745 {
9746   struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
9747
9748   if (response != NULL)
9749   {
9750     HandleResponse_ApiAddScore(response, data_raw);
9751
9752     checked_free(response);
9753   }
9754   else
9755   {
9756     Error("server response too large to handle (%d bytes)", size);
9757
9758     HandleFailure_ApiAddScore(data_raw);
9759   }
9760
9761   FreeThreadData_ApiAddScore(data_raw);
9762 }
9763
9764 static void Emscripten_ApiAddScore_Failed(unsigned handle, void *data_raw,
9765                                           int code, const char *status)
9766 {
9767   Error("server failed to handle request: %d %s", code, status);
9768
9769   HandleFailure_ApiAddScore(data_raw);
9770
9771   FreeThreadData_ApiAddScore(data_raw);
9772 }
9773
9774 static void Emscripten_ApiAddScore_Progress(unsigned handle, void *data_raw,
9775                                             int bytes, int size)
9776 {
9777   // nothing to do here
9778 }
9779
9780 static void Emscripten_ApiAddScore_HttpRequest(struct HttpRequest *request,
9781                                                void *data_raw)
9782 {
9783   if (!SetRequest_ApiAddScore(request, data_raw))
9784   {
9785     FreeThreadData_ApiAddScore(data_raw);
9786
9787     return;
9788   }
9789
9790   emscripten_async_wget2_data(request->uri,
9791                               request->method,
9792                               request->body,
9793                               data_raw,
9794                               TRUE,
9795                               Emscripten_ApiAddScore_Loaded,
9796                               Emscripten_ApiAddScore_Failed,
9797                               Emscripten_ApiAddScore_Progress);
9798 }
9799
9800 #else
9801
9802 static void ApiAddScore_HttpRequestExt(struct HttpRequest *request,
9803                                        struct HttpResponse *response,
9804                                        void *data_raw)
9805 {
9806   if (!SetRequest_ApiAddScore(request, data_raw))
9807     return;
9808
9809   if (!DoHttpRequest(request, response))
9810   {
9811     Error("HTTP request failed: %s", GetHttpError());
9812
9813     HandleFailure_ApiAddScore(data_raw);
9814
9815     return;
9816   }
9817
9818   if (!HTTP_SUCCESS(response->status_code))
9819   {
9820     Error("server failed to handle request: %d %s",
9821           response->status_code,
9822           response->status_text);
9823
9824     HandleFailure_ApiAddScore(data_raw);
9825
9826     return;
9827   }
9828
9829   HandleResponse_ApiAddScore(response, data_raw);
9830 }
9831
9832 static void ApiAddScore_HttpRequest(struct HttpRequest *request,
9833                                     struct HttpResponse *response,
9834                                     void *data_raw)
9835 {
9836   ApiAddScore_HttpRequestExt(request, response, data_raw);
9837
9838   FreeThreadData_ApiAddScore(data_raw);
9839 }
9840 #endif
9841
9842 static int ApiAddScoreThread(void *data_raw)
9843 {
9844   struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
9845   struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
9846
9847   program.api_thread_count++;
9848
9849 #if defined(PLATFORM_EMSCRIPTEN)
9850   Emscripten_ApiAddScore_HttpRequest(request, data_raw);
9851 #else
9852   ApiAddScore_HttpRequest(request, response, data_raw);
9853 #endif
9854
9855   program.api_thread_count--;
9856
9857   checked_free(request);
9858   checked_free(response);
9859
9860   return 0;
9861 }
9862
9863 static void ApiAddScoreAsThread(int nr, boolean tape_saved,
9864                                 char *score_tape_filename)
9865 {
9866   struct ApiAddScoreThreadData *data =
9867     CreateThreadData_ApiAddScore(nr, tape_saved, score_tape_filename);
9868
9869   ExecuteAsThread(ApiAddScoreThread,
9870                   "ApiAddScore", data,
9871                   "upload score to server");
9872 }
9873
9874 void SaveServerScore(int nr, boolean tape_saved)
9875 {
9876   if (!runtime.use_api_server)
9877   {
9878     PrepareScoreTapesForUpload(leveldir_current->subdir);
9879
9880     return;
9881   }
9882
9883   ApiAddScoreAsThread(nr, tape_saved, NULL);
9884 }
9885
9886 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9887                              char *score_tape_filename)
9888 {
9889   if (!runtime.use_api_server)
9890     return;
9891
9892   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9893 }
9894
9895 void LoadLocalAndServerScore(int nr, boolean download_score)
9896 {
9897   int last_added_local = scores.last_added_local;
9898
9899   // needed if only showing server scores
9900   setScoreInfoToDefaults();
9901
9902   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9903     LoadScore(nr);
9904
9905   // restore last added local score entry (before merging server scores)
9906   scores.last_added = scores.last_added_local = last_added_local;
9907
9908   if (setup.use_api_server &&
9909       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9910   {
9911     // load server scores from cache file and trigger update from server
9912     LoadServerScore(nr, download_score);
9913
9914     // merge local scores with scores from server
9915     MergeServerScore();
9916   }
9917 }
9918
9919
9920 // ============================================================================
9921 // setup file functions
9922 // ============================================================================
9923
9924 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
9925
9926
9927 static struct TokenInfo global_setup_tokens[] =
9928 {
9929   {
9930     TYPE_STRING,
9931     &setup.player_name,                         "player_name"
9932   },
9933   {
9934     TYPE_SWITCH,
9935     &setup.multiple_users,                      "multiple_users"
9936   },
9937   {
9938     TYPE_SWITCH,
9939     &setup.sound,                               "sound"
9940   },
9941   {
9942     TYPE_SWITCH,
9943     &setup.sound_loops,                         "repeating_sound_loops"
9944   },
9945   {
9946     TYPE_SWITCH,
9947     &setup.sound_music,                         "background_music"
9948   },
9949   {
9950     TYPE_SWITCH,
9951     &setup.sound_simple,                        "simple_sound_effects"
9952   },
9953   {
9954     TYPE_SWITCH,
9955     &setup.toons,                               "toons"
9956   },
9957   {
9958     TYPE_SWITCH,
9959     &setup.scroll_delay,                        "scroll_delay"
9960   },
9961   {
9962     TYPE_SWITCH,
9963     &setup.forced_scroll_delay,                 "forced_scroll_delay"
9964   },
9965   {
9966     TYPE_INTEGER,
9967     &setup.scroll_delay_value,                  "scroll_delay_value"
9968   },
9969   {
9970     TYPE_STRING,
9971     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
9972   },
9973   {
9974     TYPE_INTEGER,
9975     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
9976   },
9977   {
9978     TYPE_SWITCH,
9979     &setup.fade_screens,                        "fade_screens"
9980   },
9981   {
9982     TYPE_SWITCH,
9983     &setup.autorecord,                          "automatic_tape_recording"
9984   },
9985   {
9986     TYPE_SWITCH,
9987     &setup.auto_pause_on_start,                 "auto_pause_on_start"
9988   },
9989   {
9990     TYPE_SWITCH,
9991     &setup.show_titlescreen,                    "show_titlescreen"
9992   },
9993   {
9994     TYPE_SWITCH,
9995     &setup.quick_doors,                         "quick_doors"
9996   },
9997   {
9998     TYPE_SWITCH,
9999     &setup.team_mode,                           "team_mode"
10000   },
10001   {
10002     TYPE_SWITCH,
10003     &setup.handicap,                            "handicap"
10004   },
10005   {
10006     TYPE_SWITCH,
10007     &setup.skip_levels,                         "skip_levels"
10008   },
10009   {
10010     TYPE_SWITCH,
10011     &setup.increment_levels,                    "increment_levels"
10012   },
10013   {
10014     TYPE_SWITCH,
10015     &setup.auto_play_next_level,                "auto_play_next_level"
10016   },
10017   {
10018     TYPE_SWITCH,
10019     &setup.count_score_after_game,              "count_score_after_game"
10020   },
10021   {
10022     TYPE_SWITCH,
10023     &setup.show_scores_after_game,              "show_scores_after_game"
10024   },
10025   {
10026     TYPE_SWITCH,
10027     &setup.time_limit,                          "time_limit"
10028   },
10029   {
10030     TYPE_SWITCH,
10031     &setup.fullscreen,                          "fullscreen"
10032   },
10033   {
10034     TYPE_INTEGER,
10035     &setup.window_scaling_percent,              "window_scaling_percent"
10036   },
10037   {
10038     TYPE_STRING,
10039     &setup.window_scaling_quality,              "window_scaling_quality"
10040   },
10041   {
10042     TYPE_STRING,
10043     &setup.screen_rendering_mode,               "screen_rendering_mode"
10044   },
10045   {
10046     TYPE_STRING,
10047     &setup.vsync_mode,                          "vsync_mode"
10048   },
10049   {
10050     TYPE_SWITCH,
10051     &setup.ask_on_escape,                       "ask_on_escape"
10052   },
10053   {
10054     TYPE_SWITCH,
10055     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10056   },
10057   {
10058     TYPE_SWITCH,
10059     &setup.ask_on_game_over,                    "ask_on_game_over"
10060   },
10061   {
10062     TYPE_SWITCH,
10063     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10064   },
10065   {
10066     TYPE_SWITCH,
10067     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10068   },
10069   {
10070     TYPE_SWITCH,
10071     &setup.quick_switch,                        "quick_player_switch"
10072   },
10073   {
10074     TYPE_SWITCH,
10075     &setup.input_on_focus,                      "input_on_focus"
10076   },
10077   {
10078     TYPE_SWITCH,
10079     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10080   },
10081   {
10082     TYPE_SWITCH,
10083     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10084   },
10085   {
10086     TYPE_SWITCH,
10087     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10088   },
10089   {
10090     TYPE_SWITCH,
10091     &setup.game_speed_extended,                 "game_speed_extended"
10092   },
10093   {
10094     TYPE_INTEGER,
10095     &setup.game_frame_delay,                    "game_frame_delay"
10096   },
10097   {
10098     TYPE_SWITCH,
10099     &setup.sp_show_border_elements,             "sp_show_border_elements"
10100   },
10101   {
10102     TYPE_SWITCH,
10103     &setup.small_game_graphics,                 "small_game_graphics"
10104   },
10105   {
10106     TYPE_SWITCH,
10107     &setup.show_load_save_buttons,              "show_load_save_buttons"
10108   },
10109   {
10110     TYPE_SWITCH,
10111     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10112   },
10113   {
10114     TYPE_STRING,
10115     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10116   },
10117   {
10118     TYPE_STRING,
10119     &setup.graphics_set,                        "graphics_set"
10120   },
10121   {
10122     TYPE_STRING,
10123     &setup.sounds_set,                          "sounds_set"
10124   },
10125   {
10126     TYPE_STRING,
10127     &setup.music_set,                           "music_set"
10128   },
10129   {
10130     TYPE_SWITCH3,
10131     &setup.override_level_graphics,             "override_level_graphics"
10132   },
10133   {
10134     TYPE_SWITCH3,
10135     &setup.override_level_sounds,               "override_level_sounds"
10136   },
10137   {
10138     TYPE_SWITCH3,
10139     &setup.override_level_music,                "override_level_music"
10140   },
10141   {
10142     TYPE_INTEGER,
10143     &setup.volume_simple,                       "volume_simple"
10144   },
10145   {
10146     TYPE_INTEGER,
10147     &setup.volume_loops,                        "volume_loops"
10148   },
10149   {
10150     TYPE_INTEGER,
10151     &setup.volume_music,                        "volume_music"
10152   },
10153   {
10154     TYPE_SWITCH,
10155     &setup.network_mode,                        "network_mode"
10156   },
10157   {
10158     TYPE_PLAYER,
10159     &setup.network_player_nr,                   "network_player"
10160   },
10161   {
10162     TYPE_STRING,
10163     &setup.network_server_hostname,             "network_server_hostname"
10164   },
10165   {
10166     TYPE_STRING,
10167     &setup.touch.control_type,                  "touch.control_type"
10168   },
10169   {
10170     TYPE_INTEGER,
10171     &setup.touch.move_distance,                 "touch.move_distance"
10172   },
10173   {
10174     TYPE_INTEGER,
10175     &setup.touch.drop_distance,                 "touch.drop_distance"
10176   },
10177   {
10178     TYPE_INTEGER,
10179     &setup.touch.transparency,                  "touch.transparency"
10180   },
10181   {
10182     TYPE_INTEGER,
10183     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10184   },
10185   {
10186     TYPE_INTEGER,
10187     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10188   },
10189   {
10190     TYPE_INTEGER,
10191     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10192   },
10193   {
10194     TYPE_INTEGER,
10195     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10196   },
10197   {
10198     TYPE_INTEGER,
10199     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10200   },
10201   {
10202     TYPE_INTEGER,
10203     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10204   },
10205 };
10206
10207 static struct TokenInfo auto_setup_tokens[] =
10208 {
10209   {
10210     TYPE_INTEGER,
10211     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10212   },
10213 };
10214
10215 static struct TokenInfo server_setup_tokens[] =
10216 {
10217   {
10218     TYPE_STRING,
10219     &setup.player_uuid,                         "player_uuid"
10220   },
10221   {
10222     TYPE_INTEGER,
10223     &setup.player_version,                      "player_version"
10224   },
10225   {
10226     TYPE_SWITCH,
10227     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10228   },
10229   {
10230     TYPE_STRING,
10231     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10232   },
10233   {
10234     TYPE_STRING,
10235     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10236   },
10237   {
10238     TYPE_SWITCH,
10239     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10240   },
10241   {
10242     TYPE_SWITCH,
10243     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10244   },
10245   {
10246     TYPE_SWITCH,
10247     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10248   },
10249   {
10250     TYPE_SWITCH,
10251     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10252   },
10253   {
10254     TYPE_SWITCH,
10255     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10256   },
10257 };
10258
10259 static struct TokenInfo editor_setup_tokens[] =
10260 {
10261   {
10262     TYPE_SWITCH,
10263     &setup.editor.el_classic,                   "editor.el_classic"
10264   },
10265   {
10266     TYPE_SWITCH,
10267     &setup.editor.el_custom,                    "editor.el_custom"
10268   },
10269   {
10270     TYPE_SWITCH,
10271     &setup.editor.el_user_defined,              "editor.el_user_defined"
10272   },
10273   {
10274     TYPE_SWITCH,
10275     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10276   },
10277   {
10278     TYPE_SWITCH,
10279     &setup.editor.el_headlines,                 "editor.el_headlines"
10280   },
10281   {
10282     TYPE_SWITCH,
10283     &setup.editor.show_element_token,           "editor.show_element_token"
10284   },
10285   {
10286     TYPE_SWITCH,
10287     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10288   },
10289 };
10290
10291 static struct TokenInfo editor_cascade_setup_tokens[] =
10292 {
10293   {
10294     TYPE_SWITCH,
10295     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10296   },
10297   {
10298     TYPE_SWITCH,
10299     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10300   },
10301   {
10302     TYPE_SWITCH,
10303     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10304   },
10305   {
10306     TYPE_SWITCH,
10307     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10308   },
10309   {
10310     TYPE_SWITCH,
10311     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10312   },
10313   {
10314     TYPE_SWITCH,
10315     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10316   },
10317   {
10318     TYPE_SWITCH,
10319     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10320   },
10321   {
10322     TYPE_SWITCH,
10323     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10324   },
10325   {
10326     TYPE_SWITCH,
10327     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10328   },
10329   {
10330     TYPE_SWITCH,
10331     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10332   },
10333   {
10334     TYPE_SWITCH,
10335     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10336   },
10337   {
10338     TYPE_SWITCH,
10339     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10340   },
10341   {
10342     TYPE_SWITCH,
10343     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10344   },
10345   {
10346     TYPE_SWITCH,
10347     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10348   },
10349   {
10350     TYPE_SWITCH,
10351     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10352   },
10353   {
10354     TYPE_SWITCH,
10355     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10356   },
10357   {
10358     TYPE_SWITCH,
10359     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10360   },
10361   {
10362     TYPE_SWITCH,
10363     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10364   },
10365 };
10366
10367 static struct TokenInfo shortcut_setup_tokens[] =
10368 {
10369   {
10370     TYPE_KEY_X11,
10371     &setup.shortcut.save_game,                  "shortcut.save_game"
10372   },
10373   {
10374     TYPE_KEY_X11,
10375     &setup.shortcut.load_game,                  "shortcut.load_game"
10376   },
10377   {
10378     TYPE_KEY_X11,
10379     &setup.shortcut.restart_game,               "shortcut.restart_game"
10380   },
10381   {
10382     TYPE_KEY_X11,
10383     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10384   },
10385   {
10386     TYPE_KEY_X11,
10387     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10388   },
10389   {
10390     TYPE_KEY_X11,
10391     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10392   },
10393   {
10394     TYPE_KEY_X11,
10395     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10396   },
10397   {
10398     TYPE_KEY_X11,
10399     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10400   },
10401   {
10402     TYPE_KEY_X11,
10403     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10404   },
10405   {
10406     TYPE_KEY_X11,
10407     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10408   },
10409   {
10410     TYPE_KEY_X11,
10411     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10412   },
10413   {
10414     TYPE_KEY_X11,
10415     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10416   },
10417   {
10418     TYPE_KEY_X11,
10419     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10420   },
10421   {
10422     TYPE_KEY_X11,
10423     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10424   },
10425   {
10426     TYPE_KEY_X11,
10427     &setup.shortcut.tape_record,                "shortcut.tape_record"
10428   },
10429   {
10430     TYPE_KEY_X11,
10431     &setup.shortcut.tape_play,                  "shortcut.tape_play"
10432   },
10433   {
10434     TYPE_KEY_X11,
10435     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
10436   },
10437   {
10438     TYPE_KEY_X11,
10439     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
10440   },
10441   {
10442     TYPE_KEY_X11,
10443     &setup.shortcut.sound_music,                "shortcut.sound_music"
10444   },
10445   {
10446     TYPE_KEY_X11,
10447     &setup.shortcut.snap_left,                  "shortcut.snap_left"
10448   },
10449   {
10450     TYPE_KEY_X11,
10451     &setup.shortcut.snap_right,                 "shortcut.snap_right"
10452   },
10453   {
10454     TYPE_KEY_X11,
10455     &setup.shortcut.snap_up,                    "shortcut.snap_up"
10456   },
10457   {
10458     TYPE_KEY_X11,
10459     &setup.shortcut.snap_down,                  "shortcut.snap_down"
10460   },
10461 };
10462
10463 static struct SetupInputInfo setup_input;
10464 static struct TokenInfo player_setup_tokens[] =
10465 {
10466   {
10467     TYPE_BOOLEAN,
10468     &setup_input.use_joystick,                  ".use_joystick"
10469   },
10470   {
10471     TYPE_STRING,
10472     &setup_input.joy.device_name,               ".joy.device_name"
10473   },
10474   {
10475     TYPE_INTEGER,
10476     &setup_input.joy.xleft,                     ".joy.xleft"
10477   },
10478   {
10479     TYPE_INTEGER,
10480     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
10481   },
10482   {
10483     TYPE_INTEGER,
10484     &setup_input.joy.xright,                    ".joy.xright"
10485   },
10486   {
10487     TYPE_INTEGER,
10488     &setup_input.joy.yupper,                    ".joy.yupper"
10489   },
10490   {
10491     TYPE_INTEGER,
10492     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
10493   },
10494   {
10495     TYPE_INTEGER,
10496     &setup_input.joy.ylower,                    ".joy.ylower"
10497   },
10498   {
10499     TYPE_INTEGER,
10500     &setup_input.joy.snap,                      ".joy.snap_field"
10501   },
10502   {
10503     TYPE_INTEGER,
10504     &setup_input.joy.drop,                      ".joy.place_bomb"
10505   },
10506   {
10507     TYPE_KEY_X11,
10508     &setup_input.key.left,                      ".key.move_left"
10509   },
10510   {
10511     TYPE_KEY_X11,
10512     &setup_input.key.right,                     ".key.move_right"
10513   },
10514   {
10515     TYPE_KEY_X11,
10516     &setup_input.key.up,                        ".key.move_up"
10517   },
10518   {
10519     TYPE_KEY_X11,
10520     &setup_input.key.down,                      ".key.move_down"
10521   },
10522   {
10523     TYPE_KEY_X11,
10524     &setup_input.key.snap,                      ".key.snap_field"
10525   },
10526   {
10527     TYPE_KEY_X11,
10528     &setup_input.key.drop,                      ".key.place_bomb"
10529   },
10530 };
10531
10532 static struct TokenInfo system_setup_tokens[] =
10533 {
10534   {
10535     TYPE_STRING,
10536     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10537   },
10538   {
10539     TYPE_STRING,
10540     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10541   },
10542   {
10543     TYPE_STRING,
10544     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10545   },
10546   {
10547     TYPE_INTEGER,
10548     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10549   },
10550 };
10551
10552 static struct TokenInfo internal_setup_tokens[] =
10553 {
10554   {
10555     TYPE_STRING,
10556     &setup.internal.program_title,              "program_title"
10557   },
10558   {
10559     TYPE_STRING,
10560     &setup.internal.program_version,            "program_version"
10561   },
10562   {
10563     TYPE_STRING,
10564     &setup.internal.program_author,             "program_author"
10565   },
10566   {
10567     TYPE_STRING,
10568     &setup.internal.program_email,              "program_email"
10569   },
10570   {
10571     TYPE_STRING,
10572     &setup.internal.program_website,            "program_website"
10573   },
10574   {
10575     TYPE_STRING,
10576     &setup.internal.program_copyright,          "program_copyright"
10577   },
10578   {
10579     TYPE_STRING,
10580     &setup.internal.program_company,            "program_company"
10581   },
10582   {
10583     TYPE_STRING,
10584     &setup.internal.program_icon_file,          "program_icon_file"
10585   },
10586   {
10587     TYPE_STRING,
10588     &setup.internal.default_graphics_set,       "default_graphics_set"
10589   },
10590   {
10591     TYPE_STRING,
10592     &setup.internal.default_sounds_set,         "default_sounds_set"
10593   },
10594   {
10595     TYPE_STRING,
10596     &setup.internal.default_music_set,          "default_music_set"
10597   },
10598   {
10599     TYPE_STRING,
10600     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
10601   },
10602   {
10603     TYPE_STRING,
10604     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
10605   },
10606   {
10607     TYPE_STRING,
10608     &setup.internal.fallback_music_file,        "fallback_music_file"
10609   },
10610   {
10611     TYPE_STRING,
10612     &setup.internal.default_level_series,       "default_level_series"
10613   },
10614   {
10615     TYPE_INTEGER,
10616     &setup.internal.default_window_width,       "default_window_width"
10617   },
10618   {
10619     TYPE_INTEGER,
10620     &setup.internal.default_window_height,      "default_window_height"
10621   },
10622   {
10623     TYPE_BOOLEAN,
10624     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
10625   },
10626   {
10627     TYPE_BOOLEAN,
10628     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
10629   },
10630   {
10631     TYPE_BOOLEAN,
10632     &setup.internal.create_user_levelset,       "create_user_levelset"
10633   },
10634   {
10635     TYPE_BOOLEAN,
10636     &setup.internal.menu_game,                  "menu_game"
10637   },
10638   {
10639     TYPE_BOOLEAN,
10640     &setup.internal.menu_editor,                "menu_editor"
10641   },
10642   {
10643     TYPE_BOOLEAN,
10644     &setup.internal.menu_graphics,              "menu_graphics"
10645   },
10646   {
10647     TYPE_BOOLEAN,
10648     &setup.internal.menu_sound,                 "menu_sound"
10649   },
10650   {
10651     TYPE_BOOLEAN,
10652     &setup.internal.menu_artwork,               "menu_artwork"
10653   },
10654   {
10655     TYPE_BOOLEAN,
10656     &setup.internal.menu_input,                 "menu_input"
10657   },
10658   {
10659     TYPE_BOOLEAN,
10660     &setup.internal.menu_touch,                 "menu_touch"
10661   },
10662   {
10663     TYPE_BOOLEAN,
10664     &setup.internal.menu_shortcuts,             "menu_shortcuts"
10665   },
10666   {
10667     TYPE_BOOLEAN,
10668     &setup.internal.menu_exit,                  "menu_exit"
10669   },
10670   {
10671     TYPE_BOOLEAN,
10672     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
10673   },
10674 };
10675
10676 static struct TokenInfo debug_setup_tokens[] =
10677 {
10678   {
10679     TYPE_INTEGER,
10680     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
10681   },
10682   {
10683     TYPE_INTEGER,
10684     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
10685   },
10686   {
10687     TYPE_INTEGER,
10688     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
10689   },
10690   {
10691     TYPE_INTEGER,
10692     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
10693   },
10694   {
10695     TYPE_INTEGER,
10696     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
10697   },
10698   {
10699     TYPE_INTEGER,
10700     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
10701   },
10702   {
10703     TYPE_INTEGER,
10704     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
10705   },
10706   {
10707     TYPE_INTEGER,
10708     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
10709   },
10710   {
10711     TYPE_INTEGER,
10712     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
10713   },
10714   {
10715     TYPE_INTEGER,
10716     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
10717   },
10718   {
10719     TYPE_KEY_X11,
10720     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
10721   },
10722   {
10723     TYPE_KEY_X11,
10724     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
10725   },
10726   {
10727     TYPE_KEY_X11,
10728     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
10729   },
10730   {
10731     TYPE_KEY_X11,
10732     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
10733   },
10734   {
10735     TYPE_KEY_X11,
10736     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
10737   },
10738   {
10739     TYPE_KEY_X11,
10740     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
10741   },
10742   {
10743     TYPE_KEY_X11,
10744     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
10745   },
10746   {
10747     TYPE_KEY_X11,
10748     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
10749   },
10750   {
10751     TYPE_KEY_X11,
10752     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
10753   },
10754   {
10755     TYPE_KEY_X11,
10756     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
10757   },
10758   {
10759     TYPE_BOOLEAN,
10760     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
10761   {
10762     TYPE_BOOLEAN,
10763     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
10764   },
10765   {
10766     TYPE_BOOLEAN,
10767     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
10768   },
10769   {
10770     TYPE_SWITCH3,
10771     &setup.debug.xsn_mode,                      "debug.xsn_mode"
10772   },
10773   {
10774     TYPE_INTEGER,
10775     &setup.debug.xsn_percent,                   "debug.xsn_percent"
10776   },
10777 };
10778
10779 static struct TokenInfo options_setup_tokens[] =
10780 {
10781   {
10782     TYPE_BOOLEAN,
10783     &setup.options.verbose,                     "options.verbose"
10784   },
10785 };
10786
10787 static void setSetupInfoToDefaults(struct SetupInfo *si)
10788 {
10789   int i;
10790
10791   si->player_name = getStringCopy(getDefaultUserName(user.nr));
10792
10793   si->multiple_users = TRUE;
10794
10795   si->sound = TRUE;
10796   si->sound_loops = TRUE;
10797   si->sound_music = TRUE;
10798   si->sound_simple = TRUE;
10799   si->toons = TRUE;
10800   si->scroll_delay = TRUE;
10801   si->forced_scroll_delay = FALSE;
10802   si->scroll_delay_value = STD_SCROLL_DELAY;
10803   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10804   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10805   si->fade_screens = TRUE;
10806   si->autorecord = TRUE;
10807   si->auto_pause_on_start = FALSE;
10808   si->show_titlescreen = TRUE;
10809   si->quick_doors = FALSE;
10810   si->team_mode = FALSE;
10811   si->handicap = TRUE;
10812   si->skip_levels = TRUE;
10813   si->increment_levels = TRUE;
10814   si->auto_play_next_level = TRUE;
10815   si->count_score_after_game = TRUE;
10816   si->show_scores_after_game = TRUE;
10817   si->time_limit = TRUE;
10818   si->fullscreen = FALSE;
10819   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10820   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10821   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10822   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10823   si->ask_on_escape = TRUE;
10824   si->ask_on_escape_editor = TRUE;
10825   si->ask_on_game_over = TRUE;
10826   si->ask_on_quit_game = TRUE;
10827   si->ask_on_quit_program = TRUE;
10828   si->quick_switch = FALSE;
10829   si->input_on_focus = FALSE;
10830   si->prefer_aga_graphics = TRUE;
10831   si->prefer_lowpass_sounds = FALSE;
10832   si->prefer_extra_panel_items = TRUE;
10833   si->game_speed_extended = FALSE;
10834   si->game_frame_delay = GAME_FRAME_DELAY;
10835   si->sp_show_border_elements = FALSE;
10836   si->small_game_graphics = FALSE;
10837   si->show_load_save_buttons = FALSE;
10838   si->show_undo_redo_buttons = FALSE;
10839   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10840
10841   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10842   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
10843   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
10844
10845   si->override_level_graphics = FALSE;
10846   si->override_level_sounds = FALSE;
10847   si->override_level_music = FALSE;
10848
10849   si->volume_simple = 100;              // percent
10850   si->volume_loops = 100;               // percent
10851   si->volume_music = 100;               // percent
10852
10853   si->network_mode = FALSE;
10854   si->network_player_nr = 0;            // first player
10855   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10856
10857   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10858   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
10859   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
10860   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
10861   si->touch.draw_outlined = TRUE;
10862   si->touch.draw_pressed = TRUE;
10863
10864   for (i = 0; i < 2; i++)
10865   {
10866     char *default_grid_button[6][2] =
10867     {
10868       { "      ", "  ^^  " },
10869       { "      ", "  ^^  " },
10870       { "      ", "<<  >>" },
10871       { "      ", "<<  >>" },
10872       { "111222", "  vv  " },
10873       { "111222", "  vv  " }
10874     };
10875     int grid_xsize = DEFAULT_GRID_XSIZE(i);
10876     int grid_ysize = DEFAULT_GRID_YSIZE(i);
10877     int min_xsize = MIN(6, grid_xsize);
10878     int min_ysize = MIN(6, grid_ysize);
10879     int startx = grid_xsize - min_xsize;
10880     int starty = grid_ysize - min_ysize;
10881     int x, y;
10882
10883     // virtual buttons grid can only be set to defaults if video is initialized
10884     // (this will be repeated if virtual buttons are not loaded from setup file)
10885     if (video.initialized)
10886     {
10887       si->touch.grid_xsize[i] = grid_xsize;
10888       si->touch.grid_ysize[i] = grid_ysize;
10889     }
10890     else
10891     {
10892       si->touch.grid_xsize[i] = -1;
10893       si->touch.grid_ysize[i] = -1;
10894     }
10895
10896     for (x = 0; x < MAX_GRID_XSIZE; x++)
10897       for (y = 0; y < MAX_GRID_YSIZE; y++)
10898         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10899
10900     for (x = 0; x < min_xsize; x++)
10901       for (y = 0; y < min_ysize; y++)
10902         si->touch.grid_button[i][x][starty + y] =
10903           default_grid_button[y][0][x];
10904
10905     for (x = 0; x < min_xsize; x++)
10906       for (y = 0; y < min_ysize; y++)
10907         si->touch.grid_button[i][startx + x][starty + y] =
10908           default_grid_button[y][1][x];
10909   }
10910
10911   si->touch.grid_initialized            = video.initialized;
10912
10913   si->editor.el_boulderdash             = TRUE;
10914   si->editor.el_emerald_mine            = TRUE;
10915   si->editor.el_emerald_mine_club       = TRUE;
10916   si->editor.el_more                    = TRUE;
10917   si->editor.el_sokoban                 = TRUE;
10918   si->editor.el_supaplex                = TRUE;
10919   si->editor.el_diamond_caves           = TRUE;
10920   si->editor.el_dx_boulderdash          = TRUE;
10921
10922   si->editor.el_mirror_magic            = TRUE;
10923   si->editor.el_deflektor               = TRUE;
10924
10925   si->editor.el_chars                   = TRUE;
10926   si->editor.el_steel_chars             = TRUE;
10927
10928   si->editor.el_classic                 = TRUE;
10929   si->editor.el_custom                  = TRUE;
10930
10931   si->editor.el_user_defined            = FALSE;
10932   si->editor.el_dynamic                 = TRUE;
10933
10934   si->editor.el_headlines               = TRUE;
10935
10936   si->editor.show_element_token         = FALSE;
10937
10938   si->editor.show_read_only_warning     = TRUE;
10939
10940   si->editor.use_template_for_new_levels = TRUE;
10941
10942   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
10943   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
10944   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
10945   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10946   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
10947
10948   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
10949   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
10950   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
10951   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
10952   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10953
10954   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
10955   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
10956   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
10957   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
10958   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
10959   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
10960
10961   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
10962   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
10963   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
10964
10965   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
10966   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
10967   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
10968   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
10969
10970   for (i = 0; i < MAX_PLAYERS; i++)
10971   {
10972     si->input[i].use_joystick = FALSE;
10973     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10974     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
10975     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10976     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
10977     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
10978     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10979     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
10980     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
10981     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
10982     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
10983     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10984     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
10985     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
10986     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
10987     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
10988   }
10989
10990   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10991   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10992   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10993   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10994
10995   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
10996   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
10997   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
10998   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
10999   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11000   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11001   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11002
11003   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11004
11005   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11006   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11007   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11008
11009   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11010   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11011   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11012
11013   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11014   si->internal.choose_from_top_leveldir = FALSE;
11015   si->internal.show_scaling_in_title = TRUE;
11016   si->internal.create_user_levelset = TRUE;
11017
11018   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11019   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11020
11021   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11022   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11023   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11024   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11025   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11026   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11027   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11028   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11029   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11030   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11031
11032   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11033   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11034   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11035   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11036   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11037   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11038   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11039   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11040   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11041   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11042
11043   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11044   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11045
11046   si->debug.show_frames_per_second = FALSE;
11047
11048   si->debug.xsn_mode = AUTO;
11049   si->debug.xsn_percent = 0;
11050
11051   si->options.verbose = FALSE;
11052
11053 #if defined(PLATFORM_ANDROID)
11054   si->fullscreen = TRUE;
11055 #endif
11056
11057   setHideSetupEntry(&setup.debug.xsn_mode);
11058 }
11059
11060 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11061 {
11062   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11063 }
11064
11065 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11066 {
11067   si->player_uuid = NULL;       // (will be set later)
11068   si->player_version = 1;       // (will be set later)
11069
11070   si->use_api_server = TRUE;
11071   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11072   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11073   si->ask_for_uploading_tapes = TRUE;
11074   si->ask_for_remaining_tapes = FALSE;
11075   si->provide_uploading_tapes = TRUE;
11076   si->ask_for_using_api_server = TRUE;
11077   si->has_remaining_tapes = FALSE;
11078 }
11079
11080 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11081 {
11082   si->editor_cascade.el_bd              = TRUE;
11083   si->editor_cascade.el_em              = TRUE;
11084   si->editor_cascade.el_emc             = TRUE;
11085   si->editor_cascade.el_rnd             = TRUE;
11086   si->editor_cascade.el_sb              = TRUE;
11087   si->editor_cascade.el_sp              = TRUE;
11088   si->editor_cascade.el_dc              = TRUE;
11089   si->editor_cascade.el_dx              = TRUE;
11090
11091   si->editor_cascade.el_mm              = TRUE;
11092   si->editor_cascade.el_df              = TRUE;
11093
11094   si->editor_cascade.el_chars           = FALSE;
11095   si->editor_cascade.el_steel_chars     = FALSE;
11096   si->editor_cascade.el_ce              = FALSE;
11097   si->editor_cascade.el_ge              = FALSE;
11098   si->editor_cascade.el_es              = FALSE;
11099   si->editor_cascade.el_ref             = FALSE;
11100   si->editor_cascade.el_user            = FALSE;
11101   si->editor_cascade.el_dynamic         = FALSE;
11102 }
11103
11104 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11105
11106 static char *getHideSetupToken(void *setup_value)
11107 {
11108   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11109
11110   if (setup_value != NULL)
11111     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11112
11113   return hide_setup_token;
11114 }
11115
11116 void setHideSetupEntry(void *setup_value)
11117 {
11118   char *hide_setup_token = getHideSetupToken(setup_value);
11119
11120   if (hide_setup_hash == NULL)
11121     hide_setup_hash = newSetupFileHash();
11122
11123   if (setup_value != NULL)
11124     setHashEntry(hide_setup_hash, hide_setup_token, "");
11125 }
11126
11127 void removeHideSetupEntry(void *setup_value)
11128 {
11129   char *hide_setup_token = getHideSetupToken(setup_value);
11130
11131   if (setup_value != NULL)
11132     removeHashEntry(hide_setup_hash, hide_setup_token);
11133 }
11134
11135 boolean hideSetupEntry(void *setup_value)
11136 {
11137   char *hide_setup_token = getHideSetupToken(setup_value);
11138
11139   return (setup_value != NULL &&
11140           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11141 }
11142
11143 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11144                                       struct TokenInfo *token_info,
11145                                       int token_nr, char *token_text)
11146 {
11147   char *token_hide_text = getStringCat2(token_text, ".hide");
11148   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11149
11150   // set the value of this setup option in the setup option structure
11151   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11152
11153   // check if this setup option should be hidden in the setup menu
11154   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11155     setHideSetupEntry(token_info[token_nr].value);
11156
11157   free(token_hide_text);
11158 }
11159
11160 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11161                                       struct TokenInfo *token_info,
11162                                       int token_nr)
11163 {
11164   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11165                             token_info[token_nr].text);
11166 }
11167
11168 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11169 {
11170   int i, pnr;
11171
11172   if (!setup_file_hash)
11173     return;
11174
11175   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11176     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11177
11178   setup.touch.grid_initialized = TRUE;
11179   for (i = 0; i < 2; i++)
11180   {
11181     int grid_xsize = setup.touch.grid_xsize[i];
11182     int grid_ysize = setup.touch.grid_ysize[i];
11183     int x, y;
11184
11185     // if virtual buttons are not loaded from setup file, repeat initializing
11186     // virtual buttons grid with default values later when video is initialized
11187     if (grid_xsize == -1 ||
11188         grid_ysize == -1)
11189     {
11190       setup.touch.grid_initialized = FALSE;
11191
11192       continue;
11193     }
11194
11195     for (y = 0; y < grid_ysize; y++)
11196     {
11197       char token_string[MAX_LINE_LEN];
11198
11199       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11200
11201       char *value_string = getHashEntry(setup_file_hash, token_string);
11202
11203       if (value_string == NULL)
11204         continue;
11205
11206       for (x = 0; x < grid_xsize; x++)
11207       {
11208         char c = value_string[x];
11209
11210         setup.touch.grid_button[i][x][y] =
11211           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11212       }
11213     }
11214   }
11215
11216   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11217     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11218
11219   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11220     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11221
11222   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11223   {
11224     char prefix[30];
11225
11226     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11227
11228     setup_input = setup.input[pnr];
11229     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11230     {
11231       char full_token[100];
11232
11233       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11234       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11235                                 full_token);
11236     }
11237     setup.input[pnr] = setup_input;
11238   }
11239
11240   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11241     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11242
11243   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11244     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11245
11246   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11247     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11248
11249   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11250     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11251
11252   setHideRelatedSetupEntries();
11253 }
11254
11255 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11256 {
11257   int i;
11258
11259   if (!setup_file_hash)
11260     return;
11261
11262   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11263     setSetupInfo(auto_setup_tokens, i,
11264                  getHashEntry(setup_file_hash,
11265                               auto_setup_tokens[i].text));
11266 }
11267
11268 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11269 {
11270   int i;
11271
11272   if (!setup_file_hash)
11273     return;
11274
11275   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11276     setSetupInfo(server_setup_tokens, i,
11277                  getHashEntry(setup_file_hash,
11278                               server_setup_tokens[i].text));
11279 }
11280
11281 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11282 {
11283   int i;
11284
11285   if (!setup_file_hash)
11286     return;
11287
11288   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11289     setSetupInfo(editor_cascade_setup_tokens, i,
11290                  getHashEntry(setup_file_hash,
11291                               editor_cascade_setup_tokens[i].text));
11292 }
11293
11294 void LoadUserNames(void)
11295 {
11296   int last_user_nr = user.nr;
11297   int i;
11298
11299   if (global.user_names != NULL)
11300   {
11301     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11302       checked_free(global.user_names[i]);
11303
11304     checked_free(global.user_names);
11305   }
11306
11307   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11308
11309   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11310   {
11311     user.nr = i;
11312
11313     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11314
11315     if (setup_file_hash)
11316     {
11317       char *player_name = getHashEntry(setup_file_hash, "player_name");
11318
11319       global.user_names[i] = getFixedUserName(player_name);
11320
11321       freeSetupFileHash(setup_file_hash);
11322     }
11323
11324     if (global.user_names[i] == NULL)
11325       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11326   }
11327
11328   user.nr = last_user_nr;
11329 }
11330
11331 void LoadSetupFromFilename(char *filename)
11332 {
11333   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11334
11335   if (setup_file_hash)
11336   {
11337     decodeSetupFileHash_Default(setup_file_hash);
11338
11339     freeSetupFileHash(setup_file_hash);
11340   }
11341   else
11342   {
11343     Debug("setup", "using default setup values");
11344   }
11345 }
11346
11347 static void LoadSetup_SpecialPostProcessing(void)
11348 {
11349   char *player_name_new;
11350
11351   // needed to work around problems with fixed length strings
11352   player_name_new = getFixedUserName(setup.player_name);
11353   free(setup.player_name);
11354   setup.player_name = player_name_new;
11355
11356   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11357   if (setup.scroll_delay == FALSE)
11358   {
11359     setup.scroll_delay_value = MIN_SCROLL_DELAY;
11360     setup.scroll_delay = TRUE;                  // now always "on"
11361   }
11362
11363   // make sure that scroll delay value stays inside valid range
11364   setup.scroll_delay_value =
11365     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11366 }
11367
11368 void LoadSetup_Default(void)
11369 {
11370   char *filename;
11371
11372   // always start with reliable default values
11373   setSetupInfoToDefaults(&setup);
11374
11375   // try to load setup values from default setup file
11376   filename = getDefaultSetupFilename();
11377
11378   if (fileExists(filename))
11379     LoadSetupFromFilename(filename);
11380
11381   // try to load setup values from user setup file
11382   filename = getSetupFilename();
11383
11384   LoadSetupFromFilename(filename);
11385
11386   LoadSetup_SpecialPostProcessing();
11387 }
11388
11389 void LoadSetup_AutoSetup(void)
11390 {
11391   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11392   SetupFileHash *setup_file_hash = NULL;
11393
11394   // always start with reliable default values
11395   setSetupInfoToDefaults_AutoSetup(&setup);
11396
11397   setup_file_hash = loadSetupFileHash(filename);
11398
11399   if (setup_file_hash)
11400   {
11401     decodeSetupFileHash_AutoSetup(setup_file_hash);
11402
11403     freeSetupFileHash(setup_file_hash);
11404   }
11405
11406   free(filename);
11407 }
11408
11409 void LoadSetup_ServerSetup(void)
11410 {
11411   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11412   SetupFileHash *setup_file_hash = NULL;
11413
11414   // always start with reliable default values
11415   setSetupInfoToDefaults_ServerSetup(&setup);
11416
11417   setup_file_hash = loadSetupFileHash(filename);
11418
11419   if (setup_file_hash)
11420   {
11421     decodeSetupFileHash_ServerSetup(setup_file_hash);
11422
11423     freeSetupFileHash(setup_file_hash);
11424   }
11425
11426   free(filename);
11427
11428   if (setup.player_uuid == NULL)
11429   {
11430     // player UUID does not yet exist in setup file
11431     setup.player_uuid = getStringCopy(getUUID());
11432     setup.player_version = 2;
11433
11434     SaveSetup_ServerSetup();
11435   }
11436 }
11437
11438 void LoadSetup_EditorCascade(void)
11439 {
11440   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11441   SetupFileHash *setup_file_hash = NULL;
11442
11443   // always start with reliable default values
11444   setSetupInfoToDefaults_EditorCascade(&setup);
11445
11446   setup_file_hash = loadSetupFileHash(filename);
11447
11448   if (setup_file_hash)
11449   {
11450     decodeSetupFileHash_EditorCascade(setup_file_hash);
11451
11452     freeSetupFileHash(setup_file_hash);
11453   }
11454
11455   free(filename);
11456 }
11457
11458 void LoadSetup(void)
11459 {
11460   LoadSetup_Default();
11461   LoadSetup_AutoSetup();
11462   LoadSetup_ServerSetup();
11463   LoadSetup_EditorCascade();
11464 }
11465
11466 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11467                                            char *mapping_line)
11468 {
11469   char mapping_guid[MAX_LINE_LEN];
11470   char *mapping_start, *mapping_end;
11471
11472   // get GUID from game controller mapping line: copy complete line
11473   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11474   mapping_guid[MAX_LINE_LEN - 1] = '\0';
11475
11476   // get GUID from game controller mapping line: cut after GUID part
11477   mapping_start = strchr(mapping_guid, ',');
11478   if (mapping_start != NULL)
11479     *mapping_start = '\0';
11480
11481   // cut newline from game controller mapping line
11482   mapping_end = strchr(mapping_line, '\n');
11483   if (mapping_end != NULL)
11484     *mapping_end = '\0';
11485
11486   // add mapping entry to game controller mappings hash
11487   setHashEntry(mappings_hash, mapping_guid, mapping_line);
11488 }
11489
11490 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11491                                                  char *filename)
11492 {
11493   FILE *file;
11494
11495   if (!(file = fopen(filename, MODE_READ)))
11496   {
11497     Warn("cannot read game controller mappings file '%s'", filename);
11498
11499     return;
11500   }
11501
11502   while (!feof(file))
11503   {
11504     char line[MAX_LINE_LEN];
11505
11506     if (!fgets(line, MAX_LINE_LEN, file))
11507       break;
11508
11509     addGameControllerMappingToHash(mappings_hash, line);
11510   }
11511
11512   fclose(file);
11513 }
11514
11515 void SaveSetup_Default(void)
11516 {
11517   char *filename = getSetupFilename();
11518   FILE *file;
11519   int i, pnr;
11520
11521   InitUserDataDirectory();
11522
11523   if (!(file = fopen(filename, MODE_WRITE)))
11524   {
11525     Warn("cannot write setup file '%s'", filename);
11526
11527     return;
11528   }
11529
11530   fprintFileHeader(file, SETUP_FILENAME);
11531
11532   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11533   {
11534     // just to make things nicer :)
11535     if (global_setup_tokens[i].value == &setup.multiple_users           ||
11536         global_setup_tokens[i].value == &setup.sound                    ||
11537         global_setup_tokens[i].value == &setup.graphics_set             ||
11538         global_setup_tokens[i].value == &setup.volume_simple            ||
11539         global_setup_tokens[i].value == &setup.network_mode             ||
11540         global_setup_tokens[i].value == &setup.touch.control_type       ||
11541         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
11542         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11543       fprintf(file, "\n");
11544
11545     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11546   }
11547
11548   for (i = 0; i < 2; i++)
11549   {
11550     int grid_xsize = setup.touch.grid_xsize[i];
11551     int grid_ysize = setup.touch.grid_ysize[i];
11552     int x, y;
11553
11554     fprintf(file, "\n");
11555
11556     for (y = 0; y < grid_ysize; y++)
11557     {
11558       char token_string[MAX_LINE_LEN];
11559       char value_string[MAX_LINE_LEN];
11560
11561       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11562
11563       for (x = 0; x < grid_xsize; x++)
11564       {
11565         char c = setup.touch.grid_button[i][x][y];
11566
11567         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11568       }
11569
11570       value_string[grid_xsize] = '\0';
11571
11572       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11573     }
11574   }
11575
11576   fprintf(file, "\n");
11577   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11578     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11579
11580   fprintf(file, "\n");
11581   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11582     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11583
11584   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11585   {
11586     char prefix[30];
11587
11588     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11589     fprintf(file, "\n");
11590
11591     setup_input = setup.input[pnr];
11592     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11593       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11594   }
11595
11596   fprintf(file, "\n");
11597   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11598     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11599
11600   // (internal setup values not saved to user setup file)
11601
11602   fprintf(file, "\n");
11603   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11604     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11605         setup.debug.xsn_mode != AUTO)
11606       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11607
11608   fprintf(file, "\n");
11609   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11610     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11611
11612   fclose(file);
11613
11614   SetFilePermissions(filename, PERMS_PRIVATE);
11615 }
11616
11617 void SaveSetup_AutoSetup(void)
11618 {
11619   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11620   FILE *file;
11621   int i;
11622
11623   InitUserDataDirectory();
11624
11625   if (!(file = fopen(filename, MODE_WRITE)))
11626   {
11627     Warn("cannot write auto setup file '%s'", filename);
11628
11629     free(filename);
11630
11631     return;
11632   }
11633
11634   fprintFileHeader(file, AUTOSETUP_FILENAME);
11635
11636   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11637     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11638
11639   fclose(file);
11640
11641   SetFilePermissions(filename, PERMS_PRIVATE);
11642
11643   free(filename);
11644 }
11645
11646 void SaveSetup_ServerSetup(void)
11647 {
11648   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11649   FILE *file;
11650   int i;
11651
11652   InitUserDataDirectory();
11653
11654   if (!(file = fopen(filename, MODE_WRITE)))
11655   {
11656     Warn("cannot write server setup file '%s'", filename);
11657
11658     free(filename);
11659
11660     return;
11661   }
11662
11663   fprintFileHeader(file, SERVERSETUP_FILENAME);
11664
11665   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11666   {
11667     // just to make things nicer :)
11668     if (server_setup_tokens[i].value == &setup.use_api_server)
11669       fprintf(file, "\n");
11670
11671     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11672   }
11673
11674   fclose(file);
11675
11676   SetFilePermissions(filename, PERMS_PRIVATE);
11677
11678   free(filename);
11679 }
11680
11681 void SaveSetup_EditorCascade(void)
11682 {
11683   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11684   FILE *file;
11685   int i;
11686
11687   InitUserDataDirectory();
11688
11689   if (!(file = fopen(filename, MODE_WRITE)))
11690   {
11691     Warn("cannot write editor cascade state file '%s'", filename);
11692
11693     free(filename);
11694
11695     return;
11696   }
11697
11698   fprintFileHeader(file, EDITORCASCADE_FILENAME);
11699
11700   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11701     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11702
11703   fclose(file);
11704
11705   SetFilePermissions(filename, PERMS_PRIVATE);
11706
11707   free(filename);
11708 }
11709
11710 void SaveSetup(void)
11711 {
11712   SaveSetup_Default();
11713   SaveSetup_AutoSetup();
11714   SaveSetup_ServerSetup();
11715   SaveSetup_EditorCascade();
11716 }
11717
11718 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11719                                                   char *filename)
11720 {
11721   FILE *file;
11722
11723   if (!(file = fopen(filename, MODE_WRITE)))
11724   {
11725     Warn("cannot write game controller mappings file '%s'", filename);
11726
11727     return;
11728   }
11729
11730   BEGIN_HASH_ITERATION(mappings_hash, itr)
11731   {
11732     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11733   }
11734   END_HASH_ITERATION(mappings_hash, itr)
11735
11736   fclose(file);
11737 }
11738
11739 void SaveSetup_AddGameControllerMapping(char *mapping)
11740 {
11741   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11742   SetupFileHash *mappings_hash = newSetupFileHash();
11743
11744   InitUserDataDirectory();
11745
11746   // load existing personal game controller mappings
11747   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11748
11749   // add new mapping to personal game controller mappings
11750   addGameControllerMappingToHash(mappings_hash, mapping);
11751
11752   // save updated personal game controller mappings
11753   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11754
11755   freeSetupFileHash(mappings_hash);
11756   free(filename);
11757 }
11758
11759 void LoadCustomElementDescriptions(void)
11760 {
11761   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11762   SetupFileHash *setup_file_hash;
11763   int i;
11764
11765   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11766   {
11767     if (element_info[i].custom_description != NULL)
11768     {
11769       free(element_info[i].custom_description);
11770       element_info[i].custom_description = NULL;
11771     }
11772   }
11773
11774   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11775     return;
11776
11777   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11778   {
11779     char *token = getStringCat2(element_info[i].token_name, ".name");
11780     char *value = getHashEntry(setup_file_hash, token);
11781
11782     if (value != NULL)
11783       element_info[i].custom_description = getStringCopy(value);
11784
11785     free(token);
11786   }
11787
11788   freeSetupFileHash(setup_file_hash);
11789 }
11790
11791 static int getElementFromToken(char *token)
11792 {
11793   char *value = getHashEntry(element_token_hash, token);
11794
11795   if (value != NULL)
11796     return atoi(value);
11797
11798   Warn("unknown element token '%s'", token);
11799
11800   return EL_UNDEFINED;
11801 }
11802
11803 void FreeGlobalAnimEventInfo(void)
11804 {
11805   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11806
11807   if (gaei->event_list == NULL)
11808     return;
11809
11810   int i;
11811
11812   for (i = 0; i < gaei->num_event_lists; i++)
11813   {
11814     checked_free(gaei->event_list[i]->event_value);
11815     checked_free(gaei->event_list[i]);
11816   }
11817
11818   checked_free(gaei->event_list);
11819
11820   gaei->event_list = NULL;
11821   gaei->num_event_lists = 0;
11822 }
11823
11824 static int AddGlobalAnimEventList(void)
11825 {
11826   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11827   int list_pos = gaei->num_event_lists++;
11828
11829   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11830                                      sizeof(struct GlobalAnimEventListInfo *));
11831
11832   gaei->event_list[list_pos] =
11833     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11834
11835   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11836
11837   gaeli->event_value = NULL;
11838   gaeli->num_event_values = 0;
11839
11840   return list_pos;
11841 }
11842
11843 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11844 {
11845   // do not add empty global animation events
11846   if (event_value == ANIM_EVENT_NONE)
11847     return list_pos;
11848
11849   // if list position is undefined, create new list
11850   if (list_pos == ANIM_EVENT_UNDEFINED)
11851     list_pos = AddGlobalAnimEventList();
11852
11853   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11854   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11855   int value_pos = gaeli->num_event_values++;
11856
11857   gaeli->event_value = checked_realloc(gaeli->event_value,
11858                                        gaeli->num_event_values * sizeof(int *));
11859
11860   gaeli->event_value[value_pos] = event_value;
11861
11862   return list_pos;
11863 }
11864
11865 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11866 {
11867   if (list_pos == ANIM_EVENT_UNDEFINED)
11868     return ANIM_EVENT_NONE;
11869
11870   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11871   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11872
11873   return gaeli->event_value[value_pos];
11874 }
11875
11876 int GetGlobalAnimEventValueCount(int list_pos)
11877 {
11878   if (list_pos == ANIM_EVENT_UNDEFINED)
11879     return 0;
11880
11881   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11882   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11883
11884   return gaeli->num_event_values;
11885 }
11886
11887 // This function checks if a string <s> of the format "string1, string2, ..."
11888 // exactly contains a string <s_contained>.
11889
11890 static boolean string_has_parameter(char *s, char *s_contained)
11891 {
11892   char *substring;
11893
11894   if (s == NULL || s_contained == NULL)
11895     return FALSE;
11896
11897   if (strlen(s_contained) > strlen(s))
11898     return FALSE;
11899
11900   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11901   {
11902     char next_char = s[strlen(s_contained)];
11903
11904     // check if next character is delimiter or whitespace
11905     return (next_char == ',' || next_char == '\0' ||
11906             next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11907   }
11908
11909   // check if string contains another parameter string after a comma
11910   substring = strchr(s, ',');
11911   if (substring == NULL)        // string does not contain a comma
11912     return FALSE;
11913
11914   // advance string pointer to next character after the comma
11915   substring++;
11916
11917   // skip potential whitespaces after the comma
11918   while (*substring == ' ' || *substring == '\t')
11919     substring++;
11920
11921   return string_has_parameter(substring, s_contained);
11922 }
11923
11924 static int get_anim_parameter_value(char *s)
11925 {
11926   int event_value[] =
11927   {
11928     ANIM_EVENT_CLICK,
11929     ANIM_EVENT_INIT,
11930     ANIM_EVENT_START,
11931     ANIM_EVENT_END,
11932     ANIM_EVENT_POST
11933   };
11934   char *pattern_1[] =
11935   {
11936     "click:anim_",
11937     "init:anim_",
11938     "start:anim_",
11939     "end:anim_",
11940     "post:anim_"
11941   };
11942   char *pattern_2 = ".part_";
11943   char *matching_char = NULL;
11944   char *s_ptr = s;
11945   int pattern_1_len = 0;
11946   int result = ANIM_EVENT_NONE;
11947   int i;
11948
11949   for (i = 0; i < ARRAY_SIZE(event_value); i++)
11950   {
11951     matching_char = strstr(s_ptr, pattern_1[i]);
11952     pattern_1_len = strlen(pattern_1[i]);
11953     result = event_value[i];
11954
11955     if (matching_char != NULL)
11956       break;
11957   }
11958
11959   if (matching_char == NULL)
11960     return ANIM_EVENT_NONE;
11961
11962   s_ptr = matching_char + pattern_1_len;
11963
11964   // check for main animation number ("anim_X" or "anim_XX")
11965   if (*s_ptr >= '0' && *s_ptr <= '9')
11966   {
11967     int gic_anim_nr = (*s_ptr++ - '0');
11968
11969     if (*s_ptr >= '0' && *s_ptr <= '9')
11970       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11971
11972     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11973       return ANIM_EVENT_NONE;
11974
11975     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11976   }
11977   else
11978   {
11979     // invalid main animation number specified
11980
11981     return ANIM_EVENT_NONE;
11982   }
11983
11984   // check for animation part number ("part_X" or "part_XX") (optional)
11985   if (strPrefix(s_ptr, pattern_2))
11986   {
11987     s_ptr += strlen(pattern_2);
11988
11989     if (*s_ptr >= '0' && *s_ptr <= '9')
11990     {
11991       int gic_part_nr = (*s_ptr++ - '0');
11992
11993       if (*s_ptr >= '0' && *s_ptr <= '9')
11994         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11995
11996       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11997         return ANIM_EVENT_NONE;
11998
11999       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12000     }
12001     else
12002     {
12003       // invalid animation part number specified
12004
12005       return ANIM_EVENT_NONE;
12006     }
12007   }
12008
12009   // discard result if next character is neither delimiter nor whitespace
12010   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12011         *s_ptr == ' ' || *s_ptr == '\t'))
12012     return ANIM_EVENT_NONE;
12013
12014   return result;
12015 }
12016
12017 static int get_anim_parameter_values(char *s)
12018 {
12019   int list_pos = ANIM_EVENT_UNDEFINED;
12020   int event_value = ANIM_EVENT_DEFAULT;
12021
12022   if (string_has_parameter(s, "any"))
12023     event_value |= ANIM_EVENT_ANY;
12024
12025   if (string_has_parameter(s, "click:self") ||
12026       string_has_parameter(s, "click") ||
12027       string_has_parameter(s, "self"))
12028     event_value |= ANIM_EVENT_SELF;
12029
12030   if (string_has_parameter(s, "unclick:any"))
12031     event_value |= ANIM_EVENT_UNCLICK_ANY;
12032
12033   // if animation event found, add it to global animation event list
12034   if (event_value != ANIM_EVENT_NONE)
12035     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12036
12037   while (s != NULL)
12038   {
12039     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12040     event_value = get_anim_parameter_value(s);
12041
12042     // if animation event found, add it to global animation event list
12043     if (event_value != ANIM_EVENT_NONE)
12044       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12045
12046     // continue with next part of the string, starting with next comma
12047     s = strchr(s + 1, ',');
12048   }
12049
12050   return list_pos;
12051 }
12052
12053 static int get_anim_action_parameter_value(char *token)
12054 {
12055   // check most common default case first to massively speed things up
12056   if (strEqual(token, ARG_UNDEFINED))
12057     return ANIM_EVENT_ACTION_NONE;
12058
12059   int result = getImageIDFromToken(token);
12060
12061   if (result == -1)
12062   {
12063     char *gfx_token = getStringCat2("gfx.", token);
12064
12065     result = getImageIDFromToken(gfx_token);
12066
12067     checked_free(gfx_token);
12068   }
12069
12070   if (result == -1)
12071   {
12072     Key key = getKeyFromX11KeyName(token);
12073
12074     if (key != KSYM_UNDEFINED)
12075       result = -(int)key;
12076   }
12077
12078   if (result == -1)
12079     result = ANIM_EVENT_ACTION_NONE;
12080
12081   return result;
12082 }
12083
12084 int get_parameter_value(char *value_raw, char *suffix, int type)
12085 {
12086   char *value = getStringToLower(value_raw);
12087   int result = 0;       // probably a save default value
12088
12089   if (strEqual(suffix, ".direction"))
12090   {
12091     result = (strEqual(value, "left")  ? MV_LEFT :
12092               strEqual(value, "right") ? MV_RIGHT :
12093               strEqual(value, "up")    ? MV_UP :
12094               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12095   }
12096   else if (strEqual(suffix, ".position"))
12097   {
12098     result = (strEqual(value, "left")   ? POS_LEFT :
12099               strEqual(value, "right")  ? POS_RIGHT :
12100               strEqual(value, "top")    ? POS_TOP :
12101               strEqual(value, "upper")  ? POS_UPPER :
12102               strEqual(value, "middle") ? POS_MIDDLE :
12103               strEqual(value, "lower")  ? POS_LOWER :
12104               strEqual(value, "bottom") ? POS_BOTTOM :
12105               strEqual(value, "any")    ? POS_ANY :
12106               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12107   }
12108   else if (strEqual(suffix, ".align"))
12109   {
12110     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12111               strEqual(value, "right")  ? ALIGN_RIGHT :
12112               strEqual(value, "center") ? ALIGN_CENTER :
12113               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12114   }
12115   else if (strEqual(suffix, ".valign"))
12116   {
12117     result = (strEqual(value, "top")    ? VALIGN_TOP :
12118               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12119               strEqual(value, "middle") ? VALIGN_MIDDLE :
12120               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12121   }
12122   else if (strEqual(suffix, ".anim_mode"))
12123   {
12124     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12125               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12126               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12127               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12128               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12129               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12130               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12131               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12132               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12133               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12134               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12135               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12136               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12137               string_has_parameter(value, "all")        ? ANIM_ALL :
12138               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12139               ANIM_DEFAULT);
12140
12141     if (string_has_parameter(value, "once"))
12142       result |= ANIM_ONCE;
12143
12144     if (string_has_parameter(value, "reverse"))
12145       result |= ANIM_REVERSE;
12146
12147     if (string_has_parameter(value, "opaque_player"))
12148       result |= ANIM_OPAQUE_PLAYER;
12149
12150     if (string_has_parameter(value, "static_panel"))
12151       result |= ANIM_STATIC_PANEL;
12152   }
12153   else if (strEqual(suffix, ".init_event") ||
12154            strEqual(suffix, ".anim_event"))
12155   {
12156     result = get_anim_parameter_values(value);
12157   }
12158   else if (strEqual(suffix, ".init_delay_action") ||
12159            strEqual(suffix, ".anim_delay_action") ||
12160            strEqual(suffix, ".post_delay_action") ||
12161            strEqual(suffix, ".init_event_action") ||
12162            strEqual(suffix, ".anim_event_action"))
12163   {
12164     result = get_anim_action_parameter_value(value_raw);
12165   }
12166   else if (strEqual(suffix, ".class"))
12167   {
12168     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12169               get_hash_from_key(value));
12170   }
12171   else if (strEqual(suffix, ".style"))
12172   {
12173     result = STYLE_DEFAULT;
12174
12175     if (string_has_parameter(value, "accurate_borders"))
12176       result |= STYLE_ACCURATE_BORDERS;
12177
12178     if (string_has_parameter(value, "inner_corners"))
12179       result |= STYLE_INNER_CORNERS;
12180
12181     if (string_has_parameter(value, "reverse"))
12182       result |= STYLE_REVERSE;
12183
12184     if (string_has_parameter(value, "leftmost_position"))
12185       result |= STYLE_LEFTMOST_POSITION;
12186
12187     if (string_has_parameter(value, "block_clicks"))
12188       result |= STYLE_BLOCK;
12189
12190     if (string_has_parameter(value, "passthrough_clicks"))
12191       result |= STYLE_PASSTHROUGH;
12192
12193     if (string_has_parameter(value, "multiple_actions"))
12194       result |= STYLE_MULTIPLE_ACTIONS;
12195   }
12196   else if (strEqual(suffix, ".fade_mode"))
12197   {
12198     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12199               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12200               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12201               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12202               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12203               FADE_MODE_DEFAULT);
12204   }
12205   else if (strEqual(suffix, ".auto_delay_unit"))
12206   {
12207     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12208               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12209               AUTO_DELAY_UNIT_DEFAULT);
12210   }
12211   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12212   {
12213     result = gfx.get_font_from_token_function(value);
12214   }
12215   else          // generic parameter of type integer or boolean
12216   {
12217     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12218               type == TYPE_INTEGER ? get_integer_from_string(value) :
12219               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12220               ARG_UNDEFINED_VALUE);
12221   }
12222
12223   free(value);
12224
12225   return result;
12226 }
12227
12228 static int get_token_parameter_value(char *token, char *value_raw)
12229 {
12230   char *suffix;
12231
12232   if (token == NULL || value_raw == NULL)
12233     return ARG_UNDEFINED_VALUE;
12234
12235   suffix = strrchr(token, '.');
12236   if (suffix == NULL)
12237     suffix = token;
12238
12239   if (strEqual(suffix, ".element"))
12240     return getElementFromToken(value_raw);
12241
12242   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12243   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12244 }
12245
12246 void InitMenuDesignSettings_Static(void)
12247 {
12248   int i;
12249
12250   // always start with reliable default values from static default config
12251   for (i = 0; image_config_vars[i].token != NULL; i++)
12252   {
12253     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
12254
12255     if (value != NULL)
12256       *image_config_vars[i].value =
12257         get_token_parameter_value(image_config_vars[i].token, value);
12258   }
12259 }
12260
12261 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12262 {
12263   int i;
12264
12265   // the following initializes hierarchical values from static configuration
12266
12267   // special case: initialize "ARG_DEFAULT" values in static default config
12268   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12269   titlescreen_initial_first_default.fade_mode  =
12270     title_initial_first_default.fade_mode;
12271   titlescreen_initial_first_default.fade_delay =
12272     title_initial_first_default.fade_delay;
12273   titlescreen_initial_first_default.post_delay =
12274     title_initial_first_default.post_delay;
12275   titlescreen_initial_first_default.auto_delay =
12276     title_initial_first_default.auto_delay;
12277   titlescreen_initial_first_default.auto_delay_unit =
12278     title_initial_first_default.auto_delay_unit;
12279   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
12280   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12281   titlescreen_first_default.post_delay = title_first_default.post_delay;
12282   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12283   titlescreen_first_default.auto_delay_unit =
12284     title_first_default.auto_delay_unit;
12285   titlemessage_initial_first_default.fade_mode  =
12286     title_initial_first_default.fade_mode;
12287   titlemessage_initial_first_default.fade_delay =
12288     title_initial_first_default.fade_delay;
12289   titlemessage_initial_first_default.post_delay =
12290     title_initial_first_default.post_delay;
12291   titlemessage_initial_first_default.auto_delay =
12292     title_initial_first_default.auto_delay;
12293   titlemessage_initial_first_default.auto_delay_unit =
12294     title_initial_first_default.auto_delay_unit;
12295   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
12296   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12297   titlemessage_first_default.post_delay = title_first_default.post_delay;
12298   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12299   titlemessage_first_default.auto_delay_unit =
12300     title_first_default.auto_delay_unit;
12301
12302   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
12303   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12304   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12305   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12306   titlescreen_initial_default.auto_delay_unit =
12307     title_initial_default.auto_delay_unit;
12308   titlescreen_default.fade_mode  = title_default.fade_mode;
12309   titlescreen_default.fade_delay = title_default.fade_delay;
12310   titlescreen_default.post_delay = title_default.post_delay;
12311   titlescreen_default.auto_delay = title_default.auto_delay;
12312   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12313   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
12314   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12315   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12316   titlemessage_initial_default.auto_delay_unit =
12317     title_initial_default.auto_delay_unit;
12318   titlemessage_default.fade_mode  = title_default.fade_mode;
12319   titlemessage_default.fade_delay = title_default.fade_delay;
12320   titlemessage_default.post_delay = title_default.post_delay;
12321   titlemessage_default.auto_delay = title_default.auto_delay;
12322   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12323
12324   // special case: initialize "ARG_DEFAULT" values in static default config
12325   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12326   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12327   {
12328     titlescreen_initial_first[i] = titlescreen_initial_first_default;
12329     titlescreen_first[i] = titlescreen_first_default;
12330     titlemessage_initial_first[i] = titlemessage_initial_first_default;
12331     titlemessage_first[i] = titlemessage_first_default;
12332
12333     titlescreen_initial[i] = titlescreen_initial_default;
12334     titlescreen[i] = titlescreen_default;
12335     titlemessage_initial[i] = titlemessage_initial_default;
12336     titlemessage[i] = titlemessage_default;
12337   }
12338
12339   // special case: initialize "ARG_DEFAULT" values in static default config
12340   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12341   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12342   {
12343     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
12344       continue;
12345
12346     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12347     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12348     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12349   }
12350
12351   // special case: initialize "ARG_DEFAULT" values in static default config
12352   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12353   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12354   {
12355     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12356     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12357     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12358
12359     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
12360       continue;
12361
12362     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12363   }
12364 }
12365
12366 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12367 {
12368   static struct
12369   {
12370     struct XY *dst, *src;
12371   }
12372   game_buttons_xy[] =
12373   {
12374     { &game.button.save,        &game.button.stop       },
12375     { &game.button.pause2,      &game.button.pause      },
12376     { &game.button.load,        &game.button.play       },
12377     { &game.button.undo,        &game.button.stop       },
12378     { &game.button.redo,        &game.button.play       },
12379
12380     { NULL,                     NULL                    }
12381   };
12382   int i, j;
12383
12384   // special case: initialize later added SETUP list size from LEVELS value
12385   if (menu.list_size[GAME_MODE_SETUP] == -1)
12386     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12387
12388   // set default position for snapshot buttons to stop/pause/play buttons
12389   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12390     if ((*game_buttons_xy[i].dst).x == -1 &&
12391         (*game_buttons_xy[i].dst).y == -1)
12392       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12393
12394   // --------------------------------------------------------------------------
12395   // dynamic viewports (including playfield margins, borders and alignments)
12396   // --------------------------------------------------------------------------
12397
12398   // dynamic viewports currently only supported for landscape mode
12399   int display_width  = MAX(video.display_width, video.display_height);
12400   int display_height = MIN(video.display_width, video.display_height);
12401
12402   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12403   {
12404     struct RectWithBorder *vp_window    = &viewport.window[i];
12405     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12406     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
12407     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
12408     boolean dynamic_window_width     = (vp_window->min_width     != -1);
12409     boolean dynamic_window_height    = (vp_window->min_height    != -1);
12410     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
12411     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12412
12413     // adjust window size if min/max width/height is specified
12414
12415     if (vp_window->min_width != -1)
12416     {
12417       int window_width = display_width;
12418
12419       // when using static window height, use aspect ratio of display
12420       if (vp_window->min_height == -1)
12421         window_width = vp_window->height * display_width / display_height;
12422
12423       vp_window->width = MAX(vp_window->min_width, window_width);
12424     }
12425
12426     if (vp_window->min_height != -1)
12427     {
12428       int window_height = display_height;
12429
12430       // when using static window width, use aspect ratio of display
12431       if (vp_window->min_width == -1)
12432         window_height = vp_window->width * display_height / display_width;
12433
12434       vp_window->height = MAX(vp_window->min_height, window_height);
12435     }
12436
12437     if (vp_window->max_width != -1)
12438       vp_window->width = MIN(vp_window->width, vp_window->max_width);
12439
12440     if (vp_window->max_height != -1)
12441       vp_window->height = MIN(vp_window->height, vp_window->max_height);
12442
12443     int playfield_width  = vp_window->width;
12444     int playfield_height = vp_window->height;
12445
12446     // adjust playfield size and position according to specified margins
12447
12448     playfield_width  -= vp_playfield->margin_left;
12449     playfield_width  -= vp_playfield->margin_right;
12450
12451     playfield_height -= vp_playfield->margin_top;
12452     playfield_height -= vp_playfield->margin_bottom;
12453
12454     // adjust playfield size if min/max width/height is specified
12455
12456     if (vp_playfield->min_width != -1)
12457       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12458
12459     if (vp_playfield->min_height != -1)
12460       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12461
12462     if (vp_playfield->max_width != -1)
12463       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12464
12465     if (vp_playfield->max_height != -1)
12466       vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12467
12468     // adjust playfield position according to specified alignment
12469
12470     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12471       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12472     else if (vp_playfield->align == ALIGN_CENTER)
12473       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12474     else if (vp_playfield->align == ALIGN_RIGHT)
12475       vp_playfield->x += playfield_width - vp_playfield->width;
12476
12477     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12478       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12479     else if (vp_playfield->valign == VALIGN_MIDDLE)
12480       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12481     else if (vp_playfield->valign == VALIGN_BOTTOM)
12482       vp_playfield->y += playfield_height - vp_playfield->height;
12483
12484     vp_playfield->x += vp_playfield->margin_left;
12485     vp_playfield->y += vp_playfield->margin_top;
12486
12487     // adjust individual playfield borders if only default border is specified
12488
12489     if (vp_playfield->border_left == -1)
12490       vp_playfield->border_left = vp_playfield->border_size;
12491     if (vp_playfield->border_right == -1)
12492       vp_playfield->border_right = vp_playfield->border_size;
12493     if (vp_playfield->border_top == -1)
12494       vp_playfield->border_top = vp_playfield->border_size;
12495     if (vp_playfield->border_bottom == -1)
12496       vp_playfield->border_bottom = vp_playfield->border_size;
12497
12498     // set dynamic playfield borders if borders are specified as undefined
12499     // (but only if window size was dynamic and playfield size was static)
12500
12501     if (dynamic_window_width && !dynamic_playfield_width)
12502     {
12503       if (vp_playfield->border_left == -1)
12504       {
12505         vp_playfield->border_left = (vp_playfield->x -
12506                                      vp_playfield->margin_left);
12507         vp_playfield->x     -= vp_playfield->border_left;
12508         vp_playfield->width += vp_playfield->border_left;
12509       }
12510
12511       if (vp_playfield->border_right == -1)
12512       {
12513         vp_playfield->border_right = (vp_window->width -
12514                                       vp_playfield->x -
12515                                       vp_playfield->width -
12516                                       vp_playfield->margin_right);
12517         vp_playfield->width += vp_playfield->border_right;
12518       }
12519     }
12520
12521     if (dynamic_window_height && !dynamic_playfield_height)
12522     {
12523       if (vp_playfield->border_top == -1)
12524       {
12525         vp_playfield->border_top = (vp_playfield->y -
12526                                     vp_playfield->margin_top);
12527         vp_playfield->y      -= vp_playfield->border_top;
12528         vp_playfield->height += vp_playfield->border_top;
12529       }
12530
12531       if (vp_playfield->border_bottom == -1)
12532       {
12533         vp_playfield->border_bottom = (vp_window->height -
12534                                        vp_playfield->y -
12535                                        vp_playfield->height -
12536                                        vp_playfield->margin_bottom);
12537         vp_playfield->height += vp_playfield->border_bottom;
12538       }
12539     }
12540
12541     // adjust playfield size to be a multiple of a defined alignment tile size
12542
12543     int align_size = vp_playfield->align_size;
12544     int playfield_xtiles = vp_playfield->width  / align_size;
12545     int playfield_ytiles = vp_playfield->height / align_size;
12546     int playfield_width_corrected  = playfield_xtiles * align_size;
12547     int playfield_height_corrected = playfield_ytiles * align_size;
12548     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12549                                  i == GFX_SPECIAL_ARG_EDITOR);
12550
12551     if (is_playfield_mode &&
12552         dynamic_playfield_width &&
12553         vp_playfield->width != playfield_width_corrected)
12554     {
12555       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12556
12557       vp_playfield->width = playfield_width_corrected;
12558
12559       if (vp_playfield->align == ALIGN_LEFT)
12560       {
12561         vp_playfield->border_left += playfield_xdiff;
12562       }
12563       else if (vp_playfield->align == ALIGN_RIGHT)
12564       {
12565         vp_playfield->border_right += playfield_xdiff;
12566       }
12567       else if (vp_playfield->align == ALIGN_CENTER)
12568       {
12569         int border_left_diff  = playfield_xdiff / 2;
12570         int border_right_diff = playfield_xdiff - border_left_diff;
12571
12572         vp_playfield->border_left  += border_left_diff;
12573         vp_playfield->border_right += border_right_diff;
12574       }
12575     }
12576
12577     if (is_playfield_mode &&
12578         dynamic_playfield_height &&
12579         vp_playfield->height != playfield_height_corrected)
12580     {
12581       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12582
12583       vp_playfield->height = playfield_height_corrected;
12584
12585       if (vp_playfield->valign == VALIGN_TOP)
12586       {
12587         vp_playfield->border_top += playfield_ydiff;
12588       }
12589       else if (vp_playfield->align == VALIGN_BOTTOM)
12590       {
12591         vp_playfield->border_right += playfield_ydiff;
12592       }
12593       else if (vp_playfield->align == VALIGN_MIDDLE)
12594       {
12595         int border_top_diff    = playfield_ydiff / 2;
12596         int border_bottom_diff = playfield_ydiff - border_top_diff;
12597
12598         vp_playfield->border_top    += border_top_diff;
12599         vp_playfield->border_bottom += border_bottom_diff;
12600       }
12601     }
12602
12603     // adjust door positions according to specified alignment
12604
12605     for (j = 0; j < 2; j++)
12606     {
12607       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12608
12609       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12610         vp_door->x = ALIGNED_VP_XPOS(vp_door);
12611       else if (vp_door->align == ALIGN_CENTER)
12612         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12613       else if (vp_door->align == ALIGN_RIGHT)
12614         vp_door->x += vp_window->width - vp_door->width;
12615
12616       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12617         vp_door->y = ALIGNED_VP_YPOS(vp_door);
12618       else if (vp_door->valign == VALIGN_MIDDLE)
12619         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12620       else if (vp_door->valign == VALIGN_BOTTOM)
12621         vp_door->y += vp_window->height - vp_door->height;
12622     }
12623   }
12624 }
12625
12626 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12627 {
12628   static struct
12629   {
12630     struct XYTileSize *dst, *src;
12631     int graphic;
12632   }
12633   editor_buttons_xy[] =
12634   {
12635     {
12636       &editor.button.element_left,      &editor.palette.element_left,
12637       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12638     },
12639     {
12640       &editor.button.element_middle,    &editor.palette.element_middle,
12641       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12642     },
12643     {
12644       &editor.button.element_right,     &editor.palette.element_right,
12645       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12646     },
12647
12648     { NULL,                     NULL                    }
12649   };
12650   int i;
12651
12652   // set default position for element buttons to element graphics
12653   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12654   {
12655     if ((*editor_buttons_xy[i].dst).x == -1 &&
12656         (*editor_buttons_xy[i].dst).y == -1)
12657     {
12658       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12659
12660       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12661
12662       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12663     }
12664   }
12665
12666   // adjust editor palette rows and columns if specified to be dynamic
12667
12668   if (editor.palette.cols == -1)
12669   {
12670     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12671     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12672     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12673
12674     editor.palette.cols = (vp_width - sc_width) / bt_width;
12675
12676     if (editor.palette.x == -1)
12677     {
12678       int palette_width = editor.palette.cols * bt_width + sc_width;
12679
12680       editor.palette.x = (vp_width - palette_width) / 2;
12681     }
12682   }
12683
12684   if (editor.palette.rows == -1)
12685   {
12686     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12687     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12688     int tx_height = getFontHeight(FONT_TEXT_2);
12689
12690     editor.palette.rows = (vp_height - tx_height) / bt_height;
12691
12692     if (editor.palette.y == -1)
12693     {
12694       int palette_height = editor.palette.rows * bt_height + tx_height;
12695
12696       editor.palette.y = (vp_height - palette_height) / 2;
12697     }
12698   }
12699 }
12700
12701 static void LoadMenuDesignSettingsFromFilename(char *filename)
12702 {
12703   static struct TitleFadingInfo tfi;
12704   static struct TitleMessageInfo tmi;
12705   static struct TokenInfo title_tokens[] =
12706   {
12707     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
12708     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
12709     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
12710     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
12711     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
12712
12713     { -1,               NULL,                   NULL                    }
12714   };
12715   static struct TokenInfo titlemessage_tokens[] =
12716   {
12717     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
12718     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
12719     { TYPE_INTEGER,     &tmi.width,             ".width"                },
12720     { TYPE_INTEGER,     &tmi.height,            ".height"               },
12721     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
12722     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
12723     { TYPE_INTEGER,     &tmi.align,             ".align"                },
12724     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
12725     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
12726     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
12727     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
12728     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
12729     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
12730     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
12731     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
12732     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
12733     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
12734     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
12735
12736     { -1,               NULL,                   NULL                    }
12737   };
12738   static struct
12739   {
12740     struct TitleFadingInfo *info;
12741     char *text;
12742   }
12743   title_info[] =
12744   {
12745     // initialize first titles from "enter screen" definitions, if defined
12746     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
12747     { &title_first_default,             "menu.enter_screen.TITLE"       },
12748
12749     // initialize title screens from "next screen" definitions, if defined
12750     { &title_initial_default,           "menu.next_screen.TITLE"        },
12751     { &title_default,                   "menu.next_screen.TITLE"        },
12752
12753     { NULL,                             NULL                            }
12754   };
12755   static struct
12756   {
12757     struct TitleMessageInfo *array;
12758     char *text;
12759   }
12760   titlemessage_arrays[] =
12761   {
12762     // initialize first titles from "enter screen" definitions, if defined
12763     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
12764     { titlescreen_first,                "menu.enter_screen.TITLE"       },
12765     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
12766     { titlemessage_first,               "menu.enter_screen.TITLE"       },
12767
12768     // initialize titles from "next screen" definitions, if defined
12769     { titlescreen_initial,              "menu.next_screen.TITLE"        },
12770     { titlescreen,                      "menu.next_screen.TITLE"        },
12771     { titlemessage_initial,             "menu.next_screen.TITLE"        },
12772     { titlemessage,                     "menu.next_screen.TITLE"        },
12773
12774     // overwrite titles with title definitions, if defined
12775     { titlescreen_initial_first,        "[title_initial]"               },
12776     { titlescreen_first,                "[title]"                       },
12777     { titlemessage_initial_first,       "[title_initial]"               },
12778     { titlemessage_first,               "[title]"                       },
12779
12780     { titlescreen_initial,              "[title_initial]"               },
12781     { titlescreen,                      "[title]"                       },
12782     { titlemessage_initial,             "[title_initial]"               },
12783     { titlemessage,                     "[title]"                       },
12784
12785     // overwrite titles with title screen/message definitions, if defined
12786     { titlescreen_initial_first,        "[titlescreen_initial]"         },
12787     { titlescreen_first,                "[titlescreen]"                 },
12788     { titlemessage_initial_first,       "[titlemessage_initial]"        },
12789     { titlemessage_first,               "[titlemessage]"                },
12790
12791     { titlescreen_initial,              "[titlescreen_initial]"         },
12792     { titlescreen,                      "[titlescreen]"                 },
12793     { titlemessage_initial,             "[titlemessage_initial]"        },
12794     { titlemessage,                     "[titlemessage]"                },
12795
12796     { NULL,                             NULL                            }
12797   };
12798   SetupFileHash *setup_file_hash;
12799   int i, j, k;
12800
12801   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12802     return;
12803
12804   // the following initializes hierarchical values from dynamic configuration
12805
12806   // special case: initialize with default values that may be overwritten
12807   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12808   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12809   {
12810     struct TokenIntPtrInfo menu_config[] =
12811     {
12812       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
12813       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
12814       { "menu.list_size",       &menu.list_size[i]      }
12815     };
12816
12817     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12818     {
12819       char *token = menu_config[j].token;
12820       char *value = getHashEntry(setup_file_hash, token);
12821
12822       if (value != NULL)
12823         *menu_config[j].value = get_integer_from_string(value);
12824     }
12825   }
12826
12827   // special case: initialize with default values that may be overwritten
12828   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12829   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12830   {
12831     struct TokenIntPtrInfo menu_config[] =
12832     {
12833       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
12834       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
12835       { "menu.list_size.INFO",          &menu.list_size_info[i]         }
12836     };
12837
12838     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12839     {
12840       char *token = menu_config[j].token;
12841       char *value = getHashEntry(setup_file_hash, token);
12842
12843       if (value != NULL)
12844         *menu_config[j].value = get_integer_from_string(value);
12845     }
12846   }
12847
12848   // special case: initialize with default values that may be overwritten
12849   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12850   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12851   {
12852     struct TokenIntPtrInfo menu_config[] =
12853     {
12854       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
12855       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
12856     };
12857
12858     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12859     {
12860       char *token = menu_config[j].token;
12861       char *value = getHashEntry(setup_file_hash, token);
12862
12863       if (value != NULL)
12864         *menu_config[j].value = get_integer_from_string(value);
12865     }
12866   }
12867
12868   // special case: initialize with default values that may be overwritten
12869   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12870   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12871   {
12872     struct TokenIntPtrInfo menu_config[] =
12873     {
12874       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
12875       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
12876       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
12877       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
12878       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
12879       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
12880       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
12881       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
12882       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
12883     };
12884
12885     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12886     {
12887       char *token = menu_config[j].token;
12888       char *value = getHashEntry(setup_file_hash, token);
12889
12890       if (value != NULL)
12891         *menu_config[j].value = get_integer_from_string(value);
12892     }
12893   }
12894
12895   // special case: initialize with default values that may be overwritten
12896   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12897   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12898   {
12899     struct TokenIntPtrInfo menu_config[] =
12900     {
12901       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
12902       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12903       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12904       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
12905       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12906       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12907       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
12908       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
12909       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
12910     };
12911
12912     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12913     {
12914       char *token = menu_config[j].token;
12915       char *value = getHashEntry(setup_file_hash, token);
12916
12917       if (value != NULL)
12918         *menu_config[j].value = get_token_parameter_value(token, value);
12919     }
12920   }
12921
12922   // special case: initialize with default values that may be overwritten
12923   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12924   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12925   {
12926     struct
12927     {
12928       char *token_prefix;
12929       struct RectWithBorder *struct_ptr;
12930     }
12931     vp_struct[] =
12932     {
12933       { "viewport.window",      &viewport.window[i]     },
12934       { "viewport.playfield",   &viewport.playfield[i]  },
12935       { "viewport.door_1",      &viewport.door_1[i]     },
12936       { "viewport.door_2",      &viewport.door_2[i]     }
12937     };
12938
12939     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12940     {
12941       struct TokenIntPtrInfo vp_config[] =
12942       {
12943         { ".x",                 &vp_struct[j].struct_ptr->x             },
12944         { ".y",                 &vp_struct[j].struct_ptr->y             },
12945         { ".width",             &vp_struct[j].struct_ptr->width         },
12946         { ".height",            &vp_struct[j].struct_ptr->height        },
12947         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
12948         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
12949         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
12950         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
12951         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
12952         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
12953         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
12954         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
12955         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
12956         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
12957         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
12958         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
12959         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
12960         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
12961         { ".align",             &vp_struct[j].struct_ptr->align         },
12962         { ".valign",            &vp_struct[j].struct_ptr->valign        }
12963       };
12964
12965       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12966       {
12967         char *token = getStringCat2(vp_struct[j].token_prefix,
12968                                     vp_config[k].token);
12969         char *value = getHashEntry(setup_file_hash, token);
12970
12971         if (value != NULL)
12972           *vp_config[k].value = get_token_parameter_value(token, value);
12973
12974         free(token);
12975       }
12976     }
12977   }
12978
12979   // special case: initialize with default values that may be overwritten
12980   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12981   for (i = 0; title_info[i].info != NULL; i++)
12982   {
12983     struct TitleFadingInfo *info = title_info[i].info;
12984     char *base_token = title_info[i].text;
12985
12986     for (j = 0; title_tokens[j].type != -1; j++)
12987     {
12988       char *token = getStringCat2(base_token, title_tokens[j].text);
12989       char *value = getHashEntry(setup_file_hash, token);
12990
12991       if (value != NULL)
12992       {
12993         int parameter_value = get_token_parameter_value(token, value);
12994
12995         tfi = *info;
12996
12997         *(int *)title_tokens[j].value = (int)parameter_value;
12998
12999         *info = tfi;
13000       }
13001
13002       free(token);
13003     }
13004   }
13005
13006   // special case: initialize with default values that may be overwritten
13007   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13008   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13009   {
13010     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13011     char *base_token = titlemessage_arrays[i].text;
13012
13013     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13014     {
13015       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13016       char *value = getHashEntry(setup_file_hash, token);
13017
13018       if (value != NULL)
13019       {
13020         int parameter_value = get_token_parameter_value(token, value);
13021
13022         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13023         {
13024           tmi = array[k];
13025
13026           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13027             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13028           else
13029             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13030
13031           array[k] = tmi;
13032         }
13033       }
13034
13035       free(token);
13036     }
13037   }
13038
13039   // special case: check if network and preview player positions are redefined,
13040   // to compare this later against the main menu level preview being redefined
13041   struct TokenIntPtrInfo menu_config_players[] =
13042   {
13043     { "main.network_players.x", &menu.main.network_players.redefined    },
13044     { "main.network_players.y", &menu.main.network_players.redefined    },
13045     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13046     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13047     { "preview.x",              &preview.redefined                      },
13048     { "preview.y",              &preview.redefined                      }
13049   };
13050
13051   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13052     *menu_config_players[i].value = FALSE;
13053
13054   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13055     if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
13056       *menu_config_players[i].value = TRUE;
13057
13058   // read (and overwrite with) values that may be specified in config file
13059   for (i = 0; image_config_vars[i].token != NULL; i++)
13060   {
13061     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13062
13063     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13064     if (value != NULL && !strEqual(value, ARG_DEFAULT))
13065       *image_config_vars[i].value =
13066         get_token_parameter_value(image_config_vars[i].token, value);
13067   }
13068
13069   freeSetupFileHash(setup_file_hash);
13070 }
13071
13072 void LoadMenuDesignSettings(void)
13073 {
13074   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13075
13076   InitMenuDesignSettings_Static();
13077   InitMenuDesignSettings_SpecialPreProcessing();
13078
13079   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13080   {
13081     // first look for special settings configured in level series config
13082     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13083
13084     if (fileExists(filename_base))
13085       LoadMenuDesignSettingsFromFilename(filename_base);
13086   }
13087
13088   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13089
13090   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13091     LoadMenuDesignSettingsFromFilename(filename_local);
13092
13093   InitMenuDesignSettings_SpecialPostProcessing();
13094 }
13095
13096 void LoadMenuDesignSettings_AfterGraphics(void)
13097 {
13098   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13099 }
13100
13101 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13102 {
13103   char *filename = getEditorSetupFilename();
13104   SetupFileList *setup_file_list, *list;
13105   SetupFileHash *element_hash;
13106   int num_unknown_tokens = 0;
13107   int i;
13108
13109   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13110     return;
13111
13112   element_hash = newSetupFileHash();
13113
13114   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13115     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13116
13117   // determined size may be larger than needed (due to unknown elements)
13118   *num_elements = 0;
13119   for (list = setup_file_list; list != NULL; list = list->next)
13120     (*num_elements)++;
13121
13122   // add space for up to 3 more elements for padding that may be needed
13123   *num_elements += 3;
13124
13125   // free memory for old list of elements, if needed
13126   checked_free(*elements);
13127
13128   // allocate memory for new list of elements
13129   *elements = checked_malloc(*num_elements * sizeof(int));
13130
13131   *num_elements = 0;
13132   for (list = setup_file_list; list != NULL; list = list->next)
13133   {
13134     char *value = getHashEntry(element_hash, list->token);
13135
13136     if (value == NULL)          // try to find obsolete token mapping
13137     {
13138       char *mapped_token = get_mapped_token(list->token);
13139
13140       if (mapped_token != NULL)
13141       {
13142         value = getHashEntry(element_hash, mapped_token);
13143
13144         free(mapped_token);
13145       }
13146     }
13147
13148     if (value != NULL)
13149     {
13150       (*elements)[(*num_elements)++] = atoi(value);
13151     }
13152     else
13153     {
13154       if (num_unknown_tokens == 0)
13155       {
13156         Warn("---");
13157         Warn("unknown token(s) found in config file:");
13158         Warn("- config file: '%s'", filename);
13159
13160         num_unknown_tokens++;
13161       }
13162
13163       Warn("- token: '%s'", list->token);
13164     }
13165   }
13166
13167   if (num_unknown_tokens > 0)
13168     Warn("---");
13169
13170   while (*num_elements % 4)     // pad with empty elements, if needed
13171     (*elements)[(*num_elements)++] = EL_EMPTY;
13172
13173   freeSetupFileList(setup_file_list);
13174   freeSetupFileHash(element_hash);
13175
13176 #if 0
13177   for (i = 0; i < *num_elements; i++)
13178     Debug("editor", "element '%s' [%d]\n",
13179           element_info[(*elements)[i]].token_name, (*elements)[i]);
13180 #endif
13181 }
13182
13183 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13184                                                      boolean is_sound)
13185 {
13186   SetupFileHash *setup_file_hash = NULL;
13187   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13188   char *filename_music, *filename_prefix, *filename_info;
13189   struct
13190   {
13191     char *token;
13192     char **value_ptr;
13193   }
13194   token_to_value_ptr[] =
13195   {
13196     { "title_header",   &tmp_music_file_info.title_header       },
13197     { "artist_header",  &tmp_music_file_info.artist_header      },
13198     { "album_header",   &tmp_music_file_info.album_header       },
13199     { "year_header",    &tmp_music_file_info.year_header        },
13200
13201     { "title",          &tmp_music_file_info.title              },
13202     { "artist",         &tmp_music_file_info.artist             },
13203     { "album",          &tmp_music_file_info.album              },
13204     { "year",           &tmp_music_file_info.year               },
13205
13206     { NULL,             NULL                                    },
13207   };
13208   int i;
13209
13210   filename_music = (is_sound ? getCustomSoundFilename(basename) :
13211                     getCustomMusicFilename(basename));
13212
13213   if (filename_music == NULL)
13214     return NULL;
13215
13216   // ---------- try to replace file extension ----------
13217
13218   filename_prefix = getStringCopy(filename_music);
13219   if (strrchr(filename_prefix, '.') != NULL)
13220     *strrchr(filename_prefix, '.') = '\0';
13221   filename_info = getStringCat2(filename_prefix, ".txt");
13222
13223   if (fileExists(filename_info))
13224     setup_file_hash = loadSetupFileHash(filename_info);
13225
13226   free(filename_prefix);
13227   free(filename_info);
13228
13229   if (setup_file_hash == NULL)
13230   {
13231     // ---------- try to add file extension ----------
13232
13233     filename_prefix = getStringCopy(filename_music);
13234     filename_info = getStringCat2(filename_prefix, ".txt");
13235
13236     if (fileExists(filename_info))
13237       setup_file_hash = loadSetupFileHash(filename_info);
13238
13239     free(filename_prefix);
13240     free(filename_info);
13241   }
13242
13243   if (setup_file_hash == NULL)
13244     return NULL;
13245
13246   // ---------- music file info found ----------
13247
13248   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13249
13250   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13251   {
13252     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13253
13254     *token_to_value_ptr[i].value_ptr =
13255       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13256   }
13257
13258   tmp_music_file_info.basename = getStringCopy(basename);
13259   tmp_music_file_info.music = music;
13260   tmp_music_file_info.is_sound = is_sound;
13261
13262   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13263   *new_music_file_info = tmp_music_file_info;
13264
13265   return new_music_file_info;
13266 }
13267
13268 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13269 {
13270   return get_music_file_info_ext(basename, music, FALSE);
13271 }
13272
13273 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13274 {
13275   return get_music_file_info_ext(basename, sound, TRUE);
13276 }
13277
13278 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13279                                      char *basename, boolean is_sound)
13280 {
13281   for (; list != NULL; list = list->next)
13282     if (list->is_sound == is_sound && strEqual(list->basename, basename))
13283       return TRUE;
13284
13285   return FALSE;
13286 }
13287
13288 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13289 {
13290   return music_info_listed_ext(list, basename, FALSE);
13291 }
13292
13293 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13294 {
13295   return music_info_listed_ext(list, basename, TRUE);
13296 }
13297
13298 void LoadMusicInfo(void)
13299 {
13300   char *music_directory = getCustomMusicDirectory();
13301   int num_music = getMusicListSize();
13302   int num_music_noconf = 0;
13303   int num_sounds = getSoundListSize();
13304   Directory *dir;
13305   DirectoryEntry *dir_entry;
13306   struct FileInfo *music, *sound;
13307   struct MusicFileInfo *next, **new;
13308   int i;
13309
13310   while (music_file_info != NULL)
13311   {
13312     next = music_file_info->next;
13313
13314     checked_free(music_file_info->basename);
13315
13316     checked_free(music_file_info->title_header);
13317     checked_free(music_file_info->artist_header);
13318     checked_free(music_file_info->album_header);
13319     checked_free(music_file_info->year_header);
13320
13321     checked_free(music_file_info->title);
13322     checked_free(music_file_info->artist);
13323     checked_free(music_file_info->album);
13324     checked_free(music_file_info->year);
13325
13326     free(music_file_info);
13327
13328     music_file_info = next;
13329   }
13330
13331   new = &music_file_info;
13332
13333   for (i = 0; i < num_music; i++)
13334   {
13335     music = getMusicListEntry(i);
13336
13337     if (music->filename == NULL)
13338       continue;
13339
13340     if (strEqual(music->filename, UNDEFINED_FILENAME))
13341       continue;
13342
13343     // a configured file may be not recognized as music
13344     if (!FileIsMusic(music->filename))
13345       continue;
13346
13347     if (!music_info_listed(music_file_info, music->filename))
13348     {
13349       *new = get_music_file_info(music->filename, i);
13350
13351       if (*new != NULL)
13352         new = &(*new)->next;
13353     }
13354   }
13355
13356   if ((dir = openDirectory(music_directory)) == NULL)
13357   {
13358     Warn("cannot read music directory '%s'", music_directory);
13359
13360     return;
13361   }
13362
13363   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
13364   {
13365     char *basename = dir_entry->basename;
13366     boolean music_already_used = FALSE;
13367     int i;
13368
13369     // skip all music files that are configured in music config file
13370     for (i = 0; i < num_music; i++)
13371     {
13372       music = getMusicListEntry(i);
13373
13374       if (music->filename == NULL)
13375         continue;
13376
13377       if (strEqual(basename, music->filename))
13378       {
13379         music_already_used = TRUE;
13380         break;
13381       }
13382     }
13383
13384     if (music_already_used)
13385       continue;
13386
13387     if (!FileIsMusic(dir_entry->filename))
13388       continue;
13389
13390     if (!music_info_listed(music_file_info, basename))
13391     {
13392       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
13393
13394       if (*new != NULL)
13395         new = &(*new)->next;
13396     }
13397
13398     num_music_noconf++;
13399   }
13400
13401   closeDirectory(dir);
13402
13403   for (i = 0; i < num_sounds; i++)
13404   {
13405     sound = getSoundListEntry(i);
13406
13407     if (sound->filename == NULL)
13408       continue;
13409
13410     if (strEqual(sound->filename, UNDEFINED_FILENAME))
13411       continue;
13412
13413     // a configured file may be not recognized as sound
13414     if (!FileIsSound(sound->filename))
13415       continue;
13416
13417     if (!sound_info_listed(music_file_info, sound->filename))
13418     {
13419       *new = get_sound_file_info(sound->filename, i);
13420       if (*new != NULL)
13421         new = &(*new)->next;
13422     }
13423   }
13424 }
13425
13426 static void add_helpanim_entry(int element, int action, int direction,
13427                                int delay, int *num_list_entries)
13428 {
13429   struct HelpAnimInfo *new_list_entry;
13430   (*num_list_entries)++;
13431
13432   helpanim_info =
13433     checked_realloc(helpanim_info,
13434                     *num_list_entries * sizeof(struct HelpAnimInfo));
13435   new_list_entry = &helpanim_info[*num_list_entries - 1];
13436
13437   new_list_entry->element = element;
13438   new_list_entry->action = action;
13439   new_list_entry->direction = direction;
13440   new_list_entry->delay = delay;
13441 }
13442
13443 static void print_unknown_token(char *filename, char *token, int token_nr)
13444 {
13445   if (token_nr == 0)
13446   {
13447     Warn("---");
13448     Warn("unknown token(s) found in config file:");
13449     Warn("- config file: '%s'", filename);
13450   }
13451
13452   Warn("- token: '%s'", token);
13453 }
13454
13455 static void print_unknown_token_end(int token_nr)
13456 {
13457   if (token_nr > 0)
13458     Warn("---");
13459 }
13460
13461 void LoadHelpAnimInfo(void)
13462 {
13463   char *filename = getHelpAnimFilename();
13464   SetupFileList *setup_file_list = NULL, *list;
13465   SetupFileHash *element_hash, *action_hash, *direction_hash;
13466   int num_list_entries = 0;
13467   int num_unknown_tokens = 0;
13468   int i;
13469
13470   if (fileExists(filename))
13471     setup_file_list = loadSetupFileList(filename);
13472
13473   if (setup_file_list == NULL)
13474   {
13475     // use reliable default values from static configuration
13476     SetupFileList *insert_ptr;
13477
13478     insert_ptr = setup_file_list =
13479       newSetupFileList(helpanim_config[0].token,
13480                        helpanim_config[0].value);
13481
13482     for (i = 1; helpanim_config[i].token; i++)
13483       insert_ptr = addListEntry(insert_ptr,
13484                                 helpanim_config[i].token,
13485                                 helpanim_config[i].value);
13486   }
13487
13488   element_hash   = newSetupFileHash();
13489   action_hash    = newSetupFileHash();
13490   direction_hash = newSetupFileHash();
13491
13492   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13493     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13494
13495   for (i = 0; i < NUM_ACTIONS; i++)
13496     setHashEntry(action_hash, element_action_info[i].suffix,
13497                  i_to_a(element_action_info[i].value));
13498
13499   // do not store direction index (bit) here, but direction value!
13500   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13501     setHashEntry(direction_hash, element_direction_info[i].suffix,
13502                  i_to_a(1 << element_direction_info[i].value));
13503
13504   for (list = setup_file_list; list != NULL; list = list->next)
13505   {
13506     char *element_token, *action_token, *direction_token;
13507     char *element_value, *action_value, *direction_value;
13508     int delay = atoi(list->value);
13509
13510     if (strEqual(list->token, "end"))
13511     {
13512       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13513
13514       continue;
13515     }
13516
13517     /* first try to break element into element/action/direction parts;
13518        if this does not work, also accept combined "element[.act][.dir]"
13519        elements (like "dynamite.active"), which are unique elements */
13520
13521     if (strchr(list->token, '.') == NULL)       // token contains no '.'
13522     {
13523       element_value = getHashEntry(element_hash, list->token);
13524       if (element_value != NULL)        // element found
13525         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13526                            &num_list_entries);
13527       else
13528       {
13529         // no further suffixes found -- this is not an element
13530         print_unknown_token(filename, list->token, num_unknown_tokens++);
13531       }
13532
13533       continue;
13534     }
13535
13536     // token has format "<prefix>.<something>"
13537
13538     action_token = strchr(list->token, '.');    // suffix may be action ...
13539     direction_token = action_token;             // ... or direction
13540
13541     element_token = getStringCopy(list->token);
13542     *strchr(element_token, '.') = '\0';
13543
13544     element_value = getHashEntry(element_hash, element_token);
13545
13546     if (element_value == NULL)          // this is no element
13547     {
13548       element_value = getHashEntry(element_hash, list->token);
13549       if (element_value != NULL)        // combined element found
13550         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13551                            &num_list_entries);
13552       else
13553         print_unknown_token(filename, list->token, num_unknown_tokens++);
13554
13555       free(element_token);
13556
13557       continue;
13558     }
13559
13560     action_value = getHashEntry(action_hash, action_token);
13561
13562     if (action_value != NULL)           // action found
13563     {
13564       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13565                     &num_list_entries);
13566
13567       free(element_token);
13568
13569       continue;
13570     }
13571
13572     direction_value = getHashEntry(direction_hash, direction_token);
13573
13574     if (direction_value != NULL)        // direction found
13575     {
13576       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13577                          &num_list_entries);
13578
13579       free(element_token);
13580
13581       continue;
13582     }
13583
13584     if (strchr(action_token + 1, '.') == NULL)
13585     {
13586       // no further suffixes found -- this is not an action nor direction
13587
13588       element_value = getHashEntry(element_hash, list->token);
13589       if (element_value != NULL)        // combined element found
13590         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13591                            &num_list_entries);
13592       else
13593         print_unknown_token(filename, list->token, num_unknown_tokens++);
13594
13595       free(element_token);
13596
13597       continue;
13598     }
13599
13600     // token has format "<prefix>.<suffix>.<something>"
13601
13602     direction_token = strchr(action_token + 1, '.');
13603
13604     action_token = getStringCopy(action_token);
13605     *strchr(action_token + 1, '.') = '\0';
13606
13607     action_value = getHashEntry(action_hash, action_token);
13608
13609     if (action_value == NULL)           // this is no action
13610     {
13611       element_value = getHashEntry(element_hash, list->token);
13612       if (element_value != NULL)        // combined element found
13613         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13614                            &num_list_entries);
13615       else
13616         print_unknown_token(filename, list->token, num_unknown_tokens++);
13617
13618       free(element_token);
13619       free(action_token);
13620
13621       continue;
13622     }
13623
13624     direction_value = getHashEntry(direction_hash, direction_token);
13625
13626     if (direction_value != NULL)        // direction found
13627     {
13628       add_helpanim_entry(atoi(element_value), atoi(action_value),
13629                          atoi(direction_value), delay, &num_list_entries);
13630
13631       free(element_token);
13632       free(action_token);
13633
13634       continue;
13635     }
13636
13637     // this is no direction
13638
13639     element_value = getHashEntry(element_hash, list->token);
13640     if (element_value != NULL)          // combined element found
13641       add_helpanim_entry(atoi(element_value), -1, -1, delay,
13642                          &num_list_entries);
13643     else
13644       print_unknown_token(filename, list->token, num_unknown_tokens++);
13645
13646     free(element_token);
13647     free(action_token);
13648   }
13649
13650   print_unknown_token_end(num_unknown_tokens);
13651
13652   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13653   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
13654
13655   freeSetupFileList(setup_file_list);
13656   freeSetupFileHash(element_hash);
13657   freeSetupFileHash(action_hash);
13658   freeSetupFileHash(direction_hash);
13659
13660 #if 0
13661   for (i = 0; i < num_list_entries; i++)
13662     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13663           EL_NAME(helpanim_info[i].element),
13664           helpanim_info[i].element,
13665           helpanim_info[i].action,
13666           helpanim_info[i].direction,
13667           helpanim_info[i].delay);
13668 #endif
13669 }
13670
13671 void LoadHelpTextInfo(void)
13672 {
13673   char *filename = getHelpTextFilename();
13674   int i;
13675
13676   if (helptext_info != NULL)
13677   {
13678     freeSetupFileHash(helptext_info);
13679     helptext_info = NULL;
13680   }
13681
13682   if (fileExists(filename))
13683     helptext_info = loadSetupFileHash(filename);
13684
13685   if (helptext_info == NULL)
13686   {
13687     // use reliable default values from static configuration
13688     helptext_info = newSetupFileHash();
13689
13690     for (i = 0; helptext_config[i].token; i++)
13691       setHashEntry(helptext_info,
13692                    helptext_config[i].token,
13693                    helptext_config[i].value);
13694   }
13695
13696 #if 0
13697   BEGIN_HASH_ITERATION(helptext_info, itr)
13698   {
13699     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13700           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13701   }
13702   END_HASH_ITERATION(hash, itr)
13703 #endif
13704 }
13705
13706
13707 // ----------------------------------------------------------------------------
13708 // convert levels
13709 // ----------------------------------------------------------------------------
13710
13711 #define MAX_NUM_CONVERT_LEVELS          1000
13712
13713 void ConvertLevels(void)
13714 {
13715   static LevelDirTree *convert_leveldir = NULL;
13716   static int convert_level_nr = -1;
13717   static int num_levels_handled = 0;
13718   static int num_levels_converted = 0;
13719   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13720   int i;
13721
13722   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13723                                                global.convert_leveldir);
13724
13725   if (convert_leveldir == NULL)
13726     Fail("no such level identifier: '%s'", global.convert_leveldir);
13727
13728   leveldir_current = convert_leveldir;
13729
13730   if (global.convert_level_nr != -1)
13731   {
13732     convert_leveldir->first_level = global.convert_level_nr;
13733     convert_leveldir->last_level  = global.convert_level_nr;
13734   }
13735
13736   convert_level_nr = convert_leveldir->first_level;
13737
13738   PrintLine("=", 79);
13739   Print("Converting levels\n");
13740   PrintLine("-", 79);
13741   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13742   Print("Level series name:       '%s'\n", convert_leveldir->name);
13743   Print("Level series author:     '%s'\n", convert_leveldir->author);
13744   Print("Number of levels:        %d\n",   convert_leveldir->levels);
13745   PrintLine("=", 79);
13746   Print("\n");
13747
13748   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13749     levels_failed[i] = FALSE;
13750
13751   while (convert_level_nr <= convert_leveldir->last_level)
13752   {
13753     char *level_filename;
13754     boolean new_level;
13755
13756     level_nr = convert_level_nr++;
13757
13758     Print("Level %03d: ", level_nr);
13759
13760     LoadLevel(level_nr);
13761     if (level.no_level_file || level.no_valid_file)
13762     {
13763       Print("(no level)\n");
13764       continue;
13765     }
13766
13767     Print("converting level ... ");
13768
13769 #if 0
13770     // special case: conversion of some EMC levels as requested by ACME
13771     level.game_engine_type = GAME_ENGINE_TYPE_RND;
13772 #endif
13773
13774     level_filename = getDefaultLevelFilename(level_nr);
13775     new_level = !fileExists(level_filename);
13776
13777     if (new_level)
13778     {
13779       SaveLevel(level_nr);
13780
13781       num_levels_converted++;
13782
13783       Print("converted.\n");
13784     }
13785     else
13786     {
13787       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13788         levels_failed[level_nr] = TRUE;
13789
13790       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13791     }
13792
13793     num_levels_handled++;
13794   }
13795
13796   Print("\n");
13797   PrintLine("=", 79);
13798   Print("Number of levels handled: %d\n", num_levels_handled);
13799   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13800          (num_levels_handled ?
13801           num_levels_converted * 100 / num_levels_handled : 0));
13802   PrintLine("-", 79);
13803   Print("Summary (for automatic parsing by scripts):\n");
13804   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13805          convert_leveldir->identifier, num_levels_converted,
13806          num_levels_handled,
13807          (num_levels_handled ?
13808           num_levels_converted * 100 / num_levels_handled : 0));
13809
13810   if (num_levels_handled != num_levels_converted)
13811   {
13812     Print(", FAILED:");
13813     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13814       if (levels_failed[i])
13815         Print(" %03d", i);
13816   }
13817
13818   Print("\n");
13819   PrintLine("=", 79);
13820
13821   CloseAllAndExit(0);
13822 }
13823
13824
13825 // ----------------------------------------------------------------------------
13826 // create and save images for use in level sketches (raw BMP format)
13827 // ----------------------------------------------------------------------------
13828
13829 void CreateLevelSketchImages(void)
13830 {
13831   Bitmap *bitmap1;
13832   Bitmap *bitmap2;
13833   int i;
13834
13835   InitElementPropertiesGfxElement();
13836
13837   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13838   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13839
13840   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13841   {
13842     int element = getMappedElement(i);
13843     char basename1[16];
13844     char basename2[16];
13845     char *filename1;
13846     char *filename2;
13847
13848     sprintf(basename1, "%04d.bmp", i);
13849     sprintf(basename2, "%04ds.bmp", i);
13850
13851     filename1 = getPath2(global.create_sketch_images_dir, basename1);
13852     filename2 = getPath2(global.create_sketch_images_dir, basename2);
13853
13854     DrawSizedElement(0, 0, element, TILESIZE);
13855     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13856
13857     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13858       Fail("cannot save level sketch image file '%s'", filename1);
13859
13860     DrawSizedElement(0, 0, element, MINI_TILESIZE);
13861     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13862
13863     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13864       Fail("cannot save level sketch image file '%s'", filename2);
13865
13866     free(filename1);
13867     free(filename2);
13868
13869     // create corresponding SQL statements (for normal and small images)
13870     if (i < 1000)
13871     {
13872       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13873       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13874     }
13875
13876     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13877     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13878
13879     // optional: create content for forum level sketch demonstration post
13880     if (options.debug)
13881       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13882   }
13883
13884   FreeBitmap(bitmap1);
13885   FreeBitmap(bitmap2);
13886
13887   if (options.debug)
13888     fprintf(stderr, "\n");
13889
13890   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13891
13892   CloseAllAndExit(0);
13893 }
13894
13895
13896 // ----------------------------------------------------------------------------
13897 // create and save images for element collecting animations (raw BMP format)
13898 // ----------------------------------------------------------------------------
13899
13900 static boolean createCollectImage(int element)
13901 {
13902   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13903 }
13904
13905 void CreateCollectElementImages(void)
13906 {
13907   int i, j;
13908   int num_steps = 8;
13909   int anim_frames = num_steps - 1;
13910   int tile_size = TILESIZE;
13911   int anim_width  = tile_size * anim_frames;
13912   int anim_height = tile_size;
13913   int num_collect_images = 0;
13914   int pos_collect_images = 0;
13915
13916   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13917     if (createCollectImage(i))
13918       num_collect_images++;
13919
13920   Info("Creating %d element collecting animation images ...",
13921        num_collect_images);
13922
13923   int dst_width  = anim_width * 2;
13924   int dst_height = anim_height * num_collect_images / 2;
13925   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13926   char *basename = "RocksCollect.bmp";
13927   char *filename = getPath2(global.create_collect_images_dir, basename);
13928
13929   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13930   {
13931     if (!createCollectImage(i))
13932       continue;
13933
13934     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13935     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13936     int graphic = el2img(i);
13937     char *token_name = element_info[i].token_name;
13938     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13939     Bitmap *src_bitmap;
13940     int src_x, src_y;
13941
13942     Info("- creating collecting image for '%s' ...", token_name);
13943
13944     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13945
13946     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13947                tile_size, tile_size, 0, 0);
13948
13949     tmp_bitmap->surface_masked = tmp_bitmap->surface;
13950
13951     for (j = 0; j < anim_frames; j++)
13952     {
13953       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13954       int frame_size = frame_size_final * num_steps;
13955       int offset = (tile_size - frame_size_final) / 2;
13956       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13957
13958       while (frame_size > frame_size_final)
13959       {
13960         frame_size /= 2;
13961
13962         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13963
13964         FreeBitmap(frame_bitmap);
13965
13966         frame_bitmap = half_bitmap;
13967       }
13968
13969       BlitBitmap(frame_bitmap, dst_bitmap, 0, 0,
13970                  frame_size_final, frame_size_final,
13971                  dst_x + j * tile_size + offset, dst_y + offset);
13972
13973       FreeBitmap(frame_bitmap);
13974     }
13975
13976     tmp_bitmap->surface_masked = NULL;
13977
13978     FreeBitmap(tmp_bitmap);
13979
13980     pos_collect_images++;
13981   }
13982
13983   if (SDL_SaveBMP(dst_bitmap->surface, filename) != 0)
13984     Fail("cannot save element collecting image file '%s'", filename);
13985
13986   FreeBitmap(dst_bitmap);
13987
13988   Info("Done.");
13989
13990   CloseAllAndExit(0);
13991 }
13992
13993
13994 // ----------------------------------------------------------------------------
13995 // create and save images for custom and group elements (raw BMP format)
13996 // ----------------------------------------------------------------------------
13997
13998 void CreateCustomElementImages(char *directory)
13999 {
14000   char *src_basename = "RocksCE-template.ilbm";
14001   char *dst_basename = "RocksCE.bmp";
14002   char *src_filename = getPath2(directory, src_basename);
14003   char *dst_filename = getPath2(directory, dst_basename);
14004   Bitmap *src_bitmap;
14005   Bitmap *bitmap;
14006   int yoffset_ce = 0;
14007   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14008   int i;
14009
14010   InitVideoDefaults();
14011
14012   ReCreateBitmap(&backbuffer, video.width, video.height);
14013
14014   src_bitmap = LoadImage(src_filename);
14015
14016   bitmap = CreateBitmap(TILEX * 16 * 2,
14017                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14018                         DEFAULT_DEPTH);
14019
14020   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14021   {
14022     int x = i % 16;
14023     int y = i / 16;
14024     int ii = i + 1;
14025     int j;
14026
14027     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14028                TILEX * x, TILEY * y + yoffset_ce);
14029
14030     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14031                TILEX, TILEY,
14032                TILEX * x + TILEX * 16,
14033                TILEY * y + yoffset_ce);
14034
14035     for (j = 2; j >= 0; j--)
14036     {
14037       int c = ii % 10;
14038
14039       BlitBitmap(src_bitmap, bitmap,
14040                  TILEX + c * 7, 0, 6, 10,
14041                  TILEX * x + 6 + j * 7,
14042                  TILEY * y + 11 + yoffset_ce);
14043
14044       BlitBitmap(src_bitmap, bitmap,
14045                  TILEX + c * 8, TILEY, 6, 10,
14046                  TILEX * 16 + TILEX * x + 6 + j * 8,
14047                  TILEY * y + 10 + yoffset_ce);
14048
14049       ii /= 10;
14050     }
14051   }
14052
14053   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14054   {
14055     int x = i % 16;
14056     int y = i / 16;
14057     int ii = i + 1;
14058     int j;
14059
14060     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14061                TILEX * x, TILEY * y + yoffset_ge);
14062
14063     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14064                TILEX, TILEY,
14065                TILEX * x + TILEX * 16,
14066                TILEY * y + yoffset_ge);
14067
14068     for (j = 1; j >= 0; j--)
14069     {
14070       int c = ii % 10;
14071
14072       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14073                  TILEX * x + 6 + j * 10,
14074                  TILEY * y + 11 + yoffset_ge);
14075
14076       BlitBitmap(src_bitmap, bitmap,
14077                  TILEX + c * 8, TILEY + 12, 6, 10,
14078                  TILEX * 16 + TILEX * x + 10 + j * 8,
14079                  TILEY * y + 10 + yoffset_ge);
14080
14081       ii /= 10;
14082     }
14083   }
14084
14085   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14086     Fail("cannot save CE graphics file '%s'", dst_filename);
14087
14088   FreeBitmap(bitmap);
14089
14090   CloseAllAndExit(0);
14091 }