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