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