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