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