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