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