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