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