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