1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
19 #include "libgame/libgame.h"
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 */
31 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
32 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
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 */
46 /* (element number, number of change pages, change page number) */
47 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
49 /* (element number only) */
50 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
51 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
53 /* (nothing at all if unchanged) */
54 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
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 */
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)
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"
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
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
80 #define CONF_MASK_BYTES 0xc0
81 #define CONF_MASK_TOKEN 0x3f
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))
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)
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)
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)
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)
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]))
113 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
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]))
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;
133 struct LevelFileConfigInfo
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) */
141 void *value; /* variable that holds the data to be stored */
142 int default_value; /* initial default value for this variable */
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 */
152 static struct LevelFileConfigInfo chunk_config_INFO[] =
154 /* ---------- values not related to single elements ----------------------- */
157 -1, SAVE_CONF_ALWAYS,
158 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
159 &li.game_engine_type, GAME_ENGINE_TYPE_RND
163 -1, SAVE_CONF_ALWAYS,
164 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
165 &li.fieldx, STD_LEV_FIELDX
168 -1, SAVE_CONF_ALWAYS,
169 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
170 &li.fieldy, STD_LEV_FIELDY
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
180 -1, SAVE_CONF_ALWAYS,
181 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
187 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
193 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
194 &li.use_step_counter, FALSE
199 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
200 &li.wind_direction_initial, MV_NONE
205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
206 &li.em_slippery_gems, FALSE
211 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
212 &li.use_custom_template, FALSE
217 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
218 &li.can_move_into_acid_bits, ~0 /* default: everything can */
223 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
224 &li.dont_collide_with_bits, ~0 /* default: always deadly */
229 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
230 &li.em_explodes_by_fire, FALSE
235 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
236 &li.score[SC_TIME_BONUS], 1
241 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
242 &li.auto_exit_sokoban, FALSE
252 static struct LevelFileConfigInfo chunk_config_ELEM[] =
254 /* (these values are the same for each player) */
257 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
258 &li.block_last_field, FALSE /* default case for EM levels */
262 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
263 &li.sp_block_last_field, TRUE /* default case for SP levels */
267 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
268 &li.instant_relocation, FALSE
272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
273 &li.can_pass_to_walkable, FALSE
277 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
278 &li.block_snap_field, TRUE
282 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
283 &li.continuous_snapping, TRUE
287 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
288 &li.shifted_relocation, FALSE
291 /* (these values are different for each player) */
294 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
295 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
299 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
300 &li.initial_player_gravity[0], FALSE
304 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
305 &li.use_start_element[0], FALSE
309 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
310 &li.start_element[0], EL_PLAYER_1
314 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
315 &li.use_artwork_element[0], FALSE
319 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
320 &li.artwork_element[0], EL_PLAYER_1
324 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
325 &li.use_explosion_element[0], FALSE
329 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
330 &li.explosion_element[0], EL_PLAYER_1
334 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
335 &li.use_initial_inventory[0], FALSE
339 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
340 &li.initial_inventory_size[0], 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
351 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
352 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
357 &li.initial_player_gravity[1], FALSE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
362 &li.use_start_element[1], FALSE
366 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
367 &li.start_element[1], EL_PLAYER_2
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
372 &li.use_artwork_element[1], FALSE
376 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
377 &li.artwork_element[1], EL_PLAYER_2
381 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
382 &li.use_explosion_element[1], FALSE
386 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
387 &li.explosion_element[1], EL_PLAYER_2
391 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
392 &li.use_initial_inventory[1], FALSE
396 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
397 &li.initial_inventory_size[1], 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
408 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
409 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
414 &li.initial_player_gravity[2], FALSE
418 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
419 &li.use_start_element[2], FALSE
423 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
424 &li.start_element[2], EL_PLAYER_3
428 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
429 &li.use_artwork_element[2], FALSE
433 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
434 &li.artwork_element[2], EL_PLAYER_3
438 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
439 &li.use_explosion_element[2], FALSE
443 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
444 &li.explosion_element[2], EL_PLAYER_3
448 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
449 &li.use_initial_inventory[2], FALSE
453 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
454 &li.initial_inventory_size[2], 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
465 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
466 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
471 &li.initial_player_gravity[3], FALSE
475 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
476 &li.use_start_element[3], FALSE
480 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
481 &li.start_element[3], EL_PLAYER_4
485 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
486 &li.use_artwork_element[3], FALSE
490 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
491 &li.artwork_element[3], EL_PLAYER_4
495 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
496 &li.use_explosion_element[3], FALSE
500 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
501 &li.explosion_element[3], EL_PLAYER_4
505 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
506 &li.use_initial_inventory[3], FALSE
510 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
511 &li.initial_inventory_size[3], 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
522 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
523 &li.score[SC_EMERALD], 10
528 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
529 &li.score[SC_DIAMOND], 10
534 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
535 &li.score[SC_BUG], 10
540 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
541 &li.score[SC_SPACESHIP], 10
546 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
547 &li.score[SC_PACMAN], 10
552 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
553 &li.score[SC_NUT], 10
558 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
559 &li.score[SC_DYNAMITE], 10
564 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
565 &li.score[SC_KEY], 10
570 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
571 &li.score[SC_PEARL], 10
576 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
577 &li.score[SC_CRYSTAL], 10
582 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
583 &li.amoeba_content, EL_DIAMOND
587 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
592 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
593 &li.grow_into_diggable, TRUE
598 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
599 &li.yamyam_content, EL_ROCK, NULL,
600 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
604 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
605 &li.score[SC_YAMYAM], 10
610 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
611 &li.score[SC_ROBOT], 10
615 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
621 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
627 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
628 &li.time_magic_wall, 10
633 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
634 &li.game_of_life[0], 2
638 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
639 &li.game_of_life[1], 3
643 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
644 &li.game_of_life[2], 3
648 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
649 &li.game_of_life[3], 3
654 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
659 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
664 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
669 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
674 EL_TIMEGATE_SWITCH, -1,
675 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
676 &li.time_timegate, 10
680 EL_LIGHT_SWITCH_ACTIVE, -1,
681 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
686 EL_SHIELD_NORMAL, -1,
687 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
688 &li.shield_normal_time, 10
691 EL_SHIELD_NORMAL, -1,
692 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
693 &li.score[SC_SHIELD], 10
697 EL_SHIELD_DEADLY, -1,
698 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
699 &li.shield_deadly_time, 10
702 EL_SHIELD_DEADLY, -1,
703 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
704 &li.score[SC_SHIELD], 10
709 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
714 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
715 &li.extra_time_score, 10
719 EL_TIME_ORB_FULL, -1,
720 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
721 &li.time_orb_time, 10
724 EL_TIME_ORB_FULL, -1,
725 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
726 &li.use_time_orb_bug, FALSE
731 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
732 &li.use_spring_bug, FALSE
737 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
738 &li.android_move_time, 10
742 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
743 &li.android_clone_time, 10
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
754 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
759 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
764 EL_EMC_MAGNIFIER, -1,
765 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
766 &li.magnify_score, 10
769 EL_EMC_MAGNIFIER, -1,
770 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
775 EL_EMC_MAGIC_BALL, -1,
776 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
780 EL_EMC_MAGIC_BALL, -1,
781 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
782 &li.ball_random, FALSE
785 EL_EMC_MAGIC_BALL, -1,
786 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
787 &li.ball_state_initial, FALSE
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
796 /* ---------- unused values ----------------------------------------------- */
799 EL_UNKNOWN, SAVE_CONF_NEVER,
800 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
801 &li.score[SC_UNKNOWN_14], 10
804 EL_UNKNOWN, SAVE_CONF_NEVER,
805 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
806 &li.score[SC_UNKNOWN_15], 10
816 static struct LevelFileConfigInfo chunk_config_NOTE[] =
820 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
821 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
825 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
826 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
831 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
832 &xx_envelope.autowrap, FALSE
836 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
837 &xx_envelope.centered, FALSE
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]
855 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
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]
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]
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]
884 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
885 &xx_ei.use_gfx_element, FALSE,
886 &yy_ei.use_gfx_element
890 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
891 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
892 &yy_ei.gfx_element_initial
897 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
898 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
899 &yy_ei.access_direction
904 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
905 &xx_ei.collect_score_initial, 10,
906 &yy_ei.collect_score_initial
910 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
911 &xx_ei.collect_count_initial, 1,
912 &yy_ei.collect_count_initial
917 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
918 &xx_ei.ce_value_fixed_initial, 0,
919 &yy_ei.ce_value_fixed_initial
923 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
924 &xx_ei.ce_value_random_initial, 0,
925 &yy_ei.ce_value_random_initial
929 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
930 &xx_ei.use_last_ce_value, FALSE,
931 &yy_ei.use_last_ce_value
936 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
937 &xx_ei.push_delay_fixed, 8,
938 &yy_ei.push_delay_fixed
942 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
943 &xx_ei.push_delay_random, 8,
944 &yy_ei.push_delay_random
948 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
949 &xx_ei.drop_delay_fixed, 0,
950 &yy_ei.drop_delay_fixed
954 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
955 &xx_ei.drop_delay_random, 0,
956 &yy_ei.drop_delay_random
960 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
961 &xx_ei.move_delay_fixed, 0,
962 &yy_ei.move_delay_fixed
966 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
967 &xx_ei.move_delay_random, 0,
968 &yy_ei.move_delay_random
973 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
974 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
979 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
980 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
981 &yy_ei.move_direction_initial
985 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
986 &xx_ei.move_stepsize, TILEX / 8,
992 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
993 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
994 &yy_ei.move_enter_element
998 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
999 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1000 &yy_ei.move_leave_element
1004 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1005 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1006 &yy_ei.move_leave_type
1011 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1012 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1013 &yy_ei.slippery_type
1018 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1019 &xx_ei.explosion_type, EXPLODES_3X3,
1020 &yy_ei.explosion_type
1024 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1025 &xx_ei.explosion_delay, 16,
1026 &yy_ei.explosion_delay
1030 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1031 &xx_ei.ignition_delay, 8,
1032 &yy_ei.ignition_delay
1037 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1038 &xx_ei.content, EL_EMPTY_SPACE,
1040 &xx_num_contents, 1, 1
1043 /* ---------- "num_change_pages" must be the last entry ------------------- */
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
1060 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1062 /* ---------- "current_change_page" must be the first entry --------------- */
1065 -1, SAVE_CONF_ALWAYS,
1066 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1067 &xx_current_change_page, -1
1070 /* ---------- (the remaining entries can be in any order) ----------------- */
1074 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1075 &xx_change.can_change, FALSE
1080 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1081 &xx_event_bits[0], 0
1085 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1086 &xx_event_bits[1], 0
1091 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1092 &xx_change.trigger_player, CH_PLAYER_ANY
1096 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1097 &xx_change.trigger_side, CH_SIDE_ANY
1101 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1102 &xx_change.trigger_page, CH_PAGE_ANY
1107 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1108 &xx_change.target_element, EL_EMPTY_SPACE
1113 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1114 &xx_change.delay_fixed, 0
1118 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1119 &xx_change.delay_random, 0
1123 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1124 &xx_change.delay_frames, FRAMES_PER_SECOND
1129 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1130 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1135 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1136 &xx_change.explode, FALSE
1140 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1141 &xx_change.use_target_content, FALSE
1145 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1146 &xx_change.only_if_complete, FALSE
1150 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1151 &xx_change.use_random_replace, FALSE
1155 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1156 &xx_change.random_percentage, 100
1160 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1161 &xx_change.replace_when, CP_WHEN_EMPTY
1166 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1167 &xx_change.has_action, FALSE
1171 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1172 &xx_change.action_type, CA_NO_ACTION
1176 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1177 &xx_change.action_mode, CA_MODE_UNDEFINED
1181 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1182 &xx_change.action_arg, CA_ARG_UNDEFINED
1187 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1188 &xx_change.action_element, EL_EMPTY_SPACE
1193 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1194 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1195 &xx_num_contents, 1, 1
1205 static struct LevelFileConfigInfo chunk_config_GRPX[] =
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]
1217 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1218 &xx_ei.use_gfx_element, FALSE
1222 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1223 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1228 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1229 &xx_group.choice_mode, ANIM_RANDOM
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
1246 static struct LevelFileConfigInfo chunk_config_CONF[] = /* (OBSOLETE) */
1250 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1251 &li.block_snap_field, TRUE
1255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1256 &li.continuous_snapping, TRUE
1260 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1261 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1265 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1266 &li.use_start_element[0], FALSE
1270 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1271 &li.start_element[0], EL_PLAYER_1
1275 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1276 &li.use_artwork_element[0], FALSE
1280 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1281 &li.artwork_element[0], EL_PLAYER_1
1285 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1286 &li.use_explosion_element[0], FALSE
1290 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1291 &li.explosion_element[0], EL_PLAYER_1
1306 filetype_id_list[] =
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" },
1319 /* ========================================================================= */
1320 /* level file functions */
1321 /* ========================================================================= */
1323 static boolean check_special_flags(char *flag)
1326 printf("::: '%s', '%s', '%s'\n",
1328 options.special_flags,
1329 leveldir_current->special_flags);
1332 if (strEqual(options.special_flags, flag) ||
1333 strEqual(leveldir_current->special_flags, flag))
1339 static struct DateInfo getCurrentDate()
1341 time_t epoch_seconds = time(NULL);
1342 struct tm *now = localtime(&epoch_seconds);
1343 struct DateInfo date;
1345 date.year = now->tm_year + 1900;
1346 date.month = now->tm_mon + 1;
1347 date.day = now->tm_mday;
1349 date.src = DATE_SRC_CLOCK;
1354 static void resetEventFlags(struct ElementChangeInfo *change)
1358 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1359 change->has_event[i] = FALSE;
1362 static void resetEventBits()
1366 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1367 xx_event_bits[i] = 0;
1370 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
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)) */
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;
1384 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
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 */
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);
1398 static char *getDefaultElementDescription(struct ElementInfo *ei)
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);
1406 /* always start with reliable default values */
1407 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1408 description[i] = '\0';
1410 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1411 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1413 return &description[0];
1416 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1418 char *default_description = getDefaultElementDescription(ei);
1421 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1422 ei->description[i] = default_description[i];
1425 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1429 for (i = 0; conf[i].data_type != -1; i++)
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;
1436 if (byte_mask == CONF_MASK_MULTI_BYTES)
1438 int default_num_entities = conf[i].default_num_entities;
1439 int max_num_entities = conf[i].max_num_entities;
1441 *(int *)(conf[i].num_entities) = default_num_entities;
1443 if (data_type == TYPE_STRING)
1445 char *default_string = conf[i].default_string;
1446 char *string = (char *)(conf[i].value);
1448 strncpy(string, default_string, max_num_entities);
1450 else if (data_type == TYPE_ELEMENT_LIST)
1452 int *element_array = (int *)(conf[i].value);
1455 for (j = 0; j < max_num_entities; j++)
1456 element_array[j] = default_value;
1458 else if (data_type == TYPE_CONTENT_LIST)
1460 struct Content *content = (struct Content *)(conf[i].value);
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;
1469 else /* constant size configuration data (1, 2 or 4 bytes) */
1471 if (data_type == TYPE_BOOLEAN)
1472 *(boolean *)(conf[i].value) = default_value;
1474 *(int *) (conf[i].value) = default_value;
1479 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1483 for (i = 0; conf[i].data_type != -1; i++)
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;
1489 if (byte_mask == CONF_MASK_MULTI_BYTES)
1491 int max_num_entities = conf[i].max_num_entities;
1493 if (data_type == TYPE_STRING)
1495 char *string = (char *)(conf[i].value);
1496 char *string_copy = (char *)(conf[i].value_copy);
1498 strncpy(string_copy, string, max_num_entities);
1500 else if (data_type == TYPE_ELEMENT_LIST)
1502 int *element_array = (int *)(conf[i].value);
1503 int *element_array_copy = (int *)(conf[i].value_copy);
1506 for (j = 0; j < max_num_entities; j++)
1507 element_array_copy[j] = element_array[j];
1509 else if (data_type == TYPE_CONTENT_LIST)
1511 struct Content *content = (struct Content *)(conf[i].value);
1512 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
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];
1521 else /* constant size configuration data (1, 2 or 4 bytes) */
1523 if (data_type == TYPE_BOOLEAN)
1524 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1526 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1531 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1535 xx_ei = *ei_from; /* copy element data into temporary buffer */
1536 yy_ei = *ei_to; /* copy element data into temporary buffer */
1538 copyConfigFromConfigList(chunk_config_CUSX_base);
1543 /* ---------- reinitialize and copy change pages ---------- */
1545 ei_to->num_change_pages = ei_from->num_change_pages;
1546 ei_to->current_change_page = ei_from->current_change_page;
1548 setElementChangePages(ei_to, ei_to->num_change_pages);
1550 for (i = 0; i < ei_to->num_change_pages; i++)
1551 ei_to->change_page[i] = ei_from->change_page[i];
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;
1557 /* mark this custom element as modified */
1558 ei_to->modified_settings = TRUE;
1561 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1563 int change_page_size = sizeof(struct ElementChangeInfo);
1565 ei->num_change_pages = MAX(1, change_pages);
1568 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1570 if (ei->current_change_page >= ei->num_change_pages)
1571 ei->current_change_page = ei->num_change_pages - 1;
1573 ei->change = &ei->change_page[ei->current_change_page];
1576 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1578 xx_change = *change; /* copy change data into temporary buffer */
1581 /* (not needed; set by setConfigToDefaultsFromConfigList()) */
1582 xx_num_contents = 1;
1585 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1587 *change = xx_change;
1589 resetEventFlags(change);
1591 change->direct_action = 0;
1592 change->other_action = 0;
1594 change->pre_change_function = NULL;
1595 change->change_function = NULL;
1596 change->post_change_function = NULL;
1601 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1605 li = *level; /* copy level data into temporary buffer */
1606 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1607 *level = li; /* copy temporary buffer back to level data */
1609 setLevelInfoToDefaults_EM();
1610 setLevelInfoToDefaults_SP();
1612 level->native_em_level = &native_em_level;
1613 level->native_sp_level = &native_sp_level;
1615 level->file_version = FILE_VERSION_ACTUAL;
1616 level->game_version = GAME_VERSION_ACTUAL;
1618 level->creation_date = getCurrentDate();
1620 level->encoding_16bit_field = TRUE;
1621 level->encoding_16bit_yamyam = TRUE;
1622 level->encoding_16bit_amoeba = TRUE;
1624 for (x = 0; x < MAX_LEV_FIELDX; x++)
1625 for (y = 0; y < MAX_LEV_FIELDY; y++)
1626 level->field[x][y] = EL_SAND;
1628 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1629 level->name[i] = '\0';
1630 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1631 level->author[i] = '\0';
1633 strcpy(level->name, NAMELESS_LEVEL_NAME);
1634 strcpy(level->author, ANONYMOUS_NAME);
1636 level->field[0][0] = EL_PLAYER_1;
1637 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1639 BorderElement = EL_STEELWALL;
1641 /* set all bug compatibility flags to "false" => do not emulate this bug */
1642 level->use_action_after_change_bug = FALSE;
1644 if (leveldir_current)
1646 /* try to determine better author name than 'anonymous' */
1647 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1649 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1650 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1654 switch (LEVELCLASS(leveldir_current))
1656 case LEVELCLASS_TUTORIAL:
1657 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1660 case LEVELCLASS_CONTRIB:
1661 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1662 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1665 case LEVELCLASS_PRIVATE:
1666 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1667 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1671 /* keep default value */
1678 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1680 static boolean clipboard_elements_initialized = FALSE;
1683 InitElementPropertiesStatic();
1685 li = *level; /* copy level data into temporary buffer */
1686 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1687 *level = li; /* copy temporary buffer back to level data */
1689 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1692 struct ElementInfo *ei = &element_info[element];
1694 /* never initialize clipboard elements after the very first time */
1695 /* (to be able to use clipboard elements between several levels) */
1696 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1699 if (IS_ENVELOPE(element))
1701 int envelope_nr = element - EL_ENVELOPE_1;
1703 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1705 level->envelope[envelope_nr] = xx_envelope;
1708 if (IS_CUSTOM_ELEMENT(element) ||
1709 IS_GROUP_ELEMENT(element) ||
1710 IS_INTERNAL_ELEMENT(element))
1712 xx_ei = *ei; /* copy element data into temporary buffer */
1714 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1719 setElementChangePages(ei, 1);
1720 setElementChangeInfoToDefaults(ei->change);
1722 if (IS_CUSTOM_ELEMENT(element) ||
1723 IS_GROUP_ELEMENT(element) ||
1724 IS_INTERNAL_ELEMENT(element))
1726 setElementDescriptionToDefault(ei);
1728 ei->modified_settings = FALSE;
1731 if (IS_CUSTOM_ELEMENT(element) ||
1732 IS_INTERNAL_ELEMENT(element))
1734 /* internal values used in level editor */
1736 ei->access_type = 0;
1737 ei->access_layer = 0;
1738 ei->access_protected = 0;
1739 ei->walk_to_action = 0;
1740 ei->smash_targets = 0;
1743 ei->can_explode_by_fire = FALSE;
1744 ei->can_explode_smashed = FALSE;
1745 ei->can_explode_impact = FALSE;
1747 ei->current_change_page = 0;
1750 if (IS_GROUP_ELEMENT(element) ||
1751 IS_INTERNAL_ELEMENT(element))
1753 struct ElementGroupInfo *group;
1755 /* initialize memory for list of elements in group */
1756 if (ei->group == NULL)
1757 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1761 xx_group = *group; /* copy group data into temporary buffer */
1763 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1769 clipboard_elements_initialized = TRUE;
1772 static void setLevelInfoToDefaults(struct LevelInfo *level,
1773 boolean level_info_only)
1775 setLevelInfoToDefaults_Level(level);
1777 if (!level_info_only)
1778 setLevelInfoToDefaults_Elements(level);
1780 level->no_valid_file = FALSE;
1782 level->changed = FALSE;
1787 static void setLevelInfoToDefaults(struct LevelInfo *level,
1788 boolean level_info_only)
1790 static boolean clipboard_elements_initialized = FALSE;
1793 if (level_info_only)
1796 InitElementPropertiesStatic();
1798 li = *level; /* copy level data into temporary buffer */
1800 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1801 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1803 *level = li; /* copy temporary buffer back to level data */
1805 setLevelInfoToDefaults_EM();
1806 setLevelInfoToDefaults_SP();
1808 level->native_em_level = &native_em_level;
1809 level->native_sp_level = &native_sp_level;
1811 level->file_version = FILE_VERSION_ACTUAL;
1812 level->game_version = GAME_VERSION_ACTUAL;
1814 level->creation_date = getCurrentDate();
1816 level->encoding_16bit_field = TRUE;
1817 level->encoding_16bit_yamyam = TRUE;
1818 level->encoding_16bit_amoeba = TRUE;
1820 for (x = 0; x < MAX_LEV_FIELDX; x++)
1821 for (y = 0; y < MAX_LEV_FIELDY; y++)
1822 level->field[x][y] = EL_SAND;
1824 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1825 level->name[i] = '\0';
1826 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1827 level->author[i] = '\0';
1829 strcpy(level->name, NAMELESS_LEVEL_NAME);
1830 strcpy(level->author, ANONYMOUS_NAME);
1832 level->field[0][0] = EL_PLAYER_1;
1833 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1835 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1838 struct ElementInfo *ei = &element_info[element];
1840 /* never initialize clipboard elements after the very first time */
1841 /* (to be able to use clipboard elements between several levels) */
1842 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1845 if (IS_ENVELOPE(element))
1847 int envelope_nr = element - EL_ENVELOPE_1;
1849 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1851 level->envelope[envelope_nr] = xx_envelope;
1854 if (IS_CUSTOM_ELEMENT(element) ||
1855 IS_GROUP_ELEMENT(element) ||
1856 IS_INTERNAL_ELEMENT(element))
1858 xx_ei = *ei; /* copy element data into temporary buffer */
1860 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1865 setElementChangePages(ei, 1);
1866 setElementChangeInfoToDefaults(ei->change);
1868 if (IS_CUSTOM_ELEMENT(element) ||
1869 IS_GROUP_ELEMENT(element) ||
1870 IS_INTERNAL_ELEMENT(element))
1872 setElementDescriptionToDefault(ei);
1874 ei->modified_settings = FALSE;
1877 if (IS_CUSTOM_ELEMENT(element) ||
1878 IS_INTERNAL_ELEMENT(element))
1880 /* internal values used in level editor */
1882 ei->access_type = 0;
1883 ei->access_layer = 0;
1884 ei->access_protected = 0;
1885 ei->walk_to_action = 0;
1886 ei->smash_targets = 0;
1889 ei->can_explode_by_fire = FALSE;
1890 ei->can_explode_smashed = FALSE;
1891 ei->can_explode_impact = FALSE;
1893 ei->current_change_page = 0;
1896 if (IS_GROUP_ELEMENT(element) ||
1897 IS_INTERNAL_ELEMENT(element))
1899 struct ElementGroupInfo *group;
1901 /* initialize memory for list of elements in group */
1902 if (ei->group == NULL)
1903 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1907 xx_group = *group; /* copy group data into temporary buffer */
1909 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1915 clipboard_elements_initialized = TRUE;
1917 BorderElement = EL_STEELWALL;
1919 level->no_valid_file = FALSE;
1921 level->changed = FALSE;
1923 /* set all bug compatibility flags to "false" => do not emulate this bug */
1924 level->use_action_after_change_bug = FALSE;
1926 if (leveldir_current)
1928 /* try to determine better author name than 'anonymous' */
1929 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1931 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1932 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1936 switch (LEVELCLASS(leveldir_current))
1938 case LEVELCLASS_TUTORIAL:
1939 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1942 case LEVELCLASS_CONTRIB:
1943 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1944 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1947 case LEVELCLASS_PRIVATE:
1948 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1949 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1953 /* keep default value */
1962 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1964 level_file_info->nr = 0;
1965 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1966 level_file_info->packed = FALSE;
1967 level_file_info->basename = NULL;
1968 level_file_info->filename = NULL;
1971 static void ActivateLevelTemplate()
1975 /* Currently there is no special action needed to activate the template
1976 data, because 'element_info' property settings overwrite the original
1977 level data, while all other variables do not change. */
1979 /* Exception: 'from_level_template' elements in the original level playfield
1980 are overwritten with the corresponding elements at the same position in
1981 playfield from the level template. */
1983 for (x = 0; x < level.fieldx; x++)
1984 for (y = 0; y < level.fieldy; y++)
1985 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1986 level.field[x][y] = level_template.field[x][y];
1988 if (check_special_flags("load_xsb_to_ces"))
1990 struct LevelInfo level_backup = level;
1992 /* overwrite all individual level settings from template level settings */
1993 level = level_template;
1995 /* restore playfield size */
1996 level.fieldx = level_backup.fieldx;
1997 level.fieldy = level_backup.fieldy;
1999 /* restore playfield content */
2000 for (x = 0; x < level.fieldx; x++)
2001 for (y = 0; y < level.fieldy; y++)
2002 level.field[x][y] = level_backup.field[x][y];
2004 /* restore name and author from individual level */
2005 strcpy(level.name, level_backup.name);
2006 strcpy(level.author, level_backup.author);
2008 /* restore flag "use_custom_template" */
2009 level.use_custom_template = level_backup.use_custom_template;
2013 static char *getLevelFilenameFromBasename(char *basename)
2015 static char *filename = NULL;
2017 checked_free(filename);
2019 filename = getPath2(getCurrentLevelDir(), basename);
2024 static int getFileTypeFromBasename(char *basename)
2026 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
2028 static char *filename = NULL;
2029 struct stat file_status;
2031 /* ---------- try to determine file type from filename ---------- */
2033 /* check for typical filename of a Supaplex level package file */
2035 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2036 return LEVEL_FILE_TYPE_SP;
2038 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
2039 strncmp(basename, "LEVELS.D", 8) == 0))
2040 return LEVEL_FILE_TYPE_SP;
2043 /* check for typical filename of a Diamond Caves II level package file */
2044 if (strSuffixLower(basename, ".dc") ||
2045 strSuffixLower(basename, ".dc2"))
2046 return LEVEL_FILE_TYPE_DC;
2048 /* check for typical filename of a Sokoban level package file */
2049 if (strSuffixLower(basename, ".xsb") &&
2050 strchr(basename, '%') == NULL)
2051 return LEVEL_FILE_TYPE_SB;
2053 /* ---------- try to determine file type from filesize ---------- */
2055 checked_free(filename);
2056 filename = getPath2(getCurrentLevelDir(), basename);
2058 if (stat(filename, &file_status) == 0)
2060 /* check for typical filesize of a Supaplex level package file */
2061 if (file_status.st_size == 170496)
2062 return LEVEL_FILE_TYPE_SP;
2065 return LEVEL_FILE_TYPE_UNKNOWN;
2068 static boolean checkForPackageFromBasename(char *basename)
2070 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2071 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
2073 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2076 static char *getSingleLevelBasenameExt(int nr, char *extension)
2078 static char basename[MAX_FILENAME_LEN];
2081 sprintf(basename, "template.%s", extension);
2083 sprintf(basename, "%03d.%s", nr, extension);
2088 static char *getSingleLevelBasename(int nr)
2090 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2095 static char *getPackedLevelBasename(int type)
2097 static char basename[MAX_FILENAME_LEN];
2098 char *directory = getCurrentLevelDir();
2100 DirectoryEntry *dir_entry;
2102 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
2104 if ((dir = openDirectory(directory)) == NULL)
2106 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
2111 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
2113 char *entry_basename = dir_entry->basename;
2114 int entry_type = getFileTypeFromBasename(entry_basename);
2116 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
2118 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2121 strcpy(basename, entry_basename);
2128 closeDirectory(dir);
2135 static char *getPackedLevelBasename(int type)
2137 static char basename[MAX_FILENAME_LEN];
2138 char *directory = getCurrentLevelDir();
2140 struct dirent *dir_entry;
2142 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
2144 if ((dir = opendir(directory)) == NULL)
2146 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
2151 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2153 char *entry_basename = dir_entry->d_name;
2154 int entry_type = getFileTypeFromBasename(entry_basename);
2156 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
2158 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2161 strcpy(basename, entry_basename);
2175 static char *getSingleLevelFilename(int nr)
2177 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2181 static char *getPackedLevelFilename(int type)
2183 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2187 char *getDefaultLevelFilename(int nr)
2189 return getSingleLevelFilename(nr);
2193 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2197 lfi->packed = FALSE;
2198 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
2199 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2203 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2204 int type, char *format, ...)
2206 static char basename[MAX_FILENAME_LEN];
2209 va_start(ap, format);
2210 vsprintf(basename, format, ap);
2214 lfi->packed = FALSE;
2215 lfi->basename = basename;
2216 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2219 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2224 lfi->basename = getPackedLevelBasename(lfi->type);
2225 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2228 static int getFiletypeFromID(char *filetype_id)
2230 char *filetype_id_lower;
2231 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2234 if (filetype_id == NULL)
2235 return LEVEL_FILE_TYPE_UNKNOWN;
2237 filetype_id_lower = getStringToLower(filetype_id);
2239 for (i = 0; filetype_id_list[i].id != NULL; i++)
2241 char *id_lower = getStringToLower(filetype_id_list[i].id);
2243 if (strEqual(filetype_id_lower, id_lower))
2244 filetype = filetype_id_list[i].filetype;
2248 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2252 free(filetype_id_lower);
2257 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2261 /* special case: level number is negative => check for level template file */
2265 /* global variable "leveldir_current" must be modified in the loop below */
2266 LevelDirTree *leveldir_current_last = leveldir_current;
2268 /* check for template level in path from current to topmost tree node */
2270 while (leveldir_current != NULL)
2272 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2273 "template.%s", LEVELFILE_EXTENSION);
2275 if (fileExists(lfi->filename))
2278 leveldir_current = leveldir_current->node_parent;
2281 /* restore global variable "leveldir_current" modified in above loop */
2282 leveldir_current = leveldir_current_last;
2286 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2287 "template.%s", LEVELFILE_EXTENSION);
2291 /* no fallback if template file not existing */
2295 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2296 if (leveldir_current->level_filename != NULL)
2298 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2300 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2301 leveldir_current->level_filename, nr);
2303 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2305 if (fileExists(lfi->filename))
2309 /* check for native Rocks'n'Diamonds level file */
2310 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2311 "%03d.%s", nr, LEVELFILE_EXTENSION);
2312 if (fileExists(lfi->filename))
2315 /* check for Emerald Mine level file (V1) */
2316 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2317 'a' + (nr / 10) % 26, '0' + nr % 10);
2318 if (fileExists(lfi->filename))
2320 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2321 'A' + (nr / 10) % 26, '0' + nr % 10);
2322 if (fileExists(lfi->filename))
2325 /* check for Emerald Mine level file (V2 to V5) */
2326 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2327 if (fileExists(lfi->filename))
2330 /* check for Emerald Mine level file (V6 / single mode) */
2331 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2332 if (fileExists(lfi->filename))
2334 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2335 if (fileExists(lfi->filename))
2338 /* check for Emerald Mine level file (V6 / teamwork mode) */
2339 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2340 if (fileExists(lfi->filename))
2342 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2343 if (fileExists(lfi->filename))
2346 /* check for various packed level file formats */
2347 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2348 if (fileExists(lfi->filename))
2351 /* no known level file found -- use default values (and fail later) */
2352 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2353 "%03d.%s", nr, LEVELFILE_EXTENSION);
2356 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2358 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2359 lfi->type = getFileTypeFromBasename(lfi->basename);
2362 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2364 /* always start with reliable default values */
2365 setFileInfoToDefaults(level_file_info);
2367 level_file_info->nr = nr; /* set requested level number */
2369 determineLevelFileInfo_Filename(level_file_info);
2370 determineLevelFileInfo_Filetype(level_file_info);
2373 /* ------------------------------------------------------------------------- */
2374 /* functions for loading R'n'D level */
2375 /* ------------------------------------------------------------------------- */
2377 int getMappedElement(int element)
2379 /* remap some (historic, now obsolete) elements */
2383 case EL_PLAYER_OBSOLETE:
2384 element = EL_PLAYER_1;
2387 case EL_KEY_OBSOLETE:
2391 case EL_EM_KEY_1_FILE_OBSOLETE:
2392 element = EL_EM_KEY_1;
2395 case EL_EM_KEY_2_FILE_OBSOLETE:
2396 element = EL_EM_KEY_2;
2399 case EL_EM_KEY_3_FILE_OBSOLETE:
2400 element = EL_EM_KEY_3;
2403 case EL_EM_KEY_4_FILE_OBSOLETE:
2404 element = EL_EM_KEY_4;
2407 case EL_ENVELOPE_OBSOLETE:
2408 element = EL_ENVELOPE_1;
2416 if (element >= NUM_FILE_ELEMENTS)
2418 Error(ERR_WARN, "invalid level element %d", element);
2420 element = EL_UNKNOWN;
2428 int getMappedElementByVersion(int element, int game_version)
2430 /* remap some elements due to certain game version */
2432 if (game_version <= VERSION_IDENT(2,2,0,0))
2434 /* map game font elements */
2435 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2436 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2437 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2438 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2441 if (game_version < VERSION_IDENT(3,0,0,0))
2443 /* map Supaplex gravity tube elements */
2444 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2445 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2446 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2447 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2456 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2458 level->file_version = getFileVersion(file);
2459 level->game_version = getFileVersion(file);
2464 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2466 level->creation_date.year = getFile16BitBE(file);
2467 level->creation_date.month = getFile8Bit(file);
2468 level->creation_date.day = getFile8Bit(file);
2470 level->creation_date.src = DATE_SRC_LEVELFILE;
2475 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2477 int initial_player_stepsize;
2478 int initial_player_gravity;
2481 level->fieldx = getFile8Bit(file);
2482 level->fieldy = getFile8Bit(file);
2484 level->time = getFile16BitBE(file);
2485 level->gems_needed = getFile16BitBE(file);
2487 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2488 level->name[i] = getFile8Bit(file);
2489 level->name[MAX_LEVEL_NAME_LEN] = 0;
2491 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2492 level->score[i] = getFile8Bit(file);
2494 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2495 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2496 for (y = 0; y < 3; y++)
2497 for (x = 0; x < 3; x++)
2498 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2500 level->amoeba_speed = getFile8Bit(file);
2501 level->time_magic_wall = getFile8Bit(file);
2502 level->time_wheel = getFile8Bit(file);
2503 level->amoeba_content = getMappedElement(getFile8Bit(file));
2505 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2508 for (i = 0; i < MAX_PLAYERS; i++)
2509 level->initial_player_stepsize[i] = initial_player_stepsize;
2511 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2513 for (i = 0; i < MAX_PLAYERS; i++)
2514 level->initial_player_gravity[i] = initial_player_gravity;
2516 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2517 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2519 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2521 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2522 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2523 level->can_move_into_acid_bits = getFile32BitBE(file);
2524 level->dont_collide_with_bits = getFile8Bit(file);
2526 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2527 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2529 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2530 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2531 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2533 level->game_engine_type = getFile8Bit(file);
2535 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2540 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2544 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2545 level->name[i] = getFile8Bit(file);
2546 level->name[MAX_LEVEL_NAME_LEN] = 0;
2551 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2555 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2556 level->author[i] = getFile8Bit(file);
2557 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2562 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2565 int chunk_size_expected = level->fieldx * level->fieldy;
2567 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2568 stored with 16-bit encoding (and should be twice as big then).
2569 Even worse, playfield data was stored 16-bit when only yamyam content
2570 contained 16-bit elements and vice versa. */
2572 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2573 chunk_size_expected *= 2;
2575 if (chunk_size_expected != chunk_size)
2577 ReadUnusedBytesFromFile(file, chunk_size);
2578 return chunk_size_expected;
2581 for (y = 0; y < level->fieldy; y++)
2582 for (x = 0; x < level->fieldx; x++)
2583 level->field[x][y] =
2584 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2589 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2592 int header_size = 4;
2593 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2594 int chunk_size_expected = header_size + content_size;
2596 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2597 stored with 16-bit encoding (and should be twice as big then).
2598 Even worse, playfield data was stored 16-bit when only yamyam content
2599 contained 16-bit elements and vice versa. */
2601 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2602 chunk_size_expected += content_size;
2604 if (chunk_size_expected != chunk_size)
2606 ReadUnusedBytesFromFile(file, chunk_size);
2607 return chunk_size_expected;
2611 level->num_yamyam_contents = getFile8Bit(file);
2615 /* correct invalid number of content fields -- should never happen */
2616 if (level->num_yamyam_contents < 1 ||
2617 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2618 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2620 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2621 for (y = 0; y < 3; y++)
2622 for (x = 0; x < 3; x++)
2623 level->yamyam_content[i].e[x][y] =
2624 getMappedElement(level->encoding_16bit_field ?
2625 getFile16BitBE(file) : getFile8Bit(file));
2629 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2635 int content_xsize, content_ysize;
2637 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2639 element = getMappedElement(getFile16BitBE(file));
2640 num_contents = getFile8Bit(file);
2642 getFile8Bit(file); /* content x size (unused) */
2643 getFile8Bit(file); /* content y size (unused) */
2645 content_xsize = getFile8Bit(file);
2646 content_ysize = getFile8Bit(file);
2649 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2651 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2652 for (y = 0; y < 3; y++)
2653 for (x = 0; x < 3; x++)
2654 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2656 /* correct invalid number of content fields -- should never happen */
2657 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2658 num_contents = STD_ELEMENT_CONTENTS;
2660 if (element == EL_YAMYAM)
2662 level->num_yamyam_contents = num_contents;
2664 for (i = 0; i < num_contents; i++)
2665 for (y = 0; y < 3; y++)
2666 for (x = 0; x < 3; x++)
2667 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2669 else if (element == EL_BD_AMOEBA)
2671 level->amoeba_content = content_array[0][0][0];
2675 Error(ERR_WARN, "cannot load content for element '%d'", element);
2681 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2687 int chunk_size_expected;
2689 element = getMappedElement(getFile16BitBE(file));
2690 if (!IS_ENVELOPE(element))
2691 element = EL_ENVELOPE_1;
2693 envelope_nr = element - EL_ENVELOPE_1;
2695 envelope_len = getFile16BitBE(file);
2697 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2698 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2700 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2702 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2703 if (chunk_size_expected != chunk_size)
2705 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2706 return chunk_size_expected;
2709 for (i = 0; i < envelope_len; i++)
2710 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2715 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2717 int num_changed_custom_elements = getFile16BitBE(file);
2718 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2721 if (chunk_size_expected != chunk_size)
2723 ReadUnusedBytesFromFile(file, chunk_size - 2);
2724 return chunk_size_expected;
2727 for (i = 0; i < num_changed_custom_elements; i++)
2729 int element = getMappedElement(getFile16BitBE(file));
2730 int properties = getFile32BitBE(file);
2732 if (IS_CUSTOM_ELEMENT(element))
2733 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2735 Error(ERR_WARN, "invalid custom element number %d", element);
2737 /* older game versions that wrote level files with CUS1 chunks used
2738 different default push delay values (not yet stored in level file) */
2739 element_info[element].push_delay_fixed = 2;
2740 element_info[element].push_delay_random = 8;
2746 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2748 int num_changed_custom_elements = getFile16BitBE(file);
2749 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2752 if (chunk_size_expected != chunk_size)
2754 ReadUnusedBytesFromFile(file, chunk_size - 2);
2755 return chunk_size_expected;
2758 for (i = 0; i < num_changed_custom_elements; i++)
2760 int element = getMappedElement(getFile16BitBE(file));
2761 int custom_target_element = getMappedElement(getFile16BitBE(file));
2763 if (IS_CUSTOM_ELEMENT(element))
2764 element_info[element].change->target_element = custom_target_element;
2766 Error(ERR_WARN, "invalid custom element number %d", element);
2772 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2774 int num_changed_custom_elements = getFile16BitBE(file);
2775 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2778 if (chunk_size_expected != chunk_size)
2780 ReadUnusedBytesFromFile(file, chunk_size - 2);
2781 return chunk_size_expected;
2784 for (i = 0; i < num_changed_custom_elements; i++)
2786 int element = getMappedElement(getFile16BitBE(file));
2787 struct ElementInfo *ei = &element_info[element];
2788 unsigned int event_bits;
2790 if (!IS_CUSTOM_ELEMENT(element))
2792 Error(ERR_WARN, "invalid custom element number %d", element);
2794 element = EL_INTERNAL_DUMMY;
2797 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2798 ei->description[j] = getFile8Bit(file);
2799 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2801 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2803 /* some free bytes for future properties and padding */
2804 ReadUnusedBytesFromFile(file, 7);
2806 ei->use_gfx_element = getFile8Bit(file);
2807 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2809 ei->collect_score_initial = getFile8Bit(file);
2810 ei->collect_count_initial = getFile8Bit(file);
2812 ei->push_delay_fixed = getFile16BitBE(file);
2813 ei->push_delay_random = getFile16BitBE(file);
2814 ei->move_delay_fixed = getFile16BitBE(file);
2815 ei->move_delay_random = getFile16BitBE(file);
2817 ei->move_pattern = getFile16BitBE(file);
2818 ei->move_direction_initial = getFile8Bit(file);
2819 ei->move_stepsize = getFile8Bit(file);
2821 for (y = 0; y < 3; y++)
2822 for (x = 0; x < 3; x++)
2823 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2825 event_bits = getFile32BitBE(file);
2826 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2827 if (event_bits & (1 << j))
2828 ei->change->has_event[j] = TRUE;
2830 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2832 ei->change->delay_fixed = getFile16BitBE(file);
2833 ei->change->delay_random = getFile16BitBE(file);
2834 ei->change->delay_frames = getFile16BitBE(file);
2836 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2838 ei->change->explode = getFile8Bit(file);
2839 ei->change->use_target_content = getFile8Bit(file);
2840 ei->change->only_if_complete = getFile8Bit(file);
2841 ei->change->use_random_replace = getFile8Bit(file);
2843 ei->change->random_percentage = getFile8Bit(file);
2844 ei->change->replace_when = getFile8Bit(file);
2846 for (y = 0; y < 3; y++)
2847 for (x = 0; x < 3; x++)
2848 ei->change->target_content.e[x][y] =
2849 getMappedElement(getFile16BitBE(file));
2851 ei->slippery_type = getFile8Bit(file);
2853 /* some free bytes for future properties and padding */
2854 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2856 /* mark that this custom element has been modified */
2857 ei->modified_settings = TRUE;
2863 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2865 struct ElementInfo *ei;
2866 int chunk_size_expected;
2870 /* ---------- custom element base property values (96 bytes) ------------- */
2872 element = getMappedElement(getFile16BitBE(file));
2874 if (!IS_CUSTOM_ELEMENT(element))
2876 Error(ERR_WARN, "invalid custom element number %d", element);
2878 ReadUnusedBytesFromFile(file, chunk_size - 2);
2882 ei = &element_info[element];
2884 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2885 ei->description[i] = getFile8Bit(file);
2886 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2888 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2890 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2892 ei->num_change_pages = getFile8Bit(file);
2894 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2895 if (chunk_size_expected != chunk_size)
2897 ReadUnusedBytesFromFile(file, chunk_size - 43);
2898 return chunk_size_expected;
2901 ei->ce_value_fixed_initial = getFile16BitBE(file);
2902 ei->ce_value_random_initial = getFile16BitBE(file);
2903 ei->use_last_ce_value = getFile8Bit(file);
2905 ei->use_gfx_element = getFile8Bit(file);
2906 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2908 ei->collect_score_initial = getFile8Bit(file);
2909 ei->collect_count_initial = getFile8Bit(file);
2911 ei->drop_delay_fixed = getFile8Bit(file);
2912 ei->push_delay_fixed = getFile8Bit(file);
2913 ei->drop_delay_random = getFile8Bit(file);
2914 ei->push_delay_random = getFile8Bit(file);
2915 ei->move_delay_fixed = getFile16BitBE(file);
2916 ei->move_delay_random = getFile16BitBE(file);
2918 /* bits 0 - 15 of "move_pattern" ... */
2919 ei->move_pattern = getFile16BitBE(file);
2920 ei->move_direction_initial = getFile8Bit(file);
2921 ei->move_stepsize = getFile8Bit(file);
2923 ei->slippery_type = getFile8Bit(file);
2925 for (y = 0; y < 3; y++)
2926 for (x = 0; x < 3; x++)
2927 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2929 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2930 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2931 ei->move_leave_type = getFile8Bit(file);
2933 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2934 ei->move_pattern |= (getFile16BitBE(file) << 16);
2936 ei->access_direction = getFile8Bit(file);
2938 ei->explosion_delay = getFile8Bit(file);
2939 ei->ignition_delay = getFile8Bit(file);
2940 ei->explosion_type = getFile8Bit(file);
2942 /* some free bytes for future custom property values and padding */
2943 ReadUnusedBytesFromFile(file, 1);
2945 /* ---------- change page property values (48 bytes) --------------------- */
2947 setElementChangePages(ei, ei->num_change_pages);
2949 for (i = 0; i < ei->num_change_pages; i++)
2951 struct ElementChangeInfo *change = &ei->change_page[i];
2952 unsigned int event_bits;
2954 /* always start with reliable default values */
2955 setElementChangeInfoToDefaults(change);
2957 /* bits 0 - 31 of "has_event[]" ... */
2958 event_bits = getFile32BitBE(file);
2959 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2960 if (event_bits & (1 << j))
2961 change->has_event[j] = TRUE;
2963 change->target_element = getMappedElement(getFile16BitBE(file));
2965 change->delay_fixed = getFile16BitBE(file);
2966 change->delay_random = getFile16BitBE(file);
2967 change->delay_frames = getFile16BitBE(file);
2969 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2971 change->explode = getFile8Bit(file);
2972 change->use_target_content = getFile8Bit(file);
2973 change->only_if_complete = getFile8Bit(file);
2974 change->use_random_replace = getFile8Bit(file);
2976 change->random_percentage = getFile8Bit(file);
2977 change->replace_when = getFile8Bit(file);
2979 for (y = 0; y < 3; y++)
2980 for (x = 0; x < 3; x++)
2981 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2983 change->can_change = getFile8Bit(file);
2985 change->trigger_side = getFile8Bit(file);
2987 change->trigger_player = getFile8Bit(file);
2988 change->trigger_page = getFile8Bit(file);
2990 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2991 CH_PAGE_ANY : (1 << change->trigger_page));
2993 change->has_action = getFile8Bit(file);
2994 change->action_type = getFile8Bit(file);
2995 change->action_mode = getFile8Bit(file);
2996 change->action_arg = getFile16BitBE(file);
2998 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2999 event_bits = getFile8Bit(file);
3000 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3001 if (event_bits & (1 << (j - 32)))
3002 change->has_event[j] = TRUE;
3005 /* mark this custom element as modified */
3006 ei->modified_settings = TRUE;
3011 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3013 struct ElementInfo *ei;
3014 struct ElementGroupInfo *group;
3018 element = getMappedElement(getFile16BitBE(file));
3020 if (!IS_GROUP_ELEMENT(element))
3022 Error(ERR_WARN, "invalid group element number %d", element);
3024 ReadUnusedBytesFromFile(file, chunk_size - 2);
3028 ei = &element_info[element];
3030 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3031 ei->description[i] = getFile8Bit(file);
3032 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3034 group = element_info[element].group;
3036 group->num_elements = getFile8Bit(file);
3038 ei->use_gfx_element = getFile8Bit(file);
3039 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3041 group->choice_mode = getFile8Bit(file);
3043 /* some free bytes for future values and padding */
3044 ReadUnusedBytesFromFile(file, 3);
3046 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3047 group->element[i] = getMappedElement(getFile16BitBE(file));
3049 /* mark this group element as modified */
3050 element_info[element].modified_settings = TRUE;
3055 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3056 int element, int real_element)
3058 int micro_chunk_size = 0;
3059 int conf_type = getFile8Bit(file);
3060 int byte_mask = conf_type & CONF_MASK_BYTES;
3061 boolean element_found = FALSE;
3064 micro_chunk_size += 1;
3066 if (byte_mask == CONF_MASK_MULTI_BYTES)
3068 int num_bytes = getFile16BitBE(file);
3069 byte *buffer = checked_malloc(num_bytes);
3071 ReadBytesFromFile(file, buffer, num_bytes);
3073 for (i = 0; conf[i].data_type != -1; i++)
3075 if (conf[i].element == element &&
3076 conf[i].conf_type == conf_type)
3078 int data_type = conf[i].data_type;
3079 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3080 int max_num_entities = conf[i].max_num_entities;
3082 if (num_entities > max_num_entities)
3085 "truncating number of entities for element %d from %d to %d",
3086 element, num_entities, max_num_entities);
3088 num_entities = max_num_entities;
3091 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3092 data_type == TYPE_CONTENT_LIST))
3094 /* for element and content lists, zero entities are not allowed */
3095 Error(ERR_WARN, "found empty list of entities for element %d",
3098 /* do not set "num_entities" here to prevent reading behind buffer */
3100 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
3104 *(int *)(conf[i].num_entities) = num_entities;
3107 element_found = TRUE;
3109 if (data_type == TYPE_STRING)
3111 char *string = (char *)(conf[i].value);
3114 for (j = 0; j < max_num_entities; j++)
3115 string[j] = (j < num_entities ? buffer[j] : '\0');
3117 else if (data_type == TYPE_ELEMENT_LIST)
3119 int *element_array = (int *)(conf[i].value);
3122 for (j = 0; j < num_entities; j++)
3124 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3126 else if (data_type == TYPE_CONTENT_LIST)
3128 struct Content *content= (struct Content *)(conf[i].value);
3131 for (c = 0; c < num_entities; c++)
3132 for (y = 0; y < 3; y++)
3133 for (x = 0; x < 3; x++)
3134 content[c].e[x][y] =
3135 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3138 element_found = FALSE;
3144 checked_free(buffer);
3146 micro_chunk_size += 2 + num_bytes;
3148 else /* constant size configuration data (1, 2 or 4 bytes) */
3150 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3151 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3152 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3154 for (i = 0; conf[i].data_type != -1; i++)
3156 if (conf[i].element == element &&
3157 conf[i].conf_type == conf_type)
3159 int data_type = conf[i].data_type;
3161 if (data_type == TYPE_ELEMENT)
3162 value = getMappedElement(value);
3164 if (data_type == TYPE_BOOLEAN)
3165 *(boolean *)(conf[i].value) = value;
3167 *(int *) (conf[i].value) = value;
3169 element_found = TRUE;
3175 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3180 char *error_conf_chunk_bytes =
3181 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3182 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3183 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3184 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3185 int error_element = real_element;
3187 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3188 error_conf_chunk_bytes, error_conf_chunk_token,
3189 error_element, EL_NAME(error_element));
3192 return micro_chunk_size;
3195 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3197 int real_chunk_size = 0;
3199 li = *level; /* copy level data into temporary buffer */
3201 while (!checkEndOfFile(file))
3203 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3205 if (real_chunk_size >= chunk_size)
3209 *level = li; /* copy temporary buffer back to level data */
3211 return real_chunk_size;
3214 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3216 int real_chunk_size = 0;
3218 li = *level; /* copy level data into temporary buffer */
3220 while (!checkEndOfFile(file))
3222 int element = getMappedElement(getFile16BitBE(file));
3224 real_chunk_size += 2;
3225 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3227 if (real_chunk_size >= chunk_size)
3231 *level = li; /* copy temporary buffer back to level data */
3233 return real_chunk_size;
3236 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3238 int real_chunk_size = 0;
3240 li = *level; /* copy level data into temporary buffer */
3242 while (!checkEndOfFile(file))
3244 int element = getMappedElement(getFile16BitBE(file));
3246 real_chunk_size += 2;
3247 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3249 if (real_chunk_size >= chunk_size)
3253 *level = li; /* copy temporary buffer back to level data */
3255 return real_chunk_size;
3258 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3260 int element = getMappedElement(getFile16BitBE(file));
3261 int envelope_nr = element - EL_ENVELOPE_1;
3262 int real_chunk_size = 2;
3264 while (!checkEndOfFile(file))
3266 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3269 if (real_chunk_size >= chunk_size)
3273 level->envelope[envelope_nr] = xx_envelope;
3275 return real_chunk_size;
3278 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3280 int element = getMappedElement(getFile16BitBE(file));
3281 int real_chunk_size = 2;
3282 struct ElementInfo *ei = &element_info[element];
3285 xx_ei = *ei; /* copy element data into temporary buffer */
3287 xx_ei.num_change_pages = -1;
3289 while (!checkEndOfFile(file))
3291 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3293 if (xx_ei.num_change_pages != -1)
3296 if (real_chunk_size >= chunk_size)
3302 if (ei->num_change_pages == -1)
3304 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3307 ei->num_change_pages = 1;
3309 setElementChangePages(ei, 1);
3310 setElementChangeInfoToDefaults(ei->change);
3312 return real_chunk_size;
3315 /* initialize number of change pages stored for this custom element */
3316 setElementChangePages(ei, ei->num_change_pages);
3317 for (i = 0; i < ei->num_change_pages; i++)
3318 setElementChangeInfoToDefaults(&ei->change_page[i]);
3320 /* start with reading properties for the first change page */
3321 xx_current_change_page = 0;
3323 while (!checkEndOfFile(file))
3325 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3327 xx_change = *change; /* copy change data into temporary buffer */
3329 resetEventBits(); /* reset bits; change page might have changed */
3331 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3334 *change = xx_change;
3336 setEventFlagsFromEventBits(change);
3338 if (real_chunk_size >= chunk_size)
3342 return real_chunk_size;
3345 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3347 int element = getMappedElement(getFile16BitBE(file));
3348 int real_chunk_size = 2;
3349 struct ElementInfo *ei = &element_info[element];
3350 struct ElementGroupInfo *group = ei->group;
3352 xx_ei = *ei; /* copy element data into temporary buffer */
3353 xx_group = *group; /* copy group data into temporary buffer */
3355 while (!checkEndOfFile(file))
3357 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3360 if (real_chunk_size >= chunk_size)
3367 return real_chunk_size;
3372 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
3374 level->file_version = getFileVersion(file);
3375 level->game_version = getFileVersion(file);
3380 static int LoadLevel_DATE(FILE *file, int chunk_size, struct LevelInfo *level)
3382 level->creation_date.year = getFile16BitBE(file);
3383 level->creation_date.month = getFile8Bit(file);
3384 level->creation_date.day = getFile8Bit(file);
3386 level->creation_date.src = DATE_SRC_LEVELFILE;
3391 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
3393 int initial_player_stepsize;
3394 int initial_player_gravity;
3397 level->fieldx = getFile8Bit(file);
3398 level->fieldy = getFile8Bit(file);
3400 level->time = getFile16BitBE(file);
3401 level->gems_needed = getFile16BitBE(file);
3403 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3404 level->name[i] = getFile8Bit(file);
3405 level->name[MAX_LEVEL_NAME_LEN] = 0;
3407 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3408 level->score[i] = getFile8Bit(file);
3410 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3411 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3412 for (y = 0; y < 3; y++)
3413 for (x = 0; x < 3; x++)
3414 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3416 level->amoeba_speed = getFile8Bit(file);
3417 level->time_magic_wall = getFile8Bit(file);
3418 level->time_wheel = getFile8Bit(file);
3419 level->amoeba_content = getMappedElement(getFile8Bit(file));
3421 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3424 for (i = 0; i < MAX_PLAYERS; i++)
3425 level->initial_player_stepsize[i] = initial_player_stepsize;
3427 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3429 for (i = 0; i < MAX_PLAYERS; i++)
3430 level->initial_player_gravity[i] = initial_player_gravity;
3432 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3433 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3435 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3437 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3438 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3439 level->can_move_into_acid_bits = getFile32BitBE(file);
3440 level->dont_collide_with_bits = getFile8Bit(file);
3442 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3443 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3445 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3446 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3447 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3449 level->game_engine_type = getFile8Bit(file);
3451 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3456 static int LoadLevel_NAME(FILE *file, int chunk_size, struct LevelInfo *level)
3460 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3461 level->name[i] = getFile8Bit(file);
3462 level->name[MAX_LEVEL_NAME_LEN] = 0;
3467 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
3471 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3472 level->author[i] = getFile8Bit(file);
3473 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3478 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
3481 int chunk_size_expected = level->fieldx * level->fieldy;
3483 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3484 stored with 16-bit encoding (and should be twice as big then).
3485 Even worse, playfield data was stored 16-bit when only yamyam content
3486 contained 16-bit elements and vice versa. */
3488 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3489 chunk_size_expected *= 2;
3491 if (chunk_size_expected != chunk_size)
3493 ReadUnusedBytesFromFile(file, chunk_size);
3494 return chunk_size_expected;
3497 for (y = 0; y < level->fieldy; y++)
3498 for (x = 0; x < level->fieldx; x++)
3499 level->field[x][y] =
3500 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3505 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
3508 int header_size = 4;
3509 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3510 int chunk_size_expected = header_size + content_size;
3512 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3513 stored with 16-bit encoding (and should be twice as big then).
3514 Even worse, playfield data was stored 16-bit when only yamyam content
3515 contained 16-bit elements and vice versa. */
3517 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3518 chunk_size_expected += content_size;
3520 if (chunk_size_expected != chunk_size)
3522 ReadUnusedBytesFromFile(file, chunk_size);
3523 return chunk_size_expected;
3527 level->num_yamyam_contents = getFile8Bit(file);
3531 /* correct invalid number of content fields -- should never happen */
3532 if (level->num_yamyam_contents < 1 ||
3533 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3534 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3536 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3537 for (y = 0; y < 3; y++)
3538 for (x = 0; x < 3; x++)
3539 level->yamyam_content[i].e[x][y] =
3540 getMappedElement(level->encoding_16bit_field ?
3541 getFile16BitBE(file) : getFile8Bit(file));
3545 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
3551 int content_xsize, content_ysize;
3553 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3555 element = getMappedElement(getFile16BitBE(file));
3556 num_contents = getFile8Bit(file);
3558 getFile8Bit(file); /* content x size (unused) */
3559 getFile8Bit(file); /* content y size (unused) */
3561 content_xsize = getFile8Bit(file);
3562 content_ysize = getFile8Bit(file);
3565 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3567 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3568 for (y = 0; y < 3; y++)
3569 for (x = 0; x < 3; x++)
3570 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3572 /* correct invalid number of content fields -- should never happen */
3573 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3574 num_contents = STD_ELEMENT_CONTENTS;
3576 if (element == EL_YAMYAM)
3578 level->num_yamyam_contents = num_contents;
3580 for (i = 0; i < num_contents; i++)
3581 for (y = 0; y < 3; y++)
3582 for (x = 0; x < 3; x++)
3583 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3585 else if (element == EL_BD_AMOEBA)
3587 level->amoeba_content = content_array[0][0][0];
3591 Error(ERR_WARN, "cannot load content for element '%d'", element);
3597 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
3603 int chunk_size_expected;
3605 element = getMappedElement(getFile16BitBE(file));
3606 if (!IS_ENVELOPE(element))
3607 element = EL_ENVELOPE_1;
3609 envelope_nr = element - EL_ENVELOPE_1;
3611 envelope_len = getFile16BitBE(file);
3613 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3614 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3616 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3618 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3619 if (chunk_size_expected != chunk_size)
3621 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3622 return chunk_size_expected;
3625 for (i = 0; i < envelope_len; i++)
3626 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3631 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
3633 int num_changed_custom_elements = getFile16BitBE(file);
3634 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3637 if (chunk_size_expected != chunk_size)
3639 ReadUnusedBytesFromFile(file, chunk_size - 2);
3640 return chunk_size_expected;
3643 for (i = 0; i < num_changed_custom_elements; i++)
3645 int element = getMappedElement(getFile16BitBE(file));
3646 int properties = getFile32BitBE(file);
3648 if (IS_CUSTOM_ELEMENT(element))
3649 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3651 Error(ERR_WARN, "invalid custom element number %d", element);
3653 /* older game versions that wrote level files with CUS1 chunks used
3654 different default push delay values (not yet stored in level file) */
3655 element_info[element].push_delay_fixed = 2;
3656 element_info[element].push_delay_random = 8;
3662 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
3664 int num_changed_custom_elements = getFile16BitBE(file);
3665 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3668 if (chunk_size_expected != chunk_size)
3670 ReadUnusedBytesFromFile(file, chunk_size - 2);
3671 return chunk_size_expected;
3674 for (i = 0; i < num_changed_custom_elements; i++)
3676 int element = getMappedElement(getFile16BitBE(file));
3677 int custom_target_element = getMappedElement(getFile16BitBE(file));
3679 if (IS_CUSTOM_ELEMENT(element))
3680 element_info[element].change->target_element = custom_target_element;
3682 Error(ERR_WARN, "invalid custom element number %d", element);
3688 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
3690 int num_changed_custom_elements = getFile16BitBE(file);
3691 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3694 if (chunk_size_expected != chunk_size)
3696 ReadUnusedBytesFromFile(file, chunk_size - 2);
3697 return chunk_size_expected;
3700 for (i = 0; i < num_changed_custom_elements; i++)
3702 int element = getMappedElement(getFile16BitBE(file));
3703 struct ElementInfo *ei = &element_info[element];
3704 unsigned int event_bits;
3706 if (!IS_CUSTOM_ELEMENT(element))
3708 Error(ERR_WARN, "invalid custom element number %d", element);
3710 element = EL_INTERNAL_DUMMY;
3713 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3714 ei->description[j] = getFile8Bit(file);
3715 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3717 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3719 /* some free bytes for future properties and padding */
3720 ReadUnusedBytesFromFile(file, 7);
3722 ei->use_gfx_element = getFile8Bit(file);
3723 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3725 ei->collect_score_initial = getFile8Bit(file);
3726 ei->collect_count_initial = getFile8Bit(file);
3728 ei->push_delay_fixed = getFile16BitBE(file);
3729 ei->push_delay_random = getFile16BitBE(file);
3730 ei->move_delay_fixed = getFile16BitBE(file);
3731 ei->move_delay_random = getFile16BitBE(file);
3733 ei->move_pattern = getFile16BitBE(file);
3734 ei->move_direction_initial = getFile8Bit(file);
3735 ei->move_stepsize = getFile8Bit(file);
3737 for (y = 0; y < 3; y++)
3738 for (x = 0; x < 3; x++)
3739 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3741 event_bits = getFile32BitBE(file);
3742 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3743 if (event_bits & (1 << j))
3744 ei->change->has_event[j] = TRUE;
3746 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3748 ei->change->delay_fixed = getFile16BitBE(file);
3749 ei->change->delay_random = getFile16BitBE(file);
3750 ei->change->delay_frames = getFile16BitBE(file);
3752 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
3754 ei->change->explode = getFile8Bit(file);
3755 ei->change->use_target_content = getFile8Bit(file);
3756 ei->change->only_if_complete = getFile8Bit(file);
3757 ei->change->use_random_replace = getFile8Bit(file);
3759 ei->change->random_percentage = getFile8Bit(file);
3760 ei->change->replace_when = getFile8Bit(file);
3762 for (y = 0; y < 3; y++)
3763 for (x = 0; x < 3; x++)
3764 ei->change->target_content.e[x][y] =
3765 getMappedElement(getFile16BitBE(file));
3767 ei->slippery_type = getFile8Bit(file);
3769 /* some free bytes for future properties and padding */
3770 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3772 /* mark that this custom element has been modified */
3773 ei->modified_settings = TRUE;
3779 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
3781 struct ElementInfo *ei;
3782 int chunk_size_expected;
3786 /* ---------- custom element base property values (96 bytes) ------------- */
3788 element = getMappedElement(getFile16BitBE(file));
3790 if (!IS_CUSTOM_ELEMENT(element))
3792 Error(ERR_WARN, "invalid custom element number %d", element);
3794 ReadUnusedBytesFromFile(file, chunk_size - 2);
3798 ei = &element_info[element];
3800 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3801 ei->description[i] = getFile8Bit(file);
3802 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3804 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3806 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
3808 ei->num_change_pages = getFile8Bit(file);
3810 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3811 if (chunk_size_expected != chunk_size)
3813 ReadUnusedBytesFromFile(file, chunk_size - 43);
3814 return chunk_size_expected;
3817 ei->ce_value_fixed_initial = getFile16BitBE(file);
3818 ei->ce_value_random_initial = getFile16BitBE(file);
3819 ei->use_last_ce_value = getFile8Bit(file);
3821 ei->use_gfx_element = getFile8Bit(file);
3822 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3824 ei->collect_score_initial = getFile8Bit(file);
3825 ei->collect_count_initial = getFile8Bit(file);
3827 ei->drop_delay_fixed = getFile8Bit(file);
3828 ei->push_delay_fixed = getFile8Bit(file);
3829 ei->drop_delay_random = getFile8Bit(file);
3830 ei->push_delay_random = getFile8Bit(file);
3831 ei->move_delay_fixed = getFile16BitBE(file);
3832 ei->move_delay_random = getFile16BitBE(file);
3834 /* bits 0 - 15 of "move_pattern" ... */
3835 ei->move_pattern = getFile16BitBE(file);
3836 ei->move_direction_initial = getFile8Bit(file);
3837 ei->move_stepsize = getFile8Bit(file);
3839 ei->slippery_type = getFile8Bit(file);
3841 for (y = 0; y < 3; y++)
3842 for (x = 0; x < 3; x++)
3843 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3845 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3846 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3847 ei->move_leave_type = getFile8Bit(file);
3849 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
3850 ei->move_pattern |= (getFile16BitBE(file) << 16);
3852 ei->access_direction = getFile8Bit(file);
3854 ei->explosion_delay = getFile8Bit(file);
3855 ei->ignition_delay = getFile8Bit(file);
3856 ei->explosion_type = getFile8Bit(file);
3858 /* some free bytes for future custom property values and padding */
3859 ReadUnusedBytesFromFile(file, 1);
3861 /* ---------- change page property values (48 bytes) --------------------- */
3863 setElementChangePages(ei, ei->num_change_pages);
3865 for (i = 0; i < ei->num_change_pages; i++)
3867 struct ElementChangeInfo *change = &ei->change_page[i];
3868 unsigned int event_bits;
3870 /* always start with reliable default values */
3871 setElementChangeInfoToDefaults(change);
3873 /* bits 0 - 31 of "has_event[]" ... */
3874 event_bits = getFile32BitBE(file);
3875 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3876 if (event_bits & (1 << j))
3877 change->has_event[j] = TRUE;
3879 change->target_element = getMappedElement(getFile16BitBE(file));
3881 change->delay_fixed = getFile16BitBE(file);
3882 change->delay_random = getFile16BitBE(file);
3883 change->delay_frames = getFile16BitBE(file);
3885 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3887 change->explode = getFile8Bit(file);
3888 change->use_target_content = getFile8Bit(file);
3889 change->only_if_complete = getFile8Bit(file);
3890 change->use_random_replace = getFile8Bit(file);
3892 change->random_percentage = getFile8Bit(file);
3893 change->replace_when = getFile8Bit(file);
3895 for (y = 0; y < 3; y++)
3896 for (x = 0; x < 3; x++)
3897 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3899 change->can_change = getFile8Bit(file);
3901 change->trigger_side = getFile8Bit(file);
3903 change->trigger_player = getFile8Bit(file);
3904 change->trigger_page = getFile8Bit(file);
3906 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3907 CH_PAGE_ANY : (1 << change->trigger_page));
3909 change->has_action = getFile8Bit(file);
3910 change->action_type = getFile8Bit(file);
3911 change->action_mode = getFile8Bit(file);
3912 change->action_arg = getFile16BitBE(file);
3914 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
3915 event_bits = getFile8Bit(file);
3916 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3917 if (event_bits & (1 << (j - 32)))
3918 change->has_event[j] = TRUE;
3921 /* mark this custom element as modified */
3922 ei->modified_settings = TRUE;
3927 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
3929 struct ElementInfo *ei;
3930 struct ElementGroupInfo *group;
3934 element = getMappedElement(getFile16BitBE(file));
3936 if (!IS_GROUP_ELEMENT(element))
3938 Error(ERR_WARN, "invalid group element number %d", element);
3940 ReadUnusedBytesFromFile(file, chunk_size - 2);
3944 ei = &element_info[element];
3946 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3947 ei->description[i] = getFile8Bit(file);
3948 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3950 group = element_info[element].group;
3952 group->num_elements = getFile8Bit(file);
3954 ei->use_gfx_element = getFile8Bit(file);
3955 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3957 group->choice_mode = getFile8Bit(file);
3959 /* some free bytes for future values and padding */
3960 ReadUnusedBytesFromFile(file, 3);
3962 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3963 group->element[i] = getMappedElement(getFile16BitBE(file));
3965 /* mark this group element as modified */
3966 element_info[element].modified_settings = TRUE;
3971 static int LoadLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *conf,
3972 int element, int real_element)
3974 int micro_chunk_size = 0;
3975 int conf_type = getFile8Bit(file);
3976 int byte_mask = conf_type & CONF_MASK_BYTES;
3977 boolean element_found = FALSE;
3980 micro_chunk_size += 1;
3982 if (byte_mask == CONF_MASK_MULTI_BYTES)
3984 int num_bytes = getFile16BitBE(file);
3985 byte *buffer = checked_malloc(num_bytes);
3987 ReadBytesFromFile(file, buffer, num_bytes);
3989 for (i = 0; conf[i].data_type != -1; i++)
3991 if (conf[i].element == element &&
3992 conf[i].conf_type == conf_type)
3994 int data_type = conf[i].data_type;
3995 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3996 int max_num_entities = conf[i].max_num_entities;
3998 if (num_entities > max_num_entities)
4001 "truncating number of entities for element %d from %d to %d",
4002 element, num_entities, max_num_entities);
4004 num_entities = max_num_entities;
4007 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
4008 data_type == TYPE_CONTENT_LIST))
4010 /* for element and content lists, zero entities are not allowed */
4011 Error(ERR_WARN, "found empty list of entities for element %d",
4014 /* do not set "num_entities" here to prevent reading behind buffer */
4016 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
4020 *(int *)(conf[i].num_entities) = num_entities;
4023 element_found = TRUE;
4025 if (data_type == TYPE_STRING)
4027 char *string = (char *)(conf[i].value);
4030 for (j = 0; j < max_num_entities; j++)
4031 string[j] = (j < num_entities ? buffer[j] : '\0');
4033 else if (data_type == TYPE_ELEMENT_LIST)
4035 int *element_array = (int *)(conf[i].value);
4038 for (j = 0; j < num_entities; j++)
4040 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
4042 else if (data_type == TYPE_CONTENT_LIST)
4044 struct Content *content= (struct Content *)(conf[i].value);
4047 for (c = 0; c < num_entities; c++)
4048 for (y = 0; y < 3; y++)
4049 for (x = 0; x < 3; x++)
4050 content[c].e[x][y] =
4051 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
4054 element_found = FALSE;
4060 checked_free(buffer);
4062 micro_chunk_size += 2 + num_bytes;
4064 else /* constant size configuration data (1, 2 or 4 bytes) */
4066 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
4067 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
4068 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
4070 for (i = 0; conf[i].data_type != -1; i++)
4072 if (conf[i].element == element &&
4073 conf[i].conf_type == conf_type)
4075 int data_type = conf[i].data_type;
4077 if (data_type == TYPE_ELEMENT)
4078 value = getMappedElement(value);
4080 if (data_type == TYPE_BOOLEAN)
4081 *(boolean *)(conf[i].value) = value;
4083 *(int *) (conf[i].value) = value;
4085 element_found = TRUE;
4091 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
4096 char *error_conf_chunk_bytes =
4097 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
4098 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
4099 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
4100 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
4101 int error_element = real_element;
4103 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
4104 error_conf_chunk_bytes, error_conf_chunk_token,
4105 error_element, EL_NAME(error_element));
4108 return micro_chunk_size;
4111 static int LoadLevel_INFO(FILE *file, int chunk_size, struct LevelInfo *level)
4113 int real_chunk_size = 0;
4115 li = *level; /* copy level data into temporary buffer */
4119 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
4121 if (real_chunk_size >= chunk_size)
4125 *level = li; /* copy temporary buffer back to level data */
4127 return real_chunk_size;
4130 static int LoadLevel_CONF(FILE *file, int chunk_size, struct LevelInfo *level)
4132 int real_chunk_size = 0;
4134 li = *level; /* copy level data into temporary buffer */
4138 int element = getMappedElement(getFile16BitBE(file));
4140 real_chunk_size += 2;
4141 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
4143 if (real_chunk_size >= chunk_size)
4147 *level = li; /* copy temporary buffer back to level data */
4149 return real_chunk_size;
4152 static int LoadLevel_ELEM(FILE *file, int chunk_size, struct LevelInfo *level)
4154 int real_chunk_size = 0;
4156 li = *level; /* copy level data into temporary buffer */
4160 int element = getMappedElement(getFile16BitBE(file));
4162 real_chunk_size += 2;
4163 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
4165 if (real_chunk_size >= chunk_size)
4169 *level = li; /* copy temporary buffer back to level data */
4171 return real_chunk_size;
4174 static int LoadLevel_NOTE(FILE *file, int chunk_size, struct LevelInfo *level)
4176 int element = getMappedElement(getFile16BitBE(file));
4177 int envelope_nr = element - EL_ENVELOPE_1;
4178 int real_chunk_size = 2;
4182 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
4185 if (real_chunk_size >= chunk_size)
4189 level->envelope[envelope_nr] = xx_envelope;
4191 return real_chunk_size;
4194 static int LoadLevel_CUSX(FILE *file, int chunk_size, struct LevelInfo *level)
4196 int element = getMappedElement(getFile16BitBE(file));
4197 int real_chunk_size = 2;
4198 struct ElementInfo *ei = &element_info[element];
4201 xx_ei = *ei; /* copy element data into temporary buffer */
4203 xx_ei.num_change_pages = -1;
4207 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
4209 if (xx_ei.num_change_pages != -1)
4212 if (real_chunk_size >= chunk_size)
4218 if (ei->num_change_pages == -1)
4220 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
4223 ei->num_change_pages = 1;
4225 setElementChangePages(ei, 1);
4226 setElementChangeInfoToDefaults(ei->change);
4228 return real_chunk_size;
4231 /* initialize number of change pages stored for this custom element */
4232 setElementChangePages(ei, ei->num_change_pages);
4233 for (i = 0; i < ei->num_change_pages; i++)
4234 setElementChangeInfoToDefaults(&ei->change_page[i]);
4236 /* start with reading properties for the first change page */
4237 xx_current_change_page = 0;
4241 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4243 xx_change = *change; /* copy change data into temporary buffer */
4245 resetEventBits(); /* reset bits; change page might have changed */
4247 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4250 *change = xx_change;
4252 setEventFlagsFromEventBits(change);
4254 if (real_chunk_size >= chunk_size)
4258 return real_chunk_size;
4261 static int LoadLevel_GRPX(FILE *file, int chunk_size, struct LevelInfo *level)
4263 int element = getMappedElement(getFile16BitBE(file));
4264 int real_chunk_size = 2;
4265 struct ElementInfo *ei = &element_info[element];
4266 struct ElementGroupInfo *group = ei->group;
4268 xx_ei = *ei; /* copy element data into temporary buffer */
4269 xx_group = *group; /* copy group data into temporary buffer */
4273 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4276 if (real_chunk_size >= chunk_size)
4283 return real_chunk_size;
4290 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4291 struct LevelFileInfo *level_file_info,
4292 boolean level_info_only)
4294 char *filename = level_file_info->filename;
4295 char cookie[MAX_LINE_LEN];
4296 char chunk_name[CHUNK_ID_LEN + 1];
4300 if (!(file = openFile(filename, MODE_READ)))
4302 level->no_valid_file = TRUE;
4305 if (!level_info_only)
4306 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4308 if (level != &level_template)
4309 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4315 getFileChunkBE(file, chunk_name, NULL);
4316 if (strEqual(chunk_name, "RND1"))
4318 getFile32BitBE(file); /* not used */
4320 getFileChunkBE(file, chunk_name, NULL);
4321 if (!strEqual(chunk_name, "CAVE"))
4323 level->no_valid_file = TRUE;
4325 Error(ERR_WARN, "unknown format of level file '%s'", filename);
4332 else /* check for pre-2.0 file format with cookie string */
4334 strcpy(cookie, chunk_name);
4335 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4337 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4338 cookie[strlen(cookie) - 1] = '\0';
4340 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4342 level->no_valid_file = TRUE;
4344 Error(ERR_WARN, "unknown format of level file '%s'", filename);
4351 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4353 level->no_valid_file = TRUE;
4355 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
4362 /* pre-2.0 level files have no game version, so use file version here */
4363 level->game_version = level->file_version;
4366 if (level->file_version < FILE_VERSION_1_2)
4368 /* level files from versions before 1.2.0 without chunk structure */
4369 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4370 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4378 int (*loader)(File *, int, struct LevelInfo *);
4382 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4383 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4384 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4385 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4386 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4387 { "INFO", -1, LoadLevel_INFO },
4388 { "BODY", -1, LoadLevel_BODY },
4389 { "CONT", -1, LoadLevel_CONT },
4390 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4391 { "CNT3", -1, LoadLevel_CNT3 },
4392 { "CUS1", -1, LoadLevel_CUS1 },
4393 { "CUS2", -1, LoadLevel_CUS2 },
4394 { "CUS3", -1, LoadLevel_CUS3 },
4395 { "CUS4", -1, LoadLevel_CUS4 },
4396 { "GRP1", -1, LoadLevel_GRP1 },
4397 { "CONF", -1, LoadLevel_CONF },
4398 { "ELEM", -1, LoadLevel_ELEM },
4399 { "NOTE", -1, LoadLevel_NOTE },
4400 { "CUSX", -1, LoadLevel_CUSX },
4401 { "GRPX", -1, LoadLevel_GRPX },
4406 while (getFileChunkBE(file, chunk_name, &chunk_size))
4410 while (chunk_info[i].name != NULL &&
4411 !strEqual(chunk_name, chunk_info[i].name))
4414 if (chunk_info[i].name == NULL)
4416 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
4417 chunk_name, filename);
4418 ReadUnusedBytesFromFile(file, chunk_size);
4420 else if (chunk_info[i].size != -1 &&
4421 chunk_info[i].size != chunk_size)
4423 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
4424 chunk_size, chunk_name, filename);
4425 ReadUnusedBytesFromFile(file, chunk_size);
4429 /* call function to load this level chunk */
4430 int chunk_size_expected =
4431 (chunk_info[i].loader)(file, chunk_size, level);
4433 /* the size of some chunks cannot be checked before reading other
4434 chunks first (like "HEAD" and "BODY") that contain some header
4435 information, so check them here */
4436 if (chunk_size_expected != chunk_size)
4438 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
4439 chunk_size, chunk_name, filename);
4450 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4451 struct LevelFileInfo *level_file_info,
4452 boolean level_info_only)
4454 char *filename = level_file_info->filename;
4455 char cookie[MAX_LINE_LEN];
4456 char chunk_name[CHUNK_ID_LEN + 1];
4460 if (!(file = fopen(filename, MODE_READ)))
4462 level->no_valid_file = TRUE;
4465 if (!level_info_only)
4466 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4468 if (level != &level_template)
4469 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4475 getFileChunkBE(file, chunk_name, NULL);
4476 if (strEqual(chunk_name, "RND1"))
4478 getFile32BitBE(file); /* not used */
4480 getFileChunkBE(file, chunk_name, NULL);
4481 if (!strEqual(chunk_name, "CAVE"))
4483 level->no_valid_file = TRUE;
4485 Error(ERR_WARN, "unknown format of level file '%s'", filename);
4490 else /* check for pre-2.0 file format with cookie string */
4492 strcpy(cookie, chunk_name);
4493 if (fgets(&cookie[4], MAX_LINE_LEN - 4, file) == NULL)
4495 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4496 cookie[strlen(cookie) - 1] = '\0';
4498 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4500 level->no_valid_file = TRUE;
4502 Error(ERR_WARN, "unknown format of level file '%s'", filename);
4507 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4509 level->no_valid_file = TRUE;
4511 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
4516 /* pre-2.0 level files have no game version, so use file version here */
4517 level->game_version = level->file_version;
4520 if (level->file_version < FILE_VERSION_1_2)
4522 /* level files from versions before 1.2.0 without chunk structure */
4523 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4524 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4532 int (*loader)(FILE *, int, struct LevelInfo *);
4536 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4537 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4538 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4539 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4540 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4541 { "INFO", -1, LoadLevel_INFO },
4542 { "BODY", -1, LoadLevel_BODY },
4543 { "CONT", -1, LoadLevel_CONT },
4544 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4545 { "CNT3", -1, LoadLevel_CNT3 },
4546 { "CUS1", -1, LoadLevel_CUS1 },
4547 { "CUS2", -1, LoadLevel_CUS2 },
4548 { "CUS3", -1, LoadLevel_CUS3 },
4549 { "CUS4", -1, LoadLevel_CUS4 },
4550 { "GRP1", -1, LoadLevel_GRP1 },
4551 { "CONF", -1, LoadLevel_CONF },
4552 { "ELEM", -1, LoadLevel_ELEM },
4553 { "NOTE", -1, LoadLevel_NOTE },
4554 { "CUSX", -1, LoadLevel_CUSX },
4555 { "GRPX", -1, LoadLevel_GRPX },
4560 while (getFileChunkBE(file, chunk_name, &chunk_size))
4564 while (chunk_info[i].name != NULL &&
4565 !strEqual(chunk_name, chunk_info[i].name))
4568 if (chunk_info[i].name == NULL)
4570 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
4571 chunk_name, filename);
4572 ReadUnusedBytesFromFile(file, chunk_size);
4574 else if (chunk_info[i].size != -1 &&
4575 chunk_info[i].size != chunk_size)
4577 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
4578 chunk_size, chunk_name, filename);
4579 ReadUnusedBytesFromFile(file, chunk_size);
4583 /* call function to load this level chunk */
4584 int chunk_size_expected =
4585 (chunk_info[i].loader)(file, chunk_size, level);
4587 /* the size of some chunks cannot be checked before reading other
4588 chunks first (like "HEAD" and "BODY") that contain some header
4589 information, so check them here */
4590 if (chunk_size_expected != chunk_size)
4592 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
4593 chunk_size, chunk_name, filename);
4605 /* ------------------------------------------------------------------------- */
4606 /* functions for loading EM level */
4607 /* ------------------------------------------------------------------------- */
4611 static int map_em_element_yam(int element)
4615 case 0x00: return EL_EMPTY;
4616 case 0x01: return EL_EMERALD;
4617 case 0x02: return EL_DIAMOND;
4618 case 0x03: return EL_ROCK;
4619 case 0x04: return EL_ROBOT;
4620 case 0x05: return EL_SPACESHIP_UP;
4621 case 0x06: return EL_BOMB;
4622 case 0x07: return EL_BUG_UP;
4623 case 0x08: return EL_AMOEBA_DROP;
4624 case 0x09: return EL_NUT;
4625 case 0x0a: return EL_YAMYAM;
4626 case 0x0b: return EL_QUICKSAND_FULL;
4627 case 0x0c: return EL_SAND;
4628 case 0x0d: return EL_WALL_SLIPPERY;
4629 case 0x0e: return EL_STEELWALL;
4630 case 0x0f: return EL_WALL;
4631 case 0x10: return EL_EM_KEY_1;
4632 case 0x11: return EL_EM_KEY_2;
4633 case 0x12: return EL_EM_KEY_4;
4634 case 0x13: return EL_EM_KEY_3;
4635 case 0x14: return EL_MAGIC_WALL;
4636 case 0x15: return EL_ROBOT_WHEEL;
4637 case 0x16: return EL_DYNAMITE;
4639 case 0x17: return EL_EM_KEY_1; /* EMC */
4640 case 0x18: return EL_BUG_UP; /* EMC */
4641 case 0x1a: return EL_DIAMOND; /* EMC */
4642 case 0x1b: return EL_EMERALD; /* EMC */
4643 case 0x25: return EL_NUT; /* EMC */
4644 case 0x80: return EL_EMPTY; /* EMC */
4645 case 0x85: return EL_EM_KEY_1; /* EMC */
4646 case 0x86: return EL_EM_KEY_2; /* EMC */
4647 case 0x87: return EL_EM_KEY_4; /* EMC */
4648 case 0x88: return EL_EM_KEY_3; /* EMC */
4649 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
4650 case 0x9a: return EL_AMOEBA_WET; /* EMC */
4651 case 0xaf: return EL_DYNAMITE; /* EMC */
4652 case 0xbd: return EL_SAND; /* EMC */
4655 Error(ERR_WARN, "invalid level element %d", element);
4660 static int map_em_element_field(int element)
4662 if (element >= 0xc8 && element <= 0xe1)
4663 return EL_CHAR_A + (element - 0xc8);
4664 else if (element >= 0xe2 && element <= 0xeb)
4665 return EL_CHAR_0 + (element - 0xe2);
4669 case 0x00: return EL_ROCK;
4670 case 0x01: return EL_ROCK; /* EMC */
4671 case 0x02: return EL_DIAMOND;
4672 case 0x03: return EL_DIAMOND;
4673 case 0x04: return EL_ROBOT;
4674 case 0x05: return EL_ROBOT; /* EMC */
4675 case 0x06: return EL_EMPTY_SPACE; /* EMC */
4676 case 0x07: return EL_EMPTY_SPACE; /* EMC */
4677 case 0x08: return EL_SPACESHIP_UP;
4678 case 0x09: return EL_SPACESHIP_RIGHT;
4679 case 0x0a: return EL_SPACESHIP_DOWN;
4680 case 0x0b: return EL_SPACESHIP_LEFT;
4681 case 0x0c: return EL_SPACESHIP_UP;
4682 case 0x0d: return EL_SPACESHIP_RIGHT;
4683 case 0x0e: return EL_SPACESHIP_DOWN;
4684 case 0x0f: return EL_SPACESHIP_LEFT;
4686 case 0x10: return EL_BOMB;
4687 case 0x11: return EL_BOMB; /* EMC */
4688 case 0x12: return EL_EMERALD;
4689 case 0x13: return EL_EMERALD;
4690 case 0x14: return EL_BUG_UP;
4691 case 0x15: return EL_BUG_RIGHT;
4692 case 0x16: return EL_BUG_DOWN;
4693 case 0x17: return EL_BUG_LEFT;
4694 case 0x18: return EL_BUG_UP;
4695 case 0x19: return EL_BUG_RIGHT;
4696 case 0x1a: return EL_BUG_DOWN;
4697 case 0x1b: return EL_BUG_LEFT;
4698 case 0x1c: return EL_AMOEBA_DROP;
4699 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
4700 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
4701 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
4703 case 0x20: return EL_ROCK;
4704 case 0x21: return EL_BOMB; /* EMC */
4705 case 0x22: return EL_DIAMOND; /* EMC */
4706 case 0x23: return EL_EMERALD; /* EMC */
4707 case 0x24: return EL_MAGIC_WALL;
4708 case 0x25: return EL_NUT;
4709 case 0x26: return EL_NUT; /* EMC */
4710 case 0x27: return EL_NUT; /* EMC */
4712 /* looks like magic wheel, but is _always_ activated */
4713 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
4715 case 0x29: return EL_YAMYAM; /* up */
4716 case 0x2a: return EL_YAMYAM; /* down */
4717 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
4718 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
4719 case 0x2d: return EL_QUICKSAND_FULL;
4720 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
4721 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
4723 case 0x30: return EL_EMPTY_SPACE; /* EMC */
4724 case 0x31: return EL_SAND; /* EMC */
4725 case 0x32: return EL_SAND; /* EMC */
4726 case 0x33: return EL_SAND; /* EMC */
4727 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
4728 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
4729 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
4730 case 0x37: return EL_SAND; /* EMC */
4731 case 0x38: return EL_ROCK; /* EMC */
4732 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
4733 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
4734 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
4735 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
4736 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
4737 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
4738 case 0x3f: return EL_ACID_POOL_BOTTOM;
4740 case 0x40: return EL_EXIT_OPEN; /* 1 */
4741 case 0x41: return EL_EXIT_OPEN; /* 2 */
4742 case 0x42: return EL_EXIT_OPEN; /* 3 */
4743 case 0x43: return EL_BALLOON; /* EMC */
4744 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
4745 case 0x45: return EL_SPRING; /* EMC */
4746 case 0x46: return EL_SPRING; /* falling */ /* EMC */
4747 case 0x47: return EL_SPRING; /* left */ /* EMC */
4748 case 0x48: return EL_SPRING; /* right */ /* EMC */
4749 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
4750 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
4751 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
4752 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
4753 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
4754 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
4755 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
4757 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
4758 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
4759 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
4760 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
4761 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
4762 case 0x55: return EL_EMPTY_SPACE; /* EMC */
4763 case 0x56: return EL_EMPTY_SPACE; /* EMC */
4764 case 0x57: return EL_EMPTY_SPACE; /* EMC */
4765 case 0x58: return EL_EMPTY_SPACE; /* EMC */
4766 case 0x59: return EL_EMPTY_SPACE; /* EMC */
4767 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
4768 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
4769 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
4770 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
4771 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
4772 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
4774 case 0x60: return EL_EMPTY_SPACE; /* EMC */
4775 case 0x61: return EL_EMPTY_SPACE; /* EMC */
4776 case 0x62: return EL_EMPTY_SPACE; /* EMC */
4777 case 0x63: return EL_SPRING; /* left */ /* EMC */
4778 case 0x64: return EL_SPRING; /* right */ /* EMC */
4779 case 0x65: return EL_ACID; /* 1 */ /* EMC */
4780 case 0x66: return EL_ACID; /* 2 */ /* EMC */
4781 case 0x67: return EL_ACID; /* 3 */ /* EMC */
4782 case 0x68: return EL_ACID; /* 4 */ /* EMC */
4783 case 0x69: return EL_ACID; /* 5 */ /* EMC */
4784 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
4785 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
4786 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
4787 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
4788 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
4789 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
4791 case 0x70: return EL_EMPTY_SPACE; /* EMC */
4792 case 0x71: return EL_EMPTY_SPACE; /* EMC */
4793 case 0x72: return EL_NUT; /* left */ /* EMC */
4794 case 0x73: return EL_SAND; /* EMC (? "nut") */
4795 case 0x74: return EL_STEELWALL;
4796 case 0x75: return EL_EMPTY_SPACE; /* EMC */
4797 case 0x76: return EL_EMPTY_SPACE; /* EMC */
4798 case 0x77: return EL_BOMB; /* left */ /* EMC */
4799 case 0x78: return EL_BOMB; /* right */ /* EMC */
4800 case 0x79: return EL_ROCK; /* left */ /* EMC */
4801 case 0x7a: return EL_ROCK; /* right */ /* EMC */
4802 case 0x7b: return EL_ACID; /* (? EMC "blank") */
4803 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
4804 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
4805 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
4806 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
4808 case 0x80: return EL_EMPTY;
4809 case 0x81: return EL_WALL_SLIPPERY;
4810 case 0x82: return EL_SAND;
4811 case 0x83: return EL_STEELWALL;
4812 case 0x84: return EL_WALL;
4813 case 0x85: return EL_EM_KEY_1;
4814 case 0x86: return EL_EM_KEY_2;
4815 case 0x87: return EL_EM_KEY_4;
4816 case 0x88: return EL_EM_KEY_3;
4817 case 0x89: return EL_EM_GATE_1;
4818 case 0x8a: return EL_EM_GATE_2;
4819 case 0x8b: return EL_EM_GATE_4;
4820 case 0x8c: return EL_EM_GATE_3;
4821 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
4822 case 0x8e: return EL_EM_GATE_1_GRAY;
4823 case 0x8f: return EL_EM_GATE_2_GRAY;
4825 case 0x90: return EL_EM_GATE_4_GRAY;
4826 case 0x91: return EL_EM_GATE_3_GRAY;
4827 case 0x92: return EL_MAGIC_WALL;
4828 case 0x93: return EL_ROBOT_WHEEL;
4829 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
4830 case 0x95: return EL_ACID_POOL_TOPLEFT;
4831 case 0x96: return EL_ACID_POOL_TOPRIGHT;
4832 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
4833 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
4834 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
4835 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
4836 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
4837 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
4838 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
4839 case 0x9e: return EL_EXIT_CLOSED;
4840 case 0x9f: return EL_CHAR_LESS; /* arrow left */
4842 /* looks like normal sand, but behaves like wall */
4843 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
4844 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
4845 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
4846 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
4847 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
4848 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
4849 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
4850 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
4851 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
4852 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
4853 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
4854 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
4855 case 0xac: return EL_CHAR_COMMA; /* EMC */
4856 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
4857 case 0xae: return EL_CHAR_MINUS; /* EMC */
4858 case 0xaf: return EL_DYNAMITE;
4860 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
4861 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
4862 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
4863 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
4864 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
4865 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
4866 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
4867 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
4868 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
4869 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
4870 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
4871 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
4872 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
4873 case 0xbd: return EL_SAND; /* EMC ("dirt") */
4874 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
4875 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
4877 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
4878 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
4879 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
4880 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
4881 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
4882 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
4883 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
4884 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
4886 /* characters: see above */
4888 case 0xec: return EL_CHAR_PERIOD;
4889 case 0xed: return EL_CHAR_EXCLAM;
4890 case 0xee: return EL_CHAR_COLON;
4891 case 0xef: return EL_CHAR_QUESTION;
4893 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
4894 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
4895 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
4896 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
4897 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
4898 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
4899 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
4900 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
4902 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
4903 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
4904 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
4905 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
4906 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
4907 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
4909 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
4910 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
4913 /* should never happen (all 8-bit value cases should be handled) */
4914 Error(ERR_WARN, "invalid level element %d", element);
4919 #define EM_LEVEL_SIZE 2106
4920 #define EM_LEVEL_XSIZE 64
4921 #define EM_LEVEL_YSIZE 32
4923 static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
4924 struct LevelFileInfo *level_file_info)
4926 char *filename = level_file_info->filename;
4928 unsigned char leveldata[EM_LEVEL_SIZE];
4929 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
4930 int nr = level_file_info->nr;
4933 if (!(file = fopen(filename, MODE_READ)))
4935 level->no_valid_file = TRUE;
4937 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4942 for (i = 0; i < EM_LEVEL_SIZE; i++)
4943 leveldata[i] = fgetc(file);
4947 /* check if level data is crypted by testing against known starting bytes
4948 of the few existing crypted level files (from Emerald Mine 1 + 2) */
4950 if ((leveldata[0] == 0xf1 ||
4951 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
4953 unsigned char code0 = 0x65;
4954 unsigned char code1 = 0x11;
4956 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
4957 leveldata[0] = 0xf1;
4959 /* decode crypted level data */
4961 for (i = 0; i < EM_LEVEL_SIZE; i++)
4963 leveldata[i] ^= code0;
4964 leveldata[i] -= code1;
4966 code0 = (code0 + 7) & 0xff;
4970 level->fieldx = EM_LEVEL_XSIZE;
4971 level->fieldy = EM_LEVEL_YSIZE;
4973 level->time = header[46] * 10;
4974 level->gems_needed = header[47];
4976 /* The original Emerald Mine levels have their level number stored
4977 at the second byte of the level file...
4978 Do not trust this information at other level files, e.g. EMC,
4979 but correct it anyway (normally the first row is completely
4980 steel wall, so the correction does not hurt anyway). */
4982 if (leveldata[1] == nr)
4983 leveldata[1] = leveldata[2]; /* correct level number field */
4985 sprintf(level->name, "Level %d", nr); /* set level name */
4987 level->score[SC_EMERALD] = header[36];
4988 level->score[SC_DIAMOND] = header[37];
4989 level->score[SC_ROBOT] = header[38];
4990 level->score[SC_SPACESHIP] = header[39];
4991 level->score[SC_BUG] = header[40];
4992 level->score[SC_YAMYAM] = header[41];
4993 level->score[SC_NUT] = header[42];
4994 level->score[SC_DYNAMITE] = header[43];
4995 level->score[SC_TIME_BONUS] = header[44];
4997 level->num_yamyam_contents = 4;
4999 for (i = 0; i < level->num_yamyam_contents; i++)
5000 for (y = 0; y < 3; y++)
5001 for (x = 0; x < 3; x++)
5002 level->yamyam_content[i].e[x][y] =
5003 map_em_element_yam(header[i * 9 + y * 3 + x]);
5005 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
5006 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
5007 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
5008 level->amoeba_content = EL_DIAMOND;
5010 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5012 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
5014 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
5015 new_element = EL_AMOEBA_WET;
5017 level->field[x][y] = new_element;
5020 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
5021 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
5022 level->field[x][y] = EL_PLAYER_1;
5024 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
5025 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
5026 level->field[x][y] = EL_PLAYER_2;
5031 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
5033 static int ball_xy[8][2] =
5044 struct LevelInfo_EM *level_em = level->native_em_level;
5045 struct LEVEL *lev = level_em->lev;
5046 struct PLAYER **ply = level_em->ply;
5049 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
5050 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
5052 lev->time_seconds = level->time;
5053 lev->required_initial = level->gems_needed;
5055 lev->emerald_score = level->score[SC_EMERALD];
5056 lev->diamond_score = level->score[SC_DIAMOND];
5057 lev->alien_score = level->score[SC_ROBOT];
5058 lev->tank_score = level->score[SC_SPACESHIP];
5059 lev->bug_score = level->score[SC_BUG];
5060 lev->eater_score = level->score[SC_YAMYAM];
5061 lev->nut_score = level->score[SC_NUT];
5062 lev->dynamite_score = level->score[SC_DYNAMITE];
5063 lev->key_score = level->score[SC_KEY];
5064 lev->exit_score = level->score[SC_TIME_BONUS];
5066 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
5067 for (y = 0; y < 3; y++)
5068 for (x = 0; x < 3; x++)
5069 lev->eater_array[i][y * 3 + x] =
5070 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
5072 lev->amoeba_time = level->amoeba_speed;
5073 lev->wonderwall_time_initial = level->time_magic_wall;
5074 lev->wheel_time = level->time_wheel;
5076 lev->android_move_time = level->android_move_time;
5077 lev->android_clone_time = level->android_clone_time;
5078 lev->ball_random = level->ball_random;
5079 lev->ball_state_initial = level->ball_state_initial;
5080 lev->ball_time = level->ball_time;
5081 lev->num_ball_arrays = level->num_ball_contents;
5083 lev->lenses_score = level->lenses_score;
5084 lev->magnify_score = level->magnify_score;
5085 lev->slurp_score = level->slurp_score;
5087 lev->lenses_time = level->lenses_time;
5088 lev->magnify_time = level->magnify_time;
5090 lev->wind_direction_initial =
5091 map_direction_RND_to_EM(level->wind_direction_initial);
5092 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
5093 lev->wind_time : 0);
5095 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
5096 for (j = 0; j < 8; j++)
5097 lev->ball_array[i][j] =
5098 map_element_RND_to_EM(level->
5099 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
5101 map_android_clone_elements_RND_to_EM(level);
5103 /* first fill the complete playfield with the default border element */
5104 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
5105 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
5106 level_em->cave[x][y] = ZBORDER;
5108 if (BorderElement == EL_STEELWALL)
5110 for (y = 0; y < lev->height + 2; y++)
5111 for (x = 0; x < lev->width + 2; x++)
5112 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
5115 /* then copy the real level contents from level file into the playfield */
5116 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
5118 int new_element = map_element_RND_to_EM(level->field[x][y]);
5119 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
5120 int xx = x + 1 + offset;
5121 int yy = y + 1 + offset;
5123 if (level->field[x][y] == EL_AMOEBA_DEAD)
5124 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
5126 level_em->cave[xx][yy] = new_element;
5129 for (i = 0; i < MAX_PLAYERS; i++)
5131 ply[i]->x_initial = 0;
5132 ply[i]->y_initial = 0;
5135 /* initialize player positions and delete players from the playfield */
5136 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
5138 if (ELEM_IS_PLAYER(level->field[x][y]))
5140 int player_nr = GET_PLAYER_NR(level->field[x][y]);
5141 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
5142 int xx = x + 1 + offset;
5143 int yy = y + 1 + offset;
5145 ply[player_nr]->x_initial = xx;
5146 ply[player_nr]->y_initial = yy;
5148 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
5152 if (BorderElement == EL_STEELWALL)
5159 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
5161 static int ball_xy[8][2] =
5172 struct LevelInfo_EM *level_em = level->native_em_level;
5173 struct LEVEL *lev = level_em->lev;
5174 struct PLAYER **ply = level_em->ply;
5177 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
5178 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
5180 level->time = lev->time_seconds;
5181 level->gems_needed = lev->required_initial;
5183 sprintf(level->name, "Level %d", level->file_info.nr);
5185 level->score[SC_EMERALD] = lev->emerald_score;
5186 level->score[SC_DIAMOND] = lev->diamond_score;
5187 level->score[SC_ROBOT] = lev->alien_score;
5188 level->score[SC_SPACESHIP] = lev->tank_score;
5189 level->score[SC_BUG] = lev->bug_score;
5190 level->score[SC_YAMYAM] = lev->eater_score;
5191 level->score[SC_NUT] = lev->nut_score;
5192 level->score[SC_DYNAMITE] = lev->dynamite_score;
5193 level->score[SC_KEY] = lev->key_score;
5194 level->score[SC_TIME_BONUS] = lev->exit_score;
5196 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
5198 for (i = 0; i < level->num_yamyam_contents; i++)
5199 for (y = 0; y < 3; y++)
5200 for (x = 0; x < 3; x++)
5201 level->yamyam_content[i].e[x][y] =
5202 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
5204 level->amoeba_speed = lev->amoeba_time;
5205 level->time_magic_wall = lev->wonderwall_time_initial;
5206 level->time_wheel = lev->wheel_time;
5208 level->android_move_time = lev->android_move_time;
5209 level->android_clone_time = lev->android_clone_time;
5210 level->ball_random = lev->ball_random;
5211 level->ball_state_initial = lev->ball_state_initial;
5212 level->ball_time = lev->ball_time;
5213 level->num_ball_contents = lev->num_ball_arrays;
5215 level->lenses_score = lev->lenses_score;
5216 level->magnify_score = lev->magnify_score;
5217 level->slurp_score = lev->slurp_score;
5219 level->lenses_time = lev->lenses_time;
5220 level->magnify_time = lev->magnify_time;
5222 level->wind_direction_initial =
5223 map_direction_EM_to_RND(lev->wind_direction_initial);
5225 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
5226 for (j = 0; j < 8; j++)
5227 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
5228 map_element_EM_to_RND(lev->ball_array[i][j]);
5230 map_android_clone_elements_EM_to_RND(level);
5232 /* convert the playfield (some elements need special treatment) */
5233 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5235 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
5237 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
5238 new_element = EL_AMOEBA_DEAD;
5240 level->field[x][y] = new_element;
5243 for (i = 0; i < MAX_PLAYERS; i++)
5245 /* in case of all players set to the same field, use the first player */
5246 int nr = MAX_PLAYERS - i - 1;
5247 int jx = ply[nr]->x_initial - 1;
5248 int jy = ply[nr]->y_initial - 1;
5250 if (jx != -1 && jy != -1)
5251 level->field[jx][jy] = EL_PLAYER_1 + nr;
5256 /* ------------------------------------------------------------------------- */
5257 /* functions for loading SP level */
5258 /* ------------------------------------------------------------------------- */
5262 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
5263 #define SP_LEVEL_SIZE 1536
5264 #define SP_LEVEL_XSIZE 60
5265 #define SP_LEVEL_YSIZE 24
5266 #define SP_LEVEL_NAME_LEN 23
5268 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
5271 int initial_player_gravity;
5272 int num_special_ports;
5275 /* for details of the Supaplex level format, see Herman Perk's Supaplex
5276 documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
5278 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
5279 for (y = 0; y < SP_LEVEL_YSIZE; y++)
5281 for (x = 0; x < SP_LEVEL_XSIZE; x++)
5283 int element_old = fgetc(file);
5286 if (element_old <= 0x27)
5287 element_new = getMappedElement(EL_SP_START + element_old);
5288 else if (element_old == 0x28)
5289 element_new = EL_INVISIBLE_WALL;
5292 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
5293 Error(ERR_WARN, "invalid level element %d", element_old);
5295 element_new = EL_UNKNOWN;
5298 level->field[x][y] = element_new;
5302 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
5304 /* initial gravity: 1 == "on", anything else (0) == "off" */
5305 initial_player_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
5307 for (i = 0; i < MAX_PLAYERS; i++)
5308 level->initial_player_gravity[i] = initial_player_gravity;
5310 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
5312 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
5313 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5314 level->name[i] = fgetc(file);
5315 level->name[SP_LEVEL_NAME_LEN] = '\0';
5317 /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
5318 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
5320 /* number of infotrons needed; 0 means that Supaplex will count the total
5321 amount of infotrons in the level and use the low byte of that number
5322 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
5323 level->gems_needed = fgetc(file);
5325 /* number of special ("gravity") port entries below (maximum 10 allowed) */
5326 num_special_ports = fgetc(file);
5328 /* database of properties of up to 10 special ports (6 bytes per port) */
5329 for (i = 0; i < 10; i++)
5331 int port_location, port_x, port_y, port_element;
5334 /* high and low byte of the location of a special port; if (x, y) are the
5335 coordinates of a port in the field and (0, 0) is the top-left corner,
5336 the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
5337 of what may be expected: Supaplex works with a game field in memory
5338 which is 2 bytes per tile) */
5339 port_location = getFile16BitBE(file);
5341 /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
5342 gravity = fgetc(file);
5344 /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
5345 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
5347 /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
5348 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
5350 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
5352 if (i >= num_special_ports)
5355 port_x = (port_location / 2) % SP_LEVEL_XSIZE;
5356 port_y = (port_location / 2) / SP_LEVEL_XSIZE;
5358 if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
5359 port_y < 0 || port_y >= SP_LEVEL_YSIZE)
5361 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
5367 port_element = level->field[port_x][port_y];
5369 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5370 port_element > EL_SP_GRAVITY_PORT_UP)
5372 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
5377 /* change previous (wrong) gravity inverting special port to either
5378 gravity enabling special port or gravity disabling special port */
5379 level->field[port_x][port_y] +=
5380 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5381 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5384 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
5386 /* change special gravity ports without database entries to normal ports */
5387 for (y = 0; y < SP_LEVEL_YSIZE; y++)
5388 for (x = 0; x < SP_LEVEL_XSIZE; x++)
5389 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5390 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5391 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5393 /* auto-determine number of infotrons if it was stored as "0" -- see above */
5394 if (level->gems_needed == 0)
5396 for (y = 0; y < SP_LEVEL_YSIZE; y++)
5397 for (x = 0; x < SP_LEVEL_XSIZE; x++)
5398 if (level->field[x][y] == EL_SP_INFOTRON)
5399 level->gems_needed++;
5401 level->gems_needed &= 0xff; /* only use low byte -- see above */
5404 level->fieldx = SP_LEVEL_XSIZE;
5405 level->fieldy = SP_LEVEL_YSIZE;
5407 level->time = 0; /* no time limit */
5408 level->amoeba_speed = 0;
5409 level->time_magic_wall = 0;
5410 level->time_wheel = 0;
5411 level->amoeba_content = EL_EMPTY;
5414 /* original Supaplex does not use score values -- use default values */
5416 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5417 level->score[i] = 0;
5420 /* there are no yamyams in supaplex levels */
5421 for (i = 0; i < level->num_yamyam_contents; i++)
5422 for (y = 0; y < 3; y++)
5423 for (x = 0; x < 3; x++)
5424 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5427 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
5428 struct LevelFileInfo *level_file_info,
5429 boolean level_info_only)
5431 char *filename = level_file_info->filename;
5433 int nr = level_file_info->nr - leveldir_current->first_level;
5435 char name_first, name_last;
5436 struct LevelInfo multipart_level;
5437 int multipart_xpos, multipart_ypos;
5438 boolean is_multipart_level;
5439 boolean is_first_part;
5440 boolean reading_multipart_level = FALSE;
5441 boolean use_empty_level = FALSE;
5443 if (!(file = fopen(filename, MODE_READ)))
5445 level->no_valid_file = TRUE;
5447 if (!level_info_only)
5448 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5453 /* position file stream to the requested level inside the level package */
5454 if (level_file_info->packed &&
5455 fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
5457 level->no_valid_file = TRUE;
5459 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
5464 /* there exist Supaplex level package files with multi-part levels which
5465 can be detected as follows: instead of leading and trailing dashes ('-')
5466 to pad the level name, they have leading and trailing numbers which are
5467 the x and y coordinations of the current part of the multi-part level;
5468 if there are '?' characters instead of numbers on the left or right side
5469 of the level name, the multi-part level consists of only horizontal or
5472 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
5474 LoadLevelFromFileStream_SP(file, level, l);
5476 /* check if this level is a part of a bigger multi-part level */
5478 name_first = level->name[0];
5479 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
5481 is_multipart_level =
5482 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
5483 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
5486 ((name_first == '?' || name_first == '1') &&
5487 (name_last == '?' || name_last == '1'));
5489 /* correct leading multipart level meta information in level name */
5490 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
5491 level->name[i] = '-';
5493 /* correct trailing multipart level meta information in level name */
5494 for (i = SP_LEVEL_NAME_LEN - 1; i >= 0 && level->name[i] == name_last; i--)
5495 level->name[i] = '-';
5497 /* ---------- check for normal single level ---------- */
5499 if (!reading_multipart_level && !is_multipart_level)
5501 /* the current level is simply a normal single-part level, and we are
5502 not reading a multi-part level yet, so return the level as it is */
5507 /* ---------- check for empty level (unused multi-part) ---------- */
5509 if (!reading_multipart_level && is_multipart_level && !is_first_part)
5511 /* this is a part of a multi-part level, but not the first part
5512 (and we are not already reading parts of a multi-part level);
5513 in this case, use an empty level instead of the single part */
5515 use_empty_level = TRUE;
5520 /* ---------- check for finished multi-part level ---------- */
5522 if (reading_multipart_level &&
5523 (!is_multipart_level ||
5524 !strEqual(level->name, multipart_level.name)))
5526 /* we are already reading parts of a multi-part level, but this level is
5527 either not a multi-part level, or a part of a different multi-part
5528 level; in both cases, the multi-part level seems to be complete */
5533 /* ---------- here we have one part of a multi-part level ---------- */
5535 reading_multipart_level = TRUE;
5537 if (is_first_part) /* start with first part of new multi-part level */
5539 /* copy level info structure from first part */
5540 multipart_level = *level;
5542 /* clear playfield of new multi-part level */
5543 for (y = 0; y < MAX_LEV_FIELDY; y++)
5544 for (x = 0; x < MAX_LEV_FIELDX; x++)
5545 multipart_level.field[x][y] = EL_EMPTY;
5548 if (name_first == '?')
5550 if (name_last == '?')
5553 multipart_xpos = (int)(name_first - '0');
5554 multipart_ypos = (int)(name_last - '0');
5557 printf("----------> part (%d/%d) of multi-part level '%s'\n",
5558 multipart_xpos, multipart_ypos, multipart_level.name);
5561 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
5562 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
5564 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
5569 multipart_level.fieldx = MAX(multipart_level.fieldx,
5570 multipart_xpos * SP_LEVEL_XSIZE);
5571 multipart_level.fieldy = MAX(multipart_level.fieldy,
5572 multipart_ypos * SP_LEVEL_YSIZE);
5574 /* copy level part at the right position of multi-part level */
5575 for (y = 0; y < SP_LEVEL_YSIZE; y++)
5577 for (x = 0; x < SP_LEVEL_XSIZE; x++)
5579 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
5580 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
5582 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
5589 if (use_empty_level)
5591 setLevelInfoToDefaults(level);
5593 level->fieldx = SP_LEVEL_XSIZE;
5594 level->fieldy = SP_LEVEL_YSIZE;
5596 for (y = 0; y < SP_LEVEL_YSIZE; y++)
5597 for (x = 0; x < SP_LEVEL_XSIZE; x++)
5598 level->field[x][y] = EL_EMPTY;
5600 strcpy(level->name, "-------- EMPTY --------");
5602 Error(ERR_WARN, "single part of multi-part level -- using empty level");
5605 if (reading_multipart_level)
5606 *level = multipart_level;
5611 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
5613 struct LevelInfo_SP *level_sp = level->native_sp_level;
5614 LevelInfoType *header = &level_sp->header;
5617 level_sp->width = level->fieldx;
5618 level_sp->height = level->fieldy;
5620 for (x = 0; x < level->fieldx; x++)
5621 for (y = 0; y < level->fieldy; y++)
5622 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
5624 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
5626 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5627 header->LevelTitle[i] = level->name[i];
5628 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
5630 header->InfotronsNeeded = level->gems_needed;
5632 header->SpecialPortCount = 0;
5634 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
5636 boolean gravity_port_found = FALSE;
5637 boolean gravity_port_valid = FALSE;
5638 int gravity_port_flag;
5639 int gravity_port_base_element;
5640 int element = level->field[x][y];
5642 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
5643 element <= EL_SP_GRAVITY_ON_PORT_UP)
5645 gravity_port_found = TRUE;
5646 gravity_port_valid = TRUE;
5647 gravity_port_flag = 1;
5648 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
5650 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
5651 element <= EL_SP_GRAVITY_OFF_PORT_UP)
5653 gravity_port_found = TRUE;
5654 gravity_port_valid = TRUE;
5655 gravity_port_flag = 0;
5656 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
5658 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
5659 element <= EL_SP_GRAVITY_PORT_UP)
5661 /* change R'n'D style gravity inverting special port to normal port
5662 (there are no gravity inverting ports in native Supaplex engine) */
5664 gravity_port_found = TRUE;
5665 gravity_port_valid = FALSE;
5666 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
5669 if (gravity_port_found)
5671 if (gravity_port_valid &&
5672 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
5674 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
5676 port->PortLocation = (y * level->fieldx + x) * 2;
5677 port->Gravity = gravity_port_flag;
5679 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
5681 header->SpecialPortCount++;
5685 /* change special gravity port to normal port */
5687 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
5690 level_sp->playfield[x][y] = element - EL_SP_START;
5695 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
5697 struct LevelInfo_SP *level_sp = level->native_sp_level;
5698 LevelInfoType *header = &level_sp->header;
5701 level->fieldx = level_sp->width;
5702 level->fieldy = level_sp->height;
5704 for (x = 0; x < level->fieldx; x++)
5706 for (y = 0; y < level->fieldy; y++)
5708 int element_old = level_sp->playfield[x][y];
5709 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5711 if (element_new == EL_UNKNOWN)
5712 Error(ERR_WARN, "invalid element %d at position %d, %d",
5715 level->field[x][y] = element_new;
5719 for (i = 0; i < MAX_PLAYERS; i++)
5720 level->initial_player_gravity[i] =
5721 (header->InitialGravity == 1 ? TRUE : FALSE);
5723 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5724 level->name[i] = header->LevelTitle[i];
5725 level->name[SP_LEVEL_NAME_LEN] = '\0';
5727 level->gems_needed = header->InfotronsNeeded;
5729 for (i = 0; i < header->SpecialPortCount; i++)
5731 SpecialPortType *port = &header->SpecialPort[i];
5732 int port_location = port->PortLocation;
5733 int gravity = port->Gravity;
5734 int port_x, port_y, port_element;
5736 port_x = (port_location / 2) % level->fieldx;
5737 port_y = (port_location / 2) / level->fieldx;
5739 if (port_x < 0 || port_x >= level->fieldx ||
5740 port_y < 0 || port_y >= level->fieldy)
5742 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
5748 port_element = level->field[port_x][port_y];
5750 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5751 port_element > EL_SP_GRAVITY_PORT_UP)
5753 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
5758 /* change previous (wrong) gravity inverting special port to either
5759 gravity enabling special port or gravity disabling special port */
5760 level->field[port_x][port_y] +=
5761 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5762 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5765 /* change special gravity ports without database entries to normal ports */
5766 for (x = 0; x < level->fieldx; x++)
5767 for (y = 0; y < level->fieldy; y++)
5768 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5769 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5770 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5772 level->time = 0; /* no time limit */
5773 level->amoeba_speed = 0;
5774 level->time_magic_wall = 0;
5775 level->time_wheel = 0;
5776 level->amoeba_content = EL_EMPTY;
5779 /* original Supaplex does not use score values -- use default values */
5781 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5782 level->score[i] = 0;
5785 /* there are no yamyams in supaplex levels */
5786 for (i = 0; i < level->num_yamyam_contents; i++)
5787 for (x = 0; x < 3; x++)
5788 for (y = 0; y < 3; y++)
5789 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5792 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5794 struct LevelInfo_SP *level_sp = level->native_sp_level;
5795 struct DemoInfo_SP *demo = &level_sp->demo;
5798 /* always start with reliable default values */
5799 demo->is_available = FALSE;
5802 if (TAPE_IS_EMPTY(tape))
5805 demo->level_nr = tape.level_nr; /* (currently not used) */
5807 level_sp->header.DemoRandomSeed = tape.random_seed;
5810 for (i = 0; i < tape.length; i++)
5812 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5813 int demo_repeat = tape.pos[i].delay;
5815 for (j = 0; j < demo_repeat / 16; j++)
5816 demo->data[demo->length++] = 0xf0 | demo_action;
5818 if (demo_repeat % 16)
5819 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5822 demo->data[demo->length++] = 0xff;
5824 demo->is_available = TRUE;
5827 static void setTapeInfoToDefaults();
5829 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5831 struct LevelInfo_SP *level_sp = level->native_sp_level;
5832 struct DemoInfo_SP *demo = &level_sp->demo;
5833 char *filename = level->file_info.filename;
5836 /* always start with reliable default values */
5837 setTapeInfoToDefaults();
5839 if (!demo->is_available)
5842 tape.level_nr = demo->level_nr; /* (currently not used) */
5843 tape.length = demo->length - 1; /* without "end of demo" byte */
5844 tape.random_seed = level_sp->header.DemoRandomSeed;
5846 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5848 for (i = 0; i < demo->length - 1; i++)
5850 int demo_action = demo->data[i] & 0x0f;
5851 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5853 tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
5854 tape.pos[i].delay = demo_repeat + 1;
5857 tape.length_seconds = GetTapeLength();
5861 /* ------------------------------------------------------------------------- */
5862 /* functions for loading DC level */
5863 /* ------------------------------------------------------------------------- */
5865 #define DC_LEVEL_HEADER_SIZE 344
5867 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
5869 static int last_data_encoded;
5873 int diff_hi, diff_lo;
5874 int data_hi, data_lo;
5875 unsigned short data_decoded;
5879 last_data_encoded = 0;
5886 diff = data_encoded - last_data_encoded;
5887 diff_hi = diff & ~0xff;
5888 diff_lo = diff & 0xff;
5892 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5893 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5894 data_hi = data_hi & 0xff00;
5896 data_decoded = data_hi | data_lo;
5898 last_data_encoded = data_encoded;
5900 offset1 = (offset1 + 1) % 31;
5901 offset2 = offset2 & 0xff;
5903 return data_decoded;
5906 int getMappedElement_DC(int element)
5914 /* 0x0117 - 0x036e: (?) */
5917 /* 0x042d - 0x0684: (?) */
5933 element = EL_CRYSTAL;
5936 case 0x0e77: /* quicksand (boulder) */
5937 element = EL_QUICKSAND_FAST_FULL;
5940 case 0x0e99: /* slow quicksand (boulder) */
5941 element = EL_QUICKSAND_FULL;
5945 element = EL_EM_EXIT_OPEN;
5949 element = EL_EM_EXIT_CLOSED;
5953 element = EL_EM_STEEL_EXIT_OPEN;
5957 element = EL_EM_STEEL_EXIT_CLOSED;
5960 case 0x0f4f: /* dynamite (lit 1) */
5961 element = EL_EM_DYNAMITE_ACTIVE;
5964 case 0x0f57: /* dynamite (lit 2) */
5965 element = EL_EM_DYNAMITE_ACTIVE;
5968 case 0x0f5f: /* dynamite (lit 3) */
5969 element = EL_EM_DYNAMITE_ACTIVE;
5972 case 0x0f67: /* dynamite (lit 4) */
5973 element = EL_EM_DYNAMITE_ACTIVE;
5980 element = EL_AMOEBA_WET;
5984 element = EL_AMOEBA_DROP;
5988 element = EL_DC_MAGIC_WALL;
5992 element = EL_SPACESHIP_UP;
5996 element = EL_SPACESHIP_DOWN;
6000 element = EL_SPACESHIP_LEFT;
6004 element = EL_SPACESHIP_RIGHT;
6008 element = EL_BUG_UP;
6012 element = EL_BUG_DOWN;
6016 element = EL_BUG_LEFT;
6020 element = EL_BUG_RIGHT;
6024 element = EL_MOLE_UP;
6028 element = EL_MOLE_DOWN;
6032 element = EL_MOLE_LEFT;
6036 element = EL_MOLE_RIGHT;
6044 element = EL_YAMYAM;
6048 element = EL_SWITCHGATE_OPEN;
6052 element = EL_SWITCHGATE_CLOSED;
6056 element = EL_DC_SWITCHGATE_SWITCH_UP;
6060 element = EL_TIMEGATE_CLOSED;
6063 case 0x144c: /* conveyor belt switch (green) */
6064 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
6067 case 0x144f: /* conveyor belt switch (red) */
6068 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
6071 case 0x1452: /* conveyor belt switch (blue) */
6072 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
6076 element = EL_CONVEYOR_BELT_3_MIDDLE;
6080 element = EL_CONVEYOR_BELT_3_LEFT;
6084 element = EL_CONVEYOR_BELT_3_RIGHT;
6088 element = EL_CONVEYOR_BELT_1_MIDDLE;
6092 element = EL_CONVEYOR_BELT_1_LEFT;
6096 element = EL_CONVEYOR_BELT_1_RIGHT;
6100 element = EL_CONVEYOR_BELT_4_MIDDLE;
6104 element = EL_CONVEYOR_BELT_4_LEFT;
6108 element = EL_CONVEYOR_BELT_4_RIGHT;
6112 element = EL_EXPANDABLE_WALL_HORIZONTAL;
6116 element = EL_EXPANDABLE_WALL_VERTICAL;
6120 element = EL_EXPANDABLE_WALL_ANY;
6123 case 0x14ce: /* growing steel wall (left/right) */
6124 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
6127 case 0x14df: /* growing steel wall (up/down) */
6128 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
6131 case 0x14e8: /* growing steel wall (up/down/left/right) */
6132 element = EL_EXPANDABLE_STEELWALL_ANY;
6136 element = EL_SHIELD_DEADLY;
6140 element = EL_EXTRA_TIME;
6148 element = EL_EMPTY_SPACE;
6151 case 0x1578: /* quicksand (empty) */
6152 element = EL_QUICKSAND_FAST_EMPTY;
6155 case 0x1579: /* slow quicksand (empty) */
6156 element = EL_QUICKSAND_EMPTY;
6159 /* 0x157c - 0x158b: */
6162 /* 0x1590 - 0x159f: */
6163 /* EL_DC_LANDMINE */
6166 element = EL_EM_DYNAMITE;
6169 case 0x15a1: /* key (red) */
6170 element = EL_EM_KEY_1;
6173 case 0x15a2: /* key (yellow) */
6174 element = EL_EM_KEY_2;
6177 case 0x15a3: /* key (blue) */
6178 element = EL_EM_KEY_4;
6181 case 0x15a4: /* key (green) */
6182 element = EL_EM_KEY_3;
6185 case 0x15a5: /* key (white) */
6186 element = EL_DC_KEY_WHITE;
6190 element = EL_WALL_SLIPPERY;
6197 case 0x15a8: /* wall (not round) */
6201 case 0x15a9: /* (blue) */
6202 element = EL_CHAR_A;
6205 case 0x15aa: /* (blue) */
6206 element = EL_CHAR_B;
6209 case 0x15ab: /* (blue) */
6210 element = EL_CHAR_C;
6213 case 0x15ac: /* (blue) */
6214 element = EL_CHAR_D;
6217 case 0x15ad: /* (blue) */
6218 element = EL_CHAR_E;
6221 case 0x15ae: /* (blue) */
6222 element = EL_CHAR_F;
6225 case 0x15af: /* (blue) */
6226 element = EL_CHAR_G;
6229 case 0x15b0: /* (blue) */
6230 element = EL_CHAR_H;
6233 case 0x15b1: /* (blue) */
6234 element = EL_CHAR_I;
6237 case 0x15b2: /* (blue) */
6238 element = EL_CHAR_J;
6241 case 0x15b3: /* (blue) */
6242 element = EL_CHAR_K;
6245 case 0x15b4: /* (blue) */
6246 element = EL_CHAR_L;
6249 case 0x15b5: /* (blue) */
6250 element = EL_CHAR_M;
6253 case 0x15b6: /* (blue) */
6254 element = EL_CHAR_N;
6257 case 0x15b7: /* (blue) */
6258 element = EL_CHAR_O;
6261 case 0x15b8: /* (blue) */
6262 element = EL_CHAR_P;
6265 case 0x15b9: /* (blue) */
6266 element = EL_CHAR_Q;
6269 case 0x15ba: /* (blue) */
6270 element = EL_CHAR_R;
6273 case 0x15bb: /* (blue) */
6274 element = EL_CHAR_S;
6277 case 0x15bc: /* (blue) */
6278 element = EL_CHAR_T;
6281 case 0x15bd: /* (blue) */
6282 element = EL_CHAR_U;
6285 case 0x15be: /* (blue) */
6286 element = EL_CHAR_V;
6289 case 0x15bf: /* (blue) */
6290 element = EL_CHAR_W;
6293 case 0x15c0: /* (blue) */
6294 element = EL_CHAR_X;
6297 case 0x15c1: /* (blue) */
6298 element = EL_CHAR_Y;
6301 case 0x15c2: /* (blue) */
6302 element = EL_CHAR_Z;
6305 case 0x15c3: /* (blue) */
6306 element = EL_CHAR_AUMLAUT;
6309 case 0x15c4: /* (blue) */
6310 element = EL_CHAR_OUMLAUT;
6313 case 0x15c5: /* (blue) */
6314 element = EL_CHAR_UUMLAUT;
6317 case 0x15c6: /* (blue) */
6318 element = EL_CHAR_0;
6321 case 0x15c7: /* (blue) */
6322 element = EL_CHAR_1;
6325 case 0x15c8: /* (blue) */
6326 element = EL_CHAR_2;
6329 case 0x15c9: /* (blue) */
6330 element = EL_CHAR_3;
6333 case 0x15ca: /* (blue) */
6334 element = EL_CHAR_4;
6337 case 0x15cb: /* (blue) */
6338 element = EL_CHAR_5;
6341 case 0x15cc: /* (blue) */
6342 element = EL_CHAR_6;
6345 case 0x15cd: /* (blue) */
6346 element = EL_CHAR_7;
6349 case 0x15ce: /* (blue) */
6350 element = EL_CHAR_8;
6353 case 0x15cf: /* (blue) */
6354 element = EL_CHAR_9;
6357 case 0x15d0: /* (blue) */
6358 element = EL_CHAR_PERIOD;
6361 case 0x15d1: /* (blue) */
6362 element = EL_CHAR_EXCLAM;
6365 case 0x15d2: /* (blue) */
6366 element = EL_CHAR_COLON;
6369 case 0x15d3: /* (blue) */
6370 element = EL_CHAR_LESS;
6373 case 0x15d4: /* (blue) */
6374 element = EL_CHAR_GREATER;
6377 case 0x15d5: /* (blue) */
6378 element = EL_CHAR_QUESTION;
6381 case 0x15d6: /* (blue) */
6382 element = EL_CHAR_COPYRIGHT;
6385 case 0x15d7: /* (blue) */
6386 element = EL_CHAR_UP;
6389 case 0x15d8: /* (blue) */
6390 element = EL_CHAR_DOWN;
6393 case 0x15d9: /* (blue) */
6394 element = EL_CHAR_BUTTON;
6397 case 0x15da: /* (blue) */
6398 element = EL_CHAR_PLUS;
6401 case 0x15db: /* (blue) */
6402 element = EL_CHAR_MINUS;
6405 case 0x15dc: /* (blue) */
6406 element = EL_CHAR_APOSTROPHE;
6409 case 0x15dd: /* (blue) */
6410 element = EL_CHAR_PARENLEFT;
6413 case 0x15de: /* (blue) */
6414 element = EL_CHAR_PARENRIGHT;
6417 case 0x15df: /* (green) */
6418 element = EL_CHAR_A;
6421 case 0x15e0: /* (green) */
6422 element = EL_CHAR_B;
6425 case 0x15e1: /* (green) */
6426 element = EL_CHAR_C;
6429 case 0x15e2: /* (green) */
6430 element = EL_CHAR_D;
6433 case 0x15e3: /* (green) */
6434 element = EL_CHAR_E;
6437 case 0x15e4: /* (green) */
6438 element = EL_CHAR_F;
6441 case 0x15e5: /* (green) */
6442 element = EL_CHAR_G;
6445 case 0x15e6: /* (green) */
6446 element = EL_CHAR_H;
6449 case 0x15e7: /* (green) */
6450 element = EL_CHAR_I;
6453 case 0x15e8: /* (green) */
6454 element = EL_CHAR_J;
6457 case 0x15e9: /* (green) */
6458 element = EL_CHAR_K;
6461 case 0x15ea: /* (green) */
6462 element = EL_CHAR_L;
6465 case 0x15eb: /* (green) */
6466 element = EL_CHAR_M;
6469 case 0x15ec: /* (green) */
6470 element = EL_CHAR_N;
6473 case 0x15ed: /* (green) */
6474 element = EL_CHAR_O;
6477 case 0x15ee: /* (green) */
6478 element = EL_CHAR_P;
6481 case 0x15ef: /* (green) */
6482 element = EL_CHAR_Q;
6485 case 0x15f0: /* (green) */
6486 element = EL_CHAR_R;
6489 case 0x15f1: /* (green) */
6490 element = EL_CHAR_S;
6493 case 0x15f2: /* (green) */
6494 element = EL_CHAR_T;
6497 case 0x15f3: /* (green) */
6498 element = EL_CHAR_U;
6501 case 0x15f4: /* (green) */
6502 element = EL_CHAR_V;
6505 case 0x15f5: /* (green) */
6506 element = EL_CHAR_W;
6509 case 0x15f6: /* (green) */
6510 element = EL_CHAR_X;
6513 case 0x15f7: /* (green) */
6514 element = EL_CHAR_Y;
6517 case 0x15f8: /* (green) */
6518 element = EL_CHAR_Z;
6521 case 0x15f9: /* (green) */
6522 element = EL_CHAR_AUMLAUT;
6525 case 0x15fa: /* (green) */
6526 element = EL_CHAR_OUMLAUT;
6529 case 0x15fb: /* (green) */
6530 element = EL_CHAR_UUMLAUT;
6533 case 0x15fc: /* (green) */
6534 element = EL_CHAR_0;
6537 case 0x15fd: /* (green) */
6538 element = EL_CHAR_1;
6541 case 0x15fe: /* (green) */
6542 element = EL_CHAR_2;
6545 case 0x15ff: /* (green) */
6546 element = EL_CHAR_3;
6549 case 0x1600: /* (green) */
6550 element = EL_CHAR_4;
6553 case 0x1601: /* (green) */
6554 element = EL_CHAR_5;
6557 case 0x1602: /* (green) */
6558 element = EL_CHAR_6;
6561 case 0x1603: /* (green) */
6562 element = EL_CHAR_7;
6565 case 0x1604: /* (green) */
6566 element = EL_CHAR_8;
6569 case 0x1605: /* (green) */
6570 element = EL_CHAR_9;
6573 case 0x1606: /* (green) */
6574 element = EL_CHAR_PERIOD;
6577 case 0x1607: /* (green) */
6578 element = EL_CHAR_EXCLAM;
6581 case 0x1608: /* (green) */
6582 element = EL_CHAR_COLON;
6585 case 0x1609: /* (green) */
6586 element = EL_CHAR_LESS;
6589 case 0x160a: /* (green) */
6590 element = EL_CHAR_GREATER;
6593 case 0x160b: /* (green) */
6594 element = EL_CHAR_QUESTION;
6597 case 0x160c: /* (green) */
6598 element = EL_CHAR_COPYRIGHT;
6601 case 0x160d: /* (green) */
6602 element = EL_CHAR_UP;
6605 case 0x160e: /* (green) */
6606 element = EL_CHAR_DOWN;
6609 case 0x160f: /* (green) */
6610 element = EL_CHAR_BUTTON;
6613 case 0x1610: /* (green) */
6614 element = EL_CHAR_PLUS;
6617 case 0x1611: /* (green) */
6618 element = EL_CHAR_MINUS;
6621 case 0x1612: /* (green) */
6622 element = EL_CHAR_APOSTROPHE;
6625 case 0x1613: /* (green) */
6626 element = EL_CHAR_PARENLEFT;
6629 case 0x1614: /* (green) */
6630 element = EL_CHAR_PARENRIGHT;
6633 case 0x1615: /* (blue steel) */
6634 element = EL_STEEL_CHAR_A;
6637 case 0x1616: /* (blue steel) */
6638 element = EL_STEEL_CHAR_B;
6641 case 0x1617: /* (blue steel) */
6642 element = EL_STEEL_CHAR_C;
6645 case 0x1618: /* (blue steel) */
6646 element = EL_STEEL_CHAR_D;
6649 case 0x1619: /* (blue steel) */
6650 element = EL_STEEL_CHAR_E;
6653 case 0x161a: /* (blue steel) */
6654 element = EL_STEEL_CHAR_F;
6657 case 0x161b: /* (blue steel) */
6658 element = EL_STEEL_CHAR_G;
6661 case 0x161c: /* (blue steel) */
6662 element = EL_STEEL_CHAR_H;
6665 case 0x161d: /* (blue steel) */
6666 element = EL_STEEL_CHAR_I;
6669 case 0x161e: /* (blue steel) */
6670 element = EL_STEEL_CHAR_J;
6673 case 0x161f: /* (blue steel) */
6674 element = EL_STEEL_CHAR_K;
6677 case 0x1620: /* (blue steel) */
6678 element = EL_STEEL_CHAR_L;
6681 case 0x1621: /* (blue steel) */
6682 element = EL_STEEL_CHAR_M;
6685 case 0x1622: /* (blue steel) */
6686 element = EL_STEEL_CHAR_N;
6689 case 0x1623: /* (blue steel) */
6690 element = EL_STEEL_CHAR_O;
6693 case 0x1624: /* (blue steel) */
6694 element = EL_STEEL_CHAR_P;
6697 case 0x1625: /* (blue steel) */
6698 element = EL_STEEL_CHAR_Q;
6701 case 0x1626: /* (blue steel) */
6702 element = EL_STEEL_CHAR_R;
6705 case 0x1627: /* (blue steel) */
6706 element = EL_STEEL_CHAR_S;
6709 case 0x1628: /* (blue steel) */
6710 element = EL_STEEL_CHAR_T;
6713 case 0x1629: /* (blue steel) */
6714 element = EL_STEEL_CHAR_U;
6717 case 0x162a: /* (blue steel) */
6718 element = EL_STEEL_CHAR_V;
6721 case 0x162b: /* (blue steel) */
6722 element = EL_STEEL_CHAR_W;
6725 case 0x162c: /* (blue steel) */
6726 element = EL_STEEL_CHAR_X;
6729 case 0x162d: /* (blue steel) */
6730 element = EL_STEEL_CHAR_Y;
6733 case 0x162e: /* (blue steel) */
6734 element = EL_STEEL_CHAR_Z;
6737 case 0x162f: /* (blue steel) */
6738 element = EL_STEEL_CHAR_AUMLAUT;
6741 case 0x1630: /* (blue steel) */
6742 element = EL_STEEL_CHAR_OUMLAUT;
6745 case 0x1631: /* (blue steel) */
6746 element = EL_STEEL_CHAR_UUMLAUT;
6749 case 0x1632: /* (blue steel) */
6750 element = EL_STEEL_CHAR_0;
6753 case 0x1633: /* (blue steel) */
6754 element = EL_STEEL_CHAR_1;
6757 case 0x1634: /* (blue steel) */
6758 element = EL_STEEL_CHAR_2;
6761 case 0x1635: /* (blue steel) */
6762 element = EL_STEEL_CHAR_3;
6765 case 0x1636: /* (blue steel) */
6766 element = EL_STEEL_CHAR_4;
6769 case 0x1637: /* (blue steel) */
6770 element = EL_STEEL_CHAR_5;
6773 case 0x1638: /* (blue steel) */
6774 element = EL_STEEL_CHAR_6;
6777 case 0x1639: /* (blue steel) */
6778 element = EL_STEEL_CHAR_7;
6781 case 0x163a: /* (blue steel) */
6782 element = EL_STEEL_CHAR_8;
6785 case 0x163b: /* (blue steel) */
6786 element = EL_STEEL_CHAR_9;
6789 case 0x163c: /* (blue steel) */
6790 element = EL_STEEL_CHAR_PERIOD;
6793 case 0x163d: /* (blue steel) */
6794 element = EL_STEEL_CHAR_EXCLAM;
6797 case 0x163e: /* (blue steel) */
6798 element = EL_STEEL_CHAR_COLON;
6801 case 0x163f: /* (blue steel) */
6802 element = EL_STEEL_CHAR_LESS;
6805 case 0x1640: /* (blue steel) */
6806 element = EL_STEEL_CHAR_GREATER;
6809 case 0x1641: /* (blue steel) */
6810 element = EL_STEEL_CHAR_QUESTION;
6813 case 0x1642: /* (blue steel) */
6814 element = EL_STEEL_CHAR_COPYRIGHT;
6817 case 0x1643: /* (blue steel) */
6818 element = EL_STEEL_CHAR_UP;
6821 case 0x1644: /* (blue steel) */
6822 element = EL_STEEL_CHAR_DOWN;
6825 case 0x1645: /* (blue steel) */
6826 element = EL_STEEL_CHAR_BUTTON;
6829 case 0x1646: /* (blue steel) */
6830 element = EL_STEEL_CHAR_PLUS;
6833 case 0x1647: /* (blue steel) */
6834 element = EL_STEEL_CHAR_MINUS;
6837 case 0x1648: /* (blue steel) */
6838 element = EL_STEEL_CHAR_APOSTROPHE;
6841 case 0x1649: /* (blue steel) */
6842 element = EL_STEEL_CHAR_PARENLEFT;
6845 case 0x164a: /* (blue steel) */
6846 element = EL_STEEL_CHAR_PARENRIGHT;
6849 case 0x164b: /* (green steel) */
6850 element = EL_STEEL_CHAR_A;
6853 case 0x164c: /* (green steel) */
6854 element = EL_STEEL_CHAR_B;
6857 case 0x164d: /* (green steel) */
6858 element = EL_STEEL_CHAR_C;
6861 case 0x164e: /* (green steel) */
6862 element = EL_STEEL_CHAR_D;
6865 case 0x164f: /* (green steel) */
6866 element = EL_STEEL_CHAR_E;
6869 case 0x1650: /* (green steel) */
6870 element = EL_STEEL_CHAR_F;
6873 case 0x1651: /* (green steel) */
6874 element = EL_STEEL_CHAR_G;
6877 case 0x1652: /* (green steel) */
6878 element = EL_STEEL_CHAR_H;
6881 case 0x1653: /* (green steel) */
6882 element = EL_STEEL_CHAR_I;
6885 case 0x1654: /* (green steel) */
6886 element = EL_STEEL_CHAR_J;
6889 case 0x1655: /* (green steel) */
6890 element = EL_STEEL_CHAR_K;
6893 case 0x1656: /* (green steel) */
6894 element = EL_STEEL_CHAR_L;
6897 case 0x1657: /* (green steel) */
6898 element = EL_STEEL_CHAR_M;
6901 case 0x1658: /* (green steel) */
6902 element = EL_STEEL_CHAR_N;
6905 case 0x1659: /* (green steel) */
6906 element = EL_STEEL_CHAR_O;
6909 case 0x165a: /* (green steel) */
6910 element = EL_STEEL_CHAR_P;
6913 case 0x165b: /* (green steel) */
6914 element = EL_STEEL_CHAR_Q;
6917 case 0x165c: /* (green steel) */
6918 element = EL_STEEL_CHAR_R;
6921 case 0x165d: /* (green steel) */
6922 element = EL_STEEL_CHAR_S;
6925 case 0x165e: /* (green steel) */
6926 element = EL_STEEL_CHAR_T;
6929 case 0x165f: /* (green steel) */
6930 element = EL_STEEL_CHAR_U;
6933 case 0x1660: /* (green steel) */
6934 element = EL_STEEL_CHAR_V;
6937 case 0x1661: /* (green steel) */
6938 element = EL_STEEL_CHAR_W;
6941 case 0x1662: /* (green steel) */
6942 element = EL_STEEL_CHAR_X;
6945 case 0x1663: /* (green steel) */
6946 element = EL_STEEL_CHAR_Y;
6949 case 0x1664: /* (green steel) */
6950 element = EL_STEEL_CHAR_Z;
6953 case 0x1665: /* (green steel) */
6954 element = EL_STEEL_CHAR_AUMLAUT;
6957 case 0x1666: /* (green steel) */
6958 element = EL_STEEL_CHAR_OUMLAUT;
6961 case 0x1667: /* (green steel) */
6962 element = EL_STEEL_CHAR_UUMLAUT;
6965 case 0x1668: /* (green steel) */
6966 element = EL_STEEL_CHAR_0;
6969 case 0x1669: /* (green steel) */
6970 element = EL_STEEL_CHAR_1;
6973 case 0x166a: /* (green steel) */
6974 element = EL_STEEL_CHAR_2;
6977 case 0x166b: /* (green steel) */
6978 element = EL_STEEL_CHAR_3;
6981 case 0x166c: /* (green steel) */
6982 element = EL_STEEL_CHAR_4;
6985 case 0x166d: /* (green steel) */
6986 element = EL_STEEL_CHAR_5;
6989 case 0x166e: /* (green steel) */
6990 element = EL_STEEL_CHAR_6;
6993 case 0x166f: /* (green steel) */
6994 element = EL_STEEL_CHAR_7;
6997 case 0x1670: /* (green steel) */
6998 element = EL_STEEL_CHAR_8;
7001 case 0x1671: /* (green steel) */
7002 element = EL_STEEL_CHAR_9;
7005 case 0x1672: /* (green steel) */
7006 element = EL_STEEL_CHAR_PERIOD;
7009 case 0x1673: /* (green steel) */
7010 element = EL_STEEL_CHAR_EXCLAM;
7013 case 0x1674: /* (green steel) */
7014 element = EL_STEEL_CHAR_COLON;
7017 case 0x1675: /* (green steel) */
7018 element = EL_STEEL_CHAR_LESS;
7021 case 0x1676: /* (green steel) */
7022 element = EL_STEEL_CHAR_GREATER;
7025 case 0x1677: /* (green steel) */
7026 element = EL_STEEL_CHAR_QUESTION;
7029 case 0x1678: /* (green steel) */
7030 element = EL_STEEL_CHAR_COPYRIGHT;
7033 case 0x1679: /* (green steel) */
7034 element = EL_STEEL_CHAR_UP;
7037 case 0x167a: /* (green steel) */
7038 element = EL_STEEL_CHAR_DOWN;
7041 case 0x167b: /* (green steel) */
7042 element = EL_STEEL_CHAR_BUTTON;
7045 case 0x167c: /* (green steel) */
7046 element = EL_STEEL_CHAR_PLUS;
7049 case 0x167d: /* (green steel) */
7050 element = EL_STEEL_CHAR_MINUS;
7053 case 0x167e: /* (green steel) */
7054 element = EL_STEEL_CHAR_APOSTROPHE;
7057 case 0x167f: /* (green steel) */
7058 element = EL_STEEL_CHAR_PARENLEFT;
7061 case 0x1680: /* (green steel) */
7062 element = EL_STEEL_CHAR_PARENRIGHT;
7065 case 0x1681: /* gate (red) */
7066 element = EL_EM_GATE_1;
7069 case 0x1682: /* secret gate (red) */
7070 element = EL_GATE_1_GRAY;
7073 case 0x1683: /* gate (yellow) */
7074 element = EL_EM_GATE_2;
7077 case 0x1684: /* secret gate (yellow) */
7078 element = EL_GATE_2_GRAY;
7081 case 0x1685: /* gate (blue) */
7082 element = EL_EM_GATE_4;
7085 case 0x1686: /* secret gate (blue) */
7086 element = EL_GATE_4_GRAY;
7089 case 0x1687: /* gate (green) */
7090 element = EL_EM_GATE_3;
7093 case 0x1688: /* secret gate (green) */
7094 element = EL_GATE_3_GRAY;
7097 case 0x1689: /* gate (white) */
7098 element = EL_DC_GATE_WHITE;
7101 case 0x168a: /* secret gate (white) */
7102 element = EL_DC_GATE_WHITE_GRAY;
7105 case 0x168b: /* secret gate (no key) */
7106 element = EL_DC_GATE_FAKE_GRAY;
7110 element = EL_ROBOT_WHEEL;
7114 element = EL_DC_TIMEGATE_SWITCH;
7118 element = EL_ACID_POOL_BOTTOM;
7122 element = EL_ACID_POOL_TOPLEFT;
7126 element = EL_ACID_POOL_TOPRIGHT;
7130 element = EL_ACID_POOL_BOTTOMLEFT;
7134 element = EL_ACID_POOL_BOTTOMRIGHT;
7138 element = EL_STEELWALL;
7142 element = EL_STEELWALL_SLIPPERY;
7145 case 0x1695: /* steel wall (not round) */
7146 element = EL_STEELWALL;
7149 case 0x1696: /* steel wall (left) */
7150 element = EL_DC_STEELWALL_1_LEFT;
7153 case 0x1697: /* steel wall (bottom) */
7154 element = EL_DC_STEELWALL_1_BOTTOM;
7157 case 0x1698: /* steel wall (right) */
7158 element = EL_DC_STEELWALL_1_RIGHT;
7161 case 0x1699: /* steel wall (top) */
7162 element = EL_DC_STEELWALL_1_TOP;
7165 case 0x169a: /* steel wall (left/bottom) */
7166 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
7169 case 0x169b: /* steel wall (right/bottom) */
7170 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
7173 case 0x169c: /* steel wall (right/top) */
7174 element = EL_DC_STEELWALL_1_TOPRIGHT;
7177 case 0x169d: /* steel wall (left/top) */
7178 element = EL_DC_STEELWALL_1_TOPLEFT;
7181 case 0x169e: /* steel wall (right/bottom small) */
7182 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
7185 case 0x169f: /* steel wall (left/bottom small) */
7186 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
7189 case 0x16a0: /* steel wall (right/top small) */
7190 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
7193 case 0x16a1: /* steel wall (left/top small) */
7194 element = EL_DC_STEELWALL_1_TOPLEFT_2;
7197 case 0x16a2: /* steel wall (left/right) */
7198 element = EL_DC_STEELWALL_1_VERTICAL;
7201 case 0x16a3: /* steel wall (top/bottom) */
7202 element = EL_DC_STEELWALL_1_HORIZONTAL;
7205 case 0x16a4: /* steel wall 2 (left end) */
7206 element = EL_DC_STEELWALL_2_LEFT;
7209 case 0x16a5: /* steel wall 2 (right end) */
7210 element = EL_DC_STEELWALL_2_RIGHT;
7213 case 0x16a6: /* steel wall 2 (top end) */
7214 element = EL_DC_STEELWALL_2_TOP;
7217 case 0x16a7: /* steel wall 2 (bottom end) */
7218 element = EL_DC_STEELWALL_2_BOTTOM;
7221 case 0x16a8: /* steel wall 2 (left/right) */
7222 element = EL_DC_STEELWALL_2_HORIZONTAL;
7225 case 0x16a9: /* steel wall 2 (up/down) */
7226 element = EL_DC_STEELWALL_2_VERTICAL;
7229 case 0x16aa: /* steel wall 2 (mid) */
7230 element = EL_DC_STEELWALL_2_MIDDLE;
7234 element = EL_SIGN_EXCLAMATION;
7238 element = EL_SIGN_RADIOACTIVITY;
7242 element = EL_SIGN_STOP;
7246 element = EL_SIGN_WHEELCHAIR;
7250 element = EL_SIGN_PARKING;
7254 element = EL_SIGN_NO_ENTRY;
7258 element = EL_SIGN_HEART;
7262 element = EL_SIGN_GIVE_WAY;
7266 element = EL_SIGN_ENTRY_FORBIDDEN;
7270 element = EL_SIGN_EMERGENCY_EXIT;
7274 element = EL_SIGN_YIN_YANG;
7278 element = EL_WALL_EMERALD;
7282 element = EL_WALL_DIAMOND;
7286 element = EL_WALL_PEARL;
7290 element = EL_WALL_CRYSTAL;
7294 element = EL_INVISIBLE_WALL;
7298 element = EL_INVISIBLE_STEELWALL;
7301 /* 0x16bc - 0x16cb: */
7302 /* EL_INVISIBLE_SAND */
7305 element = EL_LIGHT_SWITCH;
7309 element = EL_ENVELOPE_1;
7313 if (element >= 0x0117 && element <= 0x036e) /* (?) */
7314 element = EL_DIAMOND;
7315 else if (element >= 0x042d && element <= 0x0684) /* (?) */
7316 element = EL_EMERALD;
7317 else if (element >= 0x157c && element <= 0x158b)
7319 else if (element >= 0x1590 && element <= 0x159f)
7320 element = EL_DC_LANDMINE;
7321 else if (element >= 0x16bc && element <= 0x16cb)
7322 element = EL_INVISIBLE_SAND;
7325 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
7326 element = EL_UNKNOWN;
7331 return getMappedElement(element);
7338 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
7341 byte header[DC_LEVEL_HEADER_SIZE];
7343 int envelope_header_pos = 62;
7344 int envelope_content_pos = 94;
7345 int level_name_pos = 251;
7346 int level_author_pos = 292;
7347 int envelope_header_len;
7348 int envelope_content_len;
7350 int level_author_len;
7352 int num_yamyam_contents;
7355 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
7357 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
7359 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7361 header[i * 2 + 0] = header_word >> 8;
7362 header[i * 2 + 1] = header_word & 0xff;
7365 /* read some values from level header to check level decoding integrity */
7366 fieldx = header[6] | (header[7] << 8);
7367 fieldy = header[8] | (header[9] << 8);
7368 num_yamyam_contents = header[60] | (header[61] << 8);
7370 /* do some simple sanity checks to ensure that level was correctly decoded */
7371 if (fieldx < 1 || fieldx > 256 ||
7372 fieldy < 1 || fieldy > 256 ||
7373 num_yamyam_contents < 1 || num_yamyam_contents > 8)
7375 level->no_valid_file = TRUE;
7377 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
7382 /* maximum envelope header size is 31 bytes */
7383 envelope_header_len = header[envelope_header_pos];
7384 /* maximum envelope content size is 110 (156?) bytes */
7385 envelope_content_len = header[envelope_content_pos];
7387 /* maximum level title size is 40 bytes */
7388 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
7389 /* maximum level author size is 30 (51?) bytes */
7390 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
7394 for (i = 0; i < envelope_header_len; i++)
7395 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7396 level->envelope[0].text[envelope_size++] =
7397 header[envelope_header_pos + 1 + i];
7399 if (envelope_header_len > 0 && envelope_content_len > 0)
7401 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7402 level->envelope[0].text[envelope_size++] = '\n';
7403 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7404 level->envelope[0].text[envelope_size++] = '\n';
7407 for (i = 0; i < envelope_content_len; i++)
7408 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7409 level->envelope[0].text[envelope_size++] =
7410 header[envelope_content_pos + 1 + i];
7412 level->envelope[0].text[envelope_size] = '\0';
7414 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
7415 level->envelope[0].ysize = 10;
7416 level->envelope[0].autowrap = TRUE;
7417 level->envelope[0].centered = TRUE;
7419 for (i = 0; i < level_name_len; i++)
7420 level->name[i] = header[level_name_pos + 1 + i];
7421 level->name[level_name_len] = '\0';
7423 for (i = 0; i < level_author_len; i++)
7424 level->author[i] = header[level_author_pos + 1 + i];
7425 level->author[level_author_len] = '\0';
7427 num_yamyam_contents = header[60] | (header[61] << 8);
7428 level->num_yamyam_contents =
7429 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
7431 for (i = 0; i < num_yamyam_contents; i++)
7433 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
7435 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7437 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
7439 int element_dc = word;
7442 if (i < MAX_ELEMENT_CONTENTS)
7443 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
7447 fieldx = header[6] | (header[7] << 8);
7448 fieldy = header[8] | (header[9] << 8);
7449 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
7450 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
7452 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
7454 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7456 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
7458 int element_dc = word;
7461 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
7462 level->field[x][y] = getMappedElement_DC(element_dc);
7465 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
7466 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
7467 level->field[x][y] = EL_PLAYER_1;
7469 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
7470 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
7471 level->field[x][y] = EL_PLAYER_2;
7473 level->gems_needed = header[18] | (header[19] << 8);
7475 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
7476 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
7477 level->score[SC_PEARL] = header[24] | (header[25] << 8);
7478 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
7479 level->score[SC_NUT] = header[28] | (header[29] << 8);
7480 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
7481 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
7482 level->score[SC_BUG] = header[34] | (header[35] << 8);
7483 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
7484 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
7485 level->score[SC_KEY] = header[40] | (header[41] << 8);
7486 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
7488 level->time = header[44] | (header[45] << 8);
7490 level->amoeba_speed = header[46] | (header[47] << 8);
7491 level->time_light = header[48] | (header[49] << 8);
7492 level->time_timegate = header[50] | (header[51] << 8);
7493 level->time_wheel = header[52] | (header[53] << 8);
7494 level->time_magic_wall = header[54] | (header[55] << 8);
7495 level->extra_time = header[56] | (header[57] << 8);
7496 level->shield_normal_time = header[58] | (header[59] << 8);
7498 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
7499 can slip down from flat walls, like normal walls and steel walls */
7500 level->em_slippery_gems = TRUE;
7503 /* Diamond Caves II levels are always surrounded by indestructible wall, but
7504 not necessarily in a rectangular way -- fill with invisible steel wall */
7506 /* !!! not always true !!! keep level and set BorderElement instead !!! */
7508 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7511 if ((x == 0 || x == level->fieldx - 1 ||
7512 y == 0 || y == level->fieldy - 1) &&
7513 level->field[x][y] == EL_EMPTY)
7514 level->field[x][y] = EL_INVISIBLE_STEELWALL;
7516 if ((x == 0 || x == level->fieldx - 1 ||
7517 y == 0 || y == level->fieldy - 1) &&
7518 level->field[x][y] == EL_EMPTY)
7519 FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
7520 level->field, level->fieldx, level->fieldy);
7526 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
7527 struct LevelFileInfo *level_file_info,
7528 boolean level_info_only)
7530 char *filename = level_file_info->filename;
7532 int num_magic_bytes = 8;
7533 char magic_bytes[num_magic_bytes + 1];
7534 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7536 if (!(file = openFile(filename, MODE_READ)))
7538 level->no_valid_file = TRUE;
7540 if (!level_info_only)
7541 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
7546 // fseek(file, 0x0000, SEEK_SET);
7548 if (level_file_info->packed)
7550 /* read "magic bytes" from start of file */
7551 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
7552 magic_bytes[0] = '\0';
7554 /* check "magic bytes" for correct file format */
7555 if (!strPrefix(magic_bytes, "DC2"))
7557 level->no_valid_file = TRUE;
7559 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
7565 if (strPrefix(magic_bytes, "DC2Win95") ||
7566 strPrefix(magic_bytes, "DC2Win98"))
7568 int position_first_level = 0x00fa;
7569 int extra_bytes = 4;
7572 /* advance file stream to first level inside the level package */
7573 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7575 /* each block of level data is followed by block of non-level data */
7576 num_levels_to_skip *= 2;
7578 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
7579 while (num_levels_to_skip >= 0)
7581 /* advance file stream to next level inside the level package */
7582 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7584 level->no_valid_file = TRUE;
7586 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
7592 /* skip apparently unused extra bytes following each level */
7593 ReadUnusedBytesFromFile(file, extra_bytes);
7595 /* read size of next level in level package */
7596 skip_bytes = getFile32BitLE(file);
7598 num_levels_to_skip--;
7603 level->no_valid_file = TRUE;
7605 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
7612 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
7619 static void LoadLevelFromFileStream_DC(FILE *file, struct LevelInfo *level,
7622 byte header[DC_LEVEL_HEADER_SIZE];
7624 int envelope_header_pos = 62;
7625 int envelope_content_pos = 94;
7626 int level_name_pos = 251;
7627 int level_author_pos = 292;
7628 int envelope_header_len;
7629 int envelope_content_len;
7631 int level_author_len;
7633 int num_yamyam_contents;
7636 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
7638 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
7640 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7642 header[i * 2 + 0] = header_word >> 8;
7643 header[i * 2 + 1] = header_word & 0xff;
7646 /* read some values from level header to check level decoding integrity */
7647 fieldx = header[6] | (header[7] << 8);
7648 fieldy = header[8] | (header[9] << 8);
7649 num_yamyam_contents = header[60] | (header[61] << 8);
7651 /* do some simple sanity checks to ensure that level was correctly decoded */
7652 if (fieldx < 1 || fieldx > 256 ||
7653 fieldy < 1 || fieldy > 256 ||
7654 num_yamyam_contents < 1 || num_yamyam_contents > 8)
7656 level->no_valid_file = TRUE;
7658 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
7663 /* maximum envelope header size is 31 bytes */
7664 envelope_header_len = header[envelope_header_pos];
7665 /* maximum envelope content size is 110 (156?) bytes */
7666 envelope_content_len = header[envelope_content_pos];
7668 /* maximum level title size is 40 bytes */
7669 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
7670 /* maximum level author size is 30 (51?) bytes */
7671 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
7675 for (i = 0; i < envelope_header_len; i++)
7676 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7677 level->envelope[0].text[envelope_size++] =
7678 header[envelope_header_pos + 1 + i];
7680 if (envelope_header_len > 0 && envelope_content_len > 0)
7682 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7683 level->envelope[0].text[envelope_size++] = '\n';
7684 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7685 level->envelope[0].text[envelope_size++] = '\n';
7688 for (i = 0; i < envelope_content_len; i++)
7689 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7690 level->envelope[0].text[envelope_size++] =
7691 header[envelope_content_pos + 1 + i];
7693 level->envelope[0].text[envelope_size] = '\0';
7695 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
7696 level->envelope[0].ysize = 10;
7697 level->envelope[0].autowrap = TRUE;
7698 level->envelope[0].centered = TRUE;
7700 for (i = 0; i < level_name_len; i++)
7701 level->name[i] = header[level_name_pos + 1 + i];
7702 level->name[level_name_len] = '\0';
7704 for (i = 0; i < level_author_len; i++)
7705 level->author[i] = header[level_author_pos + 1 + i];
7706 level->author[level_author_len] = '\0';
7708 num_yamyam_contents = header[60] | (header[61] << 8);
7709 level->num_yamyam_contents =
7710 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
7712 for (i = 0; i < num_yamyam_contents; i++)
7714 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
7716 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7718 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
7720 int element_dc = word;
7723 if (i < MAX_ELEMENT_CONTENTS)
7724 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
7728 fieldx = header[6] | (header[7] << 8);
7729 fieldy = header[8] | (header[9] << 8);
7730 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
7731 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
7733 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
7735 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7737 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
7739 int element_dc = word;
7742 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
7743 level->field[x][y] = getMappedElement_DC(element_dc);
7746 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
7747 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
7748 level->field[x][y] = EL_PLAYER_1;
7750 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
7751 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
7752 level->field[x][y] = EL_PLAYER_2;
7754 level->gems_needed = header[18] | (header[19] << 8);
7756 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
7757 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
7758 level->score[SC_PEARL] = header[24] | (header[25] << 8);
7759 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
7760 level->score[SC_NUT] = header[28] | (header[29] << 8);
7761 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
7762 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
7763 level->score[SC_BUG] = header[34] | (header[35] << 8);
7764 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
7765 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
7766 level->score[SC_KEY] = header[40] | (header[41] << 8);
7767 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
7769 level->time = header[44] | (header[45] << 8);
7771 level->amoeba_speed = header[46] | (header[47] << 8);
7772 level->time_light = header[48] | (header[49] << 8);
7773 level->time_timegate = header[50] | (header[51] << 8);
7774 level->time_wheel = header[52] | (header[53] << 8);
7775 level->time_magic_wall = header[54] | (header[55] << 8);
7776 level->extra_time = header[56] | (header[57] << 8);
7777 level->shield_normal_time = header[58] | (header[59] << 8);
7779 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
7780 can slip down from flat walls, like normal walls and steel walls */
7781 level->em_slippery_gems = TRUE;
7784 /* Diamond Caves II levels are always surrounded by indestructible wall, but
7785 not necessarily in a rectangular way -- fill with invisible steel wall */
7787 /* !!! not always true !!! keep level and set BorderElement instead !!! */
7789 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7792 if ((x == 0 || x == level->fieldx - 1 ||
7793 y == 0 || y == level->fieldy - 1) &&
7794 level->field[x][y] == EL_EMPTY)
7795 level->field[x][y] = EL_INVISIBLE_STEELWALL;
7797 if ((x == 0 || x == level->fieldx - 1 ||
7798 y == 0 || y == level->fieldy - 1) &&
7799 level->field[x][y] == EL_EMPTY)
7800 FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
7801 level->field, level->fieldx, level->fieldy);
7807 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
7808 struct LevelFileInfo *level_file_info,
7809 boolean level_info_only)
7811 char *filename = level_file_info->filename;
7813 int num_magic_bytes = 8;
7814 char magic_bytes[num_magic_bytes + 1];
7815 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7817 if (!(file = fopen(filename, MODE_READ)))
7819 level->no_valid_file = TRUE;
7821 if (!level_info_only)
7822 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
7827 // fseek(file, 0x0000, SEEK_SET);
7829 if (level_file_info->packed)
7831 /* read "magic bytes" from start of file */
7832 if (fgets(magic_bytes, num_magic_bytes + 1, file) == NULL)
7833 magic_bytes[0] = '\0';
7835 /* check "magic bytes" for correct file format */
7836 if (!strPrefix(magic_bytes, "DC2"))
7838 level->no_valid_file = TRUE;
7840 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
7846 if (strPrefix(magic_bytes, "DC2Win95") ||
7847 strPrefix(magic_bytes, "DC2Win98"))
7849 int position_first_level = 0x00fa;
7850 int extra_bytes = 4;
7853 /* advance file stream to first level inside the level package */
7854 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7856 /* each block of level data is followed by block of non-level data */
7857 num_levels_to_skip *= 2;
7859 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
7860 while (num_levels_to_skip >= 0)
7862 /* advance file stream to next level inside the level package */
7863 if (fseek(file, skip_bytes, SEEK_CUR) != 0)
7865 level->no_valid_file = TRUE;
7867 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
7873 /* skip apparently unused extra bytes following each level */
7874 ReadUnusedBytesFromFile(file, extra_bytes);
7876 /* read size of next level in level package */
7877 skip_bytes = getFile32BitLE(file);
7879 num_levels_to_skip--;
7884 level->no_valid_file = TRUE;
7886 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
7893 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
7902 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
7903 struct LevelFileInfo *level_file_info)
7905 char *filename = level_file_info->filename;
7908 int nr = level_file_info->nr - leveldir_current->first_level;
7910 byte header[DC_LEVEL_HEADER_SIZE];
7912 int envelope_header_pos = 62;
7913 int envelope_content_pos = 94;
7914 int level_name_pos = 251;
7915 int level_author_pos = 292;
7916 int envelope_header_len;
7917 int envelope_content_len;
7919 int level_author_len;
7921 int num_yamyam_contents;
7924 if (!(file = fopen(filename, MODE_READ)))
7926 level->no_valid_file = TRUE;
7928 if (!level_info_only)
7929 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
7935 /* position file stream to the requested level inside the level package */
7936 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
7938 level->no_valid_file = TRUE;
7940 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
7946 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
7948 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
7950 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7952 header[i * 2 + 0] = header_word >> 8;
7953 header[i * 2 + 1] = header_word & 0xff;
7956 /* read some values from level header to check level decoding integrity */
7957 fieldx = header[6] | (header[7] << 8);
7958 fieldy = header[8] | (header[9] << 8);
7959 num_yamyam_contents = header[60] | (header[61] << 8);
7961 /* do some simple sanity checks to ensure that level was correctly decoded */
7962 if (fieldx < 1 || fieldx > 256 ||
7963 fieldy < 1 || fieldy > 256 ||
7964 num_yamyam_contents < 1 || num_yamyam_contents > 8)
7966 level->no_valid_file = TRUE;
7968 Error(ERR_WARN, "cannot read level from file '%s' -- using empty level",
7974 /* maximum envelope header size is 31 bytes */
7975 envelope_header_len = header[envelope_header_pos];
7976 /* maximum envelope content size is 110 (156?) bytes */
7977 envelope_content_len = header[envelope_content_pos];
7979 /* maximum level title size is 40 bytes */
7980 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
7981 /* maximum level author size is 30 (51?) bytes */
7982 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
7986 for (i = 0; i < envelope_header_len; i++)
7987 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7988 level->envelope[0].text[envelope_size++] =
7989 header[envelope_header_pos + 1 + i];
7991 if (envelope_header_len > 0 && envelope_content_len > 0)
7993 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7994 level->envelope[0].text[envelope_size++] = '\n';
7995 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7996 level->envelope[0].text[envelope_size++] = '\n';
7999 for (i = 0; i < envelope_content_len; i++)
8000 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
8001 level->envelope[0].text[envelope_size++] =
8002 header[envelope_content_pos + 1 + i];
8004 level->envelope[0].text[envelope_size] = '\0';
8006 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
8007 level->envelope[0].ysize = 10;
8008 level->envelope[0].autowrap = TRUE;
8009 level->envelope[0].centered = TRUE;
8011 for (i = 0; i < level_name_len; i++)
8012 level->name[i] = header[level_name_pos + 1 + i];
8013 level->name[level_name_len] = '\0';
8015 for (i = 0; i < level_author_len; i++)
8016 level->author[i] = header[level_author_pos + 1 + i];
8017 level->author[level_author_len] = '\0';
8019 num_yamyam_contents = header[60] | (header[61] << 8);
8020 level->num_yamyam_contents =
8021 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
8023 for (i = 0; i < num_yamyam_contents; i++)
8025 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
8027 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
8029 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
8031 int element_dc = word;
8034 if (i < MAX_ELEMENT_CONTENTS)
8035 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
8039 fieldx = header[6] | (header[7] << 8);
8040 fieldy = header[8] | (header[9] << 8);
8041 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
8042 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
8044 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
8046 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
8048 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
8050 int element_dc = word;
8053 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
8054 level->field[x][y] = getMappedElement_DC(element_dc);
8057 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
8058 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
8059 level->field[x][y] = EL_PLAYER_1;
8061 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
8062 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
8063 level->field[x][y] = EL_PLAYER_2;
8065 level->gems_needed = header[18] | (header[19] << 8);
8067 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
8068 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
8069 level->score[SC_PEARL] = header[24] | (header[25] << 8);
8070 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
8071 level->score[SC_NUT] = header[28] | (header[29] << 8);
8072 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
8073 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
8074 level->score[SC_BUG] = header[34] | (header[35] << 8);
8075 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
8076 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
8077 level->score[SC_KEY] = header[40] | (header[41] << 8);
8078 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
8080 level->time = header[44] | (header[45] << 8);
8082 level->amoeba_speed = header[46] | (header[47] << 8);
8083 level->time_light = header[48] | (header[49] << 8);
8084 level->time_timegate = header[50] | (header[51] << 8);
8085 level->time_wheel = header[52] | (header[53] << 8);
8086 level->time_magic_wall = header[54] | (header[55] << 8);
8087 level->extra_time = header[56] | (header[57] << 8);
8088 level->shield_normal_time = header[58] | (header[59] << 8);
8092 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
8093 can slip down from flat walls, like normal walls and steel walls */
8094 level->em_slippery_gems = TRUE;
8097 /* Diamond Caves II levels are always surrounded by indestructible wall, but
8098 not necessarily in a rectangular way -- fill with invisible steel wall */
8100 /* !!! not always true !!! keep level and set BorderElement instead !!! */
8102 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8105 if ((x == 0 || x == level->fieldx - 1 ||
8106 y == 0 || y == level->fieldy - 1) &&
8107 level->field[x][y] == EL_EMPTY)
8108 level->field[x][y] = EL_INVISIBLE_STEELWALL;
8110 if ((x == 0 || x == level->fieldx - 1 ||
8111 y == 0 || y == level->fieldy - 1) &&
8112 level->field[x][y] == EL_EMPTY)
8113 FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
8114 level->field, level->fieldx, level->fieldy);
8123 /* ------------------------------------------------------------------------- */
8124 /* functions for loading SB level */
8125 /* ------------------------------------------------------------------------- */
8127 int getMappedElement_SB(int element_ascii, boolean use_ces)
8135 sb_element_mapping[] =
8137 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
8138 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
8139 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
8140 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
8141 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
8142 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
8143 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
8145 { '_', EL_INVISIBLE_STEELWALL, EL_CUSTOM_8 }, /* floor beyond border */
8147 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
8155 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
8156 if (element_ascii == sb_element_mapping[i].ascii)
8157 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
8159 return EL_UNDEFINED;
8164 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
8165 struct LevelFileInfo *level_file_info,
8166 boolean level_info_only)
8168 char *filename = level_file_info->filename;
8169 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
8170 char last_comment[MAX_LINE_LEN];
8171 char level_name[MAX_LINE_LEN];
8174 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
8175 boolean read_continued_line = FALSE;
8176 boolean reading_playfield = FALSE;
8177 boolean got_valid_playfield_line = FALSE;
8178 boolean invalid_playfield_char = FALSE;
8179 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
8180 int file_level_nr = 0;
8182 int x = 0, y = 0; /* initialized to make compilers happy */
8185 printf("::: looking for level number %d [%d]\n",
8186 level_file_info->nr, num_levels_to_skip);
8189 last_comment[0] = '\0';
8190 level_name[0] = '\0';
8192 if (!(file = openFile(filename, MODE_READ)))
8194 level->no_valid_file = TRUE;
8196 if (!level_info_only)
8197 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
8202 while (!checkEndOfFile(file))
8204 /* level successfully read, but next level may follow here */
8205 if (!got_valid_playfield_line && reading_playfield)
8208 printf("::: read complete playfield\n");
8211 /* read playfield from single level file -- skip remaining file */
8212 if (!level_file_info->packed)
8215 if (file_level_nr >= num_levels_to_skip)
8220 last_comment[0] = '\0';
8221 level_name[0] = '\0';
8223 reading_playfield = FALSE;
8226 got_valid_playfield_line = FALSE;
8228 /* read next line of input file */
8229 if (!getStringFromFile(file, line, MAX_LINE_LEN))
8232 /* check if line was completely read and is terminated by line break */
8233 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8236 /* cut trailing line break (this can be newline and/or carriage return) */
8237 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
8238 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
8241 /* copy raw input line for later use (mainly debugging output) */
8242 strcpy(line_raw, line);
8244 if (read_continued_line)
8246 /* append new line to existing line, if there is enough space */
8247 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
8248 strcat(previous_line, line_ptr);
8250 strcpy(line, previous_line); /* copy storage buffer to line */
8252 read_continued_line = FALSE;
8255 /* if the last character is '\', continue at next line */
8256 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
8258 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
8259 strcpy(previous_line, line); /* copy line to storage buffer */
8261 read_continued_line = TRUE;
8266 /* skip empty lines */
8267 if (line[0] == '\0')
8270 /* extract comment text from comment line */
8273 for (line_ptr = line; *line_ptr; line_ptr++)
8274 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
8277 strcpy(last_comment, line_ptr);
8280 printf("::: found comment '%s' in line %d\n", last_comment, line_nr);
8286 /* extract level title text from line containing level title */
8287 if (line[0] == '\'')
8289 strcpy(level_name, &line[1]);
8291 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
8292 level_name[strlen(level_name) - 1] = '\0';
8295 printf("::: found level name '%s' in line %d\n", level_name, line_nr);
8301 /* skip lines containing only spaces (or empty lines) */
8302 for (line_ptr = line; *line_ptr; line_ptr++)
8303 if (*line_ptr != ' ')
8305 if (*line_ptr == '\0')
8308 /* at this point, we have found a line containing part of a playfield */
8311 printf("::: found playfield row in line %d\n", line_nr);
8314 got_valid_playfield_line = TRUE;
8316 if (!reading_playfield)
8318 reading_playfield = TRUE;
8319 invalid_playfield_char = FALSE;
8321 for (x = 0; x < MAX_LEV_FIELDX; x++)
8322 for (y = 0; y < MAX_LEV_FIELDY; y++)
8323 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
8328 /* start with topmost tile row */
8332 /* skip playfield line if larger row than allowed */
8333 if (y >= MAX_LEV_FIELDY)
8336 /* start with leftmost tile column */
8339 /* read playfield elements from line */
8340 for (line_ptr = line; *line_ptr; line_ptr++)
8342 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
8344 /* stop parsing playfield line if larger column than allowed */
8345 if (x >= MAX_LEV_FIELDX)
8348 if (mapped_sb_element == EL_UNDEFINED)
8350 invalid_playfield_char = TRUE;
8355 level->field[x][y] = mapped_sb_element;
8357 /* continue with next tile column */
8360 level->fieldx = MAX(x, level->fieldx);
8363 if (invalid_playfield_char)
8365 /* if first playfield line, treat invalid lines as comment lines */
8367 reading_playfield = FALSE;
8372 /* continue with next tile row */
8380 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
8381 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
8383 if (!reading_playfield)
8385 level->no_valid_file = TRUE;
8387 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
8392 if (*level_name != '\0')
8394 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
8395 level->name[MAX_LEVEL_NAME_LEN] = '\0';
8398 printf(":1: level name: '%s'\n", level->name);
8401 else if (*last_comment != '\0')
8403 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
8404 level->name[MAX_LEVEL_NAME_LEN] = '\0';
8407 printf(":2: level name: '%s'\n", level->name);
8412 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
8415 /* set all empty fields beyond the border walls to invisible steel wall */
8416 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8418 if ((x == 0 || x == level->fieldx - 1 ||
8419 y == 0 || y == level->fieldy - 1) &&
8420 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
8421 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
8422 level->field, level->fieldx, level->fieldy);
8425 /* set special level settings for Sokoban levels */
8428 level->use_step_counter = TRUE;
8430 if (load_xsb_to_ces)
8433 /* !!! special global settings can now be set in level template !!! */
8435 level->initial_player_stepsize[0] = STEPSIZE_SLOW;
8438 /* fill smaller playfields with padding "beyond border wall" elements */
8439 if (level->fieldx < SCR_FIELDX ||
8440 level->fieldy < SCR_FIELDY)
8442 short field[level->fieldx][level->fieldy];
8443 int new_fieldx = MAX(level->fieldx, SCR_FIELDX);
8444 int new_fieldy = MAX(level->fieldy, SCR_FIELDY);
8445 int pos_fieldx = (new_fieldx - level->fieldx) / 2;
8446 int pos_fieldy = (new_fieldy - level->fieldy) / 2;
8448 /* copy old playfield (which is smaller than the visible area) */
8449 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8450 field[x][y] = level->field[x][y];
8452 /* fill new, larger playfield with "beyond border wall" elements */
8453 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
8454 level->field[x][y] = getMappedElement_SB('_', load_xsb_to_ces);
8456 /* copy the old playfield to the middle of the new playfield */
8457 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8458 level->field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
8460 level->fieldx = new_fieldx;
8461 level->fieldy = new_fieldy;
8464 level->use_custom_template = TRUE;
8470 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
8471 struct LevelFileInfo *level_file_info,
8472 boolean level_info_only)
8474 char *filename = level_file_info->filename;
8475 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
8476 char last_comment[MAX_LINE_LEN];
8477 char level_name[MAX_LINE_LEN];
8480 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
8481 boolean read_continued_line = FALSE;
8482 boolean reading_playfield = FALSE;
8483 boolean got_valid_playfield_line = FALSE;
8484 boolean invalid_playfield_char = FALSE;
8485 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
8486 int file_level_nr = 0;
8488 int x = 0, y = 0; /* initialized to make compilers happy */
8491 printf("::: looking for level number %d [%d]\n",
8492 level_file_info->nr, num_levels_to_skip);
8495 last_comment[0] = '\0';
8496 level_name[0] = '\0';
8498 if (!(file = fopen(filename, MODE_READ)))
8500 level->no_valid_file = TRUE;
8502 if (!level_info_only)
8503 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
8510 /* level successfully read, but next level may follow here */
8511 if (!got_valid_playfield_line && reading_playfield)
8514 printf("::: read complete playfield\n");
8517 /* read playfield from single level file -- skip remaining file */
8518 if (!level_file_info->packed)
8521 if (file_level_nr >= num_levels_to_skip)
8526 last_comment[0] = '\0';
8527 level_name[0] = '\0';
8529 reading_playfield = FALSE;
8532 got_valid_playfield_line = FALSE;
8534 /* read next line of input file */
8535 if (!fgets(line, MAX_LINE_LEN, file))
8538 /* check if line was completely read and is terminated by line break */
8539 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8542 /* cut trailing line break (this can be newline and/or carriage return) */
8543 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
8544 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
8547 /* copy raw input line for later use (mainly debugging output) */
8548 strcpy(line_raw, line);
8550 if (read_continued_line)
8552 /* append new line to existing line, if there is enough space */
8553 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
8554 strcat(previous_line, line_ptr);
8556 strcpy(line, previous_line); /* copy storage buffer to line */
8558 read_continued_line = FALSE;
8561 /* if the last character is '\', continue at next line */
8562 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
8564 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
8565 strcpy(previous_line, line); /* copy line to storage buffer */
8567 read_continued_line = TRUE;
8572 /* skip empty lines */
8573 if (line[0] == '\0')
8576 /* extract comment text from comment line */
8579 for (line_ptr = line; *line_ptr; line_ptr++)
8580 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
8583 strcpy(last_comment, line_ptr);
8586 printf("::: found comment '%s' in line %d\n", last_comment, line_nr);
8592 /* extract level title text from line containing level title */
8593 if (line[0] == '\'')
8595 strcpy(level_name, &line[1]);
8597 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
8598 level_name[strlen(level_name) - 1] = '\0';
8601 printf("::: found level name '%s' in line %d\n", level_name, line_nr);
8607 /* skip lines containing only spaces (or empty lines) */
8608 for (line_ptr = line; *line_ptr; line_ptr++)
8609 if (*line_ptr != ' ')
8611 if (*line_ptr == '\0')
8614 /* at this point, we have found a line containing part of a playfield */
8617 printf("::: found playfield row in line %d\n", line_nr);
8620 got_valid_playfield_line = TRUE;
8622 if (!reading_playfield)
8624 reading_playfield = TRUE;
8625 invalid_playfield_char = FALSE;
8627 for (x = 0; x < MAX_LEV_FIELDX; x++)
8628 for (y = 0; y < MAX_LEV_FIELDY; y++)
8629 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
8634 /* start with topmost tile row */
8638 /* skip playfield line if larger row than allowed */
8639 if (y >= MAX_LEV_FIELDY)
8642 /* start with leftmost tile column */
8645 /* read playfield elements from line */
8646 for (line_ptr = line; *line_ptr; line_ptr++)
8648 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
8650 /* stop parsing playfield line if larger column than allowed */
8651 if (x >= MAX_LEV_FIELDX)
8654 if (mapped_sb_element == EL_UNDEFINED)
8656 invalid_playfield_char = TRUE;
8661 level->field[x][y] = mapped_sb_element;
8663 /* continue with next tile column */
8666 level->fieldx = MAX(x, level->fieldx);
8669 if (invalid_playfield_char)
8671 /* if first playfield line, treat invalid lines as comment lines */
8673 reading_playfield = FALSE;
8678 /* continue with next tile row */
8686 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
8687 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
8689 if (!reading_playfield)
8691 level->no_valid_file = TRUE;
8693 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
8698 if (*level_name != '\0')
8700 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
8701 level->name[MAX_LEVEL_NAME_LEN] = '\0';
8704 printf(":1: level name: '%s'\n", level->name);
8707 else if (*last_comment != '\0')
8709 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
8710 level->name[MAX_LEVEL_NAME_LEN] = '\0';
8713 printf(":2: level name: '%s'\n", level->name);
8718 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
8721 /* set all empty fields beyond the border walls to invisible steel wall */
8722 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8724 if ((x == 0 || x == level->fieldx - 1 ||
8725 y == 0 || y == level->fieldy - 1) &&
8726 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
8727 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
8728 level->field, level->fieldx, level->fieldy);
8731 /* set special level settings for Sokoban levels */
8734 level->use_step_counter = TRUE;
8736 if (load_xsb_to_ces)
8739 /* !!! special global settings can now be set in level template !!! */
8741 level->initial_player_stepsize[0] = STEPSIZE_SLOW;
8744 /* fill smaller playfields with padding "beyond border wall" elements */
8745 if (level->fieldx < SCR_FIELDX ||
8746 level->fieldy < SCR_FIELDY)
8748 short field[level->fieldx][level->fieldy];
8749 int new_fieldx = MAX(level->fieldx, SCR_FIELDX);
8750 int new_fieldy = MAX(level->fieldy, SCR_FIELDY);
8751 int pos_fieldx = (new_fieldx - level->fieldx) / 2;
8752 int pos_fieldy = (new_fieldy - level->fieldy) / 2;
8754 /* copy old playfield (which is smaller than the visible area) */
8755 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8756 field[x][y] = level->field[x][y];
8758 /* fill new, larger playfield with "beyond border wall" elements */
8759 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
8760 level->field[x][y] = getMappedElement_SB('_', load_xsb_to_ces);
8762 /* copy the old playfield to the middle of the new playfield */
8763 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8764 level->field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
8766 level->fieldx = new_fieldx;
8767 level->fieldy = new_fieldy;
8770 level->use_custom_template = TRUE;
8777 /* ------------------------------------------------------------------------- */
8778 /* functions for handling native levels */
8779 /* ------------------------------------------------------------------------- */
8781 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
8782 struct LevelFileInfo *level_file_info,
8783 boolean level_info_only)
8785 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
8786 level->no_valid_file = TRUE;
8789 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
8790 struct LevelFileInfo *level_file_info,
8791 boolean level_info_only)
8795 /* determine position of requested level inside level package */
8796 if (level_file_info->packed)
8797 pos = level_file_info->nr - leveldir_current->first_level;
8799 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
8800 level->no_valid_file = TRUE;
8803 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
8805 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
8806 CopyNativeLevel_RND_to_EM(level);
8807 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
8808 CopyNativeLevel_RND_to_SP(level);
8811 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
8813 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
8814 CopyNativeLevel_EM_to_RND(level);
8815 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
8816 CopyNativeLevel_SP_to_RND(level);
8819 void SaveNativeLevel(struct LevelInfo *level)
8821 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
8823 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
8824 char *filename = getLevelFilenameFromBasename(basename);
8826 CopyNativeLevel_RND_to_SP(level);
8827 CopyNativeTape_RND_to_SP(level);
8829 SaveNativeLevel_SP(filename);
8834 /* ------------------------------------------------------------------------- */
8835 /* functions for loading generic level */
8836 /* ------------------------------------------------------------------------- */
8838 static void LoadLevelFromFileInfo(struct LevelInfo *level,
8839 struct LevelFileInfo *level_file_info,
8840 boolean level_info_only)
8842 /* always start with reliable default values */
8843 setLevelInfoToDefaults(level, level_info_only);
8845 switch (level_file_info->type)
8847 case LEVEL_FILE_TYPE_RND:
8848 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
8851 case LEVEL_FILE_TYPE_EM:
8852 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
8853 level->game_engine_type = GAME_ENGINE_TYPE_EM;
8856 case LEVEL_FILE_TYPE_SP:
8857 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
8858 level->game_engine_type = GAME_ENGINE_TYPE_SP;
8861 case LEVEL_FILE_TYPE_DC:
8862 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
8865 case LEVEL_FILE_TYPE_SB:
8866 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
8870 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
8874 /* if level file is invalid, restore level structure to default values */
8875 if (level->no_valid_file)
8877 setLevelInfoToDefaults(level, level_info_only);
8879 level->no_valid_file = TRUE; /* but keep "no valid file" flag */
8882 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
8883 level->game_engine_type = GAME_ENGINE_TYPE_RND;
8885 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
8886 CopyNativeLevel_Native_to_RND(level);
8889 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
8891 static struct LevelFileInfo level_file_info;
8893 /* always start with reliable default values */
8894 setFileInfoToDefaults(&level_file_info);
8896 level_file_info.nr = 0; /* unknown level number */
8897 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
8898 level_file_info.filename = filename;
8900 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
8903 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
8907 if (leveldir_current == NULL) /* only when dumping level */
8910 /* all engine modifications also valid for levels which use latest engine */
8911 if (level->game_version < VERSION_IDENT(3,2,0,5))
8913 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
8914 level->score[SC_TIME_BONUS] /= 10;
8918 leveldir_current->latest_engine = TRUE; /* !!! TEST ONLY !!! */
8921 if (leveldir_current->latest_engine)
8923 /* ---------- use latest game engine ----------------------------------- */
8925 /* For all levels which are forced to use the latest game engine version
8926 (normally all but user contributed, private and undefined levels), set
8927 the game engine version to the actual version; this allows for actual
8928 corrections in the game engine to take effect for existing, converted
8929 levels (from "classic" or other existing games) to make the emulation
8930 of the corresponding game more accurate, while (hopefully) not breaking
8931 existing levels created from other players. */
8933 level->game_version = GAME_VERSION_ACTUAL;
8935 /* Set special EM style gems behaviour: EM style gems slip down from
8936 normal, steel and growing wall. As this is a more fundamental change,
8937 it seems better to set the default behaviour to "off" (as it is more
8938 natural) and make it configurable in the level editor (as a property
8939 of gem style elements). Already existing converted levels (neither
8940 private nor contributed levels) are changed to the new behaviour. */
8942 if (level->file_version < FILE_VERSION_2_0)
8943 level->em_slippery_gems = TRUE;
8948 /* ---------- use game engine the level was created with ----------------- */
8950 /* For all levels which are not forced to use the latest game engine
8951 version (normally user contributed, private and undefined levels),
8952 use the version of the game engine the levels were created for.
8954 Since 2.0.1, the game engine version is now directly stored
8955 in the level file (chunk "VERS"), so there is no need anymore
8956 to set the game version from the file version (except for old,
8957 pre-2.0 levels, where the game version is still taken from the
8958 file format version used to store the level -- see above). */
8960 /* player was faster than enemies in 1.0.0 and before */
8961 if (level->file_version == FILE_VERSION_1_0)
8962 for (i = 0; i < MAX_PLAYERS; i++)
8963 level->initial_player_stepsize[i] = STEPSIZE_FAST;
8965 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
8966 if (level->game_version == VERSION_IDENT(2,0,1,0))
8967 level->em_slippery_gems = TRUE;
8969 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
8970 if (level->game_version < VERSION_IDENT(2,2,0,0))
8971 level->use_spring_bug = TRUE;
8973 if (level->game_version < VERSION_IDENT(3,2,0,5))
8975 /* time orb caused limited time in endless time levels before 3.2.0-5 */
8976 level->use_time_orb_bug = TRUE;
8978 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
8979 level->block_snap_field = FALSE;
8981 /* extra time score was same value as time left score before 3.2.0-5 */
8982 level->extra_time_score = level->score[SC_TIME_BONUS];
8985 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
8986 level->score[SC_TIME_BONUS] /= 10;
8990 if (level->game_version < VERSION_IDENT(3,2,0,7))
8992 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
8993 level->continuous_snapping = FALSE;
8996 /* only few elements were able to actively move into acid before 3.1.0 */
8997 /* trigger settings did not exist before 3.1.0; set to default "any" */
8998 if (level->game_version < VERSION_IDENT(3,1,0,0))
9000 /* correct "can move into acid" settings (all zero in old levels) */
9002 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
9003 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
9005 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
9006 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
9007 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
9008 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
9010 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9011 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
9013 /* correct trigger settings (stored as zero == "none" in old levels) */
9015 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9017 int element = EL_CUSTOM_START + i;
9018 struct ElementInfo *ei = &element_info[element];
9020 for (j = 0; j < ei->num_change_pages; j++)
9022 struct ElementChangeInfo *change = &ei->change_page[j];
9024 change->trigger_player = CH_PLAYER_ANY;
9025 change->trigger_page = CH_PAGE_ANY;
9030 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
9032 int element = EL_CUSTOM_256;
9033 struct ElementInfo *ei = &element_info[element];
9034 struct ElementChangeInfo *change = &ei->change_page[0];
9036 /* This is needed to fix a problem that was caused by a bugfix in function
9037 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
9038 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
9039 not replace walkable elements, but instead just placed the player on it,
9040 without placing the Sokoban field under the player). Unfortunately, this
9041 breaks "Snake Bite" style levels when the snake is halfway through a door
9042 that just closes (the snake head is still alive and can be moved in this
9043 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
9044 player (without Sokoban element) which then gets killed as designed). */
9046 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
9047 strncmp(ei->description, "pause b4 death", 14) == 0) &&
9048 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
9049 change->target_element = EL_PLAYER_1;
9053 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
9054 if (level->game_version < VERSION_IDENT(3,2,5,0))
9056 /* This is needed to fix a problem that was caused by a bugfix in function
9057 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
9058 corrects the behaviour when a custom element changes to another custom
9059 element with a higher element number that has change actions defined.
9060 Normally, only one change per frame is allowed for custom elements.
9061 Therefore, it is checked if a custom element already changed in the
9062 current frame; if it did, subsequent changes are suppressed.
9063 Unfortunately, this is only checked for element changes, but not for
9064 change actions, which are still executed. As the function above loops
9065 through all custom elements from lower to higher, an element change
9066 resulting in a lower CE number won't be checked again, while a target
9067 element with a higher number will also be checked, and potential change
9068 actions will get executed for this CE, too (which is wrong), while
9069 further changes are ignored (which is correct). As this bugfix breaks
9070 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
9071 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
9072 behaviour for existing levels and tapes that make use of this bug */
9074 level->use_action_after_change_bug = TRUE;
9077 /* !!! THIS DOES NOT FIX "Zelda I" (GRAPHICALLY) AND "Alan's FMV" LEVELS */
9078 /* try to detect and fix "Zelda II" levels, which are broken with 3.2.5 */
9080 int element = EL_CUSTOM_16;
9081 struct ElementInfo *ei = &element_info[element];
9083 /* This is needed to fix a problem that was caused by a bugfix in function
9084 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
9085 corrects the behaviour when a custom element changes to another custom
9086 element with a higher element number that has change actions defined.
9087 Normally, only one change per frame is allowed for custom elements.
9088 Therefore, it is checked if a custom element already changed in the
9089 current frame; if it did, subsequent changes are suppressed.
9090 Unfortunately, this is only checked for element changes, but not for
9091 change actions, which are still executed. As the function above loops
9092 through all custom elements from lower to higher, an element change
9093 resulting in a lower CE number won't be checked again, while a target
9094 element with a higher number will also be checked, and potential change
9095 actions will get executed for this CE, too (which is wrong), while
9096 further changes are ignored (which is correct). As this bugfix breaks
9097 Zelda II (but no other levels), allow the previous, incorrect behaviour
9098 for this outstanding level set to not break the game or existing tapes */
9100 if (strncmp(leveldir_current->identifier, "zelda2", 6) == 0 ||
9101 strncmp(ei->description, "scanline - row 1", 16) == 0)
9102 level->use_action_after_change_bug = TRUE;
9106 /* not centering level after relocating player was default only in 3.2.3 */
9107 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
9108 level->shifted_relocation = TRUE;
9110 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
9111 if (level->game_version < VERSION_IDENT(3,2,6,0))
9112 level->em_explodes_by_fire = TRUE;
9115 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
9119 /* map custom element change events that have changed in newer versions
9120 (these following values were accidentally changed in version 3.0.1)
9121 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
9122 if (level->game_version <= VERSION_IDENT(3,0,0,0))
9124 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9126 int element = EL_CUSTOM_START + i;
9128 /* order of checking and copying events to be mapped is important */
9129 /* (do not change the start and end value -- they are constant) */
9130 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
9132 if (HAS_CHANGE_EVENT(element, j - 2))
9134 SET_CHANGE_EVENT(element, j - 2, FALSE);
9135 SET_CHANGE_EVENT(element, j, TRUE);
9139 /* order of checking and copying events to be mapped is important */
9140 /* (do not change the start and end value -- they are constant) */
9141 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
9143 if (HAS_CHANGE_EVENT(element, j - 1))
9145 SET_CHANGE_EVENT(element, j - 1, FALSE);
9146 SET_CHANGE_EVENT(element, j, TRUE);
9152 /* initialize "can_change" field for old levels with only one change page */
9153 if (level->game_version <= VERSION_IDENT(3,0,2,0))
9155 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9157 int element = EL_CUSTOM_START + i;
9159 if (CAN_CHANGE(element))
9160 element_info[element].change->can_change = TRUE;
9164 /* correct custom element values (for old levels without these options) */
9165 if (level->game_version < VERSION_IDENT(3,1,1,0))
9167 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9169 int element = EL_CUSTOM_START + i;
9170 struct ElementInfo *ei = &element_info[element];
9172 if (ei->access_direction == MV_NO_DIRECTION)
9173 ei->access_direction = MV_ALL_DIRECTIONS;
9177 /* correct custom element values (fix invalid values for all versions) */
9180 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9182 int element = EL_CUSTOM_START + i;
9183 struct ElementInfo *ei = &element_info[element];
9185 for (j = 0; j < ei->num_change_pages; j++)
9187 struct ElementChangeInfo *change = &ei->change_page[j];
9189 if (change->trigger_player == CH_PLAYER_NONE)
9190 change->trigger_player = CH_PLAYER_ANY;
9192 if (change->trigger_side == CH_SIDE_NONE)
9193 change->trigger_side = CH_SIDE_ANY;
9198 /* initialize "can_explode" field for old levels which did not store this */
9199 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
9200 if (level->game_version <= VERSION_IDENT(3,1,0,0))
9202 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9204 int element = EL_CUSTOM_START + i;
9206 if (EXPLODES_1X1_OLD(element))
9207 element_info[element].explosion_type = EXPLODES_1X1;
9209 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
9210 EXPLODES_SMASHED(element) ||
9211 EXPLODES_IMPACT(element)));
9215 /* correct previously hard-coded move delay values for maze runner style */
9216 if (level->game_version < VERSION_IDENT(3,1,1,0))
9218 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9220 int element = EL_CUSTOM_START + i;
9222 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
9224 /* previously hard-coded and therefore ignored */
9225 element_info[element].move_delay_fixed = 9;
9226 element_info[element].move_delay_random = 0;
9231 /* map elements that have changed in newer versions */
9232 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
9233 level->game_version);
9234 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
9235 for (x = 0; x < 3; x++)
9236 for (y = 0; y < 3; y++)
9237 level->yamyam_content[i].e[x][y] =
9238 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
9239 level->game_version);
9241 /* initialize element properties for level editor etc. */
9242 InitElementPropertiesEngine(level->game_version);
9243 InitElementPropertiesAfterLoading(level->game_version);
9244 InitElementPropertiesGfxElement();
9247 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
9251 /* map elements that have changed in newer versions */
9252 for (y = 0; y < level->fieldy; y++)
9253 for (x = 0; x < level->fieldx; x++)
9254 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
9255 level->game_version);
9257 /* copy elements to runtime playfield array */
9258 for (x = 0; x < MAX_LEV_FIELDX; x++)
9259 for (y = 0; y < MAX_LEV_FIELDY; y++)
9260 Feld[x][y] = level->field[x][y];
9262 /* initialize level size variables for faster access */
9263 lev_fieldx = level->fieldx;
9264 lev_fieldy = level->fieldy;
9266 /* determine border element for this level */
9267 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
9268 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
9273 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
9275 struct LevelFileInfo *level_file_info = &level->file_info;
9277 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
9278 CopyNativeLevel_RND_to_Native(level);
9281 void LoadLevelTemplate(int nr)
9285 setLevelFileInfo(&level_template.file_info, nr);
9286 filename = level_template.file_info.filename;
9288 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
9290 LoadLevel_InitVersion(&level_template, filename);
9291 LoadLevel_InitElements(&level_template, filename);
9293 ActivateLevelTemplate();
9296 void LoadLevel(int nr)
9300 setLevelFileInfo(&level.file_info, nr);
9301 filename = level.file_info.filename;
9303 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
9305 if (level.use_custom_template)
9306 LoadLevelTemplate(-1);
9308 LoadLevel_InitVersion(&level, filename);
9309 LoadLevel_InitElements(&level, filename);
9310 LoadLevel_InitPlayfield(&level, filename);
9312 LoadLevel_InitNativeEngines(&level, filename);
9315 void LoadLevelInfoOnly(int nr)
9321 setLevelFileInfo(&level.file_info, nr);
9323 filename = level.file_info.filename;
9326 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
9329 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
9333 chunk_size += putFileVersion(file, level->file_version);
9334 chunk_size += putFileVersion(file, level->game_version);
9339 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
9343 chunk_size += putFile16BitBE(file, level->creation_date.year);
9344 chunk_size += putFile8Bit(file, level->creation_date.month);
9345 chunk_size += putFile8Bit(file, level->creation_date.day);
9351 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
9355 putFile8Bit(file, level->fieldx);
9356 putFile8Bit(file, level->fieldy);
9358 putFile16BitBE(file, level->time);
9359 putFile16BitBE(file, level->gems_needed);
9361 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
9362 putFile8Bit(file, level->name[i]);
9364 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
9365 putFile8Bit(file, level->score[i]);
9367 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
9368 for (y = 0; y < 3; y++)
9369 for (x = 0; x < 3; x++)
9370 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
9371 level->yamyam_content[i].e[x][y]));
9372 putFile8Bit(file, level->amoeba_speed);
9373 putFile8Bit(file, level->time_magic_wall);
9374 putFile8Bit(file, level->time_wheel);
9375 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
9376 level->amoeba_content));
9377 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
9378 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
9379 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
9380 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
9382 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
9384 putFile8Bit(file, (level->block_last_field ? 1 : 0));
9385 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
9386 putFile32BitBE(file, level->can_move_into_acid_bits);
9387 putFile8Bit(file, level->dont_collide_with_bits);
9389 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
9390 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
9392 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
9393 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
9394 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
9396 putFile8Bit(file, level->game_engine_type);
9398 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
9402 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
9407 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
9408 chunk_size += putFile8Bit(file, level->name[i]);
9413 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
9418 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
9419 chunk_size += putFile8Bit(file, level->author[i]);
9425 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
9430 for (y = 0; y < level->fieldy; y++)
9431 for (x = 0; x < level->fieldx; x++)
9432 if (level->encoding_16bit_field)
9433 chunk_size += putFile16BitBE(file, level->field[x][y]);
9435 chunk_size += putFile8Bit(file, level->field[x][y]);
9441 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
9446 for (y = 0; y < level->fieldy; y++)
9447 for (x = 0; x < level->fieldx; x++)
9448 chunk_size += putFile16BitBE(file, level->field[x][y]);
9454 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
9458 putFile8Bit(file, EL_YAMYAM);
9459 putFile8Bit(file, level->num_yamyam_contents);
9460 putFile8Bit(file, 0);
9461 putFile8Bit(file, 0);
9463 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
9464 for (y = 0; y < 3; y++)
9465 for (x = 0; x < 3; x++)
9466 if (level->encoding_16bit_field)
9467 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
9469 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
9474 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
9477 int num_contents, content_xsize, content_ysize;
9478 int content_array[MAX_ELEMENT_CONTENTS][3][3];
9480 if (element == EL_YAMYAM)
9482 num_contents = level->num_yamyam_contents;
9486 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
9487 for (y = 0; y < 3; y++)
9488 for (x = 0; x < 3; x++)
9489 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
9491 else if (element == EL_BD_AMOEBA)
9497 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
9498 for (y = 0; y < 3; y++)
9499 for (x = 0; x < 3; x++)
9500 content_array[i][x][y] = EL_EMPTY;
9501 content_array[0][0][0] = level->amoeba_content;
9505 /* chunk header already written -- write empty chunk data */
9506 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
9508 Error(ERR_WARN, "cannot save content for element '%d'", element);
9512 putFile16BitBE(file, element);
9513 putFile8Bit(file, num_contents);
9514 putFile8Bit(file, content_xsize);
9515 putFile8Bit(file, content_ysize);
9517 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
9519 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
9520 for (y = 0; y < 3; y++)
9521 for (x = 0; x < 3; x++)
9522 putFile16BitBE(file, content_array[i][x][y]);
9527 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
9529 int envelope_nr = element - EL_ENVELOPE_1;
9530 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
9534 chunk_size += putFile16BitBE(file, element);
9535 chunk_size += putFile16BitBE(file, envelope_len);
9536 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
9537 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
9539 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
9540 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
9542 for (i = 0; i < envelope_len; i++)
9543 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
9550 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
9551 int num_changed_custom_elements)
9555 putFile16BitBE(file, num_changed_custom_elements);
9557 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9559 int element = EL_CUSTOM_START + i;
9561 struct ElementInfo *ei = &element_info[element];
9563 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
9565 if (check < num_changed_custom_elements)
9567 putFile16BitBE(file, element);
9568 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
9575 if (check != num_changed_custom_elements) /* should not happen */
9576 Error(ERR_WARN, "inconsistent number of custom element properties");
9581 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
9582 int num_changed_custom_elements)
9586 putFile16BitBE(file, num_changed_custom_elements);
9588 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9590 int element = EL_CUSTOM_START + i;
9592 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
9594 if (check < num_changed_custom_elements)
9596 putFile16BitBE(file, element);
9597 putFile16BitBE(file, element_info[element].change->target_element);
9604 if (check != num_changed_custom_elements) /* should not happen */
9605 Error(ERR_WARN, "inconsistent number of custom target elements");
9610 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
9611 int num_changed_custom_elements)
9613 int i, j, x, y, check = 0;
9615 putFile16BitBE(file, num_changed_custom_elements);
9617 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9619 int element = EL_CUSTOM_START + i;
9620 struct ElementInfo *ei = &element_info[element];
9622 if (ei->modified_settings)
9624 if (check < num_changed_custom_elements)
9626 putFile16BitBE(file, element);
9628 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
9629 putFile8Bit(file, ei->description[j]);
9631 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
9633 /* some free bytes for future properties and padding */
9634 WriteUnusedBytesToFile(file, 7);
9636 putFile8Bit(file, ei->use_gfx_element);
9637 putFile16BitBE(file, ei->gfx_element_initial);
9639 putFile8Bit(file, ei->collect_score_initial);
9640 putFile8Bit(file, ei->collect_count_initial);
9642 putFile16BitBE(file, ei->push_delay_fixed);
9643 putFile16BitBE(file, ei->push_delay_random);
9644 putFile16BitBE(file, ei->move_delay_fixed);
9645 putFile16BitBE(file, ei->move_delay_random);
9647 putFile16BitBE(file, ei->move_pattern);
9648 putFile8Bit(file, ei->move_direction_initial);
9649 putFile8Bit(file, ei->move_stepsize);
9651 for (y = 0; y < 3; y++)
9652 for (x = 0; x < 3; x++)
9653 putFile16BitBE(file, ei->content.e[x][y]);
9655 putFile32BitBE(file, ei->change->events);
9657 putFile16BitBE(file, ei->change->target_element);
9659 putFile16BitBE(file, ei->change->delay_fixed);
9660 putFile16BitBE(file, ei->change->delay_random);
9661 putFile16BitBE(file, ei->change->delay_frames);
9663 putFile16BitBE(file, ei->change->initial_trigger_element);
9665 putFile8Bit(file, ei->change->explode);
9666 putFile8Bit(file, ei->change->use_target_content);
9667 putFile8Bit(file, ei->change->only_if_complete);
9668 putFile8Bit(file, ei->change->use_random_replace);
9670 putFile8Bit(file, ei->change->random_percentage);
9671 putFile8Bit(file, ei->change->replace_when);
9673 for (y = 0; y < 3; y++)
9674 for (x = 0; x < 3; x++)
9675 putFile16BitBE(file, ei->change->content.e[x][y]);
9677 putFile8Bit(file, ei->slippery_type);
9679 /* some free bytes for future properties and padding */
9680 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
9687 if (check != num_changed_custom_elements) /* should not happen */
9688 Error(ERR_WARN, "inconsistent number of custom element properties");
9693 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
9695 struct ElementInfo *ei = &element_info[element];
9698 /* ---------- custom element base property values (96 bytes) ------------- */
9700 putFile16BitBE(file, element);
9702 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
9703 putFile8Bit(file, ei->description[i]);
9705 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
9707 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
9709 putFile8Bit(file, ei->num_change_pages);
9711 putFile16BitBE(file, ei->ce_value_fixed_initial);
9712 putFile16BitBE(file, ei->ce_value_random_initial);
9713 putFile8Bit(file, ei->use_last_ce_value);
9715 putFile8Bit(file, ei->use_gfx_element);
9716 putFile16BitBE(file, ei->gfx_element_initial);
9718 putFile8Bit(file, ei->collect_score_initial);
9719 putFile8Bit(file, ei->collect_count_initial);
9721 putFile8Bit(file, ei->drop_delay_fixed);
9722 putFile8Bit(file, ei->push_delay_fixed);
9723 putFile8Bit(file, ei->drop_delay_random);
9724 putFile8Bit(file, ei->push_delay_random);
9725 putFile16BitBE(file, ei->move_delay_fixed);
9726 putFile16BitBE(file, ei->move_delay_random);
9728 /* bits 0 - 15 of "move_pattern" ... */
9729 putFile16BitBE(file, ei->move_pattern & 0xffff);
9730 putFile8Bit(file, ei->move_direction_initial);
9731 putFile8Bit(file, ei->move_stepsize);
9733 putFile8Bit(file, ei->slippery_type);
9735 for (y = 0; y < 3; y++)
9736 for (x = 0; x < 3; x++)
9737 putFile16BitBE(file, ei->content.e[x][y]);
9739 putFile16BitBE(file, ei->move_enter_element);
9740 putFile16BitBE(file, ei->move_leave_element);
9741 putFile8Bit(file, ei->move_leave_type);
9743 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
9744 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
9746 putFile8Bit(file, ei->access_direction);
9748 putFile8Bit(file, ei->explosion_delay);
9749 putFile8Bit(file, ei->ignition_delay);
9750 putFile8Bit(file, ei->explosion_type);
9752 /* some free bytes for future custom property values and padding */
9753 WriteUnusedBytesToFile(file, 1);
9755 /* ---------- change page property values (48 bytes) --------------------- */
9757 for (i = 0; i < ei->num_change_pages; i++)
9759 struct ElementChangeInfo *change = &ei->change_page[i];
9760 unsigned int event_bits;
9762 /* bits 0 - 31 of "has_event[]" ... */
9764 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
9765 if (change->has_event[j])
9766 event_bits |= (1 << j);
9767 putFile32BitBE(file, event_bits);
9769 putFile16BitBE(file, change->target_element);
9771 putFile16BitBE(file, change->delay_fixed);
9772 putFile16BitBE(file, change->delay_random);
9773 putFile16BitBE(file, change->delay_frames);
9775 putFile16BitBE(file, change->initial_trigger_element);
9777 putFile8Bit(file, change->explode);
9778 putFile8Bit(file, change->use_target_content);
9779 putFile8Bit(file, change->only_if_complete);
9780 putFile8Bit(file, change->use_random_replace);
9782 putFile8Bit(file, change->random_percentage);
9783 putFile8Bit(file, change->replace_when);
9785 for (y = 0; y < 3; y++)
9786 for (x = 0; x < 3; x++)
9787 putFile16BitBE(file, change->target_content.e[x][y]);
9789 putFile8Bit(file, change->can_change);
9791 putFile8Bit(file, change->trigger_side);
9793 putFile8Bit(file, change->trigger_player);
9794 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
9795 log_2(change->trigger_page)));
9797 putFile8Bit(file, change->has_action);
9798 putFile8Bit(file, change->action_type);
9799 putFile8Bit(file, change->action_mode);
9800 putFile16BitBE(file, change->action_arg);
9802 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
9804 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
9805 if (change->has_event[j])
9806 event_bits |= (1 << (j - 32));
9807 putFile8Bit(file, event_bits);
9813 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
9815 struct ElementInfo *ei = &element_info[element];
9816 struct ElementGroupInfo *group = ei->group;
9819 putFile16BitBE(file, element);
9821 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
9822 putFile8Bit(file, ei->description[i]);
9824 putFile8Bit(file, group->num_elements);
9826 putFile8Bit(file, ei->use_gfx_element);
9827 putFile16BitBE(file, ei->gfx_element_initial);
9829 putFile8Bit(file, group->choice_mode);
9831 /* some free bytes for future values and padding */
9832 WriteUnusedBytesToFile(file, 3);
9834 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
9835 putFile16BitBE(file, group->element[i]);
9839 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
9840 boolean write_element)
9842 int save_type = entry->save_type;
9843 int data_type = entry->data_type;
9844 int conf_type = entry->conf_type;
9845 int byte_mask = conf_type & CONF_MASK_BYTES;
9846 int element = entry->element;
9847 int default_value = entry->default_value;
9849 boolean modified = FALSE;
9851 if (byte_mask != CONF_MASK_MULTI_BYTES)
9853 void *value_ptr = entry->value;
9854 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
9857 /* check if any settings have been modified before saving them */
9858 if (value != default_value)
9861 /* do not save if explicitly told or if unmodified default settings */
9862 if ((save_type == SAVE_CONF_NEVER) ||
9863 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
9867 num_bytes += putFile16BitBE(file, element);
9869 num_bytes += putFile8Bit(file, conf_type);
9870 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
9871 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
9872 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
9875 else if (data_type == TYPE_STRING)
9877 char *default_string = entry->default_string;
9878 char *string = (char *)(entry->value);
9879 int string_length = strlen(string);
9882 /* check if any settings have been modified before saving them */
9883 if (!strEqual(string, default_string))
9886 /* do not save if explicitly told or if unmodified default settings */
9887 if ((save_type == SAVE_CONF_NEVER) ||
9888 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
9892 num_bytes += putFile16BitBE(file, element);
9894 num_bytes += putFile8Bit(file, conf_type);
9895 num_bytes += putFile16BitBE(file, string_length);
9897 for (i = 0; i < string_length; i++)
9898 num_bytes += putFile8Bit(file, string[i]);
9900 else if (data_type == TYPE_ELEMENT_LIST)
9902 int *element_array = (int *)(entry->value);
9903 int num_elements = *(int *)(entry->num_entities);
9906 /* check if any settings have been modified before saving them */
9907 for (i = 0; i < num_elements; i++)
9908 if (element_array[i] != default_value)
9911 /* do not save if explicitly told or if unmodified default settings */
9912 if ((save_type == SAVE_CONF_NEVER) ||
9913 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
9917 num_bytes += putFile16BitBE(file, element);
9919 num_bytes += putFile8Bit(file, conf_type);
9920 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
9922 for (i = 0; i < num_elements; i++)
9923 num_bytes += putFile16BitBE(file, element_array[i]);
9925 else if (data_type == TYPE_CONTENT_LIST)
9927 struct Content *content = (struct Content *)(entry->value);
9928 int num_contents = *(int *)(entry->num_entities);
9931 /* check if any settings have been modified before saving them */
9932 for (i = 0; i < num_contents; i++)
9933 for (y = 0; y < 3; y++)
9934 for (x = 0; x < 3; x++)
9935 if (content[i].e[x][y] != default_value)
9938 /* do not save if explicitly told or if unmodified default settings */
9939 if ((save_type == SAVE_CONF_NEVER) ||
9940 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
9944 num_bytes += putFile16BitBE(file, element);
9946 num_bytes += putFile8Bit(file, conf_type);
9947 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
9949 for (i = 0; i < num_contents; i++)
9950 for (y = 0; y < 3; y++)
9951 for (x = 0; x < 3; x++)
9952 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
9958 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
9963 li = *level; /* copy level data into temporary buffer */
9965 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
9966 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
9971 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
9976 li = *level; /* copy level data into temporary buffer */
9978 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
9979 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
9984 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
9986 int envelope_nr = element - EL_ENVELOPE_1;
9990 chunk_size += putFile16BitBE(file, element);
9992 /* copy envelope data into temporary buffer */
9993 xx_envelope = level->envelope[envelope_nr];
9995 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
9996 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
10001 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
10003 struct ElementInfo *ei = &element_info[element];
10004 int chunk_size = 0;
10007 chunk_size += putFile16BitBE(file, element);
10009 xx_ei = *ei; /* copy element data into temporary buffer */
10011 /* set default description string for this specific element */
10012 strcpy(xx_default_description, getDefaultElementDescription(ei));
10015 /* set (fixed) number of content areas (may be wrong by broken level file) */
10016 /* (this is now directly corrected for broken level files after loading) */
10017 xx_num_contents = 1;
10020 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
10021 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
10023 for (i = 0; i < ei->num_change_pages; i++)
10025 struct ElementChangeInfo *change = &ei->change_page[i];
10027 xx_current_change_page = i;
10029 xx_change = *change; /* copy change data into temporary buffer */
10032 setEventBitsFromEventFlags(change);
10034 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
10035 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
10042 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
10044 struct ElementInfo *ei = &element_info[element];
10045 struct ElementGroupInfo *group = ei->group;
10046 int chunk_size = 0;
10049 chunk_size += putFile16BitBE(file, element);
10051 xx_ei = *ei; /* copy element data into temporary buffer */
10052 xx_group = *group; /* copy group data into temporary buffer */
10054 /* set default description string for this specific element */
10055 strcpy(xx_default_description, getDefaultElementDescription(ei));
10057 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
10058 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
10063 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
10069 if (!(file = fopen(filename, MODE_WRITE)))
10071 Error(ERR_WARN, "cannot save level file '%s'", filename);
10075 level->file_version = FILE_VERSION_ACTUAL;
10076 level->game_version = GAME_VERSION_ACTUAL;
10078 level->creation_date = getCurrentDate();
10080 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10081 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
10083 chunk_size = SaveLevel_VERS(NULL, level);
10084 putFileChunkBE(file, "VERS", chunk_size);
10085 SaveLevel_VERS(file, level);
10087 chunk_size = SaveLevel_DATE(NULL, level);
10088 putFileChunkBE(file, "DATE", chunk_size);
10089 SaveLevel_DATE(file, level);
10091 chunk_size = SaveLevel_NAME(NULL, level);
10092 putFileChunkBE(file, "NAME", chunk_size);
10093 SaveLevel_NAME(file, level);
10095 chunk_size = SaveLevel_AUTH(NULL, level);
10096 putFileChunkBE(file, "AUTH", chunk_size);
10097 SaveLevel_AUTH(file, level);
10099 chunk_size = SaveLevel_INFO(NULL, level);
10100 putFileChunkBE(file, "INFO", chunk_size);
10101 SaveLevel_INFO(file, level);
10103 chunk_size = SaveLevel_BODY(NULL, level);
10104 putFileChunkBE(file, "BODY", chunk_size);
10105 SaveLevel_BODY(file, level);
10107 chunk_size = SaveLevel_ELEM(NULL, level);
10108 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
10110 putFileChunkBE(file, "ELEM", chunk_size);
10111 SaveLevel_ELEM(file, level);
10114 for (i = 0; i < NUM_ENVELOPES; i++)
10116 int element = EL_ENVELOPE_1 + i;
10118 chunk_size = SaveLevel_NOTE(NULL, level, element);
10119 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
10121 putFileChunkBE(file, "NOTE", chunk_size);
10122 SaveLevel_NOTE(file, level, element);
10126 /* if not using template level, check for non-default custom/group elements */
10127 if (!level->use_custom_template)
10129 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10131 int element = EL_CUSTOM_START + i;
10133 chunk_size = SaveLevel_CUSX(NULL, level, element);
10134 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
10136 putFileChunkBE(file, "CUSX", chunk_size);
10137 SaveLevel_CUSX(file, level, element);
10141 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10143 int element = EL_GROUP_START + i;
10145 chunk_size = SaveLevel_GRPX(NULL, level, element);
10146 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
10148 putFileChunkBE(file, "GRPX", chunk_size);
10149 SaveLevel_GRPX(file, level, element);
10156 SetFilePermissions(filename, PERMS_PRIVATE);
10159 void SaveLevel(int nr)
10161 char *filename = getDefaultLevelFilename(nr);
10163 SaveLevelFromFilename(&level, filename);
10166 void SaveLevelTemplate()
10168 char *filename = getDefaultLevelFilename(-1);
10170 SaveLevelFromFilename(&level, filename);
10173 boolean SaveLevelChecked(int nr)
10175 char *filename = getDefaultLevelFilename(nr);
10176 boolean new_level = !fileExists(filename);
10177 boolean level_saved = FALSE;
10179 if (new_level || Request("Save this level and kill the old ?", REQ_ASK))
10184 Request("Level saved !", REQ_CONFIRM);
10186 level_saved = TRUE;
10189 return level_saved;
10192 void DumpLevel(struct LevelInfo *level)
10194 if (level->no_valid_file)
10196 Error(ERR_WARN, "cannot dump -- no valid level file found");
10201 printf_line("-", 79);
10202 printf("Level xxx (file version %08d, game version %08d)\n",
10203 level->file_version, level->game_version);
10204 printf_line("-", 79);
10206 printf("Level author: '%s'\n", level->author);
10207 printf("Level title: '%s'\n", level->name);
10209 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
10211 printf("Level time: %d seconds\n", level->time);
10212 printf("Gems needed: %d\n", level->gems_needed);
10214 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
10215 printf("Time for wheel: %d seconds\n", level->time_wheel);
10216 printf("Time for light: %d seconds\n", level->time_light);
10217 printf("Time for timegate: %d seconds\n", level->time_timegate);
10219 printf("Amoeba speed: %d\n", level->amoeba_speed);
10222 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
10223 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
10224 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
10225 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
10226 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
10228 printf_line("-", 79);
10232 /* ========================================================================= */
10233 /* tape file functions */
10234 /* ========================================================================= */
10236 static void setTapeInfoToDefaults()
10240 /* always start with reliable default values (empty tape) */
10243 /* default values (also for pre-1.2 tapes) with only the first player */
10244 tape.player_participates[0] = TRUE;
10245 for (i = 1; i < MAX_PLAYERS; i++)
10246 tape.player_participates[i] = FALSE;
10248 /* at least one (default: the first) player participates in every tape */
10249 tape.num_participating_players = 1;
10251 tape.level_nr = level_nr;
10253 tape.changed = FALSE;
10255 tape.recording = FALSE;
10256 tape.playing = FALSE;
10257 tape.pausing = FALSE;
10259 tape.no_valid_file = FALSE;
10264 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
10266 tape->file_version = getFileVersion(file);
10267 tape->game_version = getFileVersion(file);
10272 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
10276 tape->random_seed = getFile32BitBE(file);
10277 tape->date = getFile32BitBE(file);
10278 tape->length = getFile32BitBE(file);
10280 /* read header fields that are new since version 1.2 */
10281 if (tape->file_version >= FILE_VERSION_1_2)
10283 byte store_participating_players = getFile8Bit(file);
10284 int engine_version;
10286 /* since version 1.2, tapes store which players participate in the tape */
10287 tape->num_participating_players = 0;
10288 for (i = 0; i < MAX_PLAYERS; i++)
10290 tape->player_participates[i] = FALSE;
10292 if (store_participating_players & (1 << i))
10294 tape->player_participates[i] = TRUE;
10295 tape->num_participating_players++;
10299 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
10301 engine_version = getFileVersion(file);
10302 if (engine_version > 0)
10303 tape->engine_version = engine_version;
10305 tape->engine_version = tape->game_version;
10311 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
10313 int level_identifier_size;
10316 level_identifier_size = getFile16BitBE(file);
10318 tape->level_identifier =
10319 checked_realloc(tape->level_identifier, level_identifier_size);
10321 for (i = 0; i < level_identifier_size; i++)
10322 tape->level_identifier[i] = getFile8Bit(file);
10324 tape->level_nr = getFile16BitBE(file);
10326 chunk_size = 2 + level_identifier_size + 2;
10331 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
10334 int chunk_size_expected =
10335 (tape->num_participating_players + 1) * tape->length;
10337 if (chunk_size_expected != chunk_size)
10339 ReadUnusedBytesFromFile(file, chunk_size);
10340 return chunk_size_expected;
10343 for (i = 0; i < tape->length; i++)
10345 if (i >= MAX_TAPE_LEN)
10348 for (j = 0; j < MAX_PLAYERS; j++)
10350 tape->pos[i].action[j] = MV_NONE;
10352 if (tape->player_participates[j])
10353 tape->pos[i].action[j] = getFile8Bit(file);
10356 tape->pos[i].delay = getFile8Bit(file);
10358 if (tape->file_version == FILE_VERSION_1_0)
10360 /* eliminate possible diagonal moves in old tapes */
10361 /* this is only for backward compatibility */
10363 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
10364 byte action = tape->pos[i].action[0];
10365 int k, num_moves = 0;
10367 for (k = 0; k<4; k++)
10369 if (action & joy_dir[k])
10371 tape->pos[i + num_moves].action[0] = joy_dir[k];
10373 tape->pos[i + num_moves].delay = 0;
10382 tape->length += num_moves;
10385 else if (tape->file_version < FILE_VERSION_2_0)
10387 /* convert pre-2.0 tapes to new tape format */
10389 if (tape->pos[i].delay > 1)
10392 tape->pos[i + 1] = tape->pos[i];
10393 tape->pos[i + 1].delay = 1;
10396 for (j = 0; j < MAX_PLAYERS; j++)
10397 tape->pos[i].action[j] = MV_NONE;
10398 tape->pos[i].delay--;
10405 if (checkEndOfFile(file))
10409 if (i != tape->length)
10410 chunk_size = (tape->num_participating_players + 1) * i;
10417 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
10419 tape->file_version = getFileVersion(file);
10420 tape->game_version = getFileVersion(file);
10425 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
10429 tape->random_seed = getFile32BitBE(file);
10430 tape->date = getFile32BitBE(file);
10431 tape->length = getFile32BitBE(file);
10433 /* read header fields that are new since version 1.2 */
10434 if (tape->file_version >= FILE_VERSION_1_2)
10436 byte store_participating_players = getFile8Bit(file);
10437 int engine_version;
10439 /* since version 1.2, tapes store which players participate in the tape */
10440 tape->num_participating_players = 0;
10441 for (i = 0; i < MAX_PLAYERS; i++)
10443 tape->player_participates[i] = FALSE;
10445 if (store_participating_players & (1 << i))
10447 tape->player_participates[i] = TRUE;
10448 tape->num_participating_players++;
10452 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
10454 engine_version = getFileVersion(file);
10455 if (engine_version > 0)
10456 tape->engine_version = engine_version;
10458 tape->engine_version = tape->game_version;
10464 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
10466 int level_identifier_size;
10469 level_identifier_size = getFile16BitBE(file);
10471 tape->level_identifier =
10472 checked_realloc(tape->level_identifier, level_identifier_size);
10474 for (i = 0; i < level_identifier_size; i++)
10475 tape->level_identifier[i] = getFile8Bit(file);
10477 tape->level_nr = getFile16BitBE(file);
10479 chunk_size = 2 + level_identifier_size + 2;
10484 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
10487 int chunk_size_expected =
10488 (tape->num_participating_players + 1) * tape->length;
10490 if (chunk_size_expected != chunk_size)
10492 ReadUnusedBytesFromFile(file, chunk_size);
10493 return chunk_size_expected;
10496 for (i = 0; i < tape->length; i++)
10498 if (i >= MAX_TAPE_LEN)
10501 for (j = 0; j < MAX_PLAYERS; j++)
10503 tape->pos[i].action[j] = MV_NONE;
10505 if (tape->player_participates[j])
10506 tape->pos[i].action[j] = getFile8Bit(file);
10509 tape->pos[i].delay = getFile8Bit(file);
10511 if (tape->file_version == FILE_VERSION_1_0)
10513 /* eliminate possible diagonal moves in old tapes */
10514 /* this is only for backward compatibility */
10516 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
10517 byte action = tape->pos[i].action[0];
10518 int k, num_moves = 0;
10520 for (k = 0; k<4; k++)
10522 if (action & joy_dir[k])
10524 tape->pos[i + num_moves].action[0] = joy_dir[k];
10526 tape->pos[i + num_moves].delay = 0;
10535 tape->length += num_moves;
10538 else if (tape->file_version < FILE_VERSION_2_0)
10540 /* convert pre-2.0 tapes to new tape format */
10542 if (tape->pos[i].delay > 1)
10545 tape->pos[i + 1] = tape->pos[i];
10546 tape->pos[i + 1].delay = 1;
10549 for (j = 0; j < MAX_PLAYERS; j++)
10550 tape->pos[i].action[j] = MV_NONE;
10551 tape->pos[i].delay--;
10562 if (i != tape->length)
10563 chunk_size = (tape->num_participating_players + 1) * i;
10572 void LoadTape_SokobanSolution(char *filename)
10575 int move_delay = TILESIZE / level.initial_player_stepsize[0];
10577 if (!(file = openFile(filename, MODE_READ)))
10579 tape.no_valid_file = TRUE;
10584 while (!checkEndOfFile(file))
10586 unsigned char c = getByteFromFile(file);
10588 if (checkEndOfFile(file))
10595 tape.pos[tape.length].action[0] = MV_UP;
10596 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10602 tape.pos[tape.length].action[0] = MV_DOWN;
10603 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10609 tape.pos[tape.length].action[0] = MV_LEFT;
10610 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10616 tape.pos[tape.length].action[0] = MV_RIGHT;
10617 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10625 /* ignore white-space characters */
10629 tape.no_valid_file = TRUE;
10631 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
10639 if (tape.no_valid_file)
10642 tape.length_seconds = GetTapeLength();
10647 void LoadTape_SokobanSolution(char *filename)
10650 int move_delay = TILESIZE / level.initial_player_stepsize[0];
10652 if (!(file = fopen(filename, MODE_READ)))
10654 tape.no_valid_file = TRUE;
10659 while (!feof(file))
10661 unsigned char c = fgetc(file);
10670 tape.pos[tape.length].action[0] = MV_UP;
10671 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10677 tape.pos[tape.length].action[0] = MV_DOWN;
10678 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10684 tape.pos[tape.length].action[0] = MV_LEFT;
10685 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10691 tape.pos[tape.length].action[0] = MV_RIGHT;
10692 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10700 /* ignore white-space characters */
10704 tape.no_valid_file = TRUE;
10706 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
10714 if (tape.no_valid_file)
10717 tape.length_seconds = GetTapeLength();
10724 void LoadTapeFromFilename(char *filename)
10726 char cookie[MAX_LINE_LEN];
10727 char chunk_name[CHUNK_ID_LEN + 1];
10731 /* always start with reliable default values */
10732 setTapeInfoToDefaults();
10734 if (strSuffix(filename, ".sln"))
10736 LoadTape_SokobanSolution(filename);
10741 if (!(file = openFile(filename, MODE_READ)))
10743 tape.no_valid_file = TRUE;
10748 getFileChunkBE(file, chunk_name, NULL);
10749 if (strEqual(chunk_name, "RND1"))
10751 getFile32BitBE(file); /* not used */
10753 getFileChunkBE(file, chunk_name, NULL);
10754 if (!strEqual(chunk_name, "TAPE"))
10756 tape.no_valid_file = TRUE;
10758 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
10765 else /* check for pre-2.0 file format with cookie string */
10767 strcpy(cookie, chunk_name);
10768 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10770 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10771 cookie[strlen(cookie) - 1] = '\0';
10773 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
10775 tape.no_valid_file = TRUE;
10777 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
10784 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
10786 tape.no_valid_file = TRUE;
10788 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
10795 /* pre-2.0 tape files have no game version, so use file version here */
10796 tape.game_version = tape.file_version;
10799 if (tape.file_version < FILE_VERSION_1_2)
10801 /* tape files from versions before 1.2.0 without chunk structure */
10802 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
10803 LoadTape_BODY(file, 2 * tape.length, &tape);
10811 int (*loader)(File *, int, struct TapeInfo *);
10815 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
10816 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
10817 { "INFO", -1, LoadTape_INFO },
10818 { "BODY", -1, LoadTape_BODY },
10822 while (getFileChunkBE(file, chunk_name, &chunk_size))
10826 while (chunk_info[i].name != NULL &&
10827 !strEqual(chunk_name, chunk_info[i].name))
10830 if (chunk_info[i].name == NULL)
10832 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
10833 chunk_name, filename);
10834 ReadUnusedBytesFromFile(file, chunk_size);
10836 else if (chunk_info[i].size != -1 &&
10837 chunk_info[i].size != chunk_size)
10839 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
10840 chunk_size, chunk_name, filename);
10841 ReadUnusedBytesFromFile(file, chunk_size);
10845 /* call function to load this tape chunk */
10846 int chunk_size_expected =
10847 (chunk_info[i].loader)(file, chunk_size, &tape);
10849 /* the size of some chunks cannot be checked before reading other
10850 chunks first (like "HEAD" and "BODY") that contain some header
10851 information, so check them here */
10852 if (chunk_size_expected != chunk_size)
10854 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
10855 chunk_size, chunk_name, filename);
10863 tape.length_seconds = GetTapeLength();
10866 printf("::: tape file version: %d\n", tape.file_version);
10867 printf("::: tape game version: %d\n", tape.game_version);
10868 printf("::: tape engine version: %d\n", tape.engine_version);
10874 void LoadTapeFromFilename(char *filename)
10876 char cookie[MAX_LINE_LEN];
10877 char chunk_name[CHUNK_ID_LEN + 1];
10881 /* always start with reliable default values */
10882 setTapeInfoToDefaults();
10884 if (strSuffix(filename, ".sln"))
10886 LoadTape_SokobanSolution(filename);
10891 if (!(file = fopen(filename, MODE_READ)))
10893 tape.no_valid_file = TRUE;
10898 getFileChunkBE(file, chunk_name, NULL);
10899 if (strEqual(chunk_name, "RND1"))
10901 getFile32BitBE(file); /* not used */
10903 getFileChunkBE(file, chunk_name, NULL);
10904 if (!strEqual(chunk_name, "TAPE"))
10906 tape.no_valid_file = TRUE;
10908 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
10913 else /* check for pre-2.0 file format with cookie string */
10915 strcpy(cookie, chunk_name);
10916 if (fgets(&cookie[4], MAX_LINE_LEN - 4, file) == NULL)
10918 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10919 cookie[strlen(cookie) - 1] = '\0';
10921 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
10923 tape.no_valid_file = TRUE;
10925 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
10930 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
10932 tape.no_valid_file = TRUE;
10934 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
10940 /* pre-2.0 tape files have no game version, so use file version here */
10941 tape.game_version = tape.file_version;
10944 if (tape.file_version < FILE_VERSION_1_2)
10946 /* tape files from versions before 1.2.0 without chunk structure */
10947 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
10948 LoadTape_BODY(file, 2 * tape.length, &tape);
10956 int (*loader)(File *, int, struct TapeInfo *);
10960 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
10961 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
10962 { "INFO", -1, LoadTape_INFO },
10963 { "BODY", -1, LoadTape_BODY },
10967 while (getFileChunkBE(file, chunk_name, &chunk_size))
10971 while (chunk_info[i].name != NULL &&
10972 !strEqual(chunk_name, chunk_info[i].name))
10975 if (chunk_info[i].name == NULL)
10977 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
10978 chunk_name, filename);
10979 ReadUnusedBytesFromFile(file, chunk_size);
10981 else if (chunk_info[i].size != -1 &&
10982 chunk_info[i].size != chunk_size)
10984 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
10985 chunk_size, chunk_name, filename);
10986 ReadUnusedBytesFromFile(file, chunk_size);
10990 /* call function to load this tape chunk */
10991 int chunk_size_expected =
10992 (chunk_info[i].loader)(file, chunk_size, &tape);
10994 /* the size of some chunks cannot be checked before reading other
10995 chunks first (like "HEAD" and "BODY") that contain some header
10996 information, so check them here */
10997 if (chunk_size_expected != chunk_size)
10999 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
11000 chunk_size, chunk_name, filename);
11008 tape.length_seconds = GetTapeLength();
11011 printf("::: tape file version: %d\n", tape.file_version);
11012 printf("::: tape game version: %d\n", tape.game_version);
11013 printf("::: tape engine version: %d\n", tape.engine_version);
11019 void LoadTape(int nr)
11021 char *filename = getTapeFilename(nr);
11023 LoadTapeFromFilename(filename);
11026 void LoadSolutionTape(int nr)
11028 char *filename = getSolutionTapeFilename(nr);
11030 LoadTapeFromFilename(filename);
11033 if (TAPE_IS_EMPTY(tape) &&
11034 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
11035 level.native_sp_level->demo.is_available)
11036 CopyNativeTape_SP_to_RND(&level);
11040 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
11042 putFileVersion(file, tape->file_version);
11043 putFileVersion(file, tape->game_version);
11046 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
11049 byte store_participating_players = 0;
11051 /* set bits for participating players for compact storage */
11052 for (i = 0; i < MAX_PLAYERS; i++)
11053 if (tape->player_participates[i])
11054 store_participating_players |= (1 << i);
11056 putFile32BitBE(file, tape->random_seed);
11057 putFile32BitBE(file, tape->date);
11058 putFile32BitBE(file, tape->length);
11060 putFile8Bit(file, store_participating_players);
11062 /* unused bytes not at the end here for 4-byte alignment of engine_version */
11063 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
11065 putFileVersion(file, tape->engine_version);
11068 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
11070 int level_identifier_size = strlen(tape->level_identifier) + 1;
11073 putFile16BitBE(file, level_identifier_size);
11075 for (i = 0; i < level_identifier_size; i++)
11076 putFile8Bit(file, tape->level_identifier[i]);
11078 putFile16BitBE(file, tape->level_nr);
11081 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
11085 for (i = 0; i < tape->length; i++)
11087 for (j = 0; j < MAX_PLAYERS; j++)
11088 if (tape->player_participates[j])
11089 putFile8Bit(file, tape->pos[i].action[j]);
11091 putFile8Bit(file, tape->pos[i].delay);
11095 void SaveTape(int nr)
11097 char *filename = getTapeFilename(nr);
11100 boolean new_tape = TRUE;
11102 int num_participating_players = 0;
11103 int info_chunk_size;
11104 int body_chunk_size;
11107 InitTapeDirectory(leveldir_current->subdir);
11110 /* if a tape still exists, ask to overwrite it */
11111 if (fileExists(filename))
11114 if (!Request("Replace old tape ?", REQ_ASK))
11119 if (!(file = fopen(filename, MODE_WRITE)))
11121 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
11125 tape.file_version = FILE_VERSION_ACTUAL;
11126 tape.game_version = GAME_VERSION_ACTUAL;
11128 /* count number of participating players */
11129 for (i = 0; i < MAX_PLAYERS; i++)
11130 if (tape.player_participates[i])
11131 num_participating_players++;
11133 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
11134 body_chunk_size = (num_participating_players + 1) * tape.length;
11136 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
11137 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
11139 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
11140 SaveTape_VERS(file, &tape);
11142 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
11143 SaveTape_HEAD(file, &tape);
11145 putFileChunkBE(file, "INFO", info_chunk_size);
11146 SaveTape_INFO(file, &tape);
11148 putFileChunkBE(file, "BODY", body_chunk_size);
11149 SaveTape_BODY(file, &tape);
11153 SetFilePermissions(filename, PERMS_PRIVATE);
11155 tape.changed = FALSE;
11159 Request("Tape saved !", REQ_CONFIRM);
11163 boolean SaveTapeChecked(int nr)
11165 char *filename = getTapeFilename(nr);
11166 boolean new_tape = !fileExists(filename);
11167 boolean tape_saved = FALSE;
11169 if (new_tape || Request("Replace old tape ?", REQ_ASK))
11174 Request("Tape saved !", REQ_CONFIRM);
11182 void DumpTape(struct TapeInfo *tape)
11184 int tape_frame_counter;
11187 if (tape->no_valid_file)
11189 Error(ERR_WARN, "cannot dump -- no valid tape file found");
11194 printf_line("-", 79);
11195 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
11196 tape->level_nr, tape->file_version, tape->game_version);
11197 printf(" (effective engine version %08d)\n",
11198 tape->engine_version);
11199 printf("Level series identifier: '%s'\n", tape->level_identifier);
11200 printf_line("-", 79);
11202 tape_frame_counter = 0;
11204 for (i = 0; i < tape->length; i++)
11206 if (i >= MAX_TAPE_LEN)
11209 printf("%04d: ", i);
11211 for (j = 0; j < MAX_PLAYERS; j++)
11213 if (tape->player_participates[j])
11215 int action = tape->pos[i].action[j];
11217 printf("%d:%02x ", j, action);
11218 printf("[%c%c%c%c|%c%c] - ",
11219 (action & JOY_LEFT ? '<' : ' '),
11220 (action & JOY_RIGHT ? '>' : ' '),
11221 (action & JOY_UP ? '^' : ' '),
11222 (action & JOY_DOWN ? 'v' : ' '),
11223 (action & JOY_BUTTON_1 ? '1' : ' '),
11224 (action & JOY_BUTTON_2 ? '2' : ' '));
11228 printf("(%03d) ", tape->pos[i].delay);
11229 printf("[%05d]\n", tape_frame_counter);
11231 tape_frame_counter += tape->pos[i].delay;
11234 printf_line("-", 79);
11238 /* ========================================================================= */
11239 /* score file functions */
11240 /* ========================================================================= */
11242 void LoadScore(int nr)
11245 char *filename = getScoreFilename(nr);
11246 char cookie[MAX_LINE_LEN];
11247 char line[MAX_LINE_LEN];
11251 /* always start with reliable default values */
11252 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
11254 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
11255 highscore[i].Score = 0;
11258 if (!(file = fopen(filename, MODE_READ)))
11261 /* check file identifier */
11262 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
11264 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
11265 cookie[strlen(cookie) - 1] = '\0';
11267 if (!checkCookieString(cookie, SCORE_COOKIE))
11269 Error(ERR_WARN, "unknown format of score file '%s'", filename);
11274 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
11276 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
11277 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
11278 if (fgets(line, MAX_LINE_LEN, file) == NULL)
11281 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
11282 line[strlen(line) - 1] = '\0';
11284 for (line_ptr = line; *line_ptr; line_ptr++)
11286 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
11288 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
11289 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
11298 void SaveScore(int nr)
11301 char *filename = getScoreFilename(nr);
11304 InitScoreDirectory(leveldir_current->subdir);
11306 if (!(file = fopen(filename, MODE_WRITE)))
11308 Error(ERR_WARN, "cannot save score for level %d", nr);
11312 fprintf(file, "%s\n\n", SCORE_COOKIE);
11314 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
11315 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
11319 SetFilePermissions(filename, PERMS_PUBLIC);
11323 /* ========================================================================= */
11324 /* setup file functions */
11325 /* ========================================================================= */
11327 #define TOKEN_STR_PLAYER_PREFIX "player_"
11330 #define SETUP_TOKEN_PLAYER_NAME 0
11331 #define SETUP_TOKEN_SOUND 1
11332 #define SETUP_TOKEN_SOUND_LOOPS 2
11333 #define SETUP_TOKEN_SOUND_MUSIC 3
11334 #define SETUP_TOKEN_SOUND_SIMPLE 4
11335 #define SETUP_TOKEN_TOONS 5
11336 #define SETUP_TOKEN_SCROLL_DELAY 6
11337 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
11338 #define SETUP_TOKEN_SOFT_SCROLLING 8
11339 #define SETUP_TOKEN_FADE_SCREENS 9
11340 #define SETUP_TOKEN_AUTORECORD 10
11341 #define SETUP_TOKEN_SHOW_TITLESCREEN 11
11342 #define SETUP_TOKEN_QUICK_DOORS 12
11343 #define SETUP_TOKEN_TEAM_MODE 13
11344 #define SETUP_TOKEN_HANDICAP 14
11345 #define SETUP_TOKEN_SKIP_LEVELS 15
11346 #define SETUP_TOKEN_TIME_LIMIT 16
11347 #define SETUP_TOKEN_FULLSCREEN 17
11348 #define SETUP_TOKEN_FULLSCREEN_MODE 18
11349 #define SETUP_TOKEN_ASK_ON_ESCAPE 19
11350 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 20
11351 #define SETUP_TOKEN_QUICK_SWITCH 21
11352 #define SETUP_TOKEN_INPUT_ON_FOCUS 22
11353 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 23
11354 #define SETUP_TOKEN_GAME_FRAME_DELAY 24
11355 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 25
11356 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 26
11357 #define SETUP_TOKEN_GRAPHICS_SET 27
11358 #define SETUP_TOKEN_SOUNDS_SET 28
11359 #define SETUP_TOKEN_MUSIC_SET 29
11360 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 30
11361 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 31
11362 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 32
11363 #define SETUP_TOKEN_VOLUME_SIMPLE 33
11364 #define SETUP_TOKEN_VOLUME_LOOPS 34
11365 #define SETUP_TOKEN_VOLUME_MUSIC 35
11367 #define NUM_GLOBAL_SETUP_TOKENS 36
11370 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
11371 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
11372 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
11373 #define SETUP_TOKEN_EDITOR_EL_MORE 3
11374 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 4
11375 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 5
11376 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 6
11377 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 7
11378 #define SETUP_TOKEN_EDITOR_EL_CHARS 8
11379 #define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS 9
11380 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 10
11381 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 11
11382 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 12
11383 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 13
11384 #define SETUP_TOKEN_EDITOR_EL_BY_GAME 14
11385 #define SETUP_TOKEN_EDITOR_EL_BY_TYPE 15
11386 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 16
11388 #define NUM_EDITOR_SETUP_TOKENS 17
11390 /* editor cascade setup */
11391 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
11392 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
11393 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
11394 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
11395 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
11396 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
11397 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
11398 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
11399 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
11400 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
11401 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
11402 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
11403 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
11404 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
11405 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
11407 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
11409 /* shortcut setup */
11410 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
11411 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
11412 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
11413 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
11414 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
11415 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
11416 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
11417 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
11418 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
11419 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
11420 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
11421 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
11422 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
11423 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
11424 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
11425 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
11426 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
11427 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
11428 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
11429 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
11430 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
11432 #define NUM_SHORTCUT_SETUP_TOKENS 21
11435 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
11436 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
11437 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
11438 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
11439 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
11440 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
11441 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
11442 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
11443 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
11444 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
11445 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
11446 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
11447 #define SETUP_TOKEN_PLAYER_KEY_UP 12
11448 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
11449 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
11450 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
11452 #define NUM_PLAYER_SETUP_TOKENS 16
11455 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
11456 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
11457 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
11459 #define NUM_SYSTEM_SETUP_TOKENS 3
11461 /* options setup */
11462 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
11464 #define NUM_OPTIONS_SETUP_TOKENS 1
11467 static struct SetupInfo si;
11468 static struct SetupEditorInfo sei;
11469 static struct SetupEditorCascadeInfo seci;
11470 static struct SetupShortcutInfo ssi;
11471 static struct SetupInputInfo sii;
11472 static struct SetupSystemInfo syi;
11473 static struct OptionInfo soi;
11475 static struct TokenInfo global_setup_tokens[] =
11477 { TYPE_STRING, &si.player_name, "player_name" },
11478 { TYPE_SWITCH, &si.sound, "sound" },
11479 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
11480 { TYPE_SWITCH, &si.sound_music, "background_music" },
11481 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
11482 { TYPE_SWITCH, &si.toons, "toons" },
11483 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
11484 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
11485 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
11486 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
11487 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
11488 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
11489 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
11490 { TYPE_SWITCH, &si.team_mode, "team_mode" },
11491 { TYPE_SWITCH, &si.handicap, "handicap" },
11492 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
11493 { TYPE_SWITCH, &si.time_limit, "time_limit" },
11494 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
11495 { TYPE_STRING, &si.fullscreen_mode, "fullscreen_mode" },
11496 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
11497 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
11498 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
11499 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
11500 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
11501 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
11502 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
11503 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
11504 { TYPE_STRING, &si.graphics_set, "graphics_set" },
11505 { TYPE_STRING, &si.sounds_set, "sounds_set" },
11506 { TYPE_STRING, &si.music_set, "music_set" },
11507 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
11508 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
11509 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
11510 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
11511 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
11512 { TYPE_INTEGER,&si.volume_music, "volume_music" },
11515 static boolean not_used = FALSE;
11516 static struct TokenInfo editor_setup_tokens[] =
11519 { TYPE_SWITCH, ¬_used, "editor.el_boulderdash" },
11520 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine" },
11521 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine_club" },
11522 { TYPE_SWITCH, ¬_used, "editor.el_more" },
11523 { TYPE_SWITCH, ¬_used, "editor.el_sokoban" },
11524 { TYPE_SWITCH, ¬_used, "editor.el_supaplex" },
11525 { TYPE_SWITCH, ¬_used, "editor.el_diamond_caves" },
11526 { TYPE_SWITCH, ¬_used, "editor.el_dx_boulderdash" },
11528 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
11529 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
11530 { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
11531 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
11532 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
11533 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
11534 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
11535 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
11537 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
11538 { TYPE_SWITCH, &sei.el_steel_chars, "editor.el_steel_chars" },
11539 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
11541 { TYPE_SWITCH, ¬_used, "editor.el_headlines" },
11543 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
11545 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
11546 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
11547 { TYPE_SWITCH, &sei.el_by_game, "editor.el_by_game" },
11548 { TYPE_SWITCH, &sei.el_by_type, "editor.el_by_type" },
11549 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
11552 static struct TokenInfo editor_cascade_setup_tokens[] =
11554 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
11555 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
11556 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
11557 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
11558 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
11559 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
11560 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
11561 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
11562 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
11563 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
11564 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
11565 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
11566 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
11567 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
11568 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
11571 static struct TokenInfo shortcut_setup_tokens[] =
11573 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
11574 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
11575 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
11576 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
11577 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
11578 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
11579 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
11580 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
11581 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
11582 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
11583 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
11584 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
11585 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
11586 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
11587 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
11588 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
11589 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
11590 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
11591 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
11592 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
11593 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
11596 static struct TokenInfo player_setup_tokens[] =
11598 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
11599 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
11600 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
11601 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
11602 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
11603 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
11604 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
11605 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
11606 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
11607 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
11608 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
11609 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
11610 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
11611 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
11612 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
11613 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
11616 static struct TokenInfo system_setup_tokens[] =
11618 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
11619 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
11620 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
11623 static struct TokenInfo options_setup_tokens[] =
11625 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
11628 static char *get_corrected_login_name(char *login_name)
11630 /* needed because player name must be a fixed length string */
11631 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
11633 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
11634 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
11636 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
11637 if (strchr(login_name_new, ' '))
11638 *strchr(login_name_new, ' ') = '\0';
11640 return login_name_new;
11643 static void setSetupInfoToDefaults(struct SetupInfo *si)
11647 si->player_name = get_corrected_login_name(getLoginName());
11650 si->sound_loops = TRUE;
11651 si->sound_music = TRUE;
11652 si->sound_simple = TRUE;
11654 si->scroll_delay = TRUE;
11655 si->scroll_delay_value = STD_SCROLL_DELAY;
11656 si->soft_scrolling = TRUE;
11657 si->fade_screens = TRUE;
11658 si->autorecord = TRUE;
11659 si->show_titlescreen = TRUE;
11660 si->quick_doors = FALSE;
11661 si->team_mode = FALSE;
11662 si->handicap = TRUE;
11663 si->skip_levels = TRUE;
11664 si->time_limit = TRUE;
11665 si->fullscreen = FALSE;
11666 si->fullscreen_mode = getStringCopy(DEFAULT_FULLSCREEN_MODE);
11667 si->ask_on_escape = TRUE;
11668 si->ask_on_escape_editor = TRUE;
11669 si->quick_switch = FALSE;
11670 si->input_on_focus = FALSE;
11671 si->prefer_aga_graphics = TRUE;
11672 si->game_frame_delay = GAME_FRAME_DELAY;
11673 si->sp_show_border_elements = FALSE;
11674 si->small_game_graphics = FALSE;
11676 si->graphics_set = getStringCopy(GFX_DEFAULT_SUBDIR);
11677 si->sounds_set = getStringCopy(SND_DEFAULT_SUBDIR);
11678 si->music_set = getStringCopy(MUS_DEFAULT_SUBDIR);
11679 si->override_level_graphics = FALSE;
11680 si->override_level_sounds = FALSE;
11681 si->override_level_music = FALSE;
11683 si->volume_simple = 100; /* percent */
11684 si->volume_loops = 100; /* percent */
11685 si->volume_music = 100; /* percent */
11687 si->editor.el_boulderdash = TRUE;
11688 si->editor.el_emerald_mine = TRUE;
11689 si->editor.el_emerald_mine_club = TRUE;
11690 si->editor.el_more = TRUE;
11691 si->editor.el_sokoban = TRUE;
11692 si->editor.el_supaplex = TRUE;
11693 si->editor.el_diamond_caves = TRUE;
11694 si->editor.el_dx_boulderdash = TRUE;
11695 si->editor.el_chars = TRUE;
11696 si->editor.el_steel_chars = TRUE;
11697 si->editor.el_custom = TRUE;
11699 si->editor.el_headlines = TRUE;
11700 si->editor.el_user_defined = FALSE;
11701 si->editor.el_dynamic = TRUE;
11703 si->editor.show_element_token = FALSE;
11705 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11706 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11707 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11709 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11710 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11711 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11712 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11713 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11715 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11716 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11717 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11718 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11719 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11720 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11722 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11723 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11724 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11726 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11727 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11728 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11729 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11731 for (i = 0; i < MAX_PLAYERS; i++)
11733 si->input[i].use_joystick = FALSE;
11734 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
11735 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11736 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11737 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11738 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11739 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11740 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11741 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11742 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11743 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11744 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11745 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11746 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11747 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11748 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11751 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11752 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11753 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11755 si->options.verbose = FALSE;
11757 #if defined(CREATE_SPECIAL_EDITION_RND_JUE)
11759 si->handicap = FALSE;
11760 si->fullscreen = TRUE;
11761 si->override_level_graphics = AUTO;
11762 si->override_level_sounds = AUTO;
11763 si->override_level_music = AUTO;
11767 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11769 si->editor_cascade.el_bd = TRUE;
11770 si->editor_cascade.el_em = TRUE;
11771 si->editor_cascade.el_emc = TRUE;
11772 si->editor_cascade.el_rnd = TRUE;
11773 si->editor_cascade.el_sb = TRUE;
11774 si->editor_cascade.el_sp = TRUE;
11775 si->editor_cascade.el_dc = TRUE;
11776 si->editor_cascade.el_dx = TRUE;
11778 si->editor_cascade.el_chars = FALSE;
11779 si->editor_cascade.el_steel_chars = FALSE;
11780 si->editor_cascade.el_ce = FALSE;
11781 si->editor_cascade.el_ge = FALSE;
11782 si->editor_cascade.el_ref = FALSE;
11783 si->editor_cascade.el_user = FALSE;
11784 si->editor_cascade.el_dynamic = FALSE;
11787 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
11791 if (!setup_file_hash)
11796 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
11797 setSetupInfo(global_setup_tokens, i,
11798 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
11802 sei = setup.editor;
11803 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
11804 setSetupInfo(editor_setup_tokens, i,
11805 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
11806 setup.editor = sei;
11808 /* shortcut setup */
11809 ssi = setup.shortcut;
11810 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
11811 setSetupInfo(shortcut_setup_tokens, i,
11812 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
11813 setup.shortcut = ssi;
11816 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11820 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11822 sii = setup.input[pnr];
11823 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
11825 char full_token[100];
11827 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11828 setSetupInfo(player_setup_tokens, i,
11829 getHashEntry(setup_file_hash, full_token));
11831 setup.input[pnr] = sii;
11835 syi = setup.system;
11836 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
11837 setSetupInfo(system_setup_tokens, i,
11838 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
11839 setup.system = syi;
11841 /* options setup */
11842 soi = setup.options;
11843 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
11844 setSetupInfo(options_setup_tokens, i,
11845 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
11846 setup.options = soi;
11849 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11853 if (!setup_file_hash)
11856 /* editor cascade setup */
11857 seci = setup.editor_cascade;
11858 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
11859 setSetupInfo(editor_cascade_setup_tokens, i,
11860 getHashEntry(setup_file_hash,
11861 editor_cascade_setup_tokens[i].text));
11862 setup.editor_cascade = seci;
11867 char *filename = getSetupFilename();
11868 SetupFileHash *setup_file_hash = NULL;
11870 /* always start with reliable default values */
11871 setSetupInfoToDefaults(&setup);
11873 setup_file_hash = loadSetupFileHash(filename);
11875 if (setup_file_hash)
11877 char *player_name_new;
11879 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
11880 decodeSetupFileHash(setup_file_hash);
11882 freeSetupFileHash(setup_file_hash);
11884 /* needed to work around problems with fixed length strings */
11885 player_name_new = get_corrected_login_name(setup.player_name);
11886 free(setup.player_name);
11887 setup.player_name = player_name_new;
11889 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
11890 if (setup.scroll_delay == FALSE)
11892 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11893 setup.scroll_delay = TRUE; /* now always "on" */
11896 /* make sure that scroll delay value stays inside valid range */
11897 setup.scroll_delay_value =
11898 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11901 Error(ERR_WARN, "using default setup values");
11904 void LoadSetup_EditorCascade()
11906 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11907 SetupFileHash *setup_file_hash = NULL;
11909 /* always start with reliable default values */
11910 setSetupInfoToDefaults_EditorCascade(&setup);
11912 setup_file_hash = loadSetupFileHash(filename);
11914 if (setup_file_hash)
11916 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
11917 decodeSetupFileHash_EditorCascade(setup_file_hash);
11919 freeSetupFileHash(setup_file_hash);
11927 char *filename = getSetupFilename();
11931 InitUserDataDirectory();
11933 if (!(file = fopen(filename, MODE_WRITE)))
11935 Error(ERR_WARN, "cannot write setup file '%s'", filename);
11939 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
11940 getCookie("SETUP")));
11941 fprintf(file, "\n");
11945 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
11947 /* just to make things nicer :) */
11948 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
11949 i == SETUP_TOKEN_GRAPHICS_SET ||
11950 i == SETUP_TOKEN_VOLUME_SIMPLE)
11951 fprintf(file, "\n");
11953 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11957 sei = setup.editor;
11958 fprintf(file, "\n");
11959 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
11960 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11962 /* shortcut setup */
11963 ssi = setup.shortcut;
11964 fprintf(file, "\n");
11965 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
11966 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11969 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11973 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11974 fprintf(file, "\n");
11976 sii = setup.input[pnr];
11977 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
11978 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11982 syi = setup.system;
11983 fprintf(file, "\n");
11984 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
11985 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11987 /* options setup */
11988 soi = setup.options;
11989 fprintf(file, "\n");
11990 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
11991 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11995 SetFilePermissions(filename, PERMS_PRIVATE);
11998 void SaveSetup_EditorCascade()
12000 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12004 InitUserDataDirectory();
12006 if (!(file = fopen(filename, MODE_WRITE)))
12008 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
12013 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
12014 getCookie("SETUP")));
12015 fprintf(file, "\n");
12017 seci = setup.editor_cascade;
12018 fprintf(file, "\n");
12019 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
12020 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12024 SetFilePermissions(filename, PERMS_PRIVATE);
12029 void LoadCustomElementDescriptions()
12031 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12032 SetupFileHash *setup_file_hash;
12035 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12037 if (element_info[i].custom_description != NULL)
12039 free(element_info[i].custom_description);
12040 element_info[i].custom_description = NULL;
12044 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12047 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12049 char *token = getStringCat2(element_info[i].token_name, ".name");
12050 char *value = getHashEntry(setup_file_hash, token);
12053 element_info[i].custom_description = getStringCopy(value);
12058 freeSetupFileHash(setup_file_hash);
12061 static int getElementFromToken(char *token)
12064 char *value = getHashEntry(element_token_hash, token);
12067 return atoi(value);
12071 /* !!! OPTIMIZE THIS BY USING HASH !!! */
12072 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
12073 if (strEqual(token, element_info[i].token_name))
12077 Error(ERR_WARN, "unknown element token '%s'", token);
12079 return EL_UNDEFINED;
12082 static int get_token_parameter_value(char *token, char *value_raw)
12086 if (token == NULL || value_raw == NULL)
12087 return ARG_UNDEFINED_VALUE;
12089 suffix = strrchr(token, '.');
12090 if (suffix == NULL)
12094 if (strEqual(suffix, ".element"))
12095 return getElementFromToken(value_raw);
12099 if (strncmp(suffix, ".font", 5) == 0)
12103 /* !!! OPTIMIZE THIS BY USING HASH !!! */
12104 for (i = 0; i < NUM_FONTS; i++)
12105 if (strEqual(value_raw, font_info[i].token_name))
12108 /* if font not found, use reliable default value */
12109 return FONT_INITIAL_1;
12113 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
12114 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12117 void InitMenuDesignSettings_Static()
12120 static SetupFileHash *image_config_hash = NULL;
12125 if (image_config_hash == NULL)
12127 image_config_hash = newSetupFileHash();
12129 for (i = 0; image_config[i].token != NULL; i++)
12130 setHashEntry(image_config_hash,
12131 image_config[i].token,
12132 image_config[i].value);
12137 /* always start with reliable default values from static default config */
12138 for (i = 0; image_config_vars[i].token != NULL; i++)
12140 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
12143 *image_config_vars[i].value =
12144 get_token_parameter_value(image_config_vars[i].token, value);
12151 /* always start with reliable default values from static default config */
12152 for (i = 0; image_config_vars[i].token != NULL; i++)
12153 for (j = 0; image_config[j].token != NULL; j++)
12154 if (strEqual(image_config_vars[i].token, image_config[j].token))
12155 *image_config_vars[i].value =
12156 get_token_parameter_value(image_config_vars[i].token,
12157 image_config[j].value);
12161 static void InitMenuDesignSettings_SpecialPreProcessing()
12165 /* the following initializes hierarchical values from static configuration */
12167 /* special case: initialize "ARG_DEFAULT" values in static default config */
12168 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
12169 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12170 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12171 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12172 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
12173 titlemessage_default.fade_mode = title_default.fade_mode;
12174 titlemessage_default.fade_delay = title_default.fade_delay;
12175 titlemessage_default.post_delay = title_default.post_delay;
12176 titlemessage_default.auto_delay = title_default.auto_delay;
12178 /* special case: initialize "ARG_DEFAULT" values in static default config */
12179 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
12180 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12182 titlemessage_initial[i] = titlemessage_initial_default;
12183 titlemessage[i] = titlemessage_default;
12186 /* special case: initialize "ARG_DEFAULT" values in static default config */
12187 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
12188 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12190 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12191 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12194 /* special case: initialize "ARG_DEFAULT" values in static default config */
12195 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
12196 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12198 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12199 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12200 if (i != GFX_SPECIAL_ARG_EDITOR) /* editor value already initialized */
12201 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12205 static void InitMenuDesignSettings_SpecialPostProcessing()
12207 /* special case: initialize later added SETUP list size from LEVELS value */
12208 if (menu.list_size[GAME_MODE_SETUP] == -1)
12209 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12212 static void LoadMenuDesignSettingsFromFilename(char *filename)
12214 static struct TitleMessageInfo tmi;
12215 static struct TokenInfo titlemessage_tokens[] =
12217 { TYPE_INTEGER, &tmi.x, ".x" },
12218 { TYPE_INTEGER, &tmi.y, ".y" },
12219 { TYPE_INTEGER, &tmi.width, ".width" },
12220 { TYPE_INTEGER, &tmi.height, ".height" },
12221 { TYPE_INTEGER, &tmi.chars, ".chars" },
12222 { TYPE_INTEGER, &tmi.lines, ".lines" },
12223 { TYPE_INTEGER, &tmi.align, ".align" },
12224 { TYPE_INTEGER, &tmi.valign, ".valign" },
12225 { TYPE_INTEGER, &tmi.font, ".font" },
12226 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12227 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12228 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12229 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12230 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12231 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12232 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12233 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12239 struct TitleMessageInfo *array;
12242 titlemessage_arrays[] =
12244 { titlemessage_initial, "[titlemessage_initial]" },
12245 { titlemessage, "[titlemessage]" },
12249 SetupFileHash *setup_file_hash;
12253 printf("LoadMenuDesignSettings from file '%s' ...\n", filename);
12256 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12259 /* the following initializes hierarchical values from dynamic configuration */
12261 /* special case: initialize with default values that may be overwritten */
12262 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
12263 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12265 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
12266 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
12267 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
12269 if (value_1 != NULL)
12270 menu.draw_xoffset[i] = get_integer_from_string(value_1);
12271 if (value_2 != NULL)
12272 menu.draw_yoffset[i] = get_integer_from_string(value_2);
12273 if (value_3 != NULL)
12274 menu.list_size[i] = get_integer_from_string(value_3);
12277 /* special case: initialize with default values that may be overwritten */
12278 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
12279 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12281 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
12282 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
12284 if (value_1 != NULL)
12285 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
12286 if (value_2 != NULL)
12287 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
12290 /* special case: initialize with default values that may be overwritten */
12291 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
12292 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12294 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
12295 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
12297 if (value_1 != NULL)
12298 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
12299 if (value_2 != NULL)
12300 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
12303 /* special case: initialize with default values that may be overwritten */
12304 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
12305 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12307 char *token_1 = "menu.enter_screen.fade_mode";
12308 char *token_2 = "menu.enter_screen.fade_delay";
12309 char *token_3 = "menu.enter_screen.post_delay";
12310 char *token_4 = "menu.leave_screen.fade_mode";
12311 char *token_5 = "menu.leave_screen.fade_delay";
12312 char *token_6 = "menu.leave_screen.post_delay";
12313 char *value_1 = getHashEntry(setup_file_hash, token_1);
12314 char *value_2 = getHashEntry(setup_file_hash, token_2);
12315 char *value_3 = getHashEntry(setup_file_hash, token_3);
12316 char *value_4 = getHashEntry(setup_file_hash, token_4);
12317 char *value_5 = getHashEntry(setup_file_hash, token_5);
12318 char *value_6 = getHashEntry(setup_file_hash, token_6);
12320 if (value_1 != NULL)
12321 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
12323 if (value_2 != NULL)
12324 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
12326 if (value_3 != NULL)
12327 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
12329 if (value_4 != NULL)
12330 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
12332 if (value_5 != NULL)
12333 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
12335 if (value_6 != NULL)
12336 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
12340 /* special case: initialize with default values that may be overwritten */
12341 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
12342 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12344 char *token_1 = "viewport.playfield.x";
12345 char *token_2 = "viewport.playfield.y";
12346 char *token_3 = "viewport.playfield.width";
12347 char *token_4 = "viewport.playfield.height";
12348 char *token_5 = "viewport.playfield.border_size";
12349 char *token_6 = "viewport.door_1.x";
12350 char *token_7 = "viewport.door_1.y";
12351 char *token_8 = "viewport.door_2.x";
12352 char *token_9 = "viewport.door_2.y";
12353 char *value_1 = getHashEntry(setup_file_hash, token_1);
12354 char *value_2 = getHashEntry(setup_file_hash, token_2);
12355 char *value_3 = getHashEntry(setup_file_hash, token_3);
12356 char *value_4 = getHashEntry(setup_file_hash, token_4);
12357 char *value_5 = getHashEntry(setup_file_hash, token_5);
12358 char *value_6 = getHashEntry(setup_file_hash, token_6);
12359 char *value_7 = getHashEntry(setup_file_hash, token_7);
12360 char *value_8 = getHashEntry(setup_file_hash, token_8);
12361 char *value_9 = getHashEntry(setup_file_hash, token_9);
12363 if (value_1 != NULL)
12364 viewport.playfield[i].x = get_token_parameter_value(token_1, value_1);
12365 if (value_2 != NULL)
12366 viewport.playfield[i].y = get_token_parameter_value(token_2, value_2);
12367 if (value_3 != NULL)
12368 viewport.playfield[i].width = get_token_parameter_value(token_3, value_3);
12369 if (value_4 != NULL)
12370 viewport.playfield[i].height = get_token_parameter_value(token_4,value_4);
12371 if (value_5 != NULL)
12372 viewport.playfield[i].border_size = get_token_parameter_value(token_5,
12374 if (value_6 != NULL)
12375 viewport.door_1[i].x = get_token_parameter_value(token_6, value_6);
12376 if (value_7 != NULL)
12377 viewport.door_1[i].y = get_token_parameter_value(token_7, value_7);
12378 if (value_8 != NULL)
12379 viewport.door_2[i].x = get_token_parameter_value(token_8, value_8);
12380 if (value_9 != NULL)
12381 viewport.door_2[i].y = get_token_parameter_value(token_9, value_9);
12384 /* special case: initialize with default values that may be overwritten */
12385 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
12386 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12388 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12389 char *base_token = titlemessage_arrays[i].text;
12391 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12393 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12394 char *value = getHashEntry(setup_file_hash, token);
12398 int parameter_value = get_token_parameter_value(token, value);
12400 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12404 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12405 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12407 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12417 /* read (and overwrite with) values that may be specified in config file */
12418 for (i = 0; image_config_vars[i].token != NULL; i++)
12420 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12422 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
12423 if (value != NULL && !strEqual(value, ARG_DEFAULT))
12424 *image_config_vars[i].value =
12425 get_token_parameter_value(image_config_vars[i].token, value);
12428 freeSetupFileHash(setup_file_hash);
12431 void LoadMenuDesignSettings()
12433 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12435 InitMenuDesignSettings_Static();
12436 InitMenuDesignSettings_SpecialPreProcessing();
12439 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12441 if (!SETUP_OVERRIDE_ARTWORK(setup, ARTWORK_TYPE_GRAPHICS))
12444 /* first look for special settings configured in level series config */
12445 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12447 if (fileExists(filename_base))
12448 LoadMenuDesignSettingsFromFilename(filename_base);
12451 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12453 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12454 LoadMenuDesignSettingsFromFilename(filename_local);
12456 InitMenuDesignSettings_SpecialPostProcessing();
12459 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12461 char *filename = getEditorSetupFilename();
12462 SetupFileList *setup_file_list, *list;
12463 SetupFileHash *element_hash;
12464 int num_unknown_tokens = 0;
12467 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12470 element_hash = newSetupFileHash();
12472 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12473 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12475 /* determined size may be larger than needed (due to unknown elements) */
12477 for (list = setup_file_list; list != NULL; list = list->next)
12480 /* add space for up to 3 more elements for padding that may be needed */
12481 *num_elements += 3;
12483 /* free memory for old list of elements, if needed */
12484 checked_free(*elements);
12486 /* allocate memory for new list of elements */
12487 *elements = checked_malloc(*num_elements * sizeof(int));
12490 for (list = setup_file_list; list != NULL; list = list->next)
12492 char *value = getHashEntry(element_hash, list->token);
12494 if (value == NULL) /* try to find obsolete token mapping */
12496 char *mapped_token = get_mapped_token(list->token);
12498 if (mapped_token != NULL)
12500 value = getHashEntry(element_hash, mapped_token);
12502 free(mapped_token);
12508 (*elements)[(*num_elements)++] = atoi(value);
12512 if (num_unknown_tokens == 0)
12514 Error(ERR_INFO_LINE, "-");
12515 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
12516 Error(ERR_INFO, "- config file: '%s'", filename);
12518 num_unknown_tokens++;
12521 Error(ERR_INFO, "- token: '%s'", list->token);
12525 if (num_unknown_tokens > 0)
12526 Error(ERR_INFO_LINE, "-");
12528 while (*num_elements % 4) /* pad with empty elements, if needed */
12529 (*elements)[(*num_elements)++] = EL_EMPTY;
12531 freeSetupFileList(setup_file_list);
12532 freeSetupFileHash(element_hash);
12535 for (i = 0; i < *num_elements; i++)
12536 printf("editor: element '%s' [%d]\n",
12537 element_info[(*elements)[i]].token_name, (*elements)[i]);
12541 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12544 SetupFileHash *setup_file_hash = NULL;
12545 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12546 char *filename_music, *filename_prefix, *filename_info;
12552 token_to_value_ptr[] =
12554 { "title_header", &tmp_music_file_info.title_header },
12555 { "artist_header", &tmp_music_file_info.artist_header },
12556 { "album_header", &tmp_music_file_info.album_header },
12557 { "year_header", &tmp_music_file_info.year_header },
12559 { "title", &tmp_music_file_info.title },
12560 { "artist", &tmp_music_file_info.artist },
12561 { "album", &tmp_music_file_info.album },
12562 { "year", &tmp_music_file_info.year },
12568 filename_music = (is_sound ? getCustomSoundFilename(basename) :
12569 getCustomMusicFilename(basename));
12571 if (filename_music == NULL)
12574 /* ---------- try to replace file extension ---------- */
12576 filename_prefix = getStringCopy(filename_music);
12577 if (strrchr(filename_prefix, '.') != NULL)
12578 *strrchr(filename_prefix, '.') = '\0';
12579 filename_info = getStringCat2(filename_prefix, ".txt");
12582 printf("trying to load file '%s'...\n", filename_info);
12585 if (fileExists(filename_info))
12586 setup_file_hash = loadSetupFileHash(filename_info);
12588 free(filename_prefix);
12589 free(filename_info);
12591 if (setup_file_hash == NULL)
12593 /* ---------- try to add file extension ---------- */
12595 filename_prefix = getStringCopy(filename_music);
12596 filename_info = getStringCat2(filename_prefix, ".txt");
12599 printf("trying to load file '%s'...\n", filename_info);
12602 if (fileExists(filename_info))
12603 setup_file_hash = loadSetupFileHash(filename_info);
12605 free(filename_prefix);
12606 free(filename_info);
12609 if (setup_file_hash == NULL)
12612 /* ---------- music file info found ---------- */
12614 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
12616 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
12618 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
12620 *token_to_value_ptr[i].value_ptr =
12621 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
12624 tmp_music_file_info.basename = getStringCopy(basename);
12625 tmp_music_file_info.music = music;
12626 tmp_music_file_info.is_sound = is_sound;
12628 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
12629 *new_music_file_info = tmp_music_file_info;
12631 return new_music_file_info;
12634 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
12636 return get_music_file_info_ext(basename, music, FALSE);
12639 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
12641 return get_music_file_info_ext(basename, sound, TRUE);
12644 static boolean music_info_listed_ext(struct MusicFileInfo *list,
12645 char *basename, boolean is_sound)
12647 for (; list != NULL; list = list->next)
12648 if (list->is_sound == is_sound && strEqual(list->basename, basename))
12654 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
12656 return music_info_listed_ext(list, basename, FALSE);
12659 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
12661 return music_info_listed_ext(list, basename, TRUE);
12666 void LoadMusicInfo()
12668 char *music_directory = getCustomMusicDirectory();
12669 int num_music = getMusicListSize();
12670 int num_music_noconf = 0;
12671 int num_sounds = getSoundListSize();
12673 DirectoryEntry *dir_entry;
12674 struct FileInfo *music, *sound;
12675 struct MusicFileInfo *next, **new;
12678 while (music_file_info != NULL)
12680 next = music_file_info->next;
12682 checked_free(music_file_info->basename);
12684 checked_free(music_file_info->title_header);
12685 checked_free(music_file_info->artist_header);
12686 checked_free(music_file_info->album_header);
12687 checked_free(music_file_info->year_header);
12689 checked_free(music_file_info->title);
12690 checked_free(music_file_info->artist);
12691 checked_free(music_file_info->album);
12692 checked_free(music_file_info->year);
12694 free(music_file_info);
12696 music_file_info = next;
12699 new = &music_file_info;
12701 for (i = 0; i < num_music; i++)
12703 music = getMusicListEntry(i);
12705 if (music->filename == NULL)
12708 if (strEqual(music->filename, UNDEFINED_FILENAME))
12711 /* a configured file may be not recognized as music */
12712 if (!FileIsMusic(music->filename))
12716 printf("::: -> '%s' (configured)\n", music->filename);
12719 if (!music_info_listed(music_file_info, music->filename))
12721 *new = get_music_file_info(music->filename, i);
12724 printf(":1: adding '%s' ['%s'] ...\n", (*new)->title, music->filename);
12727 new = &(*new)->next;
12731 if ((dir = openDirectory(music_directory)) == NULL)
12733 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
12737 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
12739 char *basename = dir_entry->basename;
12740 boolean music_already_used = FALSE;
12743 /* skip all music files that are configured in music config file */
12744 for (i = 0; i < num_music; i++)
12746 music = getMusicListEntry(i);
12748 if (music->filename == NULL)
12751 if (strEqual(basename, music->filename))
12753 music_already_used = TRUE;
12758 if (music_already_used)
12761 if (!FileIsMusic(basename))
12765 printf("::: -> '%s' (found in directory)\n", basename);
12768 if (!music_info_listed(music_file_info, basename))
12770 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12773 printf(":2: adding '%s' ['%s'] ...\n", (*new)->title, basename);
12776 new = &(*new)->next;
12779 num_music_noconf++;
12782 closeDirectory(dir);
12784 for (i = 0; i < num_sounds; i++)
12786 sound = getSoundListEntry(i);
12788 if (sound->filename == NULL)
12791 if (strEqual(sound->filename, UNDEFINED_FILENAME))
12794 /* a configured file may be not recognized as sound */
12795 if (!FileIsSound(sound->filename))
12799 printf("::: -> '%s' (configured)\n", sound->filename);
12802 if (!sound_info_listed(music_file_info, sound->filename))
12804 *new = get_sound_file_info(sound->filename, i);
12806 new = &(*new)->next;
12811 for (next = music_file_info; next != NULL; next = next->next)
12812 printf("::: title == '%s'\n", next->title);
12818 void LoadMusicInfo()
12820 char *music_directory = getCustomMusicDirectory();
12821 int num_music = getMusicListSize();
12822 int num_music_noconf = 0;
12823 int num_sounds = getSoundListSize();
12825 struct dirent *dir_entry;
12826 struct FileInfo *music, *sound;
12827 struct MusicFileInfo *next, **new;
12830 while (music_file_info != NULL)
12832 next = music_file_info->next;
12834 checked_free(music_file_info->basename);
12836 checked_free(music_file_info->title_header);
12837 checked_free(music_file_info->artist_header);
12838 checked_free(music_file_info->album_header);
12839 checked_free(music_file_info->year_header);
12841 checked_free(music_file_info->title);
12842 checked_free(music_file_info->artist);
12843 checked_free(music_file_info->album);
12844 checked_free(music_file_info->year);
12846 free(music_file_info);
12848 music_file_info = next;
12851 new = &music_file_info;
12853 for (i = 0; i < num_music; i++)
12855 music = getMusicListEntry(i);
12857 if (music->filename == NULL)
12860 if (strEqual(music->filename, UNDEFINED_FILENAME))
12863 /* a configured file may be not recognized as music */
12864 if (!FileIsMusic(music->filename))
12868 printf("::: -> '%s' (configured)\n", music->filename);
12871 if (!music_info_listed(music_file_info, music->filename))
12873 *new = get_music_file_info(music->filename, i);
12876 printf(":1: adding '%s' ['%s'] ...\n", (*new)->title, music->filename);
12879 new = &(*new)->next;
12883 if ((dir = opendir(music_directory)) == NULL)
12885 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
12889 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
12891 char *basename = dir_entry->d_name;
12892 boolean music_already_used = FALSE;
12895 /* skip all music files that are configured in music config file */
12896 for (i = 0; i < num_music; i++)
12898 music = getMusicListEntry(i);
12900 if (music->filename == NULL)
12903 if (strEqual(basename, music->filename))
12905 music_already_used = TRUE;
12910 if (music_already_used)
12913 if (!FileIsMusic(basename))
12917 printf("::: -> '%s' (found in directory)\n", basename);
12920 if (!music_info_listed(music_file_info, basename))
12922 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12925 printf(":2: adding '%s' ['%s'] ...\n", (*new)->title, basename);
12928 new = &(*new)->next;
12931 num_music_noconf++;
12936 for (i = 0; i < num_sounds; i++)
12938 sound = getSoundListEntry(i);
12940 if (sound->filename == NULL)
12943 if (strEqual(sound->filename, UNDEFINED_FILENAME))
12946 /* a configured file may be not recognized as sound */
12947 if (!FileIsSound(sound->filename))
12951 printf("::: -> '%s' (configured)\n", sound->filename);
12954 if (!sound_info_listed(music_file_info, sound->filename))
12956 *new = get_sound_file_info(sound->filename, i);
12958 new = &(*new)->next;
12963 for (next = music_file_info; next != NULL; next = next->next)
12964 printf("::: title == '%s'\n", next->title);
12970 void add_helpanim_entry(int element, int action, int direction, int delay,
12971 int *num_list_entries)
12973 struct HelpAnimInfo *new_list_entry;
12974 (*num_list_entries)++;
12977 checked_realloc(helpanim_info,
12978 *num_list_entries * sizeof(struct HelpAnimInfo));
12979 new_list_entry = &helpanim_info[*num_list_entries - 1];
12981 new_list_entry->element = element;
12982 new_list_entry->action = action;
12983 new_list_entry->direction = direction;
12984 new_list_entry->delay = delay;
12987 void print_unknown_token(char *filename, char *token, int token_nr)
12991 Error(ERR_INFO_LINE, "-");
12992 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
12993 Error(ERR_INFO, "- config file: '%s'", filename);
12996 Error(ERR_INFO, "- token: '%s'", token);
12999 void print_unknown_token_end(int token_nr)
13002 Error(ERR_INFO_LINE, "-");
13005 void LoadHelpAnimInfo()
13007 char *filename = getHelpAnimFilename();
13008 SetupFileList *setup_file_list = NULL, *list;
13009 SetupFileHash *element_hash, *action_hash, *direction_hash;
13010 int num_list_entries = 0;
13011 int num_unknown_tokens = 0;
13014 if (fileExists(filename))
13015 setup_file_list = loadSetupFileList(filename);
13017 if (setup_file_list == NULL)
13019 /* use reliable default values from static configuration */
13020 SetupFileList *insert_ptr;
13022 insert_ptr = setup_file_list =
13023 newSetupFileList(helpanim_config[0].token,
13024 helpanim_config[0].value);
13026 for (i = 1; helpanim_config[i].token; i++)
13027 insert_ptr = addListEntry(insert_ptr,
13028 helpanim_config[i].token,
13029 helpanim_config[i].value);
13032 element_hash = newSetupFileHash();
13033 action_hash = newSetupFileHash();
13034 direction_hash = newSetupFileHash();
13036 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13037 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13039 for (i = 0; i < NUM_ACTIONS; i++)
13040 setHashEntry(action_hash, element_action_info[i].suffix,
13041 i_to_a(element_action_info[i].value));
13043 /* do not store direction index (bit) here, but direction value! */
13044 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13045 setHashEntry(direction_hash, element_direction_info[i].suffix,
13046 i_to_a(1 << element_direction_info[i].value));
13048 for (list = setup_file_list; list != NULL; list = list->next)
13050 char *element_token, *action_token, *direction_token;
13051 char *element_value, *action_value, *direction_value;
13052 int delay = atoi(list->value);
13054 if (strEqual(list->token, "end"))
13056 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13061 /* first try to break element into element/action/direction parts;
13062 if this does not work, also accept combined "element[.act][.dir]"
13063 elements (like "dynamite.active"), which are unique elements */
13065 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
13067 element_value = getHashEntry(element_hash, list->token);
13068 if (element_value != NULL) /* element found */
13069 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13070 &num_list_entries);
13073 /* no further suffixes found -- this is not an element */
13074 print_unknown_token(filename, list->token, num_unknown_tokens++);
13080 /* token has format "<prefix>.<something>" */
13082 action_token = strchr(list->token, '.'); /* suffix may be action ... */
13083 direction_token = action_token; /* ... or direction */
13085 element_token = getStringCopy(list->token);
13086 *strchr(element_token, '.') = '\0';
13088 element_value = getHashEntry(element_hash, element_token);
13090 if (element_value == NULL) /* this is no element */
13092 element_value = getHashEntry(element_hash, list->token);
13093 if (element_value != NULL) /* combined element found */
13094 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13095 &num_list_entries);
13097 print_unknown_token(filename, list->token, num_unknown_tokens++);
13099 free(element_token);
13104 action_value = getHashEntry(action_hash, action_token);
13106 if (action_value != NULL) /* action found */
13108 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13109 &num_list_entries);
13111 free(element_token);
13116 direction_value = getHashEntry(direction_hash, direction_token);
13118 if (direction_value != NULL) /* direction found */
13120 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13121 &num_list_entries);
13123 free(element_token);
13128 if (strchr(action_token + 1, '.') == NULL)
13130 /* no further suffixes found -- this is not an action nor direction */
13132 element_value = getHashEntry(element_hash, list->token);
13133 if (element_value != NULL) /* combined element found */
13134 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13135 &num_list_entries);
13137 print_unknown_token(filename, list->token, num_unknown_tokens++);
13139 free(element_token);
13144 /* token has format "<prefix>.<suffix>.<something>" */
13146 direction_token = strchr(action_token + 1, '.');
13148 action_token = getStringCopy(action_token);
13149 *strchr(action_token + 1, '.') = '\0';
13151 action_value = getHashEntry(action_hash, action_token);
13153 if (action_value == NULL) /* this is no action */
13155 element_value = getHashEntry(element_hash, list->token);
13156 if (element_value != NULL) /* combined element found */
13157 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13158 &num_list_entries);
13160 print_unknown_token(filename, list->token, num_unknown_tokens++);
13162 free(element_token);
13163 free(action_token);
13168 direction_value = getHashEntry(direction_hash, direction_token);
13170 if (direction_value != NULL) /* direction found */
13172 add_helpanim_entry(atoi(element_value), atoi(action_value),
13173 atoi(direction_value), delay, &num_list_entries);
13175 free(element_token);
13176 free(action_token);
13181 /* this is no direction */
13183 element_value = getHashEntry(element_hash, list->token);
13184 if (element_value != NULL) /* combined element found */
13185 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13186 &num_list_entries);
13188 print_unknown_token(filename, list->token, num_unknown_tokens++);
13190 free(element_token);
13191 free(action_token);
13194 print_unknown_token_end(num_unknown_tokens);
13196 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13197 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13199 freeSetupFileList(setup_file_list);
13200 freeSetupFileHash(element_hash);
13201 freeSetupFileHash(action_hash);
13202 freeSetupFileHash(direction_hash);
13205 for (i = 0; i < num_list_entries; i++)
13206 printf("::: '%s': %d, %d, %d => %d\n",
13207 EL_NAME(helpanim_info[i].element),
13208 helpanim_info[i].element,
13209 helpanim_info[i].action,
13210 helpanim_info[i].direction,
13211 helpanim_info[i].delay);
13215 void LoadHelpTextInfo()
13217 char *filename = getHelpTextFilename();
13220 if (helptext_info != NULL)
13222 freeSetupFileHash(helptext_info);
13223 helptext_info = NULL;
13226 if (fileExists(filename))
13227 helptext_info = loadSetupFileHash(filename);
13229 if (helptext_info == NULL)
13231 /* use reliable default values from static configuration */
13232 helptext_info = newSetupFileHash();
13234 for (i = 0; helptext_config[i].token; i++)
13235 setHashEntry(helptext_info,
13236 helptext_config[i].token,
13237 helptext_config[i].value);
13241 BEGIN_HASH_ITERATION(helptext_info, itr)
13243 printf("::: '%s' => '%s'\n",
13244 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13246 END_HASH_ITERATION(hash, itr)
13251 /* ------------------------------------------------------------------------- */
13252 /* convert levels */
13253 /* ------------------------------------------------------------------------- */
13255 #define MAX_NUM_CONVERT_LEVELS 1000
13257 void ConvertLevels()
13259 static LevelDirTree *convert_leveldir = NULL;
13260 static int convert_level_nr = -1;
13261 static int num_levels_handled = 0;
13262 static int num_levels_converted = 0;
13263 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13266 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13267 global.convert_leveldir);
13269 if (convert_leveldir == NULL)
13270 Error(ERR_EXIT, "no such level identifier: '%s'",
13271 global.convert_leveldir);
13273 leveldir_current = convert_leveldir;
13275 if (global.convert_level_nr != -1)
13277 convert_leveldir->first_level = global.convert_level_nr;
13278 convert_leveldir->last_level = global.convert_level_nr;
13281 convert_level_nr = convert_leveldir->first_level;
13283 printf_line("=", 79);
13284 printf("Converting levels\n");
13285 printf_line("-", 79);
13286 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
13287 printf("Level series name: '%s'\n", convert_leveldir->name);
13288 printf("Level series author: '%s'\n", convert_leveldir->author);
13289 printf("Number of levels: %d\n", convert_leveldir->levels);
13290 printf_line("=", 79);
13293 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13294 levels_failed[i] = FALSE;
13296 while (convert_level_nr <= convert_leveldir->last_level)
13298 char *level_filename;
13301 level_nr = convert_level_nr++;
13303 printf("Level %03d: ", level_nr);
13305 LoadLevel(level_nr);
13306 if (level.no_valid_file)
13308 printf("(no level)\n");
13312 printf("converting level ... ");
13314 level_filename = getDefaultLevelFilename(level_nr);
13315 new_level = !fileExists(level_filename);
13319 SaveLevel(level_nr);
13321 num_levels_converted++;
13323 printf("converted.\n");
13327 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13328 levels_failed[level_nr] = TRUE;
13330 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13333 num_levels_handled++;
13337 printf_line("=", 79);
13338 printf("Number of levels handled: %d\n", num_levels_handled);
13339 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13340 (num_levels_handled ?
13341 num_levels_converted * 100 / num_levels_handled : 0));
13342 printf_line("-", 79);
13343 printf("Summary (for automatic parsing by scripts):\n");
13344 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13345 convert_leveldir->identifier, num_levels_converted,
13346 num_levels_handled,
13347 (num_levels_handled ?
13348 num_levels_converted * 100 / num_levels_handled : 0));
13350 if (num_levels_handled != num_levels_converted)
13352 printf(", FAILED:");
13353 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13354 if (levels_failed[i])
13355 printf(" %03d", i);
13359 printf_line("=", 79);
13361 CloseAllAndExit(0);
13365 /* ------------------------------------------------------------------------- */
13366 /* create and save images for use in level sketches (raw BMP format) */
13367 /* ------------------------------------------------------------------------- */
13369 void CreateLevelSketchImages()
13371 #if defined(TARGET_SDL)
13376 InitElementPropertiesGfxElement();
13378 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13379 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13381 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13383 Bitmap *src_bitmap;
13385 int element = getMappedElement(i);
13386 int graphic = el2edimg(element);
13387 char basename1[16];
13388 char basename2[16];
13392 sprintf(basename1, "%03d.bmp", i);
13393 sprintf(basename2, "%03ds.bmp", i);
13395 filename1 = getPath2(global.create_images_dir, basename1);
13396 filename2 = getPath2(global.create_images_dir, basename2);
13398 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13399 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
13402 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13403 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
13405 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
13406 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
13408 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13409 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
13415 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13418 FreeBitmap(bitmap1);
13419 FreeBitmap(bitmap2);
13424 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
13426 CloseAllAndExit(0);
13431 /* ------------------------------------------------------------------------- */
13432 /* create and save images for custom and group elements (raw BMP format) */
13433 /* ------------------------------------------------------------------------- */
13435 void CreateCustomElementImages()
13437 #if defined(TARGET_SDL)
13438 char *filename = "graphics.classic/RocksCE.bmp";
13440 Bitmap *src_bitmap;
13441 int dummy_graphic = IMG_CUSTOM_99;
13442 int yoffset_ce = 0;
13443 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13447 bitmap = CreateBitmap(TILEX * 16 * 2,
13448 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13451 getFixedGraphicSource(dummy_graphic, 0, &src_bitmap, &src_x, &src_y);
13453 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13460 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13461 TILEX * x, TILEY * y + yoffset_ce);
13463 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13465 TILEX * x + TILEX * 16,
13466 TILEY * y + yoffset_ce);
13468 for (j = 2; j >= 0; j--)
13472 BlitBitmap(src_bitmap, bitmap,
13473 TILEX + c * 7, 0, 6, 10,
13474 TILEX * x + 6 + j * 7,
13475 TILEY * y + 11 + yoffset_ce);
13477 BlitBitmap(src_bitmap, bitmap,
13478 TILEX + c * 8, TILEY, 6, 10,
13479 TILEX * 16 + TILEX * x + 6 + j * 8,
13480 TILEY * y + 10 + yoffset_ce);
13486 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13493 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13494 TILEX * x, TILEY * y + yoffset_ge);
13496 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13498 TILEX * x + TILEX * 16,
13499 TILEY * y + yoffset_ge);
13501 for (j = 1; j >= 0; j--)
13505 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13506 TILEX * x + 6 + j * 10,
13507 TILEY * y + 11 + yoffset_ge);
13509 BlitBitmap(src_bitmap, bitmap,
13510 TILEX + c * 8, TILEY + 12, 6, 10,
13511 TILEX * 16 + TILEX * x + 10 + j * 8,
13512 TILEY * y + 10 + yoffset_ge);
13518 if (SDL_SaveBMP(bitmap->surface, filename) != 0)
13519 Error(ERR_EXIT, "cannot save CE graphics file '%s'", filename);
13521 FreeBitmap(bitmap);
13523 CloseAllAndExit(0);
13528 void CreateLevelSketchImages_TEST()
13530 void CreateCustomElementImages()