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