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