fixed bug with undefined behaviour when shifting left by 32 or more
[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_ref,               "editor.cascade.el_ref"
10220   },
10221   {
10222     TYPE_SWITCH,
10223     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10224   },
10225   {
10226     TYPE_SWITCH,
10227     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10228   },
10229 };
10230
10231 static struct TokenInfo shortcut_setup_tokens[] =
10232 {
10233   {
10234     TYPE_KEY_X11,
10235     &setup.shortcut.save_game,                  "shortcut.save_game"
10236   },
10237   {
10238     TYPE_KEY_X11,
10239     &setup.shortcut.load_game,                  "shortcut.load_game"
10240   },
10241   {
10242     TYPE_KEY_X11,
10243     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10244   },
10245   {
10246     TYPE_KEY_X11,
10247     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10248   },
10249   {
10250     TYPE_KEY_X11,
10251     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10252   },
10253   {
10254     TYPE_KEY_X11,
10255     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10256   },
10257   {
10258     TYPE_KEY_X11,
10259     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10260   },
10261   {
10262     TYPE_KEY_X11,
10263     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10264   },
10265   {
10266     TYPE_KEY_X11,
10267     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10268   },
10269   {
10270     TYPE_KEY_X11,
10271     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10272   },
10273   {
10274     TYPE_KEY_X11,
10275     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10276   },
10277   {
10278     TYPE_KEY_X11,
10279     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10280   },
10281   {
10282     TYPE_KEY_X11,
10283     &setup.shortcut.tape_record,                "shortcut.tape_record"
10284   },
10285   {
10286     TYPE_KEY_X11,
10287     &setup.shortcut.tape_play,                  "shortcut.tape_play"
10288   },
10289   {
10290     TYPE_KEY_X11,
10291     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
10292   },
10293   {
10294     TYPE_KEY_X11,
10295     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
10296   },
10297   {
10298     TYPE_KEY_X11,
10299     &setup.shortcut.sound_music,                "shortcut.sound_music"
10300   },
10301   {
10302     TYPE_KEY_X11,
10303     &setup.shortcut.snap_left,                  "shortcut.snap_left"
10304   },
10305   {
10306     TYPE_KEY_X11,
10307     &setup.shortcut.snap_right,                 "shortcut.snap_right"
10308   },
10309   {
10310     TYPE_KEY_X11,
10311     &setup.shortcut.snap_up,                    "shortcut.snap_up"
10312   },
10313   {
10314     TYPE_KEY_X11,
10315     &setup.shortcut.snap_down,                  "shortcut.snap_down"
10316   },
10317 };
10318
10319 static struct SetupInputInfo setup_input;
10320 static struct TokenInfo player_setup_tokens[] =
10321 {
10322   {
10323     TYPE_BOOLEAN,
10324     &setup_input.use_joystick,                  ".use_joystick"
10325   },
10326   {
10327     TYPE_STRING,
10328     &setup_input.joy.device_name,               ".joy.device_name"
10329   },
10330   {
10331     TYPE_INTEGER,
10332     &setup_input.joy.xleft,                     ".joy.xleft"
10333   },
10334   {
10335     TYPE_INTEGER,
10336     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
10337   },
10338   {
10339     TYPE_INTEGER,
10340     &setup_input.joy.xright,                    ".joy.xright"
10341   },
10342   {
10343     TYPE_INTEGER,
10344     &setup_input.joy.yupper,                    ".joy.yupper"
10345   },
10346   {
10347     TYPE_INTEGER,
10348     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
10349   },
10350   {
10351     TYPE_INTEGER,
10352     &setup_input.joy.ylower,                    ".joy.ylower"
10353   },
10354   {
10355     TYPE_INTEGER,
10356     &setup_input.joy.snap,                      ".joy.snap_field"
10357   },
10358   {
10359     TYPE_INTEGER,
10360     &setup_input.joy.drop,                      ".joy.place_bomb"
10361   },
10362   {
10363     TYPE_KEY_X11,
10364     &setup_input.key.left,                      ".key.move_left"
10365   },
10366   {
10367     TYPE_KEY_X11,
10368     &setup_input.key.right,                     ".key.move_right"
10369   },
10370   {
10371     TYPE_KEY_X11,
10372     &setup_input.key.up,                        ".key.move_up"
10373   },
10374   {
10375     TYPE_KEY_X11,
10376     &setup_input.key.down,                      ".key.move_down"
10377   },
10378   {
10379     TYPE_KEY_X11,
10380     &setup_input.key.snap,                      ".key.snap_field"
10381   },
10382   {
10383     TYPE_KEY_X11,
10384     &setup_input.key.drop,                      ".key.place_bomb"
10385   },
10386 };
10387
10388 static struct TokenInfo system_setup_tokens[] =
10389 {
10390   {
10391     TYPE_STRING,
10392     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10393   },
10394   {
10395     TYPE_STRING,
10396     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10397   },
10398   {
10399     TYPE_STRING,
10400     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10401   },
10402   {
10403     TYPE_INTEGER,
10404     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10405   },
10406 };
10407
10408 static struct TokenInfo internal_setup_tokens[] =
10409 {
10410   {
10411     TYPE_STRING,
10412     &setup.internal.program_title,              "program_title"
10413   },
10414   {
10415     TYPE_STRING,
10416     &setup.internal.program_version,            "program_version"
10417   },
10418   {
10419     TYPE_STRING,
10420     &setup.internal.program_author,             "program_author"
10421   },
10422   {
10423     TYPE_STRING,
10424     &setup.internal.program_email,              "program_email"
10425   },
10426   {
10427     TYPE_STRING,
10428     &setup.internal.program_website,            "program_website"
10429   },
10430   {
10431     TYPE_STRING,
10432     &setup.internal.program_copyright,          "program_copyright"
10433   },
10434   {
10435     TYPE_STRING,
10436     &setup.internal.program_company,            "program_company"
10437   },
10438   {
10439     TYPE_STRING,
10440     &setup.internal.program_icon_file,          "program_icon_file"
10441   },
10442   {
10443     TYPE_STRING,
10444     &setup.internal.default_graphics_set,       "default_graphics_set"
10445   },
10446   {
10447     TYPE_STRING,
10448     &setup.internal.default_sounds_set,         "default_sounds_set"
10449   },
10450   {
10451     TYPE_STRING,
10452     &setup.internal.default_music_set,          "default_music_set"
10453   },
10454   {
10455     TYPE_STRING,
10456     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
10457   },
10458   {
10459     TYPE_STRING,
10460     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
10461   },
10462   {
10463     TYPE_STRING,
10464     &setup.internal.fallback_music_file,        "fallback_music_file"
10465   },
10466   {
10467     TYPE_STRING,
10468     &setup.internal.default_level_series,       "default_level_series"
10469   },
10470   {
10471     TYPE_INTEGER,
10472     &setup.internal.default_window_width,       "default_window_width"
10473   },
10474   {
10475     TYPE_INTEGER,
10476     &setup.internal.default_window_height,      "default_window_height"
10477   },
10478   {
10479     TYPE_BOOLEAN,
10480     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
10481   },
10482   {
10483     TYPE_BOOLEAN,
10484     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
10485   },
10486   {
10487     TYPE_BOOLEAN,
10488     &setup.internal.create_user_levelset,       "create_user_levelset"
10489   },
10490   {
10491     TYPE_BOOLEAN,
10492     &setup.internal.menu_game,                  "menu_game"
10493   },
10494   {
10495     TYPE_BOOLEAN,
10496     &setup.internal.menu_editor,                "menu_editor"
10497   },
10498   {
10499     TYPE_BOOLEAN,
10500     &setup.internal.menu_graphics,              "menu_graphics"
10501   },
10502   {
10503     TYPE_BOOLEAN,
10504     &setup.internal.menu_sound,                 "menu_sound"
10505   },
10506   {
10507     TYPE_BOOLEAN,
10508     &setup.internal.menu_artwork,               "menu_artwork"
10509   },
10510   {
10511     TYPE_BOOLEAN,
10512     &setup.internal.menu_input,                 "menu_input"
10513   },
10514   {
10515     TYPE_BOOLEAN,
10516     &setup.internal.menu_touch,                 "menu_touch"
10517   },
10518   {
10519     TYPE_BOOLEAN,
10520     &setup.internal.menu_shortcuts,             "menu_shortcuts"
10521   },
10522   {
10523     TYPE_BOOLEAN,
10524     &setup.internal.menu_exit,                  "menu_exit"
10525   },
10526   {
10527     TYPE_BOOLEAN,
10528     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
10529   },
10530 };
10531
10532 static struct TokenInfo debug_setup_tokens[] =
10533 {
10534   {
10535     TYPE_INTEGER,
10536     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
10537   },
10538   {
10539     TYPE_INTEGER,
10540     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
10541   },
10542   {
10543     TYPE_INTEGER,
10544     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
10545   },
10546   {
10547     TYPE_INTEGER,
10548     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
10549   },
10550   {
10551     TYPE_INTEGER,
10552     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
10553   },
10554   {
10555     TYPE_INTEGER,
10556     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
10557   },
10558   {
10559     TYPE_INTEGER,
10560     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
10561   },
10562   {
10563     TYPE_INTEGER,
10564     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
10565   },
10566   {
10567     TYPE_INTEGER,
10568     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
10569   },
10570   {
10571     TYPE_INTEGER,
10572     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
10573   },
10574   {
10575     TYPE_KEY_X11,
10576     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
10577   },
10578   {
10579     TYPE_KEY_X11,
10580     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
10581   },
10582   {
10583     TYPE_KEY_X11,
10584     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
10585   },
10586   {
10587     TYPE_KEY_X11,
10588     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
10589   },
10590   {
10591     TYPE_KEY_X11,
10592     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
10593   },
10594   {
10595     TYPE_KEY_X11,
10596     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
10597   },
10598   {
10599     TYPE_KEY_X11,
10600     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
10601   },
10602   {
10603     TYPE_KEY_X11,
10604     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
10605   },
10606   {
10607     TYPE_KEY_X11,
10608     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
10609   },
10610   {
10611     TYPE_KEY_X11,
10612     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
10613   },
10614   {
10615     TYPE_BOOLEAN,
10616     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
10617   {
10618     TYPE_BOOLEAN,
10619     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
10620   },
10621   {
10622     TYPE_BOOLEAN,
10623     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
10624   },
10625   {
10626     TYPE_SWITCH3,
10627     &setup.debug.xsn_mode,                      "debug.xsn_mode"
10628   },
10629   {
10630     TYPE_INTEGER,
10631     &setup.debug.xsn_percent,                   "debug.xsn_percent"
10632   },
10633 };
10634
10635 static struct TokenInfo options_setup_tokens[] =
10636 {
10637   {
10638     TYPE_BOOLEAN,
10639     &setup.options.verbose,                     "options.verbose"
10640   },
10641 };
10642
10643 static void setSetupInfoToDefaults(struct SetupInfo *si)
10644 {
10645   int i;
10646
10647   si->player_name = getStringCopy(getDefaultUserName(user.nr));
10648
10649   si->multiple_users = TRUE;
10650
10651   si->sound = TRUE;
10652   si->sound_loops = TRUE;
10653   si->sound_music = TRUE;
10654   si->sound_simple = TRUE;
10655   si->toons = TRUE;
10656   si->scroll_delay = TRUE;
10657   si->forced_scroll_delay = FALSE;
10658   si->scroll_delay_value = STD_SCROLL_DELAY;
10659   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10660   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10661   si->fade_screens = TRUE;
10662   si->autorecord = TRUE;
10663   si->show_titlescreen = TRUE;
10664   si->quick_doors = FALSE;
10665   si->team_mode = FALSE;
10666   si->handicap = TRUE;
10667   si->skip_levels = TRUE;
10668   si->increment_levels = TRUE;
10669   si->auto_play_next_level = TRUE;
10670   si->count_score_after_game = TRUE;
10671   si->show_scores_after_game = TRUE;
10672   si->time_limit = TRUE;
10673   si->fullscreen = FALSE;
10674   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10675   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10676   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10677   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10678   si->ask_on_escape = TRUE;
10679   si->ask_on_escape_editor = TRUE;
10680   si->ask_on_game_over = TRUE;
10681   si->ask_on_quit_game = TRUE;
10682   si->ask_on_quit_program = TRUE;
10683   si->quick_switch = FALSE;
10684   si->input_on_focus = FALSE;
10685   si->prefer_aga_graphics = TRUE;
10686   si->prefer_lowpass_sounds = FALSE;
10687   si->prefer_extra_panel_items = TRUE;
10688   si->game_speed_extended = FALSE;
10689   si->game_frame_delay = GAME_FRAME_DELAY;
10690   si->sp_show_border_elements = FALSE;
10691   si->small_game_graphics = FALSE;
10692   si->show_load_save_buttons = FALSE;
10693   si->show_undo_redo_buttons = FALSE;
10694   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10695
10696   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10697   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
10698   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
10699
10700   si->override_level_graphics = FALSE;
10701   si->override_level_sounds = FALSE;
10702   si->override_level_music = FALSE;
10703
10704   si->volume_simple = 100;              // percent
10705   si->volume_loops = 100;               // percent
10706   si->volume_music = 100;               // percent
10707
10708   si->network_mode = FALSE;
10709   si->network_player_nr = 0;            // first player
10710   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10711
10712   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10713   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
10714   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
10715   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
10716   si->touch.draw_outlined = TRUE;
10717   si->touch.draw_pressed = TRUE;
10718
10719   for (i = 0; i < 2; i++)
10720   {
10721     char *default_grid_button[6][2] =
10722     {
10723       { "      ", "  ^^  " },
10724       { "      ", "  ^^  " },
10725       { "      ", "<<  >>" },
10726       { "      ", "<<  >>" },
10727       { "111222", "  vv  " },
10728       { "111222", "  vv  " }
10729     };
10730     int grid_xsize = DEFAULT_GRID_XSIZE(i);
10731     int grid_ysize = DEFAULT_GRID_YSIZE(i);
10732     int min_xsize = MIN(6, grid_xsize);
10733     int min_ysize = MIN(6, grid_ysize);
10734     int startx = grid_xsize - min_xsize;
10735     int starty = grid_ysize - min_ysize;
10736     int x, y;
10737
10738     // virtual buttons grid can only be set to defaults if video is initialized
10739     // (this will be repeated if virtual buttons are not loaded from setup file)
10740     if (video.initialized)
10741     {
10742       si->touch.grid_xsize[i] = grid_xsize;
10743       si->touch.grid_ysize[i] = grid_ysize;
10744     }
10745     else
10746     {
10747       si->touch.grid_xsize[i] = -1;
10748       si->touch.grid_ysize[i] = -1;
10749     }
10750
10751     for (x = 0; x < MAX_GRID_XSIZE; x++)
10752       for (y = 0; y < MAX_GRID_YSIZE; y++)
10753         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10754
10755     for (x = 0; x < min_xsize; x++)
10756       for (y = 0; y < min_ysize; y++)
10757         si->touch.grid_button[i][x][starty + y] =
10758           default_grid_button[y][0][x];
10759
10760     for (x = 0; x < min_xsize; x++)
10761       for (y = 0; y < min_ysize; y++)
10762         si->touch.grid_button[i][startx + x][starty + y] =
10763           default_grid_button[y][1][x];
10764   }
10765
10766   si->touch.grid_initialized            = video.initialized;
10767
10768   si->editor.el_boulderdash             = TRUE;
10769   si->editor.el_emerald_mine            = TRUE;
10770   si->editor.el_emerald_mine_club       = TRUE;
10771   si->editor.el_more                    = TRUE;
10772   si->editor.el_sokoban                 = TRUE;
10773   si->editor.el_supaplex                = TRUE;
10774   si->editor.el_diamond_caves           = TRUE;
10775   si->editor.el_dx_boulderdash          = TRUE;
10776
10777   si->editor.el_mirror_magic            = TRUE;
10778   si->editor.el_deflektor               = TRUE;
10779
10780   si->editor.el_chars                   = TRUE;
10781   si->editor.el_steel_chars             = TRUE;
10782
10783   si->editor.el_classic                 = TRUE;
10784   si->editor.el_custom                  = TRUE;
10785
10786   si->editor.el_user_defined            = FALSE;
10787   si->editor.el_dynamic                 = TRUE;
10788
10789   si->editor.el_headlines               = TRUE;
10790
10791   si->editor.show_element_token         = FALSE;
10792
10793   si->editor.show_read_only_warning     = TRUE;
10794
10795   si->editor.use_template_for_new_levels = TRUE;
10796
10797   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
10798   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
10799   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
10800
10801   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
10802   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
10803   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
10804   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
10805   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10806
10807   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
10808   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
10809   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
10810   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
10811   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
10812   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
10813
10814   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
10815   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
10816   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
10817
10818   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
10819   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
10820   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
10821   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
10822
10823   for (i = 0; i < MAX_PLAYERS; i++)
10824   {
10825     si->input[i].use_joystick = FALSE;
10826     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10827     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
10828     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10829     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
10830     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
10831     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10832     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
10833     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
10834     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
10835     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
10836     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10837     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
10838     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
10839     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
10840     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
10841   }
10842
10843   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10844   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10845   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10846   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10847
10848   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
10849   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
10850   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
10851   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
10852   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
10853   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10854   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
10855
10856   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10857
10858   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10859   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
10860   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
10861
10862   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10863   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
10864   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
10865
10866   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10867   si->internal.choose_from_top_leveldir = FALSE;
10868   si->internal.show_scaling_in_title = TRUE;
10869   si->internal.create_user_levelset = TRUE;
10870
10871   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
10872   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10873
10874   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10875   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10876   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10877   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10878   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10879   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10880   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10881   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10882   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10883   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10884
10885   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10886   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10887   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10888   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10889   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10890   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10891   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10892   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10893   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10894   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10895
10896   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10897   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
10898
10899   si->debug.show_frames_per_second = FALSE;
10900
10901   si->debug.xsn_mode = AUTO;
10902   si->debug.xsn_percent = 0;
10903
10904   si->options.verbose = FALSE;
10905
10906 #if defined(PLATFORM_ANDROID)
10907   si->fullscreen = TRUE;
10908 #endif
10909
10910   setHideSetupEntry(&setup.debug.xsn_mode);
10911 }
10912
10913 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10914 {
10915   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10916 }
10917
10918 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10919 {
10920   si->player_uuid = NULL;       // (will be set later)
10921   si->player_version = 1;       // (will be set later)
10922
10923   si->use_api_server = TRUE;
10924   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10925   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10926   si->ask_for_uploading_tapes = TRUE;
10927   si->ask_for_remaining_tapes = FALSE;
10928   si->provide_uploading_tapes = TRUE;
10929   si->ask_for_using_api_server = TRUE;
10930   si->has_remaining_tapes = FALSE;
10931 }
10932
10933 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10934 {
10935   si->editor_cascade.el_bd              = TRUE;
10936   si->editor_cascade.el_em              = TRUE;
10937   si->editor_cascade.el_emc             = TRUE;
10938   si->editor_cascade.el_rnd             = TRUE;
10939   si->editor_cascade.el_sb              = TRUE;
10940   si->editor_cascade.el_sp              = TRUE;
10941   si->editor_cascade.el_dc              = TRUE;
10942   si->editor_cascade.el_dx              = TRUE;
10943
10944   si->editor_cascade.el_mm              = TRUE;
10945   si->editor_cascade.el_df              = TRUE;
10946
10947   si->editor_cascade.el_chars           = FALSE;
10948   si->editor_cascade.el_steel_chars     = FALSE;
10949   si->editor_cascade.el_ce              = FALSE;
10950   si->editor_cascade.el_ge              = FALSE;
10951   si->editor_cascade.el_ref             = FALSE;
10952   si->editor_cascade.el_user            = FALSE;
10953   si->editor_cascade.el_dynamic         = FALSE;
10954 }
10955
10956 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
10957
10958 static char *getHideSetupToken(void *setup_value)
10959 {
10960   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10961
10962   if (setup_value != NULL)
10963     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10964
10965   return hide_setup_token;
10966 }
10967
10968 void setHideSetupEntry(void *setup_value)
10969 {
10970   char *hide_setup_token = getHideSetupToken(setup_value);
10971
10972   if (hide_setup_hash == NULL)
10973     hide_setup_hash = newSetupFileHash();
10974
10975   if (setup_value != NULL)
10976     setHashEntry(hide_setup_hash, hide_setup_token, "");
10977 }
10978
10979 void removeHideSetupEntry(void *setup_value)
10980 {
10981   char *hide_setup_token = getHideSetupToken(setup_value);
10982
10983   if (setup_value != NULL)
10984     removeHashEntry(hide_setup_hash, hide_setup_token);
10985 }
10986
10987 boolean hideSetupEntry(void *setup_value)
10988 {
10989   char *hide_setup_token = getHideSetupToken(setup_value);
10990
10991   return (setup_value != NULL &&
10992           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10993 }
10994
10995 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10996                                       struct TokenInfo *token_info,
10997                                       int token_nr, char *token_text)
10998 {
10999   char *token_hide_text = getStringCat2(token_text, ".hide");
11000   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11001
11002   // set the value of this setup option in the setup option structure
11003   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11004
11005   // check if this setup option should be hidden in the setup menu
11006   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11007     setHideSetupEntry(token_info[token_nr].value);
11008
11009   free(token_hide_text);
11010 }
11011
11012 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11013                                       struct TokenInfo *token_info,
11014                                       int token_nr)
11015 {
11016   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11017                             token_info[token_nr].text);
11018 }
11019
11020 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11021 {
11022   int i, pnr;
11023
11024   if (!setup_file_hash)
11025     return;
11026
11027   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11028     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11029
11030   setup.touch.grid_initialized = TRUE;
11031   for (i = 0; i < 2; i++)
11032   {
11033     int grid_xsize = setup.touch.grid_xsize[i];
11034     int grid_ysize = setup.touch.grid_ysize[i];
11035     int x, y;
11036
11037     // if virtual buttons are not loaded from setup file, repeat initializing
11038     // virtual buttons grid with default values later when video is initialized
11039     if (grid_xsize == -1 ||
11040         grid_ysize == -1)
11041     {
11042       setup.touch.grid_initialized = FALSE;
11043
11044       continue;
11045     }
11046
11047     for (y = 0; y < grid_ysize; y++)
11048     {
11049       char token_string[MAX_LINE_LEN];
11050
11051       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11052
11053       char *value_string = getHashEntry(setup_file_hash, token_string);
11054
11055       if (value_string == NULL)
11056         continue;
11057
11058       for (x = 0; x < grid_xsize; x++)
11059       {
11060         char c = value_string[x];
11061
11062         setup.touch.grid_button[i][x][y] =
11063           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11064       }
11065     }
11066   }
11067
11068   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11069     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11070
11071   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11072     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11073
11074   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11075   {
11076     char prefix[30];
11077
11078     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11079
11080     setup_input = setup.input[pnr];
11081     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11082     {
11083       char full_token[100];
11084
11085       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11086       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11087                                 full_token);
11088     }
11089     setup.input[pnr] = setup_input;
11090   }
11091
11092   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11093     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11094
11095   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11096     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11097
11098   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11099     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11100
11101   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11102     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11103
11104   setHideRelatedSetupEntries();
11105 }
11106
11107 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11108 {
11109   int i;
11110
11111   if (!setup_file_hash)
11112     return;
11113
11114   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11115     setSetupInfo(auto_setup_tokens, i,
11116                  getHashEntry(setup_file_hash,
11117                               auto_setup_tokens[i].text));
11118 }
11119
11120 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11121 {
11122   int i;
11123
11124   if (!setup_file_hash)
11125     return;
11126
11127   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11128     setSetupInfo(server_setup_tokens, i,
11129                  getHashEntry(setup_file_hash,
11130                               server_setup_tokens[i].text));
11131 }
11132
11133 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11134 {
11135   int i;
11136
11137   if (!setup_file_hash)
11138     return;
11139
11140   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11141     setSetupInfo(editor_cascade_setup_tokens, i,
11142                  getHashEntry(setup_file_hash,
11143                               editor_cascade_setup_tokens[i].text));
11144 }
11145
11146 void LoadUserNames(void)
11147 {
11148   int last_user_nr = user.nr;
11149   int i;
11150
11151   if (global.user_names != NULL)
11152   {
11153     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11154       checked_free(global.user_names[i]);
11155
11156     checked_free(global.user_names);
11157   }
11158
11159   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11160
11161   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11162   {
11163     user.nr = i;
11164
11165     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11166
11167     if (setup_file_hash)
11168     {
11169       char *player_name = getHashEntry(setup_file_hash, "player_name");
11170
11171       global.user_names[i] = getFixedUserName(player_name);
11172
11173       freeSetupFileHash(setup_file_hash);
11174     }
11175
11176     if (global.user_names[i] == NULL)
11177       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11178   }
11179
11180   user.nr = last_user_nr;
11181 }
11182
11183 void LoadSetupFromFilename(char *filename)
11184 {
11185   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11186
11187   if (setup_file_hash)
11188   {
11189     decodeSetupFileHash_Default(setup_file_hash);
11190
11191     freeSetupFileHash(setup_file_hash);
11192   }
11193   else
11194   {
11195     Debug("setup", "using default setup values");
11196   }
11197 }
11198
11199 static void LoadSetup_SpecialPostProcessing(void)
11200 {
11201   char *player_name_new;
11202
11203   // needed to work around problems with fixed length strings
11204   player_name_new = getFixedUserName(setup.player_name);
11205   free(setup.player_name);
11206   setup.player_name = player_name_new;
11207
11208   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11209   if (setup.scroll_delay == FALSE)
11210   {
11211     setup.scroll_delay_value = MIN_SCROLL_DELAY;
11212     setup.scroll_delay = TRUE;                  // now always "on"
11213   }
11214
11215   // make sure that scroll delay value stays inside valid range
11216   setup.scroll_delay_value =
11217     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11218 }
11219
11220 void LoadSetup_Default(void)
11221 {
11222   char *filename;
11223
11224   // always start with reliable default values
11225   setSetupInfoToDefaults(&setup);
11226
11227   // try to load setup values from default setup file
11228   filename = getDefaultSetupFilename();
11229
11230   if (fileExists(filename))
11231     LoadSetupFromFilename(filename);
11232
11233   // try to load setup values from user setup file
11234   filename = getSetupFilename();
11235
11236   LoadSetupFromFilename(filename);
11237
11238   LoadSetup_SpecialPostProcessing();
11239 }
11240
11241 void LoadSetup_AutoSetup(void)
11242 {
11243   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11244   SetupFileHash *setup_file_hash = NULL;
11245
11246   // always start with reliable default values
11247   setSetupInfoToDefaults_AutoSetup(&setup);
11248
11249   setup_file_hash = loadSetupFileHash(filename);
11250
11251   if (setup_file_hash)
11252   {
11253     decodeSetupFileHash_AutoSetup(setup_file_hash);
11254
11255     freeSetupFileHash(setup_file_hash);
11256   }
11257
11258   free(filename);
11259 }
11260
11261 void LoadSetup_ServerSetup(void)
11262 {
11263   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11264   SetupFileHash *setup_file_hash = NULL;
11265
11266   // always start with reliable default values
11267   setSetupInfoToDefaults_ServerSetup(&setup);
11268
11269   setup_file_hash = loadSetupFileHash(filename);
11270
11271   if (setup_file_hash)
11272   {
11273     decodeSetupFileHash_ServerSetup(setup_file_hash);
11274
11275     freeSetupFileHash(setup_file_hash);
11276   }
11277
11278   free(filename);
11279
11280   if (setup.player_uuid == NULL)
11281   {
11282     // player UUID does not yet exist in setup file
11283     setup.player_uuid = getStringCopy(getUUID());
11284     setup.player_version = 2;
11285
11286     SaveSetup_ServerSetup();
11287   }
11288 }
11289
11290 void LoadSetup_EditorCascade(void)
11291 {
11292   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11293   SetupFileHash *setup_file_hash = NULL;
11294
11295   // always start with reliable default values
11296   setSetupInfoToDefaults_EditorCascade(&setup);
11297
11298   setup_file_hash = loadSetupFileHash(filename);
11299
11300   if (setup_file_hash)
11301   {
11302     decodeSetupFileHash_EditorCascade(setup_file_hash);
11303
11304     freeSetupFileHash(setup_file_hash);
11305   }
11306
11307   free(filename);
11308 }
11309
11310 void LoadSetup(void)
11311 {
11312   LoadSetup_Default();
11313   LoadSetup_AutoSetup();
11314   LoadSetup_ServerSetup();
11315   LoadSetup_EditorCascade();
11316 }
11317
11318 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11319                                            char *mapping_line)
11320 {
11321   char mapping_guid[MAX_LINE_LEN];
11322   char *mapping_start, *mapping_end;
11323
11324   // get GUID from game controller mapping line: copy complete line
11325   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11326   mapping_guid[MAX_LINE_LEN - 1] = '\0';
11327
11328   // get GUID from game controller mapping line: cut after GUID part
11329   mapping_start = strchr(mapping_guid, ',');
11330   if (mapping_start != NULL)
11331     *mapping_start = '\0';
11332
11333   // cut newline from game controller mapping line
11334   mapping_end = strchr(mapping_line, '\n');
11335   if (mapping_end != NULL)
11336     *mapping_end = '\0';
11337
11338   // add mapping entry to game controller mappings hash
11339   setHashEntry(mappings_hash, mapping_guid, mapping_line);
11340 }
11341
11342 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11343                                                  char *filename)
11344 {
11345   FILE *file;
11346
11347   if (!(file = fopen(filename, MODE_READ)))
11348   {
11349     Warn("cannot read game controller mappings file '%s'", filename);
11350
11351     return;
11352   }
11353
11354   while (!feof(file))
11355   {
11356     char line[MAX_LINE_LEN];
11357
11358     if (!fgets(line, MAX_LINE_LEN, file))
11359       break;
11360
11361     addGameControllerMappingToHash(mappings_hash, line);
11362   }
11363
11364   fclose(file);
11365 }
11366
11367 void SaveSetup_Default(void)
11368 {
11369   char *filename = getSetupFilename();
11370   FILE *file;
11371   int i, pnr;
11372
11373   InitUserDataDirectory();
11374
11375   if (!(file = fopen(filename, MODE_WRITE)))
11376   {
11377     Warn("cannot write setup file '%s'", filename);
11378
11379     return;
11380   }
11381
11382   fprintFileHeader(file, SETUP_FILENAME);
11383
11384   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11385   {
11386     // just to make things nicer :)
11387     if (global_setup_tokens[i].value == &setup.multiple_users           ||
11388         global_setup_tokens[i].value == &setup.sound                    ||
11389         global_setup_tokens[i].value == &setup.graphics_set             ||
11390         global_setup_tokens[i].value == &setup.volume_simple            ||
11391         global_setup_tokens[i].value == &setup.network_mode             ||
11392         global_setup_tokens[i].value == &setup.touch.control_type       ||
11393         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
11394         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11395       fprintf(file, "\n");
11396
11397     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11398   }
11399
11400   for (i = 0; i < 2; i++)
11401   {
11402     int grid_xsize = setup.touch.grid_xsize[i];
11403     int grid_ysize = setup.touch.grid_ysize[i];
11404     int x, y;
11405
11406     fprintf(file, "\n");
11407
11408     for (y = 0; y < grid_ysize; y++)
11409     {
11410       char token_string[MAX_LINE_LEN];
11411       char value_string[MAX_LINE_LEN];
11412
11413       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11414
11415       for (x = 0; x < grid_xsize; x++)
11416       {
11417         char c = setup.touch.grid_button[i][x][y];
11418
11419         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11420       }
11421
11422       value_string[grid_xsize] = '\0';
11423
11424       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11425     }
11426   }
11427
11428   fprintf(file, "\n");
11429   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11430     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11431
11432   fprintf(file, "\n");
11433   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11434     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11435
11436   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11437   {
11438     char prefix[30];
11439
11440     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11441     fprintf(file, "\n");
11442
11443     setup_input = setup.input[pnr];
11444     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11445       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11446   }
11447
11448   fprintf(file, "\n");
11449   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11450     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11451
11452   // (internal setup values not saved to user setup file)
11453
11454   fprintf(file, "\n");
11455   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11456     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11457         setup.debug.xsn_mode != AUTO)
11458       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11459
11460   fprintf(file, "\n");
11461   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11462     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11463
11464   fclose(file);
11465
11466   SetFilePermissions(filename, PERMS_PRIVATE);
11467 }
11468
11469 void SaveSetup_AutoSetup(void)
11470 {
11471   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11472   FILE *file;
11473   int i;
11474
11475   InitUserDataDirectory();
11476
11477   if (!(file = fopen(filename, MODE_WRITE)))
11478   {
11479     Warn("cannot write auto setup file '%s'", filename);
11480
11481     free(filename);
11482
11483     return;
11484   }
11485
11486   fprintFileHeader(file, AUTOSETUP_FILENAME);
11487
11488   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11489     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11490
11491   fclose(file);
11492
11493   SetFilePermissions(filename, PERMS_PRIVATE);
11494
11495   free(filename);
11496 }
11497
11498 void SaveSetup_ServerSetup(void)
11499 {
11500   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11501   FILE *file;
11502   int i;
11503
11504   InitUserDataDirectory();
11505
11506   if (!(file = fopen(filename, MODE_WRITE)))
11507   {
11508     Warn("cannot write server setup file '%s'", filename);
11509
11510     free(filename);
11511
11512     return;
11513   }
11514
11515   fprintFileHeader(file, SERVERSETUP_FILENAME);
11516
11517   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11518   {
11519     // just to make things nicer :)
11520     if (server_setup_tokens[i].value == &setup.use_api_server)
11521       fprintf(file, "\n");
11522
11523     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11524   }
11525
11526   fclose(file);
11527
11528   SetFilePermissions(filename, PERMS_PRIVATE);
11529
11530   free(filename);
11531 }
11532
11533 void SaveSetup_EditorCascade(void)
11534 {
11535   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11536   FILE *file;
11537   int i;
11538
11539   InitUserDataDirectory();
11540
11541   if (!(file = fopen(filename, MODE_WRITE)))
11542   {
11543     Warn("cannot write editor cascade state file '%s'", filename);
11544
11545     free(filename);
11546
11547     return;
11548   }
11549
11550   fprintFileHeader(file, EDITORCASCADE_FILENAME);
11551
11552   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11553     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11554
11555   fclose(file);
11556
11557   SetFilePermissions(filename, PERMS_PRIVATE);
11558
11559   free(filename);
11560 }
11561
11562 void SaveSetup(void)
11563 {
11564   SaveSetup_Default();
11565   SaveSetup_AutoSetup();
11566   SaveSetup_ServerSetup();
11567   SaveSetup_EditorCascade();
11568 }
11569
11570 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11571                                                   char *filename)
11572 {
11573   FILE *file;
11574
11575   if (!(file = fopen(filename, MODE_WRITE)))
11576   {
11577     Warn("cannot write game controller mappings file '%s'", filename);
11578
11579     return;
11580   }
11581
11582   BEGIN_HASH_ITERATION(mappings_hash, itr)
11583   {
11584     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11585   }
11586   END_HASH_ITERATION(mappings_hash, itr)
11587
11588   fclose(file);
11589 }
11590
11591 void SaveSetup_AddGameControllerMapping(char *mapping)
11592 {
11593   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11594   SetupFileHash *mappings_hash = newSetupFileHash();
11595
11596   InitUserDataDirectory();
11597
11598   // load existing personal game controller mappings
11599   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11600
11601   // add new mapping to personal game controller mappings
11602   addGameControllerMappingToHash(mappings_hash, mapping);
11603
11604   // save updated personal game controller mappings
11605   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11606
11607   freeSetupFileHash(mappings_hash);
11608   free(filename);
11609 }
11610
11611 void LoadCustomElementDescriptions(void)
11612 {
11613   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11614   SetupFileHash *setup_file_hash;
11615   int i;
11616
11617   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11618   {
11619     if (element_info[i].custom_description != NULL)
11620     {
11621       free(element_info[i].custom_description);
11622       element_info[i].custom_description = NULL;
11623     }
11624   }
11625
11626   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11627     return;
11628
11629   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11630   {
11631     char *token = getStringCat2(element_info[i].token_name, ".name");
11632     char *value = getHashEntry(setup_file_hash, token);
11633
11634     if (value != NULL)
11635       element_info[i].custom_description = getStringCopy(value);
11636
11637     free(token);
11638   }
11639
11640   freeSetupFileHash(setup_file_hash);
11641 }
11642
11643 static int getElementFromToken(char *token)
11644 {
11645   char *value = getHashEntry(element_token_hash, token);
11646
11647   if (value != NULL)
11648     return atoi(value);
11649
11650   Warn("unknown element token '%s'", token);
11651
11652   return EL_UNDEFINED;
11653 }
11654
11655 void FreeGlobalAnimEventInfo(void)
11656 {
11657   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11658
11659   if (gaei->event_list == NULL)
11660     return;
11661
11662   int i;
11663
11664   for (i = 0; i < gaei->num_event_lists; i++)
11665   {
11666     checked_free(gaei->event_list[i]->event_value);
11667     checked_free(gaei->event_list[i]);
11668   }
11669
11670   checked_free(gaei->event_list);
11671
11672   gaei->event_list = NULL;
11673   gaei->num_event_lists = 0;
11674 }
11675
11676 static int AddGlobalAnimEventList(void)
11677 {
11678   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11679   int list_pos = gaei->num_event_lists++;
11680
11681   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11682                                      sizeof(struct GlobalAnimEventListInfo *));
11683
11684   gaei->event_list[list_pos] =
11685     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11686
11687   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11688
11689   gaeli->event_value = NULL;
11690   gaeli->num_event_values = 0;
11691
11692   return list_pos;
11693 }
11694
11695 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11696 {
11697   // do not add empty global animation events
11698   if (event_value == ANIM_EVENT_NONE)
11699     return list_pos;
11700
11701   // if list position is undefined, create new list
11702   if (list_pos == ANIM_EVENT_UNDEFINED)
11703     list_pos = AddGlobalAnimEventList();
11704
11705   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11706   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11707   int value_pos = gaeli->num_event_values++;
11708
11709   gaeli->event_value = checked_realloc(gaeli->event_value,
11710                                        gaeli->num_event_values * sizeof(int *));
11711
11712   gaeli->event_value[value_pos] = event_value;
11713
11714   return list_pos;
11715 }
11716
11717 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11718 {
11719   if (list_pos == ANIM_EVENT_UNDEFINED)
11720     return ANIM_EVENT_NONE;
11721
11722   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11723   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11724
11725   return gaeli->event_value[value_pos];
11726 }
11727
11728 int GetGlobalAnimEventValueCount(int list_pos)
11729 {
11730   if (list_pos == ANIM_EVENT_UNDEFINED)
11731     return 0;
11732
11733   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11734   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11735
11736   return gaeli->num_event_values;
11737 }
11738
11739 // This function checks if a string <s> of the format "string1, string2, ..."
11740 // exactly contains a string <s_contained>.
11741
11742 static boolean string_has_parameter(char *s, char *s_contained)
11743 {
11744   char *substring;
11745
11746   if (s == NULL || s_contained == NULL)
11747     return FALSE;
11748
11749   if (strlen(s_contained) > strlen(s))
11750     return FALSE;
11751
11752   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11753   {
11754     char next_char = s[strlen(s_contained)];
11755
11756     // check if next character is delimiter or whitespace
11757     return (next_char == ',' || next_char == '\0' ||
11758             next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11759   }
11760
11761   // check if string contains another parameter string after a comma
11762   substring = strchr(s, ',');
11763   if (substring == NULL)        // string does not contain a comma
11764     return FALSE;
11765
11766   // advance string pointer to next character after the comma
11767   substring++;
11768
11769   // skip potential whitespaces after the comma
11770   while (*substring == ' ' || *substring == '\t')
11771     substring++;
11772
11773   return string_has_parameter(substring, s_contained);
11774 }
11775
11776 static int get_anim_parameter_value(char *s)
11777 {
11778   int event_value[] =
11779   {
11780     ANIM_EVENT_CLICK,
11781     ANIM_EVENT_INIT,
11782     ANIM_EVENT_START,
11783     ANIM_EVENT_END,
11784     ANIM_EVENT_POST
11785   };
11786   char *pattern_1[] =
11787   {
11788     "click:anim_",
11789     "init:anim_",
11790     "start:anim_",
11791     "end:anim_",
11792     "post:anim_"
11793   };
11794   char *pattern_2 = ".part_";
11795   char *matching_char = NULL;
11796   char *s_ptr = s;
11797   int pattern_1_len = 0;
11798   int result = ANIM_EVENT_NONE;
11799   int i;
11800
11801   for (i = 0; i < ARRAY_SIZE(event_value); i++)
11802   {
11803     matching_char = strstr(s_ptr, pattern_1[i]);
11804     pattern_1_len = strlen(pattern_1[i]);
11805     result = event_value[i];
11806
11807     if (matching_char != NULL)
11808       break;
11809   }
11810
11811   if (matching_char == NULL)
11812     return ANIM_EVENT_NONE;
11813
11814   s_ptr = matching_char + pattern_1_len;
11815
11816   // check for main animation number ("anim_X" or "anim_XX")
11817   if (*s_ptr >= '0' && *s_ptr <= '9')
11818   {
11819     int gic_anim_nr = (*s_ptr++ - '0');
11820
11821     if (*s_ptr >= '0' && *s_ptr <= '9')
11822       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11823
11824     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11825       return ANIM_EVENT_NONE;
11826
11827     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11828   }
11829   else
11830   {
11831     // invalid main animation number specified
11832
11833     return ANIM_EVENT_NONE;
11834   }
11835
11836   // check for animation part number ("part_X" or "part_XX") (optional)
11837   if (strPrefix(s_ptr, pattern_2))
11838   {
11839     s_ptr += strlen(pattern_2);
11840
11841     if (*s_ptr >= '0' && *s_ptr <= '9')
11842     {
11843       int gic_part_nr = (*s_ptr++ - '0');
11844
11845       if (*s_ptr >= '0' && *s_ptr <= '9')
11846         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11847
11848       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11849         return ANIM_EVENT_NONE;
11850
11851       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11852     }
11853     else
11854     {
11855       // invalid animation part number specified
11856
11857       return ANIM_EVENT_NONE;
11858     }
11859   }
11860
11861   // discard result if next character is neither delimiter nor whitespace
11862   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11863         *s_ptr == ' ' || *s_ptr == '\t'))
11864     return ANIM_EVENT_NONE;
11865
11866   return result;
11867 }
11868
11869 static int get_anim_parameter_values(char *s)
11870 {
11871   int list_pos = ANIM_EVENT_UNDEFINED;
11872   int event_value = ANIM_EVENT_DEFAULT;
11873
11874   if (string_has_parameter(s, "any"))
11875     event_value |= ANIM_EVENT_ANY;
11876
11877   if (string_has_parameter(s, "click:self") ||
11878       string_has_parameter(s, "click") ||
11879       string_has_parameter(s, "self"))
11880     event_value |= ANIM_EVENT_SELF;
11881
11882   if (string_has_parameter(s, "unclick:any"))
11883     event_value |= ANIM_EVENT_UNCLICK_ANY;
11884
11885   // if animation event found, add it to global animation event list
11886   if (event_value != ANIM_EVENT_NONE)
11887     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11888
11889   while (s != NULL)
11890   {
11891     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11892     event_value = get_anim_parameter_value(s);
11893
11894     // if animation event found, add it to global animation event list
11895     if (event_value != ANIM_EVENT_NONE)
11896       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11897
11898     // continue with next part of the string, starting with next comma
11899     s = strchr(s + 1, ',');
11900   }
11901
11902   return list_pos;
11903 }
11904
11905 static int get_anim_action_parameter_value(char *token)
11906 {
11907   // check most common default case first to massively speed things up
11908   if (strEqual(token, ARG_UNDEFINED))
11909     return ANIM_EVENT_ACTION_NONE;
11910
11911   int result = getImageIDFromToken(token);
11912
11913   if (result == -1)
11914   {
11915     char *gfx_token = getStringCat2("gfx.", token);
11916
11917     result = getImageIDFromToken(gfx_token);
11918
11919     checked_free(gfx_token);
11920   }
11921
11922   if (result == -1)
11923   {
11924     Key key = getKeyFromX11KeyName(token);
11925
11926     if (key != KSYM_UNDEFINED)
11927       result = -(int)key;
11928   }
11929
11930   if (result == -1)
11931     result = ANIM_EVENT_ACTION_NONE;
11932
11933   return result;
11934 }
11935
11936 int get_parameter_value(char *value_raw, char *suffix, int type)
11937 {
11938   char *value = getStringToLower(value_raw);
11939   int result = 0;       // probably a save default value
11940
11941   if (strEqual(suffix, ".direction"))
11942   {
11943     result = (strEqual(value, "left")  ? MV_LEFT :
11944               strEqual(value, "right") ? MV_RIGHT :
11945               strEqual(value, "up")    ? MV_UP :
11946               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
11947   }
11948   else if (strEqual(suffix, ".position"))
11949   {
11950     result = (strEqual(value, "left")   ? POS_LEFT :
11951               strEqual(value, "right")  ? POS_RIGHT :
11952               strEqual(value, "top")    ? POS_TOP :
11953               strEqual(value, "upper")  ? POS_UPPER :
11954               strEqual(value, "middle") ? POS_MIDDLE :
11955               strEqual(value, "lower")  ? POS_LOWER :
11956               strEqual(value, "bottom") ? POS_BOTTOM :
11957               strEqual(value, "any")    ? POS_ANY :
11958               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
11959   }
11960   else if (strEqual(suffix, ".align"))
11961   {
11962     result = (strEqual(value, "left")   ? ALIGN_LEFT :
11963               strEqual(value, "right")  ? ALIGN_RIGHT :
11964               strEqual(value, "center") ? ALIGN_CENTER :
11965               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11966   }
11967   else if (strEqual(suffix, ".valign"))
11968   {
11969     result = (strEqual(value, "top")    ? VALIGN_TOP :
11970               strEqual(value, "bottom") ? VALIGN_BOTTOM :
11971               strEqual(value, "middle") ? VALIGN_MIDDLE :
11972               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11973   }
11974   else if (strEqual(suffix, ".anim_mode"))
11975   {
11976     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
11977               string_has_parameter(value, "loop")       ? ANIM_LOOP :
11978               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
11979               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
11980               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
11981               string_has_parameter(value, "random")     ? ANIM_RANDOM :
11982               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11983               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
11984               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
11985               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
11986               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11987               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
11988               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
11989               string_has_parameter(value, "all")        ? ANIM_ALL :
11990               string_has_parameter(value, "tiled")      ? ANIM_TILED :
11991               ANIM_DEFAULT);
11992
11993     if (string_has_parameter(value, "once"))
11994       result |= ANIM_ONCE;
11995
11996     if (string_has_parameter(value, "reverse"))
11997       result |= ANIM_REVERSE;
11998
11999     if (string_has_parameter(value, "opaque_player"))
12000       result |= ANIM_OPAQUE_PLAYER;
12001
12002     if (string_has_parameter(value, "static_panel"))
12003       result |= ANIM_STATIC_PANEL;
12004   }
12005   else if (strEqual(suffix, ".init_event") ||
12006            strEqual(suffix, ".anim_event"))
12007   {
12008     result = get_anim_parameter_values(value);
12009   }
12010   else if (strEqual(suffix, ".init_delay_action") ||
12011            strEqual(suffix, ".anim_delay_action") ||
12012            strEqual(suffix, ".post_delay_action") ||
12013            strEqual(suffix, ".init_event_action") ||
12014            strEqual(suffix, ".anim_event_action"))
12015   {
12016     result = get_anim_action_parameter_value(value_raw);
12017   }
12018   else if (strEqual(suffix, ".class"))
12019   {
12020     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12021               get_hash_from_key(value));
12022   }
12023   else if (strEqual(suffix, ".style"))
12024   {
12025     result = STYLE_DEFAULT;
12026
12027     if (string_has_parameter(value, "accurate_borders"))
12028       result |= STYLE_ACCURATE_BORDERS;
12029
12030     if (string_has_parameter(value, "inner_corners"))
12031       result |= STYLE_INNER_CORNERS;
12032
12033     if (string_has_parameter(value, "reverse"))
12034       result |= STYLE_REVERSE;
12035
12036     if (string_has_parameter(value, "leftmost_position"))
12037       result |= STYLE_LEFTMOST_POSITION;
12038
12039     if (string_has_parameter(value, "block_clicks"))
12040       result |= STYLE_BLOCK;
12041
12042     if (string_has_parameter(value, "passthrough_clicks"))
12043       result |= STYLE_PASSTHROUGH;
12044
12045     if (string_has_parameter(value, "multiple_actions"))
12046       result |= STYLE_MULTIPLE_ACTIONS;
12047   }
12048   else if (strEqual(suffix, ".fade_mode"))
12049   {
12050     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12051               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12052               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12053               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12054               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12055               FADE_MODE_DEFAULT);
12056   }
12057   else if (strEqual(suffix, ".auto_delay_unit"))
12058   {
12059     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12060               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12061               AUTO_DELAY_UNIT_DEFAULT);
12062   }
12063   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12064   {
12065     result = gfx.get_font_from_token_function(value);
12066   }
12067   else          // generic parameter of type integer or boolean
12068   {
12069     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12070               type == TYPE_INTEGER ? get_integer_from_string(value) :
12071               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12072               ARG_UNDEFINED_VALUE);
12073   }
12074
12075   free(value);
12076
12077   return result;
12078 }
12079
12080 static int get_token_parameter_value(char *token, char *value_raw)
12081 {
12082   char *suffix;
12083
12084   if (token == NULL || value_raw == NULL)
12085     return ARG_UNDEFINED_VALUE;
12086
12087   suffix = strrchr(token, '.');
12088   if (suffix == NULL)
12089     suffix = token;
12090
12091   if (strEqual(suffix, ".element"))
12092     return getElementFromToken(value_raw);
12093
12094   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12095   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12096 }
12097
12098 void InitMenuDesignSettings_Static(void)
12099 {
12100   int i;
12101
12102   // always start with reliable default values from static default config
12103   for (i = 0; image_config_vars[i].token != NULL; i++)
12104   {
12105     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
12106
12107     if (value != NULL)
12108       *image_config_vars[i].value =
12109         get_token_parameter_value(image_config_vars[i].token, value);
12110   }
12111 }
12112
12113 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12114 {
12115   int i;
12116
12117   // the following initializes hierarchical values from static configuration
12118
12119   // special case: initialize "ARG_DEFAULT" values in static default config
12120   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12121   titlescreen_initial_first_default.fade_mode  =
12122     title_initial_first_default.fade_mode;
12123   titlescreen_initial_first_default.fade_delay =
12124     title_initial_first_default.fade_delay;
12125   titlescreen_initial_first_default.post_delay =
12126     title_initial_first_default.post_delay;
12127   titlescreen_initial_first_default.auto_delay =
12128     title_initial_first_default.auto_delay;
12129   titlescreen_initial_first_default.auto_delay_unit =
12130     title_initial_first_default.auto_delay_unit;
12131   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
12132   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12133   titlescreen_first_default.post_delay = title_first_default.post_delay;
12134   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12135   titlescreen_first_default.auto_delay_unit =
12136     title_first_default.auto_delay_unit;
12137   titlemessage_initial_first_default.fade_mode  =
12138     title_initial_first_default.fade_mode;
12139   titlemessage_initial_first_default.fade_delay =
12140     title_initial_first_default.fade_delay;
12141   titlemessage_initial_first_default.post_delay =
12142     title_initial_first_default.post_delay;
12143   titlemessage_initial_first_default.auto_delay =
12144     title_initial_first_default.auto_delay;
12145   titlemessage_initial_first_default.auto_delay_unit =
12146     title_initial_first_default.auto_delay_unit;
12147   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
12148   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12149   titlemessage_first_default.post_delay = title_first_default.post_delay;
12150   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12151   titlemessage_first_default.auto_delay_unit =
12152     title_first_default.auto_delay_unit;
12153
12154   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
12155   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12156   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12157   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12158   titlescreen_initial_default.auto_delay_unit =
12159     title_initial_default.auto_delay_unit;
12160   titlescreen_default.fade_mode  = title_default.fade_mode;
12161   titlescreen_default.fade_delay = title_default.fade_delay;
12162   titlescreen_default.post_delay = title_default.post_delay;
12163   titlescreen_default.auto_delay = title_default.auto_delay;
12164   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12165   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
12166   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12167   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12168   titlemessage_initial_default.auto_delay_unit =
12169     title_initial_default.auto_delay_unit;
12170   titlemessage_default.fade_mode  = title_default.fade_mode;
12171   titlemessage_default.fade_delay = title_default.fade_delay;
12172   titlemessage_default.post_delay = title_default.post_delay;
12173   titlemessage_default.auto_delay = title_default.auto_delay;
12174   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12175
12176   // special case: initialize "ARG_DEFAULT" values in static default config
12177   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12178   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12179   {
12180     titlescreen_initial_first[i] = titlescreen_initial_first_default;
12181     titlescreen_first[i] = titlescreen_first_default;
12182     titlemessage_initial_first[i] = titlemessage_initial_first_default;
12183     titlemessage_first[i] = titlemessage_first_default;
12184
12185     titlescreen_initial[i] = titlescreen_initial_default;
12186     titlescreen[i] = titlescreen_default;
12187     titlemessage_initial[i] = titlemessage_initial_default;
12188     titlemessage[i] = titlemessage_default;
12189   }
12190
12191   // special case: initialize "ARG_DEFAULT" values in static default config
12192   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12193   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12194   {
12195     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
12196       continue;
12197
12198     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12199     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12200     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12201   }
12202
12203   // special case: initialize "ARG_DEFAULT" values in static default config
12204   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12205   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12206   {
12207     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12208     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12209     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12210
12211     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
12212       continue;
12213
12214     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12215   }
12216 }
12217
12218 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12219 {
12220   static struct
12221   {
12222     struct XY *dst, *src;
12223   }
12224   game_buttons_xy[] =
12225   {
12226     { &game.button.save,        &game.button.stop       },
12227     { &game.button.pause2,      &game.button.pause      },
12228     { &game.button.load,        &game.button.play       },
12229     { &game.button.undo,        &game.button.stop       },
12230     { &game.button.redo,        &game.button.play       },
12231
12232     { NULL,                     NULL                    }
12233   };
12234   int i, j;
12235
12236   // special case: initialize later added SETUP list size from LEVELS value
12237   if (menu.list_size[GAME_MODE_SETUP] == -1)
12238     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12239
12240   // set default position for snapshot buttons to stop/pause/play buttons
12241   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12242     if ((*game_buttons_xy[i].dst).x == -1 &&
12243         (*game_buttons_xy[i].dst).y == -1)
12244       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12245
12246   // --------------------------------------------------------------------------
12247   // dynamic viewports (including playfield margins, borders and alignments)
12248   // --------------------------------------------------------------------------
12249
12250   // dynamic viewports currently only supported for landscape mode
12251   int display_width  = MAX(video.display_width, video.display_height);
12252   int display_height = MIN(video.display_width, video.display_height);
12253
12254   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12255   {
12256     struct RectWithBorder *vp_window    = &viewport.window[i];
12257     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12258     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
12259     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
12260     boolean dynamic_window_width     = (vp_window->min_width     != -1);
12261     boolean dynamic_window_height    = (vp_window->min_height    != -1);
12262     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
12263     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12264
12265     // adjust window size if min/max width/height is specified
12266
12267     if (vp_window->min_width != -1)
12268     {
12269       int window_width = display_width;
12270
12271       // when using static window height, use aspect ratio of display
12272       if (vp_window->min_height == -1)
12273         window_width = vp_window->height * display_width / display_height;
12274
12275       vp_window->width = MAX(vp_window->min_width, window_width);
12276     }
12277
12278     if (vp_window->min_height != -1)
12279     {
12280       int window_height = display_height;
12281
12282       // when using static window width, use aspect ratio of display
12283       if (vp_window->min_width == -1)
12284         window_height = vp_window->width * display_height / display_width;
12285
12286       vp_window->height = MAX(vp_window->min_height, window_height);
12287     }
12288
12289     if (vp_window->max_width != -1)
12290       vp_window->width = MIN(vp_window->width, vp_window->max_width);
12291
12292     if (vp_window->max_height != -1)
12293       vp_window->height = MIN(vp_window->height, vp_window->max_height);
12294
12295     int playfield_width  = vp_window->width;
12296     int playfield_height = vp_window->height;
12297
12298     // adjust playfield size and position according to specified margins
12299
12300     playfield_width  -= vp_playfield->margin_left;
12301     playfield_width  -= vp_playfield->margin_right;
12302
12303     playfield_height -= vp_playfield->margin_top;
12304     playfield_height -= vp_playfield->margin_bottom;
12305
12306     // adjust playfield size if min/max width/height is specified
12307
12308     if (vp_playfield->min_width != -1)
12309       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12310
12311     if (vp_playfield->min_height != -1)
12312       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12313
12314     if (vp_playfield->max_width != -1)
12315       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12316
12317     if (vp_playfield->max_height != -1)
12318       vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12319
12320     // adjust playfield position according to specified alignment
12321
12322     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12323       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12324     else if (vp_playfield->align == ALIGN_CENTER)
12325       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12326     else if (vp_playfield->align == ALIGN_RIGHT)
12327       vp_playfield->x += playfield_width - vp_playfield->width;
12328
12329     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12330       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12331     else if (vp_playfield->valign == VALIGN_MIDDLE)
12332       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12333     else if (vp_playfield->valign == VALIGN_BOTTOM)
12334       vp_playfield->y += playfield_height - vp_playfield->height;
12335
12336     vp_playfield->x += vp_playfield->margin_left;
12337     vp_playfield->y += vp_playfield->margin_top;
12338
12339     // adjust individual playfield borders if only default border is specified
12340
12341     if (vp_playfield->border_left == -1)
12342       vp_playfield->border_left = vp_playfield->border_size;
12343     if (vp_playfield->border_right == -1)
12344       vp_playfield->border_right = vp_playfield->border_size;
12345     if (vp_playfield->border_top == -1)
12346       vp_playfield->border_top = vp_playfield->border_size;
12347     if (vp_playfield->border_bottom == -1)
12348       vp_playfield->border_bottom = vp_playfield->border_size;
12349
12350     // set dynamic playfield borders if borders are specified as undefined
12351     // (but only if window size was dynamic and playfield size was static)
12352
12353     if (dynamic_window_width && !dynamic_playfield_width)
12354     {
12355       if (vp_playfield->border_left == -1)
12356       {
12357         vp_playfield->border_left = (vp_playfield->x -
12358                                      vp_playfield->margin_left);
12359         vp_playfield->x     -= vp_playfield->border_left;
12360         vp_playfield->width += vp_playfield->border_left;
12361       }
12362
12363       if (vp_playfield->border_right == -1)
12364       {
12365         vp_playfield->border_right = (vp_window->width -
12366                                       vp_playfield->x -
12367                                       vp_playfield->width -
12368                                       vp_playfield->margin_right);
12369         vp_playfield->width += vp_playfield->border_right;
12370       }
12371     }
12372
12373     if (dynamic_window_height && !dynamic_playfield_height)
12374     {
12375       if (vp_playfield->border_top == -1)
12376       {
12377         vp_playfield->border_top = (vp_playfield->y -
12378                                     vp_playfield->margin_top);
12379         vp_playfield->y      -= vp_playfield->border_top;
12380         vp_playfield->height += vp_playfield->border_top;
12381       }
12382
12383       if (vp_playfield->border_bottom == -1)
12384       {
12385         vp_playfield->border_bottom = (vp_window->height -
12386                                        vp_playfield->y -
12387                                        vp_playfield->height -
12388                                        vp_playfield->margin_bottom);
12389         vp_playfield->height += vp_playfield->border_bottom;
12390       }
12391     }
12392
12393     // adjust playfield size to be a multiple of a defined alignment tile size
12394
12395     int align_size = vp_playfield->align_size;
12396     int playfield_xtiles = vp_playfield->width  / align_size;
12397     int playfield_ytiles = vp_playfield->height / align_size;
12398     int playfield_width_corrected  = playfield_xtiles * align_size;
12399     int playfield_height_corrected = playfield_ytiles * align_size;
12400     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12401                                  i == GFX_SPECIAL_ARG_EDITOR);
12402
12403     if (is_playfield_mode &&
12404         dynamic_playfield_width &&
12405         vp_playfield->width != playfield_width_corrected)
12406     {
12407       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12408
12409       vp_playfield->width = playfield_width_corrected;
12410
12411       if (vp_playfield->align == ALIGN_LEFT)
12412       {
12413         vp_playfield->border_left += playfield_xdiff;
12414       }
12415       else if (vp_playfield->align == ALIGN_RIGHT)
12416       {
12417         vp_playfield->border_right += playfield_xdiff;
12418       }
12419       else if (vp_playfield->align == ALIGN_CENTER)
12420       {
12421         int border_left_diff  = playfield_xdiff / 2;
12422         int border_right_diff = playfield_xdiff - border_left_diff;
12423
12424         vp_playfield->border_left  += border_left_diff;
12425         vp_playfield->border_right += border_right_diff;
12426       }
12427     }
12428
12429     if (is_playfield_mode &&
12430         dynamic_playfield_height &&
12431         vp_playfield->height != playfield_height_corrected)
12432     {
12433       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12434
12435       vp_playfield->height = playfield_height_corrected;
12436
12437       if (vp_playfield->valign == VALIGN_TOP)
12438       {
12439         vp_playfield->border_top += playfield_ydiff;
12440       }
12441       else if (vp_playfield->align == VALIGN_BOTTOM)
12442       {
12443         vp_playfield->border_right += playfield_ydiff;
12444       }
12445       else if (vp_playfield->align == VALIGN_MIDDLE)
12446       {
12447         int border_top_diff    = playfield_ydiff / 2;
12448         int border_bottom_diff = playfield_ydiff - border_top_diff;
12449
12450         vp_playfield->border_top    += border_top_diff;
12451         vp_playfield->border_bottom += border_bottom_diff;
12452       }
12453     }
12454
12455     // adjust door positions according to specified alignment
12456
12457     for (j = 0; j < 2; j++)
12458     {
12459       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12460
12461       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12462         vp_door->x = ALIGNED_VP_XPOS(vp_door);
12463       else if (vp_door->align == ALIGN_CENTER)
12464         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12465       else if (vp_door->align == ALIGN_RIGHT)
12466         vp_door->x += vp_window->width - vp_door->width;
12467
12468       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12469         vp_door->y = ALIGNED_VP_YPOS(vp_door);
12470       else if (vp_door->valign == VALIGN_MIDDLE)
12471         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12472       else if (vp_door->valign == VALIGN_BOTTOM)
12473         vp_door->y += vp_window->height - vp_door->height;
12474     }
12475   }
12476 }
12477
12478 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12479 {
12480   static struct
12481   {
12482     struct XYTileSize *dst, *src;
12483     int graphic;
12484   }
12485   editor_buttons_xy[] =
12486   {
12487     {
12488       &editor.button.element_left,      &editor.palette.element_left,
12489       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12490     },
12491     {
12492       &editor.button.element_middle,    &editor.palette.element_middle,
12493       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12494     },
12495     {
12496       &editor.button.element_right,     &editor.palette.element_right,
12497       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12498     },
12499
12500     { NULL,                     NULL                    }
12501   };
12502   int i;
12503
12504   // set default position for element buttons to element graphics
12505   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12506   {
12507     if ((*editor_buttons_xy[i].dst).x == -1 &&
12508         (*editor_buttons_xy[i].dst).y == -1)
12509     {
12510       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12511
12512       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12513
12514       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12515     }
12516   }
12517
12518   // adjust editor palette rows and columns if specified to be dynamic
12519
12520   if (editor.palette.cols == -1)
12521   {
12522     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12523     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12524     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12525
12526     editor.palette.cols = (vp_width - sc_width) / bt_width;
12527
12528     if (editor.palette.x == -1)
12529     {
12530       int palette_width = editor.palette.cols * bt_width + sc_width;
12531
12532       editor.palette.x = (vp_width - palette_width) / 2;
12533     }
12534   }
12535
12536   if (editor.palette.rows == -1)
12537   {
12538     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12539     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12540     int tx_height = getFontHeight(FONT_TEXT_2);
12541
12542     editor.palette.rows = (vp_height - tx_height) / bt_height;
12543
12544     if (editor.palette.y == -1)
12545     {
12546       int palette_height = editor.palette.rows * bt_height + tx_height;
12547
12548       editor.palette.y = (vp_height - palette_height) / 2;
12549     }
12550   }
12551 }
12552
12553 static void LoadMenuDesignSettingsFromFilename(char *filename)
12554 {
12555   static struct TitleFadingInfo tfi;
12556   static struct TitleMessageInfo tmi;
12557   static struct TokenInfo title_tokens[] =
12558   {
12559     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
12560     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
12561     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
12562     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
12563     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
12564
12565     { -1,               NULL,                   NULL                    }
12566   };
12567   static struct TokenInfo titlemessage_tokens[] =
12568   {
12569     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
12570     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
12571     { TYPE_INTEGER,     &tmi.width,             ".width"                },
12572     { TYPE_INTEGER,     &tmi.height,            ".height"               },
12573     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
12574     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
12575     { TYPE_INTEGER,     &tmi.align,             ".align"                },
12576     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
12577     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
12578     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
12579     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
12580     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
12581     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
12582     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
12583     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
12584     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
12585     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
12586     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
12587
12588     { -1,               NULL,                   NULL                    }
12589   };
12590   static struct
12591   {
12592     struct TitleFadingInfo *info;
12593     char *text;
12594   }
12595   title_info[] =
12596   {
12597     // initialize first titles from "enter screen" definitions, if defined
12598     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
12599     { &title_first_default,             "menu.enter_screen.TITLE"       },
12600
12601     // initialize title screens from "next screen" definitions, if defined
12602     { &title_initial_default,           "menu.next_screen.TITLE"        },
12603     { &title_default,                   "menu.next_screen.TITLE"        },
12604
12605     { NULL,                             NULL                            }
12606   };
12607   static struct
12608   {
12609     struct TitleMessageInfo *array;
12610     char *text;
12611   }
12612   titlemessage_arrays[] =
12613   {
12614     // initialize first titles from "enter screen" definitions, if defined
12615     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
12616     { titlescreen_first,                "menu.enter_screen.TITLE"       },
12617     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
12618     { titlemessage_first,               "menu.enter_screen.TITLE"       },
12619
12620     // initialize titles from "next screen" definitions, if defined
12621     { titlescreen_initial,              "menu.next_screen.TITLE"        },
12622     { titlescreen,                      "menu.next_screen.TITLE"        },
12623     { titlemessage_initial,             "menu.next_screen.TITLE"        },
12624     { titlemessage,                     "menu.next_screen.TITLE"        },
12625
12626     // overwrite titles with title definitions, if defined
12627     { titlescreen_initial_first,        "[title_initial]"               },
12628     { titlescreen_first,                "[title]"                       },
12629     { titlemessage_initial_first,       "[title_initial]"               },
12630     { titlemessage_first,               "[title]"                       },
12631
12632     { titlescreen_initial,              "[title_initial]"               },
12633     { titlescreen,                      "[title]"                       },
12634     { titlemessage_initial,             "[title_initial]"               },
12635     { titlemessage,                     "[title]"                       },
12636
12637     // overwrite titles with title screen/message definitions, if defined
12638     { titlescreen_initial_first,        "[titlescreen_initial]"         },
12639     { titlescreen_first,                "[titlescreen]"                 },
12640     { titlemessage_initial_first,       "[titlemessage_initial]"        },
12641     { titlemessage_first,               "[titlemessage]"                },
12642
12643     { titlescreen_initial,              "[titlescreen_initial]"         },
12644     { titlescreen,                      "[titlescreen]"                 },
12645     { titlemessage_initial,             "[titlemessage_initial]"        },
12646     { titlemessage,                     "[titlemessage]"                },
12647
12648     { NULL,                             NULL                            }
12649   };
12650   SetupFileHash *setup_file_hash;
12651   int i, j, k;
12652
12653   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12654     return;
12655
12656   // the following initializes hierarchical values from dynamic configuration
12657
12658   // special case: initialize with default values that may be overwritten
12659   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12660   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12661   {
12662     struct TokenIntPtrInfo menu_config[] =
12663     {
12664       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
12665       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
12666       { "menu.list_size",       &menu.list_size[i]      }
12667     };
12668
12669     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12670     {
12671       char *token = menu_config[j].token;
12672       char *value = getHashEntry(setup_file_hash, token);
12673
12674       if (value != NULL)
12675         *menu_config[j].value = get_integer_from_string(value);
12676     }
12677   }
12678
12679   // special case: initialize with default values that may be overwritten
12680   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12681   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12682   {
12683     struct TokenIntPtrInfo menu_config[] =
12684     {
12685       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
12686       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
12687       { "menu.list_size.INFO",          &menu.list_size_info[i]         }
12688     };
12689
12690     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12691     {
12692       char *token = menu_config[j].token;
12693       char *value = getHashEntry(setup_file_hash, token);
12694
12695       if (value != NULL)
12696         *menu_config[j].value = get_integer_from_string(value);
12697     }
12698   }
12699
12700   // special case: initialize with default values that may be overwritten
12701   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12702   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12703   {
12704     struct TokenIntPtrInfo menu_config[] =
12705     {
12706       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
12707       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
12708     };
12709
12710     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12711     {
12712       char *token = menu_config[j].token;
12713       char *value = getHashEntry(setup_file_hash, token);
12714
12715       if (value != NULL)
12716         *menu_config[j].value = get_integer_from_string(value);
12717     }
12718   }
12719
12720   // special case: initialize with default values that may be overwritten
12721   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12722   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12723   {
12724     struct TokenIntPtrInfo menu_config[] =
12725     {
12726       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
12727       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
12728       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
12729       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
12730       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
12731       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
12732       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
12733       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
12734       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
12735     };
12736
12737     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12738     {
12739       char *token = menu_config[j].token;
12740       char *value = getHashEntry(setup_file_hash, token);
12741
12742       if (value != NULL)
12743         *menu_config[j].value = get_integer_from_string(value);
12744     }
12745   }
12746
12747   // special case: initialize with default values that may be overwritten
12748   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12749   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12750   {
12751     struct TokenIntPtrInfo menu_config[] =
12752     {
12753       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
12754       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12755       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12756       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
12757       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12758       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12759       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
12760       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
12761       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
12762     };
12763
12764     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12765     {
12766       char *token = menu_config[j].token;
12767       char *value = getHashEntry(setup_file_hash, token);
12768
12769       if (value != NULL)
12770         *menu_config[j].value = get_token_parameter_value(token, value);
12771     }
12772   }
12773
12774   // special case: initialize with default values that may be overwritten
12775   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12776   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12777   {
12778     struct
12779     {
12780       char *token_prefix;
12781       struct RectWithBorder *struct_ptr;
12782     }
12783     vp_struct[] =
12784     {
12785       { "viewport.window",      &viewport.window[i]     },
12786       { "viewport.playfield",   &viewport.playfield[i]  },
12787       { "viewport.door_1",      &viewport.door_1[i]     },
12788       { "viewport.door_2",      &viewport.door_2[i]     }
12789     };
12790
12791     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12792     {
12793       struct TokenIntPtrInfo vp_config[] =
12794       {
12795         { ".x",                 &vp_struct[j].struct_ptr->x             },
12796         { ".y",                 &vp_struct[j].struct_ptr->y             },
12797         { ".width",             &vp_struct[j].struct_ptr->width         },
12798         { ".height",            &vp_struct[j].struct_ptr->height        },
12799         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
12800         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
12801         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
12802         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
12803         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
12804         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
12805         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
12806         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
12807         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
12808         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
12809         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
12810         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
12811         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
12812         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
12813         { ".align",             &vp_struct[j].struct_ptr->align         },
12814         { ".valign",            &vp_struct[j].struct_ptr->valign        }
12815       };
12816
12817       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12818       {
12819         char *token = getStringCat2(vp_struct[j].token_prefix,
12820                                     vp_config[k].token);
12821         char *value = getHashEntry(setup_file_hash, token);
12822
12823         if (value != NULL)
12824           *vp_config[k].value = get_token_parameter_value(token, value);
12825
12826         free(token);
12827       }
12828     }
12829   }
12830
12831   // special case: initialize with default values that may be overwritten
12832   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12833   for (i = 0; title_info[i].info != NULL; i++)
12834   {
12835     struct TitleFadingInfo *info = title_info[i].info;
12836     char *base_token = title_info[i].text;
12837
12838     for (j = 0; title_tokens[j].type != -1; j++)
12839     {
12840       char *token = getStringCat2(base_token, title_tokens[j].text);
12841       char *value = getHashEntry(setup_file_hash, token);
12842
12843       if (value != NULL)
12844       {
12845         int parameter_value = get_token_parameter_value(token, value);
12846
12847         tfi = *info;
12848
12849         *(int *)title_tokens[j].value = (int)parameter_value;
12850
12851         *info = tfi;
12852       }
12853
12854       free(token);
12855     }
12856   }
12857
12858   // special case: initialize with default values that may be overwritten
12859   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12860   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12861   {
12862     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12863     char *base_token = titlemessage_arrays[i].text;
12864
12865     for (j = 0; titlemessage_tokens[j].type != -1; j++)
12866     {
12867       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12868       char *value = getHashEntry(setup_file_hash, token);
12869
12870       if (value != NULL)
12871       {
12872         int parameter_value = get_token_parameter_value(token, value);
12873
12874         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12875         {
12876           tmi = array[k];
12877
12878           if (titlemessage_tokens[j].type == TYPE_INTEGER)
12879             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
12880           else
12881             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12882
12883           array[k] = tmi;
12884         }
12885       }
12886
12887       free(token);
12888     }
12889   }
12890
12891   // special case: check if network and preview player positions are redefined,
12892   // to compare this later against the main menu level preview being redefined
12893   struct TokenIntPtrInfo menu_config_players[] =
12894   {
12895     { "main.network_players.x", &menu.main.network_players.redefined    },
12896     { "main.network_players.y", &menu.main.network_players.redefined    },
12897     { "main.preview_players.x", &menu.main.preview_players.redefined    },
12898     { "main.preview_players.y", &menu.main.preview_players.redefined    },
12899     { "preview.x",              &preview.redefined                      },
12900     { "preview.y",              &preview.redefined                      }
12901   };
12902
12903   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12904     *menu_config_players[i].value = FALSE;
12905
12906   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12907     if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
12908       *menu_config_players[i].value = TRUE;
12909
12910   // read (and overwrite with) values that may be specified in config file
12911   for (i = 0; image_config_vars[i].token != NULL; i++)
12912   {
12913     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12914
12915     // (ignore definitions set to "[DEFAULT]" which are already initialized)
12916     if (value != NULL && !strEqual(value, ARG_DEFAULT))
12917       *image_config_vars[i].value =
12918         get_token_parameter_value(image_config_vars[i].token, value);
12919   }
12920
12921   freeSetupFileHash(setup_file_hash);
12922 }
12923
12924 void LoadMenuDesignSettings(void)
12925 {
12926   char *filename_base = UNDEFINED_FILENAME, *filename_local;
12927
12928   InitMenuDesignSettings_Static();
12929   InitMenuDesignSettings_SpecialPreProcessing();
12930
12931   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12932   {
12933     // first look for special settings configured in level series config
12934     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12935
12936     if (fileExists(filename_base))
12937       LoadMenuDesignSettingsFromFilename(filename_base);
12938   }
12939
12940   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12941
12942   if (filename_local != NULL && !strEqual(filename_base, filename_local))
12943     LoadMenuDesignSettingsFromFilename(filename_local);
12944
12945   InitMenuDesignSettings_SpecialPostProcessing();
12946 }
12947
12948 void LoadMenuDesignSettings_AfterGraphics(void)
12949 {
12950   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12951 }
12952
12953 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12954 {
12955   char *filename = getEditorSetupFilename();
12956   SetupFileList *setup_file_list, *list;
12957   SetupFileHash *element_hash;
12958   int num_unknown_tokens = 0;
12959   int i;
12960
12961   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12962     return;
12963
12964   element_hash = newSetupFileHash();
12965
12966   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12967     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12968
12969   // determined size may be larger than needed (due to unknown elements)
12970   *num_elements = 0;
12971   for (list = setup_file_list; list != NULL; list = list->next)
12972     (*num_elements)++;
12973
12974   // add space for up to 3 more elements for padding that may be needed
12975   *num_elements += 3;
12976
12977   // free memory for old list of elements, if needed
12978   checked_free(*elements);
12979
12980   // allocate memory for new list of elements
12981   *elements = checked_malloc(*num_elements * sizeof(int));
12982
12983   *num_elements = 0;
12984   for (list = setup_file_list; list != NULL; list = list->next)
12985   {
12986     char *value = getHashEntry(element_hash, list->token);
12987
12988     if (value == NULL)          // try to find obsolete token mapping
12989     {
12990       char *mapped_token = get_mapped_token(list->token);
12991
12992       if (mapped_token != NULL)
12993       {
12994         value = getHashEntry(element_hash, mapped_token);
12995
12996         free(mapped_token);
12997       }
12998     }
12999
13000     if (value != NULL)
13001     {
13002       (*elements)[(*num_elements)++] = atoi(value);
13003     }
13004     else
13005     {
13006       if (num_unknown_tokens == 0)
13007       {
13008         Warn("---");
13009         Warn("unknown token(s) found in config file:");
13010         Warn("- config file: '%s'", filename);
13011
13012         num_unknown_tokens++;
13013       }
13014
13015       Warn("- token: '%s'", list->token);
13016     }
13017   }
13018
13019   if (num_unknown_tokens > 0)
13020     Warn("---");
13021
13022   while (*num_elements % 4)     // pad with empty elements, if needed
13023     (*elements)[(*num_elements)++] = EL_EMPTY;
13024
13025   freeSetupFileList(setup_file_list);
13026   freeSetupFileHash(element_hash);
13027
13028 #if 0
13029   for (i = 0; i < *num_elements; i++)
13030     Debug("editor", "element '%s' [%d]\n",
13031           element_info[(*elements)[i]].token_name, (*elements)[i]);
13032 #endif
13033 }
13034
13035 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13036                                                      boolean is_sound)
13037 {
13038   SetupFileHash *setup_file_hash = NULL;
13039   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13040   char *filename_music, *filename_prefix, *filename_info;
13041   struct
13042   {
13043     char *token;
13044     char **value_ptr;
13045   }
13046   token_to_value_ptr[] =
13047   {
13048     { "title_header",   &tmp_music_file_info.title_header       },
13049     { "artist_header",  &tmp_music_file_info.artist_header      },
13050     { "album_header",   &tmp_music_file_info.album_header       },
13051     { "year_header",    &tmp_music_file_info.year_header        },
13052
13053     { "title",          &tmp_music_file_info.title              },
13054     { "artist",         &tmp_music_file_info.artist             },
13055     { "album",          &tmp_music_file_info.album              },
13056     { "year",           &tmp_music_file_info.year               },
13057
13058     { NULL,             NULL                                    },
13059   };
13060   int i;
13061
13062   filename_music = (is_sound ? getCustomSoundFilename(basename) :
13063                     getCustomMusicFilename(basename));
13064
13065   if (filename_music == NULL)
13066     return NULL;
13067
13068   // ---------- try to replace file extension ----------
13069
13070   filename_prefix = getStringCopy(filename_music);
13071   if (strrchr(filename_prefix, '.') != NULL)
13072     *strrchr(filename_prefix, '.') = '\0';
13073   filename_info = getStringCat2(filename_prefix, ".txt");
13074
13075   if (fileExists(filename_info))
13076     setup_file_hash = loadSetupFileHash(filename_info);
13077
13078   free(filename_prefix);
13079   free(filename_info);
13080
13081   if (setup_file_hash == NULL)
13082   {
13083     // ---------- try to add file extension ----------
13084
13085     filename_prefix = getStringCopy(filename_music);
13086     filename_info = getStringCat2(filename_prefix, ".txt");
13087
13088     if (fileExists(filename_info))
13089       setup_file_hash = loadSetupFileHash(filename_info);
13090
13091     free(filename_prefix);
13092     free(filename_info);
13093   }
13094
13095   if (setup_file_hash == NULL)
13096     return NULL;
13097
13098   // ---------- music file info found ----------
13099
13100   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13101
13102   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13103   {
13104     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13105
13106     *token_to_value_ptr[i].value_ptr =
13107       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13108   }
13109
13110   tmp_music_file_info.basename = getStringCopy(basename);
13111   tmp_music_file_info.music = music;
13112   tmp_music_file_info.is_sound = is_sound;
13113
13114   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13115   *new_music_file_info = tmp_music_file_info;
13116
13117   return new_music_file_info;
13118 }
13119
13120 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13121 {
13122   return get_music_file_info_ext(basename, music, FALSE);
13123 }
13124
13125 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13126 {
13127   return get_music_file_info_ext(basename, sound, TRUE);
13128 }
13129
13130 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13131                                      char *basename, boolean is_sound)
13132 {
13133   for (; list != NULL; list = list->next)
13134     if (list->is_sound == is_sound && strEqual(list->basename, basename))
13135       return TRUE;
13136
13137   return FALSE;
13138 }
13139
13140 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13141 {
13142   return music_info_listed_ext(list, basename, FALSE);
13143 }
13144
13145 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13146 {
13147   return music_info_listed_ext(list, basename, TRUE);
13148 }
13149
13150 void LoadMusicInfo(void)
13151 {
13152   char *music_directory = getCustomMusicDirectory();
13153   int num_music = getMusicListSize();
13154   int num_music_noconf = 0;
13155   int num_sounds = getSoundListSize();
13156   Directory *dir;
13157   DirectoryEntry *dir_entry;
13158   struct FileInfo *music, *sound;
13159   struct MusicFileInfo *next, **new;
13160   int i;
13161
13162   while (music_file_info != NULL)
13163   {
13164     next = music_file_info->next;
13165
13166     checked_free(music_file_info->basename);
13167
13168     checked_free(music_file_info->title_header);
13169     checked_free(music_file_info->artist_header);
13170     checked_free(music_file_info->album_header);
13171     checked_free(music_file_info->year_header);
13172
13173     checked_free(music_file_info->title);
13174     checked_free(music_file_info->artist);
13175     checked_free(music_file_info->album);
13176     checked_free(music_file_info->year);
13177
13178     free(music_file_info);
13179
13180     music_file_info = next;
13181   }
13182
13183   new = &music_file_info;
13184
13185   for (i = 0; i < num_music; i++)
13186   {
13187     music = getMusicListEntry(i);
13188
13189     if (music->filename == NULL)
13190       continue;
13191
13192     if (strEqual(music->filename, UNDEFINED_FILENAME))
13193       continue;
13194
13195     // a configured file may be not recognized as music
13196     if (!FileIsMusic(music->filename))
13197       continue;
13198
13199     if (!music_info_listed(music_file_info, music->filename))
13200     {
13201       *new = get_music_file_info(music->filename, i);
13202
13203       if (*new != NULL)
13204         new = &(*new)->next;
13205     }
13206   }
13207
13208   if ((dir = openDirectory(music_directory)) == NULL)
13209   {
13210     Warn("cannot read music directory '%s'", music_directory);
13211
13212     return;
13213   }
13214
13215   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
13216   {
13217     char *basename = dir_entry->basename;
13218     boolean music_already_used = FALSE;
13219     int i;
13220
13221     // skip all music files that are configured in music config file
13222     for (i = 0; i < num_music; i++)
13223     {
13224       music = getMusicListEntry(i);
13225
13226       if (music->filename == NULL)
13227         continue;
13228
13229       if (strEqual(basename, music->filename))
13230       {
13231         music_already_used = TRUE;
13232         break;
13233       }
13234     }
13235
13236     if (music_already_used)
13237       continue;
13238
13239     if (!FileIsMusic(dir_entry->filename))
13240       continue;
13241
13242     if (!music_info_listed(music_file_info, basename))
13243     {
13244       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
13245
13246       if (*new != NULL)
13247         new = &(*new)->next;
13248     }
13249
13250     num_music_noconf++;
13251   }
13252
13253   closeDirectory(dir);
13254
13255   for (i = 0; i < num_sounds; i++)
13256   {
13257     sound = getSoundListEntry(i);
13258
13259     if (sound->filename == NULL)
13260       continue;
13261
13262     if (strEqual(sound->filename, UNDEFINED_FILENAME))
13263       continue;
13264
13265     // a configured file may be not recognized as sound
13266     if (!FileIsSound(sound->filename))
13267       continue;
13268
13269     if (!sound_info_listed(music_file_info, sound->filename))
13270     {
13271       *new = get_sound_file_info(sound->filename, i);
13272       if (*new != NULL)
13273         new = &(*new)->next;
13274     }
13275   }
13276 }
13277
13278 static void add_helpanim_entry(int element, int action, int direction,
13279                                int delay, int *num_list_entries)
13280 {
13281   struct HelpAnimInfo *new_list_entry;
13282   (*num_list_entries)++;
13283
13284   helpanim_info =
13285     checked_realloc(helpanim_info,
13286                     *num_list_entries * sizeof(struct HelpAnimInfo));
13287   new_list_entry = &helpanim_info[*num_list_entries - 1];
13288
13289   new_list_entry->element = element;
13290   new_list_entry->action = action;
13291   new_list_entry->direction = direction;
13292   new_list_entry->delay = delay;
13293 }
13294
13295 static void print_unknown_token(char *filename, char *token, int token_nr)
13296 {
13297   if (token_nr == 0)
13298   {
13299     Warn("---");
13300     Warn("unknown token(s) found in config file:");
13301     Warn("- config file: '%s'", filename);
13302   }
13303
13304   Warn("- token: '%s'", token);
13305 }
13306
13307 static void print_unknown_token_end(int token_nr)
13308 {
13309   if (token_nr > 0)
13310     Warn("---");
13311 }
13312
13313 void LoadHelpAnimInfo(void)
13314 {
13315   char *filename = getHelpAnimFilename();
13316   SetupFileList *setup_file_list = NULL, *list;
13317   SetupFileHash *element_hash, *action_hash, *direction_hash;
13318   int num_list_entries = 0;
13319   int num_unknown_tokens = 0;
13320   int i;
13321
13322   if (fileExists(filename))
13323     setup_file_list = loadSetupFileList(filename);
13324
13325   if (setup_file_list == NULL)
13326   {
13327     // use reliable default values from static configuration
13328     SetupFileList *insert_ptr;
13329
13330     insert_ptr = setup_file_list =
13331       newSetupFileList(helpanim_config[0].token,
13332                        helpanim_config[0].value);
13333
13334     for (i = 1; helpanim_config[i].token; i++)
13335       insert_ptr = addListEntry(insert_ptr,
13336                                 helpanim_config[i].token,
13337                                 helpanim_config[i].value);
13338   }
13339
13340   element_hash   = newSetupFileHash();
13341   action_hash    = newSetupFileHash();
13342   direction_hash = newSetupFileHash();
13343
13344   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13345     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13346
13347   for (i = 0; i < NUM_ACTIONS; i++)
13348     setHashEntry(action_hash, element_action_info[i].suffix,
13349                  i_to_a(element_action_info[i].value));
13350
13351   // do not store direction index (bit) here, but direction value!
13352   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13353     setHashEntry(direction_hash, element_direction_info[i].suffix,
13354                  i_to_a(1 << element_direction_info[i].value));
13355
13356   for (list = setup_file_list; list != NULL; list = list->next)
13357   {
13358     char *element_token, *action_token, *direction_token;
13359     char *element_value, *action_value, *direction_value;
13360     int delay = atoi(list->value);
13361
13362     if (strEqual(list->token, "end"))
13363     {
13364       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13365
13366       continue;
13367     }
13368
13369     /* first try to break element into element/action/direction parts;
13370        if this does not work, also accept combined "element[.act][.dir]"
13371        elements (like "dynamite.active"), which are unique elements */
13372
13373     if (strchr(list->token, '.') == NULL)       // token contains no '.'
13374     {
13375       element_value = getHashEntry(element_hash, list->token);
13376       if (element_value != NULL)        // element found
13377         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13378                            &num_list_entries);
13379       else
13380       {
13381         // no further suffixes found -- this is not an element
13382         print_unknown_token(filename, list->token, num_unknown_tokens++);
13383       }
13384
13385       continue;
13386     }
13387
13388     // token has format "<prefix>.<something>"
13389
13390     action_token = strchr(list->token, '.');    // suffix may be action ...
13391     direction_token = action_token;             // ... or direction
13392
13393     element_token = getStringCopy(list->token);
13394     *strchr(element_token, '.') = '\0';
13395
13396     element_value = getHashEntry(element_hash, element_token);
13397
13398     if (element_value == NULL)          // this is no element
13399     {
13400       element_value = getHashEntry(element_hash, list->token);
13401       if (element_value != NULL)        // combined element found
13402         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13403                            &num_list_entries);
13404       else
13405         print_unknown_token(filename, list->token, num_unknown_tokens++);
13406
13407       free(element_token);
13408
13409       continue;
13410     }
13411
13412     action_value = getHashEntry(action_hash, action_token);
13413
13414     if (action_value != NULL)           // action found
13415     {
13416       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13417                     &num_list_entries);
13418
13419       free(element_token);
13420
13421       continue;
13422     }
13423
13424     direction_value = getHashEntry(direction_hash, direction_token);
13425
13426     if (direction_value != NULL)        // direction found
13427     {
13428       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13429                          &num_list_entries);
13430
13431       free(element_token);
13432
13433       continue;
13434     }
13435
13436     if (strchr(action_token + 1, '.') == NULL)
13437     {
13438       // no further suffixes found -- this is not an action nor direction
13439
13440       element_value = getHashEntry(element_hash, list->token);
13441       if (element_value != NULL)        // combined element found
13442         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13443                            &num_list_entries);
13444       else
13445         print_unknown_token(filename, list->token, num_unknown_tokens++);
13446
13447       free(element_token);
13448
13449       continue;
13450     }
13451
13452     // token has format "<prefix>.<suffix>.<something>"
13453
13454     direction_token = strchr(action_token + 1, '.');
13455
13456     action_token = getStringCopy(action_token);
13457     *strchr(action_token + 1, '.') = '\0';
13458
13459     action_value = getHashEntry(action_hash, action_token);
13460
13461     if (action_value == NULL)           // this is no action
13462     {
13463       element_value = getHashEntry(element_hash, list->token);
13464       if (element_value != NULL)        // combined element found
13465         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13466                            &num_list_entries);
13467       else
13468         print_unknown_token(filename, list->token, num_unknown_tokens++);
13469
13470       free(element_token);
13471       free(action_token);
13472
13473       continue;
13474     }
13475
13476     direction_value = getHashEntry(direction_hash, direction_token);
13477
13478     if (direction_value != NULL)        // direction found
13479     {
13480       add_helpanim_entry(atoi(element_value), atoi(action_value),
13481                          atoi(direction_value), delay, &num_list_entries);
13482
13483       free(element_token);
13484       free(action_token);
13485
13486       continue;
13487     }
13488
13489     // this is no direction
13490
13491     element_value = getHashEntry(element_hash, list->token);
13492     if (element_value != NULL)          // combined element found
13493       add_helpanim_entry(atoi(element_value), -1, -1, delay,
13494                          &num_list_entries);
13495     else
13496       print_unknown_token(filename, list->token, num_unknown_tokens++);
13497
13498     free(element_token);
13499     free(action_token);
13500   }
13501
13502   print_unknown_token_end(num_unknown_tokens);
13503
13504   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13505   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
13506
13507   freeSetupFileList(setup_file_list);
13508   freeSetupFileHash(element_hash);
13509   freeSetupFileHash(action_hash);
13510   freeSetupFileHash(direction_hash);
13511
13512 #if 0
13513   for (i = 0; i < num_list_entries; i++)
13514     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13515           EL_NAME(helpanim_info[i].element),
13516           helpanim_info[i].element,
13517           helpanim_info[i].action,
13518           helpanim_info[i].direction,
13519           helpanim_info[i].delay);
13520 #endif
13521 }
13522
13523 void LoadHelpTextInfo(void)
13524 {
13525   char *filename = getHelpTextFilename();
13526   int i;
13527
13528   if (helptext_info != NULL)
13529   {
13530     freeSetupFileHash(helptext_info);
13531     helptext_info = NULL;
13532   }
13533
13534   if (fileExists(filename))
13535     helptext_info = loadSetupFileHash(filename);
13536
13537   if (helptext_info == NULL)
13538   {
13539     // use reliable default values from static configuration
13540     helptext_info = newSetupFileHash();
13541
13542     for (i = 0; helptext_config[i].token; i++)
13543       setHashEntry(helptext_info,
13544                    helptext_config[i].token,
13545                    helptext_config[i].value);
13546   }
13547
13548 #if 0
13549   BEGIN_HASH_ITERATION(helptext_info, itr)
13550   {
13551     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13552           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13553   }
13554   END_HASH_ITERATION(hash, itr)
13555 #endif
13556 }
13557
13558
13559 // ----------------------------------------------------------------------------
13560 // convert levels
13561 // ----------------------------------------------------------------------------
13562
13563 #define MAX_NUM_CONVERT_LEVELS          1000
13564
13565 void ConvertLevels(void)
13566 {
13567   static LevelDirTree *convert_leveldir = NULL;
13568   static int convert_level_nr = -1;
13569   static int num_levels_handled = 0;
13570   static int num_levels_converted = 0;
13571   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13572   int i;
13573
13574   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13575                                                global.convert_leveldir);
13576
13577   if (convert_leveldir == NULL)
13578     Fail("no such level identifier: '%s'", global.convert_leveldir);
13579
13580   leveldir_current = convert_leveldir;
13581
13582   if (global.convert_level_nr != -1)
13583   {
13584     convert_leveldir->first_level = global.convert_level_nr;
13585     convert_leveldir->last_level  = global.convert_level_nr;
13586   }
13587
13588   convert_level_nr = convert_leveldir->first_level;
13589
13590   PrintLine("=", 79);
13591   Print("Converting levels\n");
13592   PrintLine("-", 79);
13593   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13594   Print("Level series name:       '%s'\n", convert_leveldir->name);
13595   Print("Level series author:     '%s'\n", convert_leveldir->author);
13596   Print("Number of levels:        %d\n",   convert_leveldir->levels);
13597   PrintLine("=", 79);
13598   Print("\n");
13599
13600   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13601     levels_failed[i] = FALSE;
13602
13603   while (convert_level_nr <= convert_leveldir->last_level)
13604   {
13605     char *level_filename;
13606     boolean new_level;
13607
13608     level_nr = convert_level_nr++;
13609
13610     Print("Level %03d: ", level_nr);
13611
13612     LoadLevel(level_nr);
13613     if (level.no_level_file || level.no_valid_file)
13614     {
13615       Print("(no level)\n");
13616       continue;
13617     }
13618
13619     Print("converting level ... ");
13620
13621 #if 0
13622     // special case: conversion of some EMC levels as requested by ACME
13623     level.game_engine_type = GAME_ENGINE_TYPE_RND;
13624 #endif
13625
13626     level_filename = getDefaultLevelFilename(level_nr);
13627     new_level = !fileExists(level_filename);
13628
13629     if (new_level)
13630     {
13631       SaveLevel(level_nr);
13632
13633       num_levels_converted++;
13634
13635       Print("converted.\n");
13636     }
13637     else
13638     {
13639       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13640         levels_failed[level_nr] = TRUE;
13641
13642       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13643     }
13644
13645     num_levels_handled++;
13646   }
13647
13648   Print("\n");
13649   PrintLine("=", 79);
13650   Print("Number of levels handled: %d\n", num_levels_handled);
13651   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13652          (num_levels_handled ?
13653           num_levels_converted * 100 / num_levels_handled : 0));
13654   PrintLine("-", 79);
13655   Print("Summary (for automatic parsing by scripts):\n");
13656   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13657          convert_leveldir->identifier, num_levels_converted,
13658          num_levels_handled,
13659          (num_levels_handled ?
13660           num_levels_converted * 100 / num_levels_handled : 0));
13661
13662   if (num_levels_handled != num_levels_converted)
13663   {
13664     Print(", FAILED:");
13665     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13666       if (levels_failed[i])
13667         Print(" %03d", i);
13668   }
13669
13670   Print("\n");
13671   PrintLine("=", 79);
13672
13673   CloseAllAndExit(0);
13674 }
13675
13676
13677 // ----------------------------------------------------------------------------
13678 // create and save images for use in level sketches (raw BMP format)
13679 // ----------------------------------------------------------------------------
13680
13681 void CreateLevelSketchImages(void)
13682 {
13683   Bitmap *bitmap1;
13684   Bitmap *bitmap2;
13685   int i;
13686
13687   InitElementPropertiesGfxElement();
13688
13689   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13690   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13691
13692   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13693   {
13694     int element = getMappedElement(i);
13695     char basename1[16];
13696     char basename2[16];
13697     char *filename1;
13698     char *filename2;
13699
13700     sprintf(basename1, "%04d.bmp", i);
13701     sprintf(basename2, "%04ds.bmp", i);
13702
13703     filename1 = getPath2(global.create_sketch_images_dir, basename1);
13704     filename2 = getPath2(global.create_sketch_images_dir, basename2);
13705
13706     DrawSizedElement(0, 0, element, TILESIZE);
13707     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13708
13709     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13710       Fail("cannot save level sketch image file '%s'", filename1);
13711
13712     DrawSizedElement(0, 0, element, MINI_TILESIZE);
13713     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13714
13715     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13716       Fail("cannot save level sketch image file '%s'", filename2);
13717
13718     free(filename1);
13719     free(filename2);
13720
13721     // create corresponding SQL statements (for normal and small images)
13722     if (i < 1000)
13723     {
13724       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13725       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13726     }
13727
13728     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13729     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13730
13731     // optional: create content for forum level sketch demonstration post
13732     if (options.debug)
13733       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13734   }
13735
13736   FreeBitmap(bitmap1);
13737   FreeBitmap(bitmap2);
13738
13739   if (options.debug)
13740     fprintf(stderr, "\n");
13741
13742   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13743
13744   CloseAllAndExit(0);
13745 }
13746
13747
13748 // ----------------------------------------------------------------------------
13749 // create and save images for element collecting animations (raw BMP format)
13750 // ----------------------------------------------------------------------------
13751
13752 static boolean createCollectImage(int element)
13753 {
13754   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13755 }
13756
13757 void CreateCollectElementImages(void)
13758 {
13759   int i, j;
13760   int num_steps = 8;
13761   int anim_frames = num_steps - 1;
13762   int tile_size = TILESIZE;
13763   int anim_width  = tile_size * anim_frames;
13764   int anim_height = tile_size;
13765   int num_collect_images = 0;
13766   int pos_collect_images = 0;
13767
13768   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13769     if (createCollectImage(i))
13770       num_collect_images++;
13771
13772   Info("Creating %d element collecting animation images ...",
13773        num_collect_images);
13774
13775   int dst_width  = anim_width * 2;
13776   int dst_height = anim_height * num_collect_images / 2;
13777   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13778   char *basename = "RocksCollect.bmp";
13779   char *filename = getPath2(global.create_collect_images_dir, basename);
13780
13781   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13782   {
13783     if (!createCollectImage(i))
13784       continue;
13785
13786     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13787     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13788     int graphic = el2img(i);
13789     char *token_name = element_info[i].token_name;
13790     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13791     Bitmap *src_bitmap;
13792     int src_x, src_y;
13793
13794     Info("- creating collecting image for '%s' ...", token_name);
13795
13796     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13797
13798     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13799                tile_size, tile_size, 0, 0);
13800
13801     tmp_bitmap->surface_masked = tmp_bitmap->surface;
13802
13803     for (j = 0; j < anim_frames; j++)
13804     {
13805       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13806       int frame_size = frame_size_final * num_steps;
13807       int offset = (tile_size - frame_size_final) / 2;
13808       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13809
13810       while (frame_size > frame_size_final)
13811       {
13812         frame_size /= 2;
13813
13814         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13815
13816         FreeBitmap(frame_bitmap);
13817
13818         frame_bitmap = half_bitmap;
13819       }
13820
13821       BlitBitmap(frame_bitmap, dst_bitmap, 0, 0,
13822                  frame_size_final, frame_size_final,
13823                  dst_x + j * tile_size + offset, dst_y + offset);
13824
13825       FreeBitmap(frame_bitmap);
13826     }
13827
13828     tmp_bitmap->surface_masked = NULL;
13829
13830     FreeBitmap(tmp_bitmap);
13831
13832     pos_collect_images++;
13833   }
13834
13835   if (SDL_SaveBMP(dst_bitmap->surface, filename) != 0)
13836     Fail("cannot save element collecting image file '%s'", filename);
13837
13838   FreeBitmap(dst_bitmap);
13839
13840   Info("Done.");
13841
13842   CloseAllAndExit(0);
13843 }
13844
13845
13846 // ----------------------------------------------------------------------------
13847 // create and save images for custom and group elements (raw BMP format)
13848 // ----------------------------------------------------------------------------
13849
13850 void CreateCustomElementImages(char *directory)
13851 {
13852   char *src_basename = "RocksCE-template.ilbm";
13853   char *dst_basename = "RocksCE.bmp";
13854   char *src_filename = getPath2(directory, src_basename);
13855   char *dst_filename = getPath2(directory, dst_basename);
13856   Bitmap *src_bitmap;
13857   Bitmap *bitmap;
13858   int yoffset_ce = 0;
13859   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13860   int i;
13861
13862   InitVideoDefaults();
13863
13864   ReCreateBitmap(&backbuffer, video.width, video.height);
13865
13866   src_bitmap = LoadImage(src_filename);
13867
13868   bitmap = CreateBitmap(TILEX * 16 * 2,
13869                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13870                         DEFAULT_DEPTH);
13871
13872   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13873   {
13874     int x = i % 16;
13875     int y = i / 16;
13876     int ii = i + 1;
13877     int j;
13878
13879     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13880                TILEX * x, TILEY * y + yoffset_ce);
13881
13882     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13883                TILEX, TILEY,
13884                TILEX * x + TILEX * 16,
13885                TILEY * y + yoffset_ce);
13886
13887     for (j = 2; j >= 0; j--)
13888     {
13889       int c = ii % 10;
13890
13891       BlitBitmap(src_bitmap, bitmap,
13892                  TILEX + c * 7, 0, 6, 10,
13893                  TILEX * x + 6 + j * 7,
13894                  TILEY * y + 11 + yoffset_ce);
13895
13896       BlitBitmap(src_bitmap, bitmap,
13897                  TILEX + c * 8, TILEY, 6, 10,
13898                  TILEX * 16 + TILEX * x + 6 + j * 8,
13899                  TILEY * y + 10 + yoffset_ce);
13900
13901       ii /= 10;
13902     }
13903   }
13904
13905   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13906   {
13907     int x = i % 16;
13908     int y = i / 16;
13909     int ii = i + 1;
13910     int j;
13911
13912     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13913                TILEX * x, TILEY * y + yoffset_ge);
13914
13915     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13916                TILEX, TILEY,
13917                TILEX * x + TILEX * 16,
13918                TILEY * y + yoffset_ge);
13919
13920     for (j = 1; j >= 0; j--)
13921     {
13922       int c = ii % 10;
13923
13924       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13925                  TILEX * x + 6 + j * 10,
13926                  TILEY * y + 11 + yoffset_ge);
13927
13928       BlitBitmap(src_bitmap, bitmap,
13929                  TILEX + c * 8, TILEY + 12, 6, 10,
13930                  TILEX * 16 + TILEX * x + 10 + j * 8,
13931                  TILEY * y + 10 + yoffset_ge);
13932
13933       ii /= 10;
13934     }
13935   }
13936
13937   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13938     Fail("cannot save CE graphics file '%s'", dst_filename);
13939
13940   FreeBitmap(bitmap);
13941
13942   CloseAllAndExit(0);
13943 }