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