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 :
2454 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
2456 level->file_version = getFileVersion(file);
2457 level->game_version = getFileVersion(file);
2462 static int LoadLevel_DATE(FILE *file, int chunk_size, struct LevelInfo *level)
2464 level->creation_date.year = getFile16BitBE(file);
2465 level->creation_date.month = getFile8Bit(file);
2466 level->creation_date.day = getFile8Bit(file);
2468 level->creation_date.src = DATE_SRC_LEVELFILE;
2473 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
2475 int initial_player_stepsize;
2476 int initial_player_gravity;
2479 level->fieldx = getFile8Bit(file);
2480 level->fieldy = getFile8Bit(file);
2482 level->time = getFile16BitBE(file);
2483 level->gems_needed = getFile16BitBE(file);
2485 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2486 level->name[i] = getFile8Bit(file);
2487 level->name[MAX_LEVEL_NAME_LEN] = 0;
2489 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2490 level->score[i] = getFile8Bit(file);
2492 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2493 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2494 for (y = 0; y < 3; y++)
2495 for (x = 0; x < 3; x++)
2496 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2498 level->amoeba_speed = getFile8Bit(file);
2499 level->time_magic_wall = getFile8Bit(file);
2500 level->time_wheel = getFile8Bit(file);
2501 level->amoeba_content = getMappedElement(getFile8Bit(file));
2503 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2506 for (i = 0; i < MAX_PLAYERS; i++)
2507 level->initial_player_stepsize[i] = initial_player_stepsize;
2509 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2511 for (i = 0; i < MAX_PLAYERS; i++)
2512 level->initial_player_gravity[i] = initial_player_gravity;
2514 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2515 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2517 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2519 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2520 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2521 level->can_move_into_acid_bits = getFile32BitBE(file);
2522 level->dont_collide_with_bits = getFile8Bit(file);
2524 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2525 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2527 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2528 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2529 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2531 level->game_engine_type = getFile8Bit(file);
2533 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2538 static int LoadLevel_NAME(FILE *file, int chunk_size, struct LevelInfo *level)
2542 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2543 level->name[i] = getFile8Bit(file);
2544 level->name[MAX_LEVEL_NAME_LEN] = 0;
2549 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
2553 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2554 level->author[i] = getFile8Bit(file);
2555 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2560 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
2563 int chunk_size_expected = level->fieldx * level->fieldy;
2565 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2566 stored with 16-bit encoding (and should be twice as big then).
2567 Even worse, playfield data was stored 16-bit when only yamyam content
2568 contained 16-bit elements and vice versa. */
2570 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2571 chunk_size_expected *= 2;
2573 if (chunk_size_expected != chunk_size)
2575 ReadUnusedBytesFromFile(file, chunk_size);
2576 return chunk_size_expected;
2579 for (y = 0; y < level->fieldy; y++)
2580 for (x = 0; x < level->fieldx; x++)
2581 level->field[x][y] =
2582 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2587 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
2590 int header_size = 4;
2591 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2592 int chunk_size_expected = header_size + content_size;
2594 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2595 stored with 16-bit encoding (and should be twice as big then).
2596 Even worse, playfield data was stored 16-bit when only yamyam content
2597 contained 16-bit elements and vice versa. */
2599 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2600 chunk_size_expected += content_size;
2602 if (chunk_size_expected != chunk_size)
2604 ReadUnusedBytesFromFile(file, chunk_size);
2605 return chunk_size_expected;
2609 level->num_yamyam_contents = getFile8Bit(file);
2613 /* correct invalid number of content fields -- should never happen */
2614 if (level->num_yamyam_contents < 1 ||
2615 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2616 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2618 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2619 for (y = 0; y < 3; y++)
2620 for (x = 0; x < 3; x++)
2621 level->yamyam_content[i].e[x][y] =
2622 getMappedElement(level->encoding_16bit_field ?
2623 getFile16BitBE(file) : getFile8Bit(file));
2627 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
2633 int content_xsize, content_ysize;
2635 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2637 element = getMappedElement(getFile16BitBE(file));
2638 num_contents = getFile8Bit(file);
2640 getFile8Bit(file); /* content x size (unused) */
2641 getFile8Bit(file); /* content y size (unused) */
2643 content_xsize = getFile8Bit(file);
2644 content_ysize = getFile8Bit(file);
2647 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2649 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2650 for (y = 0; y < 3; y++)
2651 for (x = 0; x < 3; x++)
2652 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2654 /* correct invalid number of content fields -- should never happen */
2655 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2656 num_contents = STD_ELEMENT_CONTENTS;
2658 if (element == EL_YAMYAM)
2660 level->num_yamyam_contents = num_contents;
2662 for (i = 0; i < num_contents; i++)
2663 for (y = 0; y < 3; y++)
2664 for (x = 0; x < 3; x++)
2665 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2667 else if (element == EL_BD_AMOEBA)
2669 level->amoeba_content = content_array[0][0][0];
2673 Error(ERR_WARN, "cannot load content for element '%d'", element);
2679 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
2685 int chunk_size_expected;
2687 element = getMappedElement(getFile16BitBE(file));
2688 if (!IS_ENVELOPE(element))
2689 element = EL_ENVELOPE_1;
2691 envelope_nr = element - EL_ENVELOPE_1;
2693 envelope_len = getFile16BitBE(file);
2695 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2696 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2698 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2700 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2701 if (chunk_size_expected != chunk_size)
2703 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2704 return chunk_size_expected;
2707 for (i = 0; i < envelope_len; i++)
2708 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2713 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
2715 int num_changed_custom_elements = getFile16BitBE(file);
2716 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2719 if (chunk_size_expected != chunk_size)
2721 ReadUnusedBytesFromFile(file, chunk_size - 2);
2722 return chunk_size_expected;
2725 for (i = 0; i < num_changed_custom_elements; i++)
2727 int element = getMappedElement(getFile16BitBE(file));
2728 int properties = getFile32BitBE(file);
2730 if (IS_CUSTOM_ELEMENT(element))
2731 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2733 Error(ERR_WARN, "invalid custom element number %d", element);
2735 /* older game versions that wrote level files with CUS1 chunks used
2736 different default push delay values (not yet stored in level file) */
2737 element_info[element].push_delay_fixed = 2;
2738 element_info[element].push_delay_random = 8;
2744 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
2746 int num_changed_custom_elements = getFile16BitBE(file);
2747 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2750 if (chunk_size_expected != chunk_size)
2752 ReadUnusedBytesFromFile(file, chunk_size - 2);
2753 return chunk_size_expected;
2756 for (i = 0; i < num_changed_custom_elements; i++)
2758 int element = getMappedElement(getFile16BitBE(file));
2759 int custom_target_element = getMappedElement(getFile16BitBE(file));
2761 if (IS_CUSTOM_ELEMENT(element))
2762 element_info[element].change->target_element = custom_target_element;
2764 Error(ERR_WARN, "invalid custom element number %d", element);
2770 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
2772 int num_changed_custom_elements = getFile16BitBE(file);
2773 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2776 if (chunk_size_expected != chunk_size)
2778 ReadUnusedBytesFromFile(file, chunk_size - 2);
2779 return chunk_size_expected;
2782 for (i = 0; i < num_changed_custom_elements; i++)
2784 int element = getMappedElement(getFile16BitBE(file));
2785 struct ElementInfo *ei = &element_info[element];
2786 unsigned int event_bits;
2788 if (!IS_CUSTOM_ELEMENT(element))
2790 Error(ERR_WARN, "invalid custom element number %d", element);
2792 element = EL_INTERNAL_DUMMY;
2795 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2796 ei->description[j] = getFile8Bit(file);
2797 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2799 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2801 /* some free bytes for future properties and padding */
2802 ReadUnusedBytesFromFile(file, 7);
2804 ei->use_gfx_element = getFile8Bit(file);
2805 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2807 ei->collect_score_initial = getFile8Bit(file);
2808 ei->collect_count_initial = getFile8Bit(file);
2810 ei->push_delay_fixed = getFile16BitBE(file);
2811 ei->push_delay_random = getFile16BitBE(file);
2812 ei->move_delay_fixed = getFile16BitBE(file);
2813 ei->move_delay_random = getFile16BitBE(file);
2815 ei->move_pattern = getFile16BitBE(file);
2816 ei->move_direction_initial = getFile8Bit(file);
2817 ei->move_stepsize = getFile8Bit(file);
2819 for (y = 0; y < 3; y++)
2820 for (x = 0; x < 3; x++)
2821 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2823 event_bits = getFile32BitBE(file);
2824 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2825 if (event_bits & (1 << j))
2826 ei->change->has_event[j] = TRUE;
2828 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2830 ei->change->delay_fixed = getFile16BitBE(file);
2831 ei->change->delay_random = getFile16BitBE(file);
2832 ei->change->delay_frames = getFile16BitBE(file);
2834 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2836 ei->change->explode = getFile8Bit(file);
2837 ei->change->use_target_content = getFile8Bit(file);
2838 ei->change->only_if_complete = getFile8Bit(file);
2839 ei->change->use_random_replace = getFile8Bit(file);
2841 ei->change->random_percentage = getFile8Bit(file);
2842 ei->change->replace_when = getFile8Bit(file);
2844 for (y = 0; y < 3; y++)
2845 for (x = 0; x < 3; x++)
2846 ei->change->target_content.e[x][y] =
2847 getMappedElement(getFile16BitBE(file));
2849 ei->slippery_type = getFile8Bit(file);
2851 /* some free bytes for future properties and padding */
2852 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2854 /* mark that this custom element has been modified */
2855 ei->modified_settings = TRUE;
2861 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
2863 struct ElementInfo *ei;
2864 int chunk_size_expected;
2868 /* ---------- custom element base property values (96 bytes) ------------- */
2870 element = getMappedElement(getFile16BitBE(file));
2872 if (!IS_CUSTOM_ELEMENT(element))
2874 Error(ERR_WARN, "invalid custom element number %d", element);
2876 ReadUnusedBytesFromFile(file, chunk_size - 2);
2880 ei = &element_info[element];
2882 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2883 ei->description[i] = getFile8Bit(file);
2884 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2886 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2888 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2890 ei->num_change_pages = getFile8Bit(file);
2892 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2893 if (chunk_size_expected != chunk_size)
2895 ReadUnusedBytesFromFile(file, chunk_size - 43);
2896 return chunk_size_expected;
2899 ei->ce_value_fixed_initial = getFile16BitBE(file);
2900 ei->ce_value_random_initial = getFile16BitBE(file);
2901 ei->use_last_ce_value = getFile8Bit(file);
2903 ei->use_gfx_element = getFile8Bit(file);
2904 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2906 ei->collect_score_initial = getFile8Bit(file);
2907 ei->collect_count_initial = getFile8Bit(file);
2909 ei->drop_delay_fixed = getFile8Bit(file);
2910 ei->push_delay_fixed = getFile8Bit(file);
2911 ei->drop_delay_random = getFile8Bit(file);
2912 ei->push_delay_random = getFile8Bit(file);
2913 ei->move_delay_fixed = getFile16BitBE(file);
2914 ei->move_delay_random = getFile16BitBE(file);
2916 /* bits 0 - 15 of "move_pattern" ... */
2917 ei->move_pattern = getFile16BitBE(file);
2918 ei->move_direction_initial = getFile8Bit(file);
2919 ei->move_stepsize = getFile8Bit(file);
2921 ei->slippery_type = getFile8Bit(file);
2923 for (y = 0; y < 3; y++)
2924 for (x = 0; x < 3; x++)
2925 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2927 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2928 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2929 ei->move_leave_type = getFile8Bit(file);
2931 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2932 ei->move_pattern |= (getFile16BitBE(file) << 16);
2934 ei->access_direction = getFile8Bit(file);
2936 ei->explosion_delay = getFile8Bit(file);
2937 ei->ignition_delay = getFile8Bit(file);
2938 ei->explosion_type = getFile8Bit(file);
2940 /* some free bytes for future custom property values and padding */
2941 ReadUnusedBytesFromFile(file, 1);
2943 /* ---------- change page property values (48 bytes) --------------------- */
2945 setElementChangePages(ei, ei->num_change_pages);
2947 for (i = 0; i < ei->num_change_pages; i++)
2949 struct ElementChangeInfo *change = &ei->change_page[i];
2950 unsigned int event_bits;
2952 /* always start with reliable default values */
2953 setElementChangeInfoToDefaults(change);
2955 /* bits 0 - 31 of "has_event[]" ... */
2956 event_bits = getFile32BitBE(file);
2957 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2958 if (event_bits & (1 << j))
2959 change->has_event[j] = TRUE;
2961 change->target_element = getMappedElement(getFile16BitBE(file));
2963 change->delay_fixed = getFile16BitBE(file);
2964 change->delay_random = getFile16BitBE(file);
2965 change->delay_frames = getFile16BitBE(file);
2967 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2969 change->explode = getFile8Bit(file);
2970 change->use_target_content = getFile8Bit(file);
2971 change->only_if_complete = getFile8Bit(file);
2972 change->use_random_replace = getFile8Bit(file);
2974 change->random_percentage = getFile8Bit(file);
2975 change->replace_when = getFile8Bit(file);
2977 for (y = 0; y < 3; y++)
2978 for (x = 0; x < 3; x++)
2979 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2981 change->can_change = getFile8Bit(file);
2983 change->trigger_side = getFile8Bit(file);
2985 change->trigger_player = getFile8Bit(file);
2986 change->trigger_page = getFile8Bit(file);
2988 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2989 CH_PAGE_ANY : (1 << change->trigger_page));
2991 change->has_action = getFile8Bit(file);
2992 change->action_type = getFile8Bit(file);
2993 change->action_mode = getFile8Bit(file);
2994 change->action_arg = getFile16BitBE(file);
2996 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2997 event_bits = getFile8Bit(file);
2998 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2999 if (event_bits & (1 << (j - 32)))
3000 change->has_event[j] = TRUE;
3003 /* mark this custom element as modified */
3004 ei->modified_settings = TRUE;
3009 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
3011 struct ElementInfo *ei;
3012 struct ElementGroupInfo *group;
3016 element = getMappedElement(getFile16BitBE(file));
3018 if (!IS_GROUP_ELEMENT(element))
3020 Error(ERR_WARN, "invalid group element number %d", element);
3022 ReadUnusedBytesFromFile(file, chunk_size - 2);
3026 ei = &element_info[element];
3028 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3029 ei->description[i] = getFile8Bit(file);
3030 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3032 group = element_info[element].group;
3034 group->num_elements = getFile8Bit(file);
3036 ei->use_gfx_element = getFile8Bit(file);
3037 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3039 group->choice_mode = getFile8Bit(file);
3041 /* some free bytes for future values and padding */
3042 ReadUnusedBytesFromFile(file, 3);
3044 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3045 group->element[i] = getMappedElement(getFile16BitBE(file));
3047 /* mark this group element as modified */
3048 element_info[element].modified_settings = TRUE;
3053 static int LoadLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *conf,
3054 int element, int real_element)
3056 int micro_chunk_size = 0;
3057 int conf_type = getFile8Bit(file);
3058 int byte_mask = conf_type & CONF_MASK_BYTES;
3059 boolean element_found = FALSE;
3062 micro_chunk_size += 1;
3064 if (byte_mask == CONF_MASK_MULTI_BYTES)
3066 int num_bytes = getFile16BitBE(file);
3067 byte *buffer = checked_malloc(num_bytes);
3069 ReadBytesFromFile(file, buffer, num_bytes);
3071 for (i = 0; conf[i].data_type != -1; i++)
3073 if (conf[i].element == element &&
3074 conf[i].conf_type == conf_type)
3076 int data_type = conf[i].data_type;
3077 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3078 int max_num_entities = conf[i].max_num_entities;
3080 if (num_entities > max_num_entities)
3083 "truncating number of entities for element %d from %d to %d",
3084 element, num_entities, max_num_entities);
3086 num_entities = max_num_entities;
3089 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3090 data_type == TYPE_CONTENT_LIST))
3092 /* for element and content lists, zero entities are not allowed */
3093 Error(ERR_WARN, "found empty list of entities for element %d",
3096 /* do not set "num_entities" here to prevent reading behind buffer */
3098 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
3102 *(int *)(conf[i].num_entities) = num_entities;
3105 element_found = TRUE;
3107 if (data_type == TYPE_STRING)
3109 char *string = (char *)(conf[i].value);
3112 for (j = 0; j < max_num_entities; j++)
3113 string[j] = (j < num_entities ? buffer[j] : '\0');
3115 else if (data_type == TYPE_ELEMENT_LIST)
3117 int *element_array = (int *)(conf[i].value);
3120 for (j = 0; j < num_entities; j++)
3122 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3124 else if (data_type == TYPE_CONTENT_LIST)
3126 struct Content *content= (struct Content *)(conf[i].value);
3129 for (c = 0; c < num_entities; c++)
3130 for (y = 0; y < 3; y++)
3131 for (x = 0; x < 3; x++)
3132 content[c].e[x][y] =
3133 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3136 element_found = FALSE;
3142 checked_free(buffer);
3144 micro_chunk_size += 2 + num_bytes;
3146 else /* constant size configuration data (1, 2 or 4 bytes) */
3148 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3149 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3150 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3152 for (i = 0; conf[i].data_type != -1; i++)
3154 if (conf[i].element == element &&
3155 conf[i].conf_type == conf_type)
3157 int data_type = conf[i].data_type;
3159 if (data_type == TYPE_ELEMENT)
3160 value = getMappedElement(value);
3162 if (data_type == TYPE_BOOLEAN)
3163 *(boolean *)(conf[i].value) = value;
3165 *(int *) (conf[i].value) = value;
3167 element_found = TRUE;
3173 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3178 char *error_conf_chunk_bytes =
3179 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3180 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3181 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3182 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3183 int error_element = real_element;
3185 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3186 error_conf_chunk_bytes, error_conf_chunk_token,
3187 error_element, EL_NAME(error_element));
3190 return micro_chunk_size;
3193 static int LoadLevel_INFO(FILE *file, int chunk_size, struct LevelInfo *level)
3195 int real_chunk_size = 0;
3197 li = *level; /* copy level data into temporary buffer */
3201 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3203 if (real_chunk_size >= chunk_size)
3207 *level = li; /* copy temporary buffer back to level data */
3209 return real_chunk_size;
3212 static int LoadLevel_CONF(FILE *file, int chunk_size, struct LevelInfo *level)
3214 int real_chunk_size = 0;
3216 li = *level; /* copy level data into temporary buffer */
3220 int element = getMappedElement(getFile16BitBE(file));
3222 real_chunk_size += 2;
3223 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3225 if (real_chunk_size >= chunk_size)
3229 *level = li; /* copy temporary buffer back to level data */
3231 return real_chunk_size;
3234 static int LoadLevel_ELEM(FILE *file, int chunk_size, struct LevelInfo *level)
3236 int real_chunk_size = 0;
3238 li = *level; /* copy level data into temporary buffer */
3242 int element = getMappedElement(getFile16BitBE(file));
3244 real_chunk_size += 2;
3245 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3247 if (real_chunk_size >= chunk_size)
3251 *level = li; /* copy temporary buffer back to level data */
3253 return real_chunk_size;
3256 static int LoadLevel_NOTE(FILE *file, int chunk_size, struct LevelInfo *level)
3258 int element = getMappedElement(getFile16BitBE(file));
3259 int envelope_nr = element - EL_ENVELOPE_1;
3260 int real_chunk_size = 2;
3264 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3267 if (real_chunk_size >= chunk_size)
3271 level->envelope[envelope_nr] = xx_envelope;
3273 return real_chunk_size;
3276 static int LoadLevel_CUSX(FILE *file, int chunk_size, struct LevelInfo *level)
3278 int element = getMappedElement(getFile16BitBE(file));
3279 int real_chunk_size = 2;
3280 struct ElementInfo *ei = &element_info[element];
3283 xx_ei = *ei; /* copy element data into temporary buffer */
3285 xx_ei.num_change_pages = -1;
3289 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3291 if (xx_ei.num_change_pages != -1)
3294 if (real_chunk_size >= chunk_size)
3300 if (ei->num_change_pages == -1)
3302 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3305 ei->num_change_pages = 1;
3307 setElementChangePages(ei, 1);
3308 setElementChangeInfoToDefaults(ei->change);
3310 return real_chunk_size;
3313 /* initialize number of change pages stored for this custom element */
3314 setElementChangePages(ei, ei->num_change_pages);
3315 for (i = 0; i < ei->num_change_pages; i++)
3316 setElementChangeInfoToDefaults(&ei->change_page[i]);
3318 /* start with reading properties for the first change page */
3319 xx_current_change_page = 0;
3323 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3325 xx_change = *change; /* copy change data into temporary buffer */
3327 resetEventBits(); /* reset bits; change page might have changed */
3329 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3332 *change = xx_change;
3334 setEventFlagsFromEventBits(change);
3336 if (real_chunk_size >= chunk_size)
3340 return real_chunk_size;
3343 static int LoadLevel_GRPX(FILE *file, int chunk_size, struct LevelInfo *level)
3345 int element = getMappedElement(getFile16BitBE(file));
3346 int real_chunk_size = 2;
3347 struct ElementInfo *ei = &element_info[element];
3348 struct ElementGroupInfo *group = ei->group;
3350 xx_ei = *ei; /* copy element data into temporary buffer */
3351 xx_group = *group; /* copy group data into temporary buffer */
3355 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3358 if (real_chunk_size >= chunk_size)
3365 return real_chunk_size;
3368 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3369 struct LevelFileInfo *level_file_info,
3370 boolean level_info_only)
3372 char *filename = level_file_info->filename;
3373 char cookie[MAX_LINE_LEN];
3374 char chunk_name[CHUNK_ID_LEN + 1];
3378 if (!(file = fopen(filename, MODE_READ)))
3380 level->no_valid_file = TRUE;
3383 if (!level_info_only)
3384 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3386 if (level != &level_template)
3387 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3393 getFileChunkBE(file, chunk_name, NULL);
3394 if (strEqual(chunk_name, "RND1"))
3396 getFile32BitBE(file); /* not used */
3398 getFileChunkBE(file, chunk_name, NULL);
3399 if (!strEqual(chunk_name, "CAVE"))
3401 level->no_valid_file = TRUE;
3403 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3408 else /* check for pre-2.0 file format with cookie string */
3410 strcpy(cookie, chunk_name);
3411 if (fgets(&cookie[4], MAX_LINE_LEN - 4, file) == NULL)
3413 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3414 cookie[strlen(cookie) - 1] = '\0';
3416 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3418 level->no_valid_file = TRUE;
3420 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3425 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3427 level->no_valid_file = TRUE;
3429 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3434 /* pre-2.0 level files have no game version, so use file version here */
3435 level->game_version = level->file_version;
3438 if (level->file_version < FILE_VERSION_1_2)
3440 /* level files from versions before 1.2.0 without chunk structure */
3441 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3442 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3450 int (*loader)(FILE *, int, struct LevelInfo *);
3454 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3455 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3456 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3457 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3458 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3459 { "INFO", -1, LoadLevel_INFO },
3460 { "BODY", -1, LoadLevel_BODY },
3461 { "CONT", -1, LoadLevel_CONT },
3462 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3463 { "CNT3", -1, LoadLevel_CNT3 },
3464 { "CUS1", -1, LoadLevel_CUS1 },
3465 { "CUS2", -1, LoadLevel_CUS2 },
3466 { "CUS3", -1, LoadLevel_CUS3 },
3467 { "CUS4", -1, LoadLevel_CUS4 },
3468 { "GRP1", -1, LoadLevel_GRP1 },
3469 { "CONF", -1, LoadLevel_CONF },
3470 { "ELEM", -1, LoadLevel_ELEM },
3471 { "NOTE", -1, LoadLevel_NOTE },
3472 { "CUSX", -1, LoadLevel_CUSX },
3473 { "GRPX", -1, LoadLevel_GRPX },
3478 while (getFileChunkBE(file, chunk_name, &chunk_size))
3482 while (chunk_info[i].name != NULL &&
3483 !strEqual(chunk_name, chunk_info[i].name))
3486 if (chunk_info[i].name == NULL)
3488 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3489 chunk_name, filename);
3490 ReadUnusedBytesFromFile(file, chunk_size);
3492 else if (chunk_info[i].size != -1 &&
3493 chunk_info[i].size != chunk_size)
3495 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3496 chunk_size, chunk_name, filename);
3497 ReadUnusedBytesFromFile(file, chunk_size);
3501 /* call function to load this level chunk */
3502 int chunk_size_expected =
3503 (chunk_info[i].loader)(file, chunk_size, level);
3505 /* the size of some chunks cannot be checked before reading other
3506 chunks first (like "HEAD" and "BODY") that contain some header
3507 information, so check them here */
3508 if (chunk_size_expected != chunk_size)
3510 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3511 chunk_size, chunk_name, filename);
3520 /* ------------------------------------------------------------------------- */
3521 /* functions for loading EM level */
3522 /* ------------------------------------------------------------------------- */
3526 static int map_em_element_yam(int element)
3530 case 0x00: return EL_EMPTY;
3531 case 0x01: return EL_EMERALD;
3532 case 0x02: return EL_DIAMOND;
3533 case 0x03: return EL_ROCK;
3534 case 0x04: return EL_ROBOT;
3535 case 0x05: return EL_SPACESHIP_UP;
3536 case 0x06: return EL_BOMB;
3537 case 0x07: return EL_BUG_UP;
3538 case 0x08: return EL_AMOEBA_DROP;
3539 case 0x09: return EL_NUT;
3540 case 0x0a: return EL_YAMYAM;
3541 case 0x0b: return EL_QUICKSAND_FULL;
3542 case 0x0c: return EL_SAND;
3543 case 0x0d: return EL_WALL_SLIPPERY;
3544 case 0x0e: return EL_STEELWALL;
3545 case 0x0f: return EL_WALL;
3546 case 0x10: return EL_EM_KEY_1;
3547 case 0x11: return EL_EM_KEY_2;
3548 case 0x12: return EL_EM_KEY_4;
3549 case 0x13: return EL_EM_KEY_3;
3550 case 0x14: return EL_MAGIC_WALL;
3551 case 0x15: return EL_ROBOT_WHEEL;
3552 case 0x16: return EL_DYNAMITE;
3554 case 0x17: return EL_EM_KEY_1; /* EMC */
3555 case 0x18: return EL_BUG_UP; /* EMC */
3556 case 0x1a: return EL_DIAMOND; /* EMC */
3557 case 0x1b: return EL_EMERALD; /* EMC */
3558 case 0x25: return EL_NUT; /* EMC */
3559 case 0x80: return EL_EMPTY; /* EMC */
3560 case 0x85: return EL_EM_KEY_1; /* EMC */
3561 case 0x86: return EL_EM_KEY_2; /* EMC */
3562 case 0x87: return EL_EM_KEY_4; /* EMC */
3563 case 0x88: return EL_EM_KEY_3; /* EMC */
3564 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
3565 case 0x9a: return EL_AMOEBA_WET; /* EMC */
3566 case 0xaf: return EL_DYNAMITE; /* EMC */
3567 case 0xbd: return EL_SAND; /* EMC */
3570 Error(ERR_WARN, "invalid level element %d", element);
3575 static int map_em_element_field(int element)
3577 if (element >= 0xc8 && element <= 0xe1)
3578 return EL_CHAR_A + (element - 0xc8);
3579 else if (element >= 0xe2 && element <= 0xeb)
3580 return EL_CHAR_0 + (element - 0xe2);
3584 case 0x00: return EL_ROCK;
3585 case 0x01: return EL_ROCK; /* EMC */
3586 case 0x02: return EL_DIAMOND;
3587 case 0x03: return EL_DIAMOND;
3588 case 0x04: return EL_ROBOT;
3589 case 0x05: return EL_ROBOT; /* EMC */
3590 case 0x06: return EL_EMPTY_SPACE; /* EMC */
3591 case 0x07: return EL_EMPTY_SPACE; /* EMC */
3592 case 0x08: return EL_SPACESHIP_UP;
3593 case 0x09: return EL_SPACESHIP_RIGHT;
3594 case 0x0a: return EL_SPACESHIP_DOWN;
3595 case 0x0b: return EL_SPACESHIP_LEFT;
3596 case 0x0c: return EL_SPACESHIP_UP;
3597 case 0x0d: return EL_SPACESHIP_RIGHT;
3598 case 0x0e: return EL_SPACESHIP_DOWN;
3599 case 0x0f: return EL_SPACESHIP_LEFT;
3601 case 0x10: return EL_BOMB;
3602 case 0x11: return EL_BOMB; /* EMC */
3603 case 0x12: return EL_EMERALD;
3604 case 0x13: return EL_EMERALD;
3605 case 0x14: return EL_BUG_UP;
3606 case 0x15: return EL_BUG_RIGHT;
3607 case 0x16: return EL_BUG_DOWN;
3608 case 0x17: return EL_BUG_LEFT;
3609 case 0x18: return EL_BUG_UP;
3610 case 0x19: return EL_BUG_RIGHT;
3611 case 0x1a: return EL_BUG_DOWN;
3612 case 0x1b: return EL_BUG_LEFT;
3613 case 0x1c: return EL_AMOEBA_DROP;
3614 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
3615 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
3616 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
3618 case 0x20: return EL_ROCK;
3619 case 0x21: return EL_BOMB; /* EMC */
3620 case 0x22: return EL_DIAMOND; /* EMC */
3621 case 0x23: return EL_EMERALD; /* EMC */
3622 case 0x24: return EL_MAGIC_WALL;
3623 case 0x25: return EL_NUT;
3624 case 0x26: return EL_NUT; /* EMC */
3625 case 0x27: return EL_NUT; /* EMC */
3627 /* looks like magic wheel, but is _always_ activated */
3628 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
3630 case 0x29: return EL_YAMYAM; /* up */
3631 case 0x2a: return EL_YAMYAM; /* down */
3632 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
3633 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
3634 case 0x2d: return EL_QUICKSAND_FULL;
3635 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
3636 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
3638 case 0x30: return EL_EMPTY_SPACE; /* EMC */
3639 case 0x31: return EL_SAND; /* EMC */
3640 case 0x32: return EL_SAND; /* EMC */
3641 case 0x33: return EL_SAND; /* EMC */
3642 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
3643 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
3644 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
3645 case 0x37: return EL_SAND; /* EMC */
3646 case 0x38: return EL_ROCK; /* EMC */
3647 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
3648 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
3649 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
3650 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
3651 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
3652 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
3653 case 0x3f: return EL_ACID_POOL_BOTTOM;
3655 case 0x40: return EL_EXIT_OPEN; /* 1 */
3656 case 0x41: return EL_EXIT_OPEN; /* 2 */
3657 case 0x42: return EL_EXIT_OPEN; /* 3 */
3658 case 0x43: return EL_BALLOON; /* EMC */
3659 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
3660 case 0x45: return EL_SPRING; /* EMC */
3661 case 0x46: return EL_SPRING; /* falling */ /* EMC */
3662 case 0x47: return EL_SPRING; /* left */ /* EMC */
3663 case 0x48: return EL_SPRING; /* right */ /* EMC */
3664 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
3665 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
3666 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
3667 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
3668 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
3669 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
3670 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
3672 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
3673 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
3674 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
3675 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
3676 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
3677 case 0x55: return EL_EMPTY_SPACE; /* EMC */
3678 case 0x56: return EL_EMPTY_SPACE; /* EMC */
3679 case 0x57: return EL_EMPTY_SPACE; /* EMC */
3680 case 0x58: return EL_EMPTY_SPACE; /* EMC */
3681 case 0x59: return EL_EMPTY_SPACE; /* EMC */
3682 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
3683 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
3684 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
3685 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
3686 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
3687 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
3689 case 0x60: return EL_EMPTY_SPACE; /* EMC */
3690 case 0x61: return EL_EMPTY_SPACE; /* EMC */
3691 case 0x62: return EL_EMPTY_SPACE; /* EMC */
3692 case 0x63: return EL_SPRING; /* left */ /* EMC */
3693 case 0x64: return EL_SPRING; /* right */ /* EMC */
3694 case 0x65: return EL_ACID; /* 1 */ /* EMC */
3695 case 0x66: return EL_ACID; /* 2 */ /* EMC */
3696 case 0x67: return EL_ACID; /* 3 */ /* EMC */
3697 case 0x68: return EL_ACID; /* 4 */ /* EMC */
3698 case 0x69: return EL_ACID; /* 5 */ /* EMC */
3699 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
3700 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
3701 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
3702 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
3703 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
3704 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
3706 case 0x70: return EL_EMPTY_SPACE; /* EMC */
3707 case 0x71: return EL_EMPTY_SPACE; /* EMC */
3708 case 0x72: return EL_NUT; /* left */ /* EMC */
3709 case 0x73: return EL_SAND; /* EMC (? "nut") */
3710 case 0x74: return EL_STEELWALL;
3711 case 0x75: return EL_EMPTY_SPACE; /* EMC */
3712 case 0x76: return EL_EMPTY_SPACE; /* EMC */
3713 case 0x77: return EL_BOMB; /* left */ /* EMC */
3714 case 0x78: return EL_BOMB; /* right */ /* EMC */
3715 case 0x79: return EL_ROCK; /* left */ /* EMC */
3716 case 0x7a: return EL_ROCK; /* right */ /* EMC */
3717 case 0x7b: return EL_ACID; /* (? EMC "blank") */
3718 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
3719 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
3720 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
3721 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
3723 case 0x80: return EL_EMPTY;
3724 case 0x81: return EL_WALL_SLIPPERY;
3725 case 0x82: return EL_SAND;
3726 case 0x83: return EL_STEELWALL;
3727 case 0x84: return EL_WALL;
3728 case 0x85: return EL_EM_KEY_1;
3729 case 0x86: return EL_EM_KEY_2;
3730 case 0x87: return EL_EM_KEY_4;
3731 case 0x88: return EL_EM_KEY_3;
3732 case 0x89: return EL_EM_GATE_1;
3733 case 0x8a: return EL_EM_GATE_2;
3734 case 0x8b: return EL_EM_GATE_4;
3735 case 0x8c: return EL_EM_GATE_3;
3736 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
3737 case 0x8e: return EL_EM_GATE_1_GRAY;
3738 case 0x8f: return EL_EM_GATE_2_GRAY;
3740 case 0x90: return EL_EM_GATE_4_GRAY;
3741 case 0x91: return EL_EM_GATE_3_GRAY;
3742 case 0x92: return EL_MAGIC_WALL;
3743 case 0x93: return EL_ROBOT_WHEEL;
3744 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
3745 case 0x95: return EL_ACID_POOL_TOPLEFT;
3746 case 0x96: return EL_ACID_POOL_TOPRIGHT;
3747 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
3748 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
3749 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
3750 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
3751 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
3752 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
3753 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
3754 case 0x9e: return EL_EXIT_CLOSED;
3755 case 0x9f: return EL_CHAR_LESS; /* arrow left */
3757 /* looks like normal sand, but behaves like wall */
3758 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
3759 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
3760 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
3761 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
3762 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
3763 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
3764 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
3765 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
3766 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
3767 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
3768 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
3769 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
3770 case 0xac: return EL_CHAR_COMMA; /* EMC */
3771 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
3772 case 0xae: return EL_CHAR_MINUS; /* EMC */
3773 case 0xaf: return EL_DYNAMITE;
3775 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
3776 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
3777 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
3778 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
3779 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
3780 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
3781 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
3782 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
3783 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
3784 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
3785 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
3786 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
3787 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
3788 case 0xbd: return EL_SAND; /* EMC ("dirt") */
3789 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
3790 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
3792 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
3793 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
3794 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
3795 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
3796 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
3797 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
3798 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
3799 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
3801 /* characters: see above */
3803 case 0xec: return EL_CHAR_PERIOD;
3804 case 0xed: return EL_CHAR_EXCLAM;
3805 case 0xee: return EL_CHAR_COLON;
3806 case 0xef: return EL_CHAR_QUESTION;
3808 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
3809 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
3810 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
3811 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
3812 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
3813 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
3814 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
3815 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
3817 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
3818 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
3819 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
3820 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
3821 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
3822 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
3824 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
3825 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
3828 /* should never happen (all 8-bit value cases should be handled) */
3829 Error(ERR_WARN, "invalid level element %d", element);
3834 #define EM_LEVEL_SIZE 2106
3835 #define EM_LEVEL_XSIZE 64
3836 #define EM_LEVEL_YSIZE 32
3838 static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
3839 struct LevelFileInfo *level_file_info)
3841 char *filename = level_file_info->filename;
3843 unsigned char leveldata[EM_LEVEL_SIZE];
3844 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
3845 int nr = level_file_info->nr;
3848 if (!(file = fopen(filename, MODE_READ)))
3850 level->no_valid_file = TRUE;
3852 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3857 for (i = 0; i < EM_LEVEL_SIZE; i++)
3858 leveldata[i] = fgetc(file);
3862 /* check if level data is crypted by testing against known starting bytes
3863 of the few existing crypted level files (from Emerald Mine 1 + 2) */
3865 if ((leveldata[0] == 0xf1 ||
3866 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
3868 unsigned char code0 = 0x65;
3869 unsigned char code1 = 0x11;
3871 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
3872 leveldata[0] = 0xf1;
3874 /* decode crypted level data */
3876 for (i = 0; i < EM_LEVEL_SIZE; i++)
3878 leveldata[i] ^= code0;
3879 leveldata[i] -= code1;
3881 code0 = (code0 + 7) & 0xff;
3885 level->fieldx = EM_LEVEL_XSIZE;
3886 level->fieldy = EM_LEVEL_YSIZE;
3888 level->time = header[46] * 10;
3889 level->gems_needed = header[47];
3891 /* The original Emerald Mine levels have their level number stored
3892 at the second byte of the level file...
3893 Do not trust this information at other level files, e.g. EMC,
3894 but correct it anyway (normally the first row is completely
3895 steel wall, so the correction does not hurt anyway). */
3897 if (leveldata[1] == nr)
3898 leveldata[1] = leveldata[2]; /* correct level number field */
3900 sprintf(level->name, "Level %d", nr); /* set level name */
3902 level->score[SC_EMERALD] = header[36];
3903 level->score[SC_DIAMOND] = header[37];
3904 level->score[SC_ROBOT] = header[38];
3905 level->score[SC_SPACESHIP] = header[39];
3906 level->score[SC_BUG] = header[40];
3907 level->score[SC_YAMYAM] = header[41];
3908 level->score[SC_NUT] = header[42];
3909 level->score[SC_DYNAMITE] = header[43];
3910 level->score[SC_TIME_BONUS] = header[44];
3912 level->num_yamyam_contents = 4;
3914 for (i = 0; i < level->num_yamyam_contents; i++)
3915 for (y = 0; y < 3; y++)
3916 for (x = 0; x < 3; x++)
3917 level->yamyam_content[i].e[x][y] =
3918 map_em_element_yam(header[i * 9 + y * 3 + x]);
3920 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
3921 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
3922 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
3923 level->amoeba_content = EL_DIAMOND;
3925 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3927 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
3929 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
3930 new_element = EL_AMOEBA_WET;
3932 level->field[x][y] = new_element;
3935 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
3936 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
3937 level->field[x][y] = EL_PLAYER_1;
3939 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
3940 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
3941 level->field[x][y] = EL_PLAYER_2;
3946 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3948 static int ball_xy[8][2] =
3959 struct LevelInfo_EM *level_em = level->native_em_level;
3960 struct LEVEL *lev = level_em->lev;
3961 struct PLAYER **ply = level_em->ply;
3964 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3965 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3967 lev->time_seconds = level->time;
3968 lev->required_initial = level->gems_needed;
3970 lev->emerald_score = level->score[SC_EMERALD];
3971 lev->diamond_score = level->score[SC_DIAMOND];
3972 lev->alien_score = level->score[SC_ROBOT];
3973 lev->tank_score = level->score[SC_SPACESHIP];
3974 lev->bug_score = level->score[SC_BUG];
3975 lev->eater_score = level->score[SC_YAMYAM];
3976 lev->nut_score = level->score[SC_NUT];
3977 lev->dynamite_score = level->score[SC_DYNAMITE];
3978 lev->key_score = level->score[SC_KEY];
3979 lev->exit_score = level->score[SC_TIME_BONUS];
3981 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3982 for (y = 0; y < 3; y++)
3983 for (x = 0; x < 3; x++)
3984 lev->eater_array[i][y * 3 + x] =
3985 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3987 lev->amoeba_time = level->amoeba_speed;
3988 lev->wonderwall_time_initial = level->time_magic_wall;
3989 lev->wheel_time = level->time_wheel;
3991 lev->android_move_time = level->android_move_time;
3992 lev->android_clone_time = level->android_clone_time;
3993 lev->ball_random = level->ball_random;
3994 lev->ball_state_initial = level->ball_state_initial;
3995 lev->ball_time = level->ball_time;
3996 lev->num_ball_arrays = level->num_ball_contents;
3998 lev->lenses_score = level->lenses_score;
3999 lev->magnify_score = level->magnify_score;
4000 lev->slurp_score = level->slurp_score;
4002 lev->lenses_time = level->lenses_time;
4003 lev->magnify_time = level->magnify_time;
4005 lev->wind_direction_initial =
4006 map_direction_RND_to_EM(level->wind_direction_initial);
4007 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
4008 lev->wind_time : 0);
4010 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4011 for (j = 0; j < 8; j++)
4012 lev->ball_array[i][j] =
4013 map_element_RND_to_EM(level->
4014 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
4016 map_android_clone_elements_RND_to_EM(level);
4018 /* first fill the complete playfield with the default border element */
4019 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4020 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4021 level_em->cave[x][y] = ZBORDER;
4023 if (BorderElement == EL_STEELWALL)
4025 for (y = 0; y < lev->height + 2; y++)
4026 for (x = 0; x < lev->width + 2; x++)
4027 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
4030 /* then copy the real level contents from level file into the playfield */
4031 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
4033 int new_element = map_element_RND_to_EM(level->field[x][y]);
4034 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
4035 int xx = x + 1 + offset;
4036 int yy = y + 1 + offset;
4038 if (level->field[x][y] == EL_AMOEBA_DEAD)
4039 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
4041 level_em->cave[xx][yy] = new_element;
4044 for (i = 0; i < MAX_PLAYERS; i++)
4046 ply[i]->x_initial = 0;
4047 ply[i]->y_initial = 0;
4050 /* initialize player positions and delete players from the playfield */
4051 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
4053 if (ELEM_IS_PLAYER(level->field[x][y]))
4055 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4056 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
4057 int xx = x + 1 + offset;
4058 int yy = y + 1 + offset;
4060 ply[player_nr]->x_initial = xx;
4061 ply[player_nr]->y_initial = yy;
4063 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
4067 if (BorderElement == EL_STEELWALL)
4074 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4076 static int ball_xy[8][2] =
4087 struct LevelInfo_EM *level_em = level->native_em_level;
4088 struct LEVEL *lev = level_em->lev;
4089 struct PLAYER **ply = level_em->ply;
4092 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
4093 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
4095 level->time = lev->time_seconds;
4096 level->gems_needed = lev->required_initial;
4098 sprintf(level->name, "Level %d", level->file_info.nr);
4100 level->score[SC_EMERALD] = lev->emerald_score;
4101 level->score[SC_DIAMOND] = lev->diamond_score;
4102 level->score[SC_ROBOT] = lev->alien_score;
4103 level->score[SC_SPACESHIP] = lev->tank_score;
4104 level->score[SC_BUG] = lev->bug_score;
4105 level->score[SC_YAMYAM] = lev->eater_score;
4106 level->score[SC_NUT] = lev->nut_score;
4107 level->score[SC_DYNAMITE] = lev->dynamite_score;
4108 level->score[SC_KEY] = lev->key_score;
4109 level->score[SC_TIME_BONUS] = lev->exit_score;
4111 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
4113 for (i = 0; i < level->num_yamyam_contents; i++)
4114 for (y = 0; y < 3; y++)
4115 for (x = 0; x < 3; x++)
4116 level->yamyam_content[i].e[x][y] =
4117 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
4119 level->amoeba_speed = lev->amoeba_time;
4120 level->time_magic_wall = lev->wonderwall_time_initial;
4121 level->time_wheel = lev->wheel_time;
4123 level->android_move_time = lev->android_move_time;
4124 level->android_clone_time = lev->android_clone_time;
4125 level->ball_random = lev->ball_random;
4126 level->ball_state_initial = lev->ball_state_initial;
4127 level->ball_time = lev->ball_time;
4128 level->num_ball_contents = lev->num_ball_arrays;
4130 level->lenses_score = lev->lenses_score;
4131 level->magnify_score = lev->magnify_score;
4132 level->slurp_score = lev->slurp_score;
4134 level->lenses_time = lev->lenses_time;
4135 level->magnify_time = lev->magnify_time;
4137 level->wind_direction_initial =
4138 map_direction_EM_to_RND(lev->wind_direction_initial);
4140 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4141 for (j = 0; j < 8; j++)
4142 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4143 map_element_EM_to_RND(lev->ball_array[i][j]);
4145 map_android_clone_elements_EM_to_RND(level);
4147 /* convert the playfield (some elements need special treatment) */
4148 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4150 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
4152 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4153 new_element = EL_AMOEBA_DEAD;
4155 level->field[x][y] = new_element;
4158 for (i = 0; i < MAX_PLAYERS; i++)
4160 /* in case of all players set to the same field, use the first player */
4161 int nr = MAX_PLAYERS - i - 1;
4162 int jx = ply[nr]->x_initial - 1;
4163 int jy = ply[nr]->y_initial - 1;
4165 if (jx != -1 && jy != -1)
4166 level->field[jx][jy] = EL_PLAYER_1 + nr;
4171 /* ------------------------------------------------------------------------- */
4172 /* functions for loading SP level */
4173 /* ------------------------------------------------------------------------- */
4177 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
4178 #define SP_LEVEL_SIZE 1536
4179 #define SP_LEVEL_XSIZE 60
4180 #define SP_LEVEL_YSIZE 24
4181 #define SP_LEVEL_NAME_LEN 23
4183 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
4186 int initial_player_gravity;
4187 int num_special_ports;
4190 /* for details of the Supaplex level format, see Herman Perk's Supaplex
4191 documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
4193 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
4194 for (y = 0; y < SP_LEVEL_YSIZE; y++)
4196 for (x = 0; x < SP_LEVEL_XSIZE; x++)
4198 int element_old = fgetc(file);
4201 if (element_old <= 0x27)
4202 element_new = getMappedElement(EL_SP_START + element_old);
4203 else if (element_old == 0x28)
4204 element_new = EL_INVISIBLE_WALL;
4207 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
4208 Error(ERR_WARN, "invalid level element %d", element_old);
4210 element_new = EL_UNKNOWN;
4213 level->field[x][y] = element_new;
4217 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
4219 /* initial gravity: 1 == "on", anything else (0) == "off" */
4220 initial_player_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
4222 for (i = 0; i < MAX_PLAYERS; i++)
4223 level->initial_player_gravity[i] = initial_player_gravity;
4225 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
4227 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
4228 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4229 level->name[i] = fgetc(file);
4230 level->name[SP_LEVEL_NAME_LEN] = '\0';
4232 /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
4233 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
4235 /* number of infotrons needed; 0 means that Supaplex will count the total
4236 amount of infotrons in the level and use the low byte of that number
4237 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
4238 level->gems_needed = fgetc(file);
4240 /* number of special ("gravity") port entries below (maximum 10 allowed) */
4241 num_special_ports = fgetc(file);
4243 /* database of properties of up to 10 special ports (6 bytes per port) */
4244 for (i = 0; i < 10; i++)
4246 int port_location, port_x, port_y, port_element;
4249 /* high and low byte of the location of a special port; if (x, y) are the
4250 coordinates of a port in the field and (0, 0) is the top-left corner,
4251 the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
4252 of what may be expected: Supaplex works with a game field in memory
4253 which is 2 bytes per tile) */
4254 port_location = getFile16BitBE(file);
4256 /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
4257 gravity = fgetc(file);
4259 /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
4260 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
4262 /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
4263 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
4265 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
4267 if (i >= num_special_ports)
4270 port_x = (port_location / 2) % SP_LEVEL_XSIZE;
4271 port_y = (port_location / 2) / SP_LEVEL_XSIZE;
4273 if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
4274 port_y < 0 || port_y >= SP_LEVEL_YSIZE)
4276 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
4282 port_element = level->field[port_x][port_y];
4284 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4285 port_element > EL_SP_GRAVITY_PORT_UP)
4287 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
4292 /* change previous (wrong) gravity inverting special port to either
4293 gravity enabling special port or gravity disabling special port */
4294 level->field[port_x][port_y] +=
4295 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4296 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4299 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
4301 /* change special gravity ports without database entries to normal ports */
4302 for (y = 0; y < SP_LEVEL_YSIZE; y++)
4303 for (x = 0; x < SP_LEVEL_XSIZE; x++)
4304 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4305 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4306 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4308 /* auto-determine number of infotrons if it was stored as "0" -- see above */
4309 if (level->gems_needed == 0)
4311 for (y = 0; y < SP_LEVEL_YSIZE; y++)
4312 for (x = 0; x < SP_LEVEL_XSIZE; x++)
4313 if (level->field[x][y] == EL_SP_INFOTRON)
4314 level->gems_needed++;
4316 level->gems_needed &= 0xff; /* only use low byte -- see above */
4319 level->fieldx = SP_LEVEL_XSIZE;
4320 level->fieldy = SP_LEVEL_YSIZE;
4322 level->time = 0; /* no time limit */
4323 level->amoeba_speed = 0;
4324 level->time_magic_wall = 0;
4325 level->time_wheel = 0;
4326 level->amoeba_content = EL_EMPTY;
4329 /* original Supaplex does not use score values -- use default values */
4331 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4332 level->score[i] = 0;
4335 /* there are no yamyams in supaplex levels */
4336 for (i = 0; i < level->num_yamyam_contents; i++)
4337 for (y = 0; y < 3; y++)
4338 for (x = 0; x < 3; x++)
4339 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4342 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
4343 struct LevelFileInfo *level_file_info,
4344 boolean level_info_only)
4346 char *filename = level_file_info->filename;
4348 int nr = level_file_info->nr - leveldir_current->first_level;
4350 char name_first, name_last;
4351 struct LevelInfo multipart_level;
4352 int multipart_xpos, multipart_ypos;
4353 boolean is_multipart_level;
4354 boolean is_first_part;
4355 boolean reading_multipart_level = FALSE;
4356 boolean use_empty_level = FALSE;
4358 if (!(file = fopen(filename, MODE_READ)))
4360 level->no_valid_file = TRUE;
4362 if (!level_info_only)
4363 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4368 /* position file stream to the requested level inside the level package */
4369 if (level_file_info->packed &&
4370 fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
4372 level->no_valid_file = TRUE;
4374 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
4379 /* there exist Supaplex level package files with multi-part levels which
4380 can be detected as follows: instead of leading and trailing dashes ('-')
4381 to pad the level name, they have leading and trailing numbers which are
4382 the x and y coordinations of the current part of the multi-part level;
4383 if there are '?' characters instead of numbers on the left or right side
4384 of the level name, the multi-part level consists of only horizontal or
4387 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
4389 LoadLevelFromFileStream_SP(file, level, l);
4391 /* check if this level is a part of a bigger multi-part level */
4393 name_first = level->name[0];
4394 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
4396 is_multipart_level =
4397 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
4398 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
4401 ((name_first == '?' || name_first == '1') &&
4402 (name_last == '?' || name_last == '1'));
4404 /* correct leading multipart level meta information in level name */
4405 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
4406 level->name[i] = '-';
4408 /* correct trailing multipart level meta information in level name */
4409 for (i = SP_LEVEL_NAME_LEN - 1; i >= 0 && level->name[i] == name_last; i--)
4410 level->name[i] = '-';
4412 /* ---------- check for normal single level ---------- */
4414 if (!reading_multipart_level && !is_multipart_level)
4416 /* the current level is simply a normal single-part level, and we are
4417 not reading a multi-part level yet, so return the level as it is */
4422 /* ---------- check for empty level (unused multi-part) ---------- */
4424 if (!reading_multipart_level && is_multipart_level && !is_first_part)
4426 /* this is a part of a multi-part level, but not the first part
4427 (and we are not already reading parts of a multi-part level);
4428 in this case, use an empty level instead of the single part */
4430 use_empty_level = TRUE;
4435 /* ---------- check for finished multi-part level ---------- */
4437 if (reading_multipart_level &&
4438 (!is_multipart_level ||
4439 !strEqual(level->name, multipart_level.name)))
4441 /* we are already reading parts of a multi-part level, but this level is
4442 either not a multi-part level, or a part of a different multi-part
4443 level; in both cases, the multi-part level seems to be complete */
4448 /* ---------- here we have one part of a multi-part level ---------- */
4450 reading_multipart_level = TRUE;
4452 if (is_first_part) /* start with first part of new multi-part level */
4454 /* copy level info structure from first part */
4455 multipart_level = *level;
4457 /* clear playfield of new multi-part level */
4458 for (y = 0; y < MAX_LEV_FIELDY; y++)
4459 for (x = 0; x < MAX_LEV_FIELDX; x++)
4460 multipart_level.field[x][y] = EL_EMPTY;
4463 if (name_first == '?')
4465 if (name_last == '?')
4468 multipart_xpos = (int)(name_first - '0');
4469 multipart_ypos = (int)(name_last - '0');
4472 printf("----------> part (%d/%d) of multi-part level '%s'\n",
4473 multipart_xpos, multipart_ypos, multipart_level.name);
4476 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
4477 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
4479 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
4484 multipart_level.fieldx = MAX(multipart_level.fieldx,
4485 multipart_xpos * SP_LEVEL_XSIZE);
4486 multipart_level.fieldy = MAX(multipart_level.fieldy,
4487 multipart_ypos * SP_LEVEL_YSIZE);
4489 /* copy level part at the right position of multi-part level */
4490 for (y = 0; y < SP_LEVEL_YSIZE; y++)
4492 for (x = 0; x < SP_LEVEL_XSIZE; x++)
4494 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
4495 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
4497 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
4504 if (use_empty_level)
4506 setLevelInfoToDefaults(level);
4508 level->fieldx = SP_LEVEL_XSIZE;
4509 level->fieldy = SP_LEVEL_YSIZE;
4511 for (y = 0; y < SP_LEVEL_YSIZE; y++)
4512 for (x = 0; x < SP_LEVEL_XSIZE; x++)
4513 level->field[x][y] = EL_EMPTY;
4515 strcpy(level->name, "-------- EMPTY --------");
4517 Error(ERR_WARN, "single part of multi-part level -- using empty level");
4520 if (reading_multipart_level)
4521 *level = multipart_level;
4526 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4528 struct LevelInfo_SP *level_sp = level->native_sp_level;
4529 LevelInfoType *header = &level_sp->header;
4532 level_sp->width = level->fieldx;
4533 level_sp->height = level->fieldy;
4535 for (x = 0; x < level->fieldx; x++)
4536 for (y = 0; y < level->fieldy; y++)
4537 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4539 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4541 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4542 header->LevelTitle[i] = level->name[i];
4543 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
4545 header->InfotronsNeeded = level->gems_needed;
4547 header->SpecialPortCount = 0;
4549 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4551 boolean gravity_port_found = FALSE;
4552 boolean gravity_port_valid = FALSE;
4553 int gravity_port_flag;
4554 int gravity_port_base_element;
4555 int element = level->field[x][y];
4557 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4558 element <= EL_SP_GRAVITY_ON_PORT_UP)
4560 gravity_port_found = TRUE;
4561 gravity_port_valid = TRUE;
4562 gravity_port_flag = 1;
4563 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4565 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4566 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4568 gravity_port_found = TRUE;
4569 gravity_port_valid = TRUE;
4570 gravity_port_flag = 0;
4571 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4573 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4574 element <= EL_SP_GRAVITY_PORT_UP)
4576 /* change R'n'D style gravity inverting special port to normal port
4577 (there are no gravity inverting ports in native Supaplex engine) */
4579 gravity_port_found = TRUE;
4580 gravity_port_valid = FALSE;
4581 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4584 if (gravity_port_found)
4586 if (gravity_port_valid &&
4587 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4589 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4591 port->PortLocation = (y * level->fieldx + x) * 2;
4592 port->Gravity = gravity_port_flag;
4594 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4596 header->SpecialPortCount++;
4600 /* change special gravity port to normal port */
4602 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4605 level_sp->playfield[x][y] = element - EL_SP_START;
4610 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4612 struct LevelInfo_SP *level_sp = level->native_sp_level;
4613 LevelInfoType *header = &level_sp->header;
4616 level->fieldx = level_sp->width;
4617 level->fieldy = level_sp->height;
4619 for (x = 0; x < level->fieldx; x++)
4621 for (y = 0; y < level->fieldy; y++)
4623 int element_old = level_sp->playfield[x][y];
4624 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4626 if (element_new == EL_UNKNOWN)
4627 Error(ERR_WARN, "invalid element %d at position %d, %d",
4630 level->field[x][y] = element_new;
4634 for (i = 0; i < MAX_PLAYERS; i++)
4635 level->initial_player_gravity[i] =
4636 (header->InitialGravity == 1 ? TRUE : FALSE);
4638 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4639 level->name[i] = header->LevelTitle[i];
4640 level->name[SP_LEVEL_NAME_LEN] = '\0';
4642 level->gems_needed = header->InfotronsNeeded;
4644 for (i = 0; i < header->SpecialPortCount; i++)
4646 SpecialPortType *port = &header->SpecialPort[i];
4647 int port_location = port->PortLocation;
4648 int gravity = port->Gravity;
4649 int port_x, port_y, port_element;
4651 port_x = (port_location / 2) % level->fieldx;
4652 port_y = (port_location / 2) / level->fieldx;
4654 if (port_x < 0 || port_x >= level->fieldx ||
4655 port_y < 0 || port_y >= level->fieldy)
4657 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
4663 port_element = level->field[port_x][port_y];
4665 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4666 port_element > EL_SP_GRAVITY_PORT_UP)
4668 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
4673 /* change previous (wrong) gravity inverting special port to either
4674 gravity enabling special port or gravity disabling special port */
4675 level->field[port_x][port_y] +=
4676 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4677 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4680 /* change special gravity ports without database entries to normal ports */
4681 for (x = 0; x < level->fieldx; x++)
4682 for (y = 0; y < level->fieldy; y++)
4683 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4684 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4685 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4687 level->time = 0; /* no time limit */
4688 level->amoeba_speed = 0;
4689 level->time_magic_wall = 0;
4690 level->time_wheel = 0;
4691 level->amoeba_content = EL_EMPTY;
4694 /* original Supaplex does not use score values -- use default values */
4696 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4697 level->score[i] = 0;
4700 /* there are no yamyams in supaplex levels */
4701 for (i = 0; i < level->num_yamyam_contents; i++)
4702 for (x = 0; x < 3; x++)
4703 for (y = 0; y < 3; y++)
4704 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4707 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4709 struct LevelInfo_SP *level_sp = level->native_sp_level;
4710 struct DemoInfo_SP *demo = &level_sp->demo;
4713 /* always start with reliable default values */
4714 demo->is_available = FALSE;
4717 if (TAPE_IS_EMPTY(tape))
4720 demo->level_nr = tape.level_nr; /* (currently not used) */
4722 level_sp->header.DemoRandomSeed = tape.random_seed;
4725 for (i = 0; i < tape.length; i++)
4727 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4728 int demo_repeat = tape.pos[i].delay;
4730 for (j = 0; j < demo_repeat / 16; j++)
4731 demo->data[demo->length++] = 0xf0 | demo_action;
4733 if (demo_repeat % 16)
4734 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4737 demo->data[demo->length++] = 0xff;
4739 demo->is_available = TRUE;
4742 static void setTapeInfoToDefaults();
4744 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4746 struct LevelInfo_SP *level_sp = level->native_sp_level;
4747 struct DemoInfo_SP *demo = &level_sp->demo;
4748 char *filename = level->file_info.filename;
4751 /* always start with reliable default values */
4752 setTapeInfoToDefaults();
4754 if (!demo->is_available)
4757 tape.level_nr = demo->level_nr; /* (currently not used) */
4758 tape.length = demo->length - 1; /* without "end of demo" byte */
4759 tape.random_seed = level_sp->header.DemoRandomSeed;
4761 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4763 for (i = 0; i < demo->length - 1; i++)
4765 int demo_action = demo->data[i] & 0x0f;
4766 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4768 tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
4769 tape.pos[i].delay = demo_repeat + 1;
4772 tape.length_seconds = GetTapeLength();
4776 /* ------------------------------------------------------------------------- */
4777 /* functions for loading DC level */
4778 /* ------------------------------------------------------------------------- */
4780 #define DC_LEVEL_HEADER_SIZE 344
4782 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
4784 static int last_data_encoded;
4788 int diff_hi, diff_lo;
4789 int data_hi, data_lo;
4790 unsigned short data_decoded;
4794 last_data_encoded = 0;
4801 diff = data_encoded - last_data_encoded;
4802 diff_hi = diff & ~0xff;
4803 diff_lo = diff & 0xff;
4807 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4808 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4809 data_hi = data_hi & 0xff00;
4811 data_decoded = data_hi | data_lo;
4813 last_data_encoded = data_encoded;
4815 offset1 = (offset1 + 1) % 31;
4816 offset2 = offset2 & 0xff;
4818 return data_decoded;
4821 int getMappedElement_DC(int element)
4829 /* 0x0117 - 0x036e: (?) */
4832 /* 0x042d - 0x0684: (?) */
4848 element = EL_CRYSTAL;
4851 case 0x0e77: /* quicksand (boulder) */
4852 element = EL_QUICKSAND_FAST_FULL;
4855 case 0x0e99: /* slow quicksand (boulder) */
4856 element = EL_QUICKSAND_FULL;
4860 element = EL_EM_EXIT_OPEN;
4864 element = EL_EM_EXIT_CLOSED;
4868 element = EL_EM_STEEL_EXIT_OPEN;
4872 element = EL_EM_STEEL_EXIT_CLOSED;
4875 case 0x0f4f: /* dynamite (lit 1) */
4876 element = EL_EM_DYNAMITE_ACTIVE;
4879 case 0x0f57: /* dynamite (lit 2) */
4880 element = EL_EM_DYNAMITE_ACTIVE;
4883 case 0x0f5f: /* dynamite (lit 3) */
4884 element = EL_EM_DYNAMITE_ACTIVE;
4887 case 0x0f67: /* dynamite (lit 4) */
4888 element = EL_EM_DYNAMITE_ACTIVE;
4895 element = EL_AMOEBA_WET;
4899 element = EL_AMOEBA_DROP;
4903 element = EL_DC_MAGIC_WALL;
4907 element = EL_SPACESHIP_UP;
4911 element = EL_SPACESHIP_DOWN;
4915 element = EL_SPACESHIP_LEFT;
4919 element = EL_SPACESHIP_RIGHT;
4923 element = EL_BUG_UP;
4927 element = EL_BUG_DOWN;
4931 element = EL_BUG_LEFT;
4935 element = EL_BUG_RIGHT;
4939 element = EL_MOLE_UP;
4943 element = EL_MOLE_DOWN;
4947 element = EL_MOLE_LEFT;
4951 element = EL_MOLE_RIGHT;
4959 element = EL_YAMYAM;
4963 element = EL_SWITCHGATE_OPEN;
4967 element = EL_SWITCHGATE_CLOSED;
4971 element = EL_DC_SWITCHGATE_SWITCH_UP;
4975 element = EL_TIMEGATE_CLOSED;
4978 case 0x144c: /* conveyor belt switch (green) */
4979 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4982 case 0x144f: /* conveyor belt switch (red) */
4983 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4986 case 0x1452: /* conveyor belt switch (blue) */
4987 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4991 element = EL_CONVEYOR_BELT_3_MIDDLE;
4995 element = EL_CONVEYOR_BELT_3_LEFT;
4999 element = EL_CONVEYOR_BELT_3_RIGHT;
5003 element = EL_CONVEYOR_BELT_1_MIDDLE;
5007 element = EL_CONVEYOR_BELT_1_LEFT;
5011 element = EL_CONVEYOR_BELT_1_RIGHT;
5015 element = EL_CONVEYOR_BELT_4_MIDDLE;
5019 element = EL_CONVEYOR_BELT_4_LEFT;
5023 element = EL_CONVEYOR_BELT_4_RIGHT;
5027 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5031 element = EL_EXPANDABLE_WALL_VERTICAL;
5035 element = EL_EXPANDABLE_WALL_ANY;
5038 case 0x14ce: /* growing steel wall (left/right) */
5039 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5042 case 0x14df: /* growing steel wall (up/down) */
5043 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5046 case 0x14e8: /* growing steel wall (up/down/left/right) */
5047 element = EL_EXPANDABLE_STEELWALL_ANY;
5051 element = EL_SHIELD_DEADLY;
5055 element = EL_EXTRA_TIME;
5063 element = EL_EMPTY_SPACE;
5066 case 0x1578: /* quicksand (empty) */
5067 element = EL_QUICKSAND_FAST_EMPTY;
5070 case 0x1579: /* slow quicksand (empty) */
5071 element = EL_QUICKSAND_EMPTY;
5074 /* 0x157c - 0x158b: */
5077 /* 0x1590 - 0x159f: */
5078 /* EL_DC_LANDMINE */
5081 element = EL_EM_DYNAMITE;
5084 case 0x15a1: /* key (red) */
5085 element = EL_EM_KEY_1;
5088 case 0x15a2: /* key (yellow) */
5089 element = EL_EM_KEY_2;
5092 case 0x15a3: /* key (blue) */
5093 element = EL_EM_KEY_4;
5096 case 0x15a4: /* key (green) */
5097 element = EL_EM_KEY_3;
5100 case 0x15a5: /* key (white) */
5101 element = EL_DC_KEY_WHITE;
5105 element = EL_WALL_SLIPPERY;
5112 case 0x15a8: /* wall (not round) */
5116 case 0x15a9: /* (blue) */
5117 element = EL_CHAR_A;
5120 case 0x15aa: /* (blue) */
5121 element = EL_CHAR_B;
5124 case 0x15ab: /* (blue) */
5125 element = EL_CHAR_C;
5128 case 0x15ac: /* (blue) */
5129 element = EL_CHAR_D;
5132 case 0x15ad: /* (blue) */
5133 element = EL_CHAR_E;
5136 case 0x15ae: /* (blue) */
5137 element = EL_CHAR_F;
5140 case 0x15af: /* (blue) */
5141 element = EL_CHAR_G;
5144 case 0x15b0: /* (blue) */
5145 element = EL_CHAR_H;
5148 case 0x15b1: /* (blue) */
5149 element = EL_CHAR_I;
5152 case 0x15b2: /* (blue) */
5153 element = EL_CHAR_J;
5156 case 0x15b3: /* (blue) */
5157 element = EL_CHAR_K;
5160 case 0x15b4: /* (blue) */
5161 element = EL_CHAR_L;
5164 case 0x15b5: /* (blue) */
5165 element = EL_CHAR_M;
5168 case 0x15b6: /* (blue) */
5169 element = EL_CHAR_N;
5172 case 0x15b7: /* (blue) */
5173 element = EL_CHAR_O;
5176 case 0x15b8: /* (blue) */
5177 element = EL_CHAR_P;
5180 case 0x15b9: /* (blue) */
5181 element = EL_CHAR_Q;
5184 case 0x15ba: /* (blue) */
5185 element = EL_CHAR_R;
5188 case 0x15bb: /* (blue) */
5189 element = EL_CHAR_S;
5192 case 0x15bc: /* (blue) */
5193 element = EL_CHAR_T;
5196 case 0x15bd: /* (blue) */
5197 element = EL_CHAR_U;
5200 case 0x15be: /* (blue) */
5201 element = EL_CHAR_V;
5204 case 0x15bf: /* (blue) */
5205 element = EL_CHAR_W;
5208 case 0x15c0: /* (blue) */
5209 element = EL_CHAR_X;
5212 case 0x15c1: /* (blue) */
5213 element = EL_CHAR_Y;
5216 case 0x15c2: /* (blue) */
5217 element = EL_CHAR_Z;
5220 case 0x15c3: /* (blue) */
5221 element = EL_CHAR_AUMLAUT;
5224 case 0x15c4: /* (blue) */
5225 element = EL_CHAR_OUMLAUT;
5228 case 0x15c5: /* (blue) */
5229 element = EL_CHAR_UUMLAUT;
5232 case 0x15c6: /* (blue) */
5233 element = EL_CHAR_0;
5236 case 0x15c7: /* (blue) */
5237 element = EL_CHAR_1;
5240 case 0x15c8: /* (blue) */
5241 element = EL_CHAR_2;
5244 case 0x15c9: /* (blue) */
5245 element = EL_CHAR_3;
5248 case 0x15ca: /* (blue) */
5249 element = EL_CHAR_4;
5252 case 0x15cb: /* (blue) */
5253 element = EL_CHAR_5;
5256 case 0x15cc: /* (blue) */
5257 element = EL_CHAR_6;
5260 case 0x15cd: /* (blue) */
5261 element = EL_CHAR_7;
5264 case 0x15ce: /* (blue) */
5265 element = EL_CHAR_8;
5268 case 0x15cf: /* (blue) */
5269 element = EL_CHAR_9;
5272 case 0x15d0: /* (blue) */
5273 element = EL_CHAR_PERIOD;
5276 case 0x15d1: /* (blue) */
5277 element = EL_CHAR_EXCLAM;
5280 case 0x15d2: /* (blue) */
5281 element = EL_CHAR_COLON;
5284 case 0x15d3: /* (blue) */
5285 element = EL_CHAR_LESS;
5288 case 0x15d4: /* (blue) */
5289 element = EL_CHAR_GREATER;
5292 case 0x15d5: /* (blue) */
5293 element = EL_CHAR_QUESTION;
5296 case 0x15d6: /* (blue) */
5297 element = EL_CHAR_COPYRIGHT;
5300 case 0x15d7: /* (blue) */
5301 element = EL_CHAR_UP;
5304 case 0x15d8: /* (blue) */
5305 element = EL_CHAR_DOWN;
5308 case 0x15d9: /* (blue) */
5309 element = EL_CHAR_BUTTON;
5312 case 0x15da: /* (blue) */
5313 element = EL_CHAR_PLUS;
5316 case 0x15db: /* (blue) */
5317 element = EL_CHAR_MINUS;
5320 case 0x15dc: /* (blue) */
5321 element = EL_CHAR_APOSTROPHE;
5324 case 0x15dd: /* (blue) */
5325 element = EL_CHAR_PARENLEFT;
5328 case 0x15de: /* (blue) */
5329 element = EL_CHAR_PARENRIGHT;
5332 case 0x15df: /* (green) */
5333 element = EL_CHAR_A;
5336 case 0x15e0: /* (green) */
5337 element = EL_CHAR_B;
5340 case 0x15e1: /* (green) */
5341 element = EL_CHAR_C;
5344 case 0x15e2: /* (green) */
5345 element = EL_CHAR_D;
5348 case 0x15e3: /* (green) */
5349 element = EL_CHAR_E;
5352 case 0x15e4: /* (green) */
5353 element = EL_CHAR_F;
5356 case 0x15e5: /* (green) */
5357 element = EL_CHAR_G;
5360 case 0x15e6: /* (green) */
5361 element = EL_CHAR_H;
5364 case 0x15e7: /* (green) */
5365 element = EL_CHAR_I;
5368 case 0x15e8: /* (green) */
5369 element = EL_CHAR_J;
5372 case 0x15e9: /* (green) */
5373 element = EL_CHAR_K;
5376 case 0x15ea: /* (green) */
5377 element = EL_CHAR_L;
5380 case 0x15eb: /* (green) */
5381 element = EL_CHAR_M;
5384 case 0x15ec: /* (green) */
5385 element = EL_CHAR_N;
5388 case 0x15ed: /* (green) */
5389 element = EL_CHAR_O;
5392 case 0x15ee: /* (green) */
5393 element = EL_CHAR_P;
5396 case 0x15ef: /* (green) */
5397 element = EL_CHAR_Q;
5400 case 0x15f0: /* (green) */
5401 element = EL_CHAR_R;
5404 case 0x15f1: /* (green) */
5405 element = EL_CHAR_S;
5408 case 0x15f2: /* (green) */
5409 element = EL_CHAR_T;
5412 case 0x15f3: /* (green) */
5413 element = EL_CHAR_U;
5416 case 0x15f4: /* (green) */
5417 element = EL_CHAR_V;
5420 case 0x15f5: /* (green) */
5421 element = EL_CHAR_W;
5424 case 0x15f6: /* (green) */
5425 element = EL_CHAR_X;
5428 case 0x15f7: /* (green) */
5429 element = EL_CHAR_Y;
5432 case 0x15f8: /* (green) */
5433 element = EL_CHAR_Z;
5436 case 0x15f9: /* (green) */
5437 element = EL_CHAR_AUMLAUT;
5440 case 0x15fa: /* (green) */
5441 element = EL_CHAR_OUMLAUT;
5444 case 0x15fb: /* (green) */
5445 element = EL_CHAR_UUMLAUT;
5448 case 0x15fc: /* (green) */
5449 element = EL_CHAR_0;
5452 case 0x15fd: /* (green) */
5453 element = EL_CHAR_1;
5456 case 0x15fe: /* (green) */
5457 element = EL_CHAR_2;
5460 case 0x15ff: /* (green) */
5461 element = EL_CHAR_3;
5464 case 0x1600: /* (green) */
5465 element = EL_CHAR_4;
5468 case 0x1601: /* (green) */
5469 element = EL_CHAR_5;
5472 case 0x1602: /* (green) */
5473 element = EL_CHAR_6;
5476 case 0x1603: /* (green) */
5477 element = EL_CHAR_7;
5480 case 0x1604: /* (green) */
5481 element = EL_CHAR_8;
5484 case 0x1605: /* (green) */
5485 element = EL_CHAR_9;
5488 case 0x1606: /* (green) */
5489 element = EL_CHAR_PERIOD;
5492 case 0x1607: /* (green) */
5493 element = EL_CHAR_EXCLAM;
5496 case 0x1608: /* (green) */
5497 element = EL_CHAR_COLON;
5500 case 0x1609: /* (green) */
5501 element = EL_CHAR_LESS;
5504 case 0x160a: /* (green) */
5505 element = EL_CHAR_GREATER;
5508 case 0x160b: /* (green) */
5509 element = EL_CHAR_QUESTION;
5512 case 0x160c: /* (green) */
5513 element = EL_CHAR_COPYRIGHT;
5516 case 0x160d: /* (green) */
5517 element = EL_CHAR_UP;
5520 case 0x160e: /* (green) */
5521 element = EL_CHAR_DOWN;
5524 case 0x160f: /* (green) */
5525 element = EL_CHAR_BUTTON;
5528 case 0x1610: /* (green) */
5529 element = EL_CHAR_PLUS;
5532 case 0x1611: /* (green) */
5533 element = EL_CHAR_MINUS;
5536 case 0x1612: /* (green) */
5537 element = EL_CHAR_APOSTROPHE;
5540 case 0x1613: /* (green) */
5541 element = EL_CHAR_PARENLEFT;
5544 case 0x1614: /* (green) */
5545 element = EL_CHAR_PARENRIGHT;
5548 case 0x1615: /* (blue steel) */
5549 element = EL_STEEL_CHAR_A;
5552 case 0x1616: /* (blue steel) */
5553 element = EL_STEEL_CHAR_B;
5556 case 0x1617: /* (blue steel) */
5557 element = EL_STEEL_CHAR_C;
5560 case 0x1618: /* (blue steel) */
5561 element = EL_STEEL_CHAR_D;
5564 case 0x1619: /* (blue steel) */
5565 element = EL_STEEL_CHAR_E;
5568 case 0x161a: /* (blue steel) */
5569 element = EL_STEEL_CHAR_F;
5572 case 0x161b: /* (blue steel) */
5573 element = EL_STEEL_CHAR_G;
5576 case 0x161c: /* (blue steel) */
5577 element = EL_STEEL_CHAR_H;
5580 case 0x161d: /* (blue steel) */
5581 element = EL_STEEL_CHAR_I;
5584 case 0x161e: /* (blue steel) */
5585 element = EL_STEEL_CHAR_J;
5588 case 0x161f: /* (blue steel) */
5589 element = EL_STEEL_CHAR_K;
5592 case 0x1620: /* (blue steel) */
5593 element = EL_STEEL_CHAR_L;
5596 case 0x1621: /* (blue steel) */
5597 element = EL_STEEL_CHAR_M;
5600 case 0x1622: /* (blue steel) */
5601 element = EL_STEEL_CHAR_N;
5604 case 0x1623: /* (blue steel) */
5605 element = EL_STEEL_CHAR_O;
5608 case 0x1624: /* (blue steel) */
5609 element = EL_STEEL_CHAR_P;
5612 case 0x1625: /* (blue steel) */
5613 element = EL_STEEL_CHAR_Q;
5616 case 0x1626: /* (blue steel) */
5617 element = EL_STEEL_CHAR_R;
5620 case 0x1627: /* (blue steel) */
5621 element = EL_STEEL_CHAR_S;
5624 case 0x1628: /* (blue steel) */
5625 element = EL_STEEL_CHAR_T;
5628 case 0x1629: /* (blue steel) */
5629 element = EL_STEEL_CHAR_U;
5632 case 0x162a: /* (blue steel) */
5633 element = EL_STEEL_CHAR_V;
5636 case 0x162b: /* (blue steel) */
5637 element = EL_STEEL_CHAR_W;
5640 case 0x162c: /* (blue steel) */
5641 element = EL_STEEL_CHAR_X;
5644 case 0x162d: /* (blue steel) */
5645 element = EL_STEEL_CHAR_Y;
5648 case 0x162e: /* (blue steel) */
5649 element = EL_STEEL_CHAR_Z;
5652 case 0x162f: /* (blue steel) */
5653 element = EL_STEEL_CHAR_AUMLAUT;
5656 case 0x1630: /* (blue steel) */
5657 element = EL_STEEL_CHAR_OUMLAUT;
5660 case 0x1631: /* (blue steel) */
5661 element = EL_STEEL_CHAR_UUMLAUT;
5664 case 0x1632: /* (blue steel) */
5665 element = EL_STEEL_CHAR_0;
5668 case 0x1633: /* (blue steel) */
5669 element = EL_STEEL_CHAR_1;
5672 case 0x1634: /* (blue steel) */
5673 element = EL_STEEL_CHAR_2;
5676 case 0x1635: /* (blue steel) */
5677 element = EL_STEEL_CHAR_3;
5680 case 0x1636: /* (blue steel) */
5681 element = EL_STEEL_CHAR_4;
5684 case 0x1637: /* (blue steel) */
5685 element = EL_STEEL_CHAR_5;
5688 case 0x1638: /* (blue steel) */
5689 element = EL_STEEL_CHAR_6;
5692 case 0x1639: /* (blue steel) */
5693 element = EL_STEEL_CHAR_7;
5696 case 0x163a: /* (blue steel) */
5697 element = EL_STEEL_CHAR_8;
5700 case 0x163b: /* (blue steel) */
5701 element = EL_STEEL_CHAR_9;
5704 case 0x163c: /* (blue steel) */
5705 element = EL_STEEL_CHAR_PERIOD;
5708 case 0x163d: /* (blue steel) */
5709 element = EL_STEEL_CHAR_EXCLAM;
5712 case 0x163e: /* (blue steel) */
5713 element = EL_STEEL_CHAR_COLON;
5716 case 0x163f: /* (blue steel) */
5717 element = EL_STEEL_CHAR_LESS;
5720 case 0x1640: /* (blue steel) */
5721 element = EL_STEEL_CHAR_GREATER;
5724 case 0x1641: /* (blue steel) */
5725 element = EL_STEEL_CHAR_QUESTION;
5728 case 0x1642: /* (blue steel) */
5729 element = EL_STEEL_CHAR_COPYRIGHT;
5732 case 0x1643: /* (blue steel) */
5733 element = EL_STEEL_CHAR_UP;
5736 case 0x1644: /* (blue steel) */
5737 element = EL_STEEL_CHAR_DOWN;
5740 case 0x1645: /* (blue steel) */
5741 element = EL_STEEL_CHAR_BUTTON;
5744 case 0x1646: /* (blue steel) */
5745 element = EL_STEEL_CHAR_PLUS;
5748 case 0x1647: /* (blue steel) */
5749 element = EL_STEEL_CHAR_MINUS;
5752 case 0x1648: /* (blue steel) */
5753 element = EL_STEEL_CHAR_APOSTROPHE;
5756 case 0x1649: /* (blue steel) */
5757 element = EL_STEEL_CHAR_PARENLEFT;
5760 case 0x164a: /* (blue steel) */
5761 element = EL_STEEL_CHAR_PARENRIGHT;
5764 case 0x164b: /* (green steel) */
5765 element = EL_STEEL_CHAR_A;
5768 case 0x164c: /* (green steel) */
5769 element = EL_STEEL_CHAR_B;
5772 case 0x164d: /* (green steel) */
5773 element = EL_STEEL_CHAR_C;
5776 case 0x164e: /* (green steel) */
5777 element = EL_STEEL_CHAR_D;
5780 case 0x164f: /* (green steel) */
5781 element = EL_STEEL_CHAR_E;
5784 case 0x1650: /* (green steel) */
5785 element = EL_STEEL_CHAR_F;
5788 case 0x1651: /* (green steel) */
5789 element = EL_STEEL_CHAR_G;
5792 case 0x1652: /* (green steel) */
5793 element = EL_STEEL_CHAR_H;
5796 case 0x1653: /* (green steel) */
5797 element = EL_STEEL_CHAR_I;
5800 case 0x1654: /* (green steel) */
5801 element = EL_STEEL_CHAR_J;
5804 case 0x1655: /* (green steel) */
5805 element = EL_STEEL_CHAR_K;
5808 case 0x1656: /* (green steel) */
5809 element = EL_STEEL_CHAR_L;
5812 case 0x1657: /* (green steel) */
5813 element = EL_STEEL_CHAR_M;
5816 case 0x1658: /* (green steel) */
5817 element = EL_STEEL_CHAR_N;
5820 case 0x1659: /* (green steel) */
5821 element = EL_STEEL_CHAR_O;
5824 case 0x165a: /* (green steel) */
5825 element = EL_STEEL_CHAR_P;
5828 case 0x165b: /* (green steel) */
5829 element = EL_STEEL_CHAR_Q;
5832 case 0x165c: /* (green steel) */
5833 element = EL_STEEL_CHAR_R;
5836 case 0x165d: /* (green steel) */
5837 element = EL_STEEL_CHAR_S;
5840 case 0x165e: /* (green steel) */
5841 element = EL_STEEL_CHAR_T;
5844 case 0x165f: /* (green steel) */
5845 element = EL_STEEL_CHAR_U;
5848 case 0x1660: /* (green steel) */
5849 element = EL_STEEL_CHAR_V;
5852 case 0x1661: /* (green steel) */
5853 element = EL_STEEL_CHAR_W;
5856 case 0x1662: /* (green steel) */
5857 element = EL_STEEL_CHAR_X;
5860 case 0x1663: /* (green steel) */
5861 element = EL_STEEL_CHAR_Y;
5864 case 0x1664: /* (green steel) */
5865 element = EL_STEEL_CHAR_Z;
5868 case 0x1665: /* (green steel) */
5869 element = EL_STEEL_CHAR_AUMLAUT;
5872 case 0x1666: /* (green steel) */
5873 element = EL_STEEL_CHAR_OUMLAUT;
5876 case 0x1667: /* (green steel) */
5877 element = EL_STEEL_CHAR_UUMLAUT;
5880 case 0x1668: /* (green steel) */
5881 element = EL_STEEL_CHAR_0;
5884 case 0x1669: /* (green steel) */
5885 element = EL_STEEL_CHAR_1;
5888 case 0x166a: /* (green steel) */
5889 element = EL_STEEL_CHAR_2;
5892 case 0x166b: /* (green steel) */
5893 element = EL_STEEL_CHAR_3;
5896 case 0x166c: /* (green steel) */
5897 element = EL_STEEL_CHAR_4;
5900 case 0x166d: /* (green steel) */
5901 element = EL_STEEL_CHAR_5;
5904 case 0x166e: /* (green steel) */
5905 element = EL_STEEL_CHAR_6;
5908 case 0x166f: /* (green steel) */
5909 element = EL_STEEL_CHAR_7;
5912 case 0x1670: /* (green steel) */
5913 element = EL_STEEL_CHAR_8;
5916 case 0x1671: /* (green steel) */
5917 element = EL_STEEL_CHAR_9;
5920 case 0x1672: /* (green steel) */
5921 element = EL_STEEL_CHAR_PERIOD;
5924 case 0x1673: /* (green steel) */
5925 element = EL_STEEL_CHAR_EXCLAM;
5928 case 0x1674: /* (green steel) */
5929 element = EL_STEEL_CHAR_COLON;
5932 case 0x1675: /* (green steel) */
5933 element = EL_STEEL_CHAR_LESS;
5936 case 0x1676: /* (green steel) */
5937 element = EL_STEEL_CHAR_GREATER;
5940 case 0x1677: /* (green steel) */
5941 element = EL_STEEL_CHAR_QUESTION;
5944 case 0x1678: /* (green steel) */
5945 element = EL_STEEL_CHAR_COPYRIGHT;
5948 case 0x1679: /* (green steel) */
5949 element = EL_STEEL_CHAR_UP;
5952 case 0x167a: /* (green steel) */
5953 element = EL_STEEL_CHAR_DOWN;
5956 case 0x167b: /* (green steel) */
5957 element = EL_STEEL_CHAR_BUTTON;
5960 case 0x167c: /* (green steel) */
5961 element = EL_STEEL_CHAR_PLUS;
5964 case 0x167d: /* (green steel) */
5965 element = EL_STEEL_CHAR_MINUS;
5968 case 0x167e: /* (green steel) */
5969 element = EL_STEEL_CHAR_APOSTROPHE;
5972 case 0x167f: /* (green steel) */
5973 element = EL_STEEL_CHAR_PARENLEFT;
5976 case 0x1680: /* (green steel) */
5977 element = EL_STEEL_CHAR_PARENRIGHT;
5980 case 0x1681: /* gate (red) */
5981 element = EL_EM_GATE_1;
5984 case 0x1682: /* secret gate (red) */
5985 element = EL_GATE_1_GRAY;
5988 case 0x1683: /* gate (yellow) */
5989 element = EL_EM_GATE_2;
5992 case 0x1684: /* secret gate (yellow) */
5993 element = EL_GATE_2_GRAY;
5996 case 0x1685: /* gate (blue) */
5997 element = EL_EM_GATE_4;
6000 case 0x1686: /* secret gate (blue) */
6001 element = EL_GATE_4_GRAY;
6004 case 0x1687: /* gate (green) */
6005 element = EL_EM_GATE_3;
6008 case 0x1688: /* secret gate (green) */
6009 element = EL_GATE_3_GRAY;
6012 case 0x1689: /* gate (white) */
6013 element = EL_DC_GATE_WHITE;
6016 case 0x168a: /* secret gate (white) */
6017 element = EL_DC_GATE_WHITE_GRAY;
6020 case 0x168b: /* secret gate (no key) */
6021 element = EL_DC_GATE_FAKE_GRAY;
6025 element = EL_ROBOT_WHEEL;
6029 element = EL_DC_TIMEGATE_SWITCH;
6033 element = EL_ACID_POOL_BOTTOM;
6037 element = EL_ACID_POOL_TOPLEFT;
6041 element = EL_ACID_POOL_TOPRIGHT;
6045 element = EL_ACID_POOL_BOTTOMLEFT;
6049 element = EL_ACID_POOL_BOTTOMRIGHT;
6053 element = EL_STEELWALL;
6057 element = EL_STEELWALL_SLIPPERY;
6060 case 0x1695: /* steel wall (not round) */
6061 element = EL_STEELWALL;
6064 case 0x1696: /* steel wall (left) */
6065 element = EL_DC_STEELWALL_1_LEFT;
6068 case 0x1697: /* steel wall (bottom) */
6069 element = EL_DC_STEELWALL_1_BOTTOM;
6072 case 0x1698: /* steel wall (right) */
6073 element = EL_DC_STEELWALL_1_RIGHT;
6076 case 0x1699: /* steel wall (top) */
6077 element = EL_DC_STEELWALL_1_TOP;
6080 case 0x169a: /* steel wall (left/bottom) */
6081 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6084 case 0x169b: /* steel wall (right/bottom) */
6085 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6088 case 0x169c: /* steel wall (right/top) */
6089 element = EL_DC_STEELWALL_1_TOPRIGHT;
6092 case 0x169d: /* steel wall (left/top) */
6093 element = EL_DC_STEELWALL_1_TOPLEFT;
6096 case 0x169e: /* steel wall (right/bottom small) */
6097 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6100 case 0x169f: /* steel wall (left/bottom small) */
6101 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6104 case 0x16a0: /* steel wall (right/top small) */
6105 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6108 case 0x16a1: /* steel wall (left/top small) */
6109 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6112 case 0x16a2: /* steel wall (left/right) */
6113 element = EL_DC_STEELWALL_1_VERTICAL;
6116 case 0x16a3: /* steel wall (top/bottom) */
6117 element = EL_DC_STEELWALL_1_HORIZONTAL;
6120 case 0x16a4: /* steel wall 2 (left end) */
6121 element = EL_DC_STEELWALL_2_LEFT;
6124 case 0x16a5: /* steel wall 2 (right end) */
6125 element = EL_DC_STEELWALL_2_RIGHT;
6128 case 0x16a6: /* steel wall 2 (top end) */
6129 element = EL_DC_STEELWALL_2_TOP;
6132 case 0x16a7: /* steel wall 2 (bottom end) */
6133 element = EL_DC_STEELWALL_2_BOTTOM;
6136 case 0x16a8: /* steel wall 2 (left/right) */
6137 element = EL_DC_STEELWALL_2_HORIZONTAL;
6140 case 0x16a9: /* steel wall 2 (up/down) */
6141 element = EL_DC_STEELWALL_2_VERTICAL;
6144 case 0x16aa: /* steel wall 2 (mid) */
6145 element = EL_DC_STEELWALL_2_MIDDLE;
6149 element = EL_SIGN_EXCLAMATION;
6153 element = EL_SIGN_RADIOACTIVITY;
6157 element = EL_SIGN_STOP;
6161 element = EL_SIGN_WHEELCHAIR;
6165 element = EL_SIGN_PARKING;
6169 element = EL_SIGN_NO_ENTRY;
6173 element = EL_SIGN_HEART;
6177 element = EL_SIGN_GIVE_WAY;
6181 element = EL_SIGN_ENTRY_FORBIDDEN;
6185 element = EL_SIGN_EMERGENCY_EXIT;
6189 element = EL_SIGN_YIN_YANG;
6193 element = EL_WALL_EMERALD;
6197 element = EL_WALL_DIAMOND;
6201 element = EL_WALL_PEARL;
6205 element = EL_WALL_CRYSTAL;
6209 element = EL_INVISIBLE_WALL;
6213 element = EL_INVISIBLE_STEELWALL;
6216 /* 0x16bc - 0x16cb: */
6217 /* EL_INVISIBLE_SAND */
6220 element = EL_LIGHT_SWITCH;
6224 element = EL_ENVELOPE_1;
6228 if (element >= 0x0117 && element <= 0x036e) /* (?) */
6229 element = EL_DIAMOND;
6230 else if (element >= 0x042d && element <= 0x0684) /* (?) */
6231 element = EL_EMERALD;
6232 else if (element >= 0x157c && element <= 0x158b)
6234 else if (element >= 0x1590 && element <= 0x159f)
6235 element = EL_DC_LANDMINE;
6236 else if (element >= 0x16bc && element <= 0x16cb)
6237 element = EL_INVISIBLE_SAND;
6240 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
6241 element = EL_UNKNOWN;
6246 return getMappedElement(element);
6251 static void LoadLevelFromFileStream_DC(FILE *file, struct LevelInfo *level,
6254 byte header[DC_LEVEL_HEADER_SIZE];
6256 int envelope_header_pos = 62;
6257 int envelope_content_pos = 94;
6258 int level_name_pos = 251;
6259 int level_author_pos = 292;
6260 int envelope_header_len;
6261 int envelope_content_len;
6263 int level_author_len;
6265 int num_yamyam_contents;
6268 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
6270 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6272 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6274 header[i * 2 + 0] = header_word >> 8;
6275 header[i * 2 + 1] = header_word & 0xff;
6278 /* read some values from level header to check level decoding integrity */
6279 fieldx = header[6] | (header[7] << 8);
6280 fieldy = header[8] | (header[9] << 8);
6281 num_yamyam_contents = header[60] | (header[61] << 8);
6283 /* do some simple sanity checks to ensure that level was correctly decoded */
6284 if (fieldx < 1 || fieldx > 256 ||
6285 fieldy < 1 || fieldy > 256 ||
6286 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6288 level->no_valid_file = TRUE;
6290 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
6295 /* maximum envelope header size is 31 bytes */
6296 envelope_header_len = header[envelope_header_pos];
6297 /* maximum envelope content size is 110 (156?) bytes */
6298 envelope_content_len = header[envelope_content_pos];
6300 /* maximum level title size is 40 bytes */
6301 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6302 /* maximum level author size is 30 (51?) bytes */
6303 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6307 for (i = 0; i < envelope_header_len; i++)
6308 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6309 level->envelope[0].text[envelope_size++] =
6310 header[envelope_header_pos + 1 + i];
6312 if (envelope_header_len > 0 && envelope_content_len > 0)
6314 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6315 level->envelope[0].text[envelope_size++] = '\n';
6316 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6317 level->envelope[0].text[envelope_size++] = '\n';
6320 for (i = 0; i < envelope_content_len; i++)
6321 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6322 level->envelope[0].text[envelope_size++] =
6323 header[envelope_content_pos + 1 + i];
6325 level->envelope[0].text[envelope_size] = '\0';
6327 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6328 level->envelope[0].ysize = 10;
6329 level->envelope[0].autowrap = TRUE;
6330 level->envelope[0].centered = TRUE;
6332 for (i = 0; i < level_name_len; i++)
6333 level->name[i] = header[level_name_pos + 1 + i];
6334 level->name[level_name_len] = '\0';
6336 for (i = 0; i < level_author_len; i++)
6337 level->author[i] = header[level_author_pos + 1 + i];
6338 level->author[level_author_len] = '\0';
6340 num_yamyam_contents = header[60] | (header[61] << 8);
6341 level->num_yamyam_contents =
6342 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6344 for (i = 0; i < num_yamyam_contents; i++)
6346 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6348 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6350 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6352 int element_dc = word;
6355 if (i < MAX_ELEMENT_CONTENTS)
6356 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6360 fieldx = header[6] | (header[7] << 8);
6361 fieldy = header[8] | (header[9] << 8);
6362 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6363 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6365 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6367 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6369 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6371 int element_dc = word;
6374 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6375 level->field[x][y] = getMappedElement_DC(element_dc);
6378 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6379 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6380 level->field[x][y] = EL_PLAYER_1;
6382 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6383 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6384 level->field[x][y] = EL_PLAYER_2;
6386 level->gems_needed = header[18] | (header[19] << 8);
6388 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6389 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6390 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6391 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6392 level->score[SC_NUT] = header[28] | (header[29] << 8);
6393 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6394 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6395 level->score[SC_BUG] = header[34] | (header[35] << 8);
6396 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6397 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6398 level->score[SC_KEY] = header[40] | (header[41] << 8);
6399 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6401 level->time = header[44] | (header[45] << 8);
6403 level->amoeba_speed = header[46] | (header[47] << 8);
6404 level->time_light = header[48] | (header[49] << 8);
6405 level->time_timegate = header[50] | (header[51] << 8);
6406 level->time_wheel = header[52] | (header[53] << 8);
6407 level->time_magic_wall = header[54] | (header[55] << 8);
6408 level->extra_time = header[56] | (header[57] << 8);
6409 level->shield_normal_time = header[58] | (header[59] << 8);
6411 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6412 can slip down from flat walls, like normal walls and steel walls */
6413 level->em_slippery_gems = TRUE;
6416 /* Diamond Caves II levels are always surrounded by indestructible wall, but
6417 not necessarily in a rectangular way -- fill with invisible steel wall */
6419 /* !!! not always true !!! keep level and set BorderElement instead !!! */
6421 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6424 if ((x == 0 || x == level->fieldx - 1 ||
6425 y == 0 || y == level->fieldy - 1) &&
6426 level->field[x][y] == EL_EMPTY)
6427 level->field[x][y] = EL_INVISIBLE_STEELWALL;
6429 if ((x == 0 || x == level->fieldx - 1 ||
6430 y == 0 || y == level->fieldy - 1) &&
6431 level->field[x][y] == EL_EMPTY)
6432 FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
6433 level->field, level->fieldx, level->fieldy);
6439 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6440 struct LevelFileInfo *level_file_info,
6441 boolean level_info_only)
6443 char *filename = level_file_info->filename;
6445 int num_magic_bytes = 8;
6446 char magic_bytes[num_magic_bytes + 1];
6447 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6449 if (!(file = fopen(filename, MODE_READ)))
6451 level->no_valid_file = TRUE;
6453 if (!level_info_only)
6454 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6459 // fseek(file, 0x0000, SEEK_SET);
6461 if (level_file_info->packed)
6463 /* read "magic bytes" from start of file */
6464 if (fgets(magic_bytes, num_magic_bytes + 1, file) == NULL)
6465 magic_bytes[0] = '\0';
6467 /* check "magic bytes" for correct file format */
6468 if (!strPrefix(magic_bytes, "DC2"))
6470 level->no_valid_file = TRUE;
6472 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
6478 if (strPrefix(magic_bytes, "DC2Win95") ||
6479 strPrefix(magic_bytes, "DC2Win98"))
6481 int position_first_level = 0x00fa;
6482 int extra_bytes = 4;
6485 /* advance file stream to first level inside the level package */
6486 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6488 /* each block of level data is followed by block of non-level data */
6489 num_levels_to_skip *= 2;
6491 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
6492 while (num_levels_to_skip >= 0)
6494 /* advance file stream to next level inside the level package */
6495 if (fseek(file, skip_bytes, SEEK_CUR) != 0)
6497 level->no_valid_file = TRUE;
6499 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
6505 /* skip apparently unused extra bytes following each level */
6506 ReadUnusedBytesFromFile(file, extra_bytes);
6508 /* read size of next level in level package */
6509 skip_bytes = getFile32BitLE(file);
6511 num_levels_to_skip--;
6516 level->no_valid_file = TRUE;
6518 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
6525 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
6532 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6533 struct LevelFileInfo *level_file_info)
6535 char *filename = level_file_info->filename;
6538 int nr = level_file_info->nr - leveldir_current->first_level;
6540 byte header[DC_LEVEL_HEADER_SIZE];
6542 int envelope_header_pos = 62;
6543 int envelope_content_pos = 94;
6544 int level_name_pos = 251;
6545 int level_author_pos = 292;
6546 int envelope_header_len;
6547 int envelope_content_len;
6549 int level_author_len;
6551 int num_yamyam_contents;
6554 if (!(file = fopen(filename, MODE_READ)))
6556 level->no_valid_file = TRUE;
6558 if (!level_info_only)
6559 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6565 /* position file stream to the requested level inside the level package */
6566 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
6568 level->no_valid_file = TRUE;
6570 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
6576 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
6578 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6580 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6582 header[i * 2 + 0] = header_word >> 8;
6583 header[i * 2 + 1] = header_word & 0xff;
6586 /* read some values from level header to check level decoding integrity */
6587 fieldx = header[6] | (header[7] << 8);
6588 fieldy = header[8] | (header[9] << 8);
6589 num_yamyam_contents = header[60] | (header[61] << 8);
6591 /* do some simple sanity checks to ensure that level was correctly decoded */
6592 if (fieldx < 1 || fieldx > 256 ||
6593 fieldy < 1 || fieldy > 256 ||
6594 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6596 level->no_valid_file = TRUE;
6598 Error(ERR_WARN, "cannot read level from file '%s' -- using empty level",
6604 /* maximum envelope header size is 31 bytes */
6605 envelope_header_len = header[envelope_header_pos];
6606 /* maximum envelope content size is 110 (156?) bytes */
6607 envelope_content_len = header[envelope_content_pos];
6609 /* maximum level title size is 40 bytes */
6610 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6611 /* maximum level author size is 30 (51?) bytes */
6612 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6616 for (i = 0; i < envelope_header_len; i++)
6617 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6618 level->envelope[0].text[envelope_size++] =
6619 header[envelope_header_pos + 1 + i];
6621 if (envelope_header_len > 0 && envelope_content_len > 0)
6623 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6624 level->envelope[0].text[envelope_size++] = '\n';
6625 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6626 level->envelope[0].text[envelope_size++] = '\n';
6629 for (i = 0; i < envelope_content_len; i++)
6630 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6631 level->envelope[0].text[envelope_size++] =
6632 header[envelope_content_pos + 1 + i];
6634 level->envelope[0].text[envelope_size] = '\0';
6636 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6637 level->envelope[0].ysize = 10;
6638 level->envelope[0].autowrap = TRUE;
6639 level->envelope[0].centered = TRUE;
6641 for (i = 0; i < level_name_len; i++)
6642 level->name[i] = header[level_name_pos + 1 + i];
6643 level->name[level_name_len] = '\0';
6645 for (i = 0; i < level_author_len; i++)
6646 level->author[i] = header[level_author_pos + 1 + i];
6647 level->author[level_author_len] = '\0';
6649 num_yamyam_contents = header[60] | (header[61] << 8);
6650 level->num_yamyam_contents =
6651 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6653 for (i = 0; i < num_yamyam_contents; i++)
6655 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6657 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6659 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6661 int element_dc = word;
6664 if (i < MAX_ELEMENT_CONTENTS)
6665 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6669 fieldx = header[6] | (header[7] << 8);
6670 fieldy = header[8] | (header[9] << 8);
6671 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6672 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6674 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6676 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6678 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6680 int element_dc = word;
6683 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6684 level->field[x][y] = getMappedElement_DC(element_dc);
6687 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6688 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6689 level->field[x][y] = EL_PLAYER_1;
6691 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6692 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6693 level->field[x][y] = EL_PLAYER_2;
6695 level->gems_needed = header[18] | (header[19] << 8);
6697 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6698 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6699 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6700 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6701 level->score[SC_NUT] = header[28] | (header[29] << 8);
6702 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6703 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6704 level->score[SC_BUG] = header[34] | (header[35] << 8);
6705 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6706 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6707 level->score[SC_KEY] = header[40] | (header[41] << 8);
6708 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6710 level->time = header[44] | (header[45] << 8);
6712 level->amoeba_speed = header[46] | (header[47] << 8);
6713 level->time_light = header[48] | (header[49] << 8);
6714 level->time_timegate = header[50] | (header[51] << 8);
6715 level->time_wheel = header[52] | (header[53] << 8);
6716 level->time_magic_wall = header[54] | (header[55] << 8);
6717 level->extra_time = header[56] | (header[57] << 8);
6718 level->shield_normal_time = header[58] | (header[59] << 8);
6722 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6723 can slip down from flat walls, like normal walls and steel walls */
6724 level->em_slippery_gems = TRUE;
6727 /* Diamond Caves II levels are always surrounded by indestructible wall, but
6728 not necessarily in a rectangular way -- fill with invisible steel wall */
6730 /* !!! not always true !!! keep level and set BorderElement instead !!! */
6732 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6735 if ((x == 0 || x == level->fieldx - 1 ||
6736 y == 0 || y == level->fieldy - 1) &&
6737 level->field[x][y] == EL_EMPTY)
6738 level->field[x][y] = EL_INVISIBLE_STEELWALL;
6740 if ((x == 0 || x == level->fieldx - 1 ||
6741 y == 0 || y == level->fieldy - 1) &&
6742 level->field[x][y] == EL_EMPTY)
6743 FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
6744 level->field, level->fieldx, level->fieldy);
6753 /* ------------------------------------------------------------------------- */
6754 /* functions for loading SB level */
6755 /* ------------------------------------------------------------------------- */
6757 int getMappedElement_SB(int element_ascii, boolean use_ces)
6765 sb_element_mapping[] =
6767 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
6768 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
6769 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
6770 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
6771 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
6772 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
6773 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
6775 { '_', EL_INVISIBLE_STEELWALL, EL_CUSTOM_8 }, /* floor beyond border */
6777 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
6785 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6786 if (element_ascii == sb_element_mapping[i].ascii)
6787 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6789 return EL_UNDEFINED;
6792 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6793 struct LevelFileInfo *level_file_info,
6794 boolean level_info_only)
6796 char *filename = level_file_info->filename;
6797 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6798 char last_comment[MAX_LINE_LEN];
6799 char level_name[MAX_LINE_LEN];
6802 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6803 boolean read_continued_line = FALSE;
6804 boolean reading_playfield = FALSE;
6805 boolean got_valid_playfield_line = FALSE;
6806 boolean invalid_playfield_char = FALSE;
6807 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6808 int file_level_nr = 0;
6810 int x = 0, y = 0; /* initialized to make compilers happy */
6813 printf("::: looking for level number %d [%d]\n",
6814 level_file_info->nr, num_levels_to_skip);
6817 last_comment[0] = '\0';
6818 level_name[0] = '\0';
6820 if (!(file = fopen(filename, MODE_READ)))
6822 level->no_valid_file = TRUE;
6824 if (!level_info_only)
6825 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6832 /* level successfully read, but next level may follow here */
6833 if (!got_valid_playfield_line && reading_playfield)
6836 printf("::: read complete playfield\n");
6839 /* read playfield from single level file -- skip remaining file */
6840 if (!level_file_info->packed)
6843 if (file_level_nr >= num_levels_to_skip)
6848 last_comment[0] = '\0';
6849 level_name[0] = '\0';
6851 reading_playfield = FALSE;
6854 got_valid_playfield_line = FALSE;
6856 /* read next line of input file */
6857 if (!fgets(line, MAX_LINE_LEN, file))
6860 /* check if line was completely read and is terminated by line break */
6861 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6864 /* cut trailing line break (this can be newline and/or carriage return) */
6865 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6866 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6869 /* copy raw input line for later use (mainly debugging output) */
6870 strcpy(line_raw, line);
6872 if (read_continued_line)
6874 /* append new line to existing line, if there is enough space */
6875 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6876 strcat(previous_line, line_ptr);
6878 strcpy(line, previous_line); /* copy storage buffer to line */
6880 read_continued_line = FALSE;
6883 /* if the last character is '\', continue at next line */
6884 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6886 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
6887 strcpy(previous_line, line); /* copy line to storage buffer */
6889 read_continued_line = TRUE;
6894 /* skip empty lines */
6895 if (line[0] == '\0')
6898 /* extract comment text from comment line */
6901 for (line_ptr = line; *line_ptr; line_ptr++)
6902 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6905 strcpy(last_comment, line_ptr);
6908 printf("::: found comment '%s' in line %d\n", last_comment, line_nr);
6914 /* extract level title text from line containing level title */
6915 if (line[0] == '\'')
6917 strcpy(level_name, &line[1]);
6919 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6920 level_name[strlen(level_name) - 1] = '\0';
6923 printf("::: found level name '%s' in line %d\n", level_name, line_nr);
6929 /* skip lines containing only spaces (or empty lines) */
6930 for (line_ptr = line; *line_ptr; line_ptr++)
6931 if (*line_ptr != ' ')
6933 if (*line_ptr == '\0')
6936 /* at this point, we have found a line containing part of a playfield */
6939 printf("::: found playfield row in line %d\n", line_nr);
6942 got_valid_playfield_line = TRUE;
6944 if (!reading_playfield)
6946 reading_playfield = TRUE;
6947 invalid_playfield_char = FALSE;
6949 for (x = 0; x < MAX_LEV_FIELDX; x++)
6950 for (y = 0; y < MAX_LEV_FIELDY; y++)
6951 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6956 /* start with topmost tile row */
6960 /* skip playfield line if larger row than allowed */
6961 if (y >= MAX_LEV_FIELDY)
6964 /* start with leftmost tile column */
6967 /* read playfield elements from line */
6968 for (line_ptr = line; *line_ptr; line_ptr++)
6970 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6972 /* stop parsing playfield line if larger column than allowed */
6973 if (x >= MAX_LEV_FIELDX)
6976 if (mapped_sb_element == EL_UNDEFINED)
6978 invalid_playfield_char = TRUE;
6983 level->field[x][y] = mapped_sb_element;
6985 /* continue with next tile column */
6988 level->fieldx = MAX(x, level->fieldx);
6991 if (invalid_playfield_char)
6993 /* if first playfield line, treat invalid lines as comment lines */
6995 reading_playfield = FALSE;
7000 /* continue with next tile row */
7008 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7009 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7011 if (!reading_playfield)
7013 level->no_valid_file = TRUE;
7015 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
7020 if (*level_name != '\0')
7022 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7023 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7026 printf(":1: level name: '%s'\n", level->name);
7029 else if (*last_comment != '\0')
7031 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7032 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7035 printf(":2: level name: '%s'\n", level->name);
7040 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7043 /* set all empty fields beyond the border walls to invisible steel wall */
7044 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7046 if ((x == 0 || x == level->fieldx - 1 ||
7047 y == 0 || y == level->fieldy - 1) &&
7048 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7049 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7050 level->field, level->fieldx, level->fieldy);
7053 /* set special level settings for Sokoban levels */
7056 level->use_step_counter = TRUE;
7058 if (load_xsb_to_ces)
7061 /* !!! special global settings can now be set in level template !!! */
7063 level->initial_player_stepsize[0] = STEPSIZE_SLOW;
7066 /* fill smaller playfields with padding "beyond border wall" elements */
7067 if (level->fieldx < SCR_FIELDX ||
7068 level->fieldy < SCR_FIELDY)
7070 short field[level->fieldx][level->fieldy];
7071 int new_fieldx = MAX(level->fieldx, SCR_FIELDX);
7072 int new_fieldy = MAX(level->fieldy, SCR_FIELDY);
7073 int pos_fieldx = (new_fieldx - level->fieldx) / 2;
7074 int pos_fieldy = (new_fieldy - level->fieldy) / 2;
7076 /* copy old playfield (which is smaller than the visible area) */
7077 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7078 field[x][y] = level->field[x][y];
7080 /* fill new, larger playfield with "beyond border wall" elements */
7081 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
7082 level->field[x][y] = getMappedElement_SB('_', load_xsb_to_ces);
7084 /* copy the old playfield to the middle of the new playfield */
7085 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7086 level->field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
7088 level->fieldx = new_fieldx;
7089 level->fieldy = new_fieldy;
7092 level->use_custom_template = TRUE;
7097 /* ------------------------------------------------------------------------- */
7098 /* functions for handling native levels */
7099 /* ------------------------------------------------------------------------- */
7101 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7102 struct LevelFileInfo *level_file_info,
7103 boolean level_info_only)
7105 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7106 level->no_valid_file = TRUE;
7109 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7110 struct LevelFileInfo *level_file_info,
7111 boolean level_info_only)
7115 /* determine position of requested level inside level package */
7116 if (level_file_info->packed)
7117 pos = level_file_info->nr - leveldir_current->first_level;
7119 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7120 level->no_valid_file = TRUE;
7123 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7125 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7126 CopyNativeLevel_RND_to_EM(level);
7127 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7128 CopyNativeLevel_RND_to_SP(level);
7131 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7133 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7134 CopyNativeLevel_EM_to_RND(level);
7135 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7136 CopyNativeLevel_SP_to_RND(level);
7139 void SaveNativeLevel(struct LevelInfo *level)
7141 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7143 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
7144 char *filename = getLevelFilenameFromBasename(basename);
7146 CopyNativeLevel_RND_to_SP(level);
7147 CopyNativeTape_RND_to_SP(level);
7149 SaveNativeLevel_SP(filename);
7154 /* ------------------------------------------------------------------------- */
7155 /* functions for loading generic level */
7156 /* ------------------------------------------------------------------------- */
7158 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7159 struct LevelFileInfo *level_file_info,
7160 boolean level_info_only)
7162 /* always start with reliable default values */
7163 setLevelInfoToDefaults(level, level_info_only);
7165 switch (level_file_info->type)
7167 case LEVEL_FILE_TYPE_RND:
7168 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7171 case LEVEL_FILE_TYPE_EM:
7172 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7173 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7176 case LEVEL_FILE_TYPE_SP:
7177 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7178 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7181 case LEVEL_FILE_TYPE_DC:
7182 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7185 case LEVEL_FILE_TYPE_SB:
7186 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7190 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7194 /* if level file is invalid, restore level structure to default values */
7195 if (level->no_valid_file)
7197 setLevelInfoToDefaults(level, level_info_only);
7199 level->no_valid_file = TRUE; /* but keep "no valid file" flag */
7202 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7203 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7205 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7206 CopyNativeLevel_Native_to_RND(level);
7209 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7211 static struct LevelFileInfo level_file_info;
7213 /* always start with reliable default values */
7214 setFileInfoToDefaults(&level_file_info);
7216 level_file_info.nr = 0; /* unknown level number */
7217 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
7218 level_file_info.filename = filename;
7220 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7223 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
7227 if (leveldir_current == NULL) /* only when dumping level */
7230 /* all engine modifications also valid for levels which use latest engine */
7231 if (level->game_version < VERSION_IDENT(3,2,0,5))
7233 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
7234 level->score[SC_TIME_BONUS] /= 10;
7238 leveldir_current->latest_engine = TRUE; /* !!! TEST ONLY !!! */
7241 if (leveldir_current->latest_engine)
7243 /* ---------- use latest game engine ----------------------------------- */
7245 /* For all levels which are forced to use the latest game engine version
7246 (normally all but user contributed, private and undefined levels), set
7247 the game engine version to the actual version; this allows for actual
7248 corrections in the game engine to take effect for existing, converted
7249 levels (from "classic" or other existing games) to make the emulation
7250 of the corresponding game more accurate, while (hopefully) not breaking
7251 existing levels created from other players. */
7253 level->game_version = GAME_VERSION_ACTUAL;
7255 /* Set special EM style gems behaviour: EM style gems slip down from
7256 normal, steel and growing wall. As this is a more fundamental change,
7257 it seems better to set the default behaviour to "off" (as it is more
7258 natural) and make it configurable in the level editor (as a property
7259 of gem style elements). Already existing converted levels (neither
7260 private nor contributed levels) are changed to the new behaviour. */
7262 if (level->file_version < FILE_VERSION_2_0)
7263 level->em_slippery_gems = TRUE;
7268 /* ---------- use game engine the level was created with ----------------- */
7270 /* For all levels which are not forced to use the latest game engine
7271 version (normally user contributed, private and undefined levels),
7272 use the version of the game engine the levels were created for.
7274 Since 2.0.1, the game engine version is now directly stored
7275 in the level file (chunk "VERS"), so there is no need anymore
7276 to set the game version from the file version (except for old,
7277 pre-2.0 levels, where the game version is still taken from the
7278 file format version used to store the level -- see above). */
7280 /* player was faster than enemies in 1.0.0 and before */
7281 if (level->file_version == FILE_VERSION_1_0)
7282 for (i = 0; i < MAX_PLAYERS; i++)
7283 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7285 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
7286 if (level->game_version == VERSION_IDENT(2,0,1,0))
7287 level->em_slippery_gems = TRUE;
7289 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
7290 if (level->game_version < VERSION_IDENT(2,2,0,0))
7291 level->use_spring_bug = TRUE;
7293 if (level->game_version < VERSION_IDENT(3,2,0,5))
7295 /* time orb caused limited time in endless time levels before 3.2.0-5 */
7296 level->use_time_orb_bug = TRUE;
7298 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
7299 level->block_snap_field = FALSE;
7301 /* extra time score was same value as time left score before 3.2.0-5 */
7302 level->extra_time_score = level->score[SC_TIME_BONUS];
7305 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
7306 level->score[SC_TIME_BONUS] /= 10;
7310 if (level->game_version < VERSION_IDENT(3,2,0,7))
7312 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
7313 level->continuous_snapping = FALSE;
7316 /* only few elements were able to actively move into acid before 3.1.0 */
7317 /* trigger settings did not exist before 3.1.0; set to default "any" */
7318 if (level->game_version < VERSION_IDENT(3,1,0,0))
7320 /* correct "can move into acid" settings (all zero in old levels) */
7322 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
7323 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
7325 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7326 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7327 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7328 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7330 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7331 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7333 /* correct trigger settings (stored as zero == "none" in old levels) */
7335 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7337 int element = EL_CUSTOM_START + i;
7338 struct ElementInfo *ei = &element_info[element];
7340 for (j = 0; j < ei->num_change_pages; j++)
7342 struct ElementChangeInfo *change = &ei->change_page[j];
7344 change->trigger_player = CH_PLAYER_ANY;
7345 change->trigger_page = CH_PAGE_ANY;
7350 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
7352 int element = EL_CUSTOM_256;
7353 struct ElementInfo *ei = &element_info[element];
7354 struct ElementChangeInfo *change = &ei->change_page[0];
7356 /* This is needed to fix a problem that was caused by a bugfix in function
7357 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7358 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7359 not replace walkable elements, but instead just placed the player on it,
7360 without placing the Sokoban field under the player). Unfortunately, this
7361 breaks "Snake Bite" style levels when the snake is halfway through a door
7362 that just closes (the snake head is still alive and can be moved in this
7363 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7364 player (without Sokoban element) which then gets killed as designed). */
7366 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7367 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7368 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7369 change->target_element = EL_PLAYER_1;
7373 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
7374 if (level->game_version < VERSION_IDENT(3,2,5,0))
7376 /* This is needed to fix a problem that was caused by a bugfix in function
7377 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7378 corrects the behaviour when a custom element changes to another custom
7379 element with a higher element number that has change actions defined.
7380 Normally, only one change per frame is allowed for custom elements.
7381 Therefore, it is checked if a custom element already changed in the
7382 current frame; if it did, subsequent changes are suppressed.
7383 Unfortunately, this is only checked for element changes, but not for
7384 change actions, which are still executed. As the function above loops
7385 through all custom elements from lower to higher, an element change
7386 resulting in a lower CE number won't be checked again, while a target
7387 element with a higher number will also be checked, and potential change
7388 actions will get executed for this CE, too (which is wrong), while
7389 further changes are ignored (which is correct). As this bugfix breaks
7390 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7391 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7392 behaviour for existing levels and tapes that make use of this bug */
7394 level->use_action_after_change_bug = TRUE;
7397 /* !!! THIS DOES NOT FIX "Zelda I" (GRAPHICALLY) AND "Alan's FMV" LEVELS */
7398 /* try to detect and fix "Zelda II" levels, which are broken with 3.2.5 */
7400 int element = EL_CUSTOM_16;
7401 struct ElementInfo *ei = &element_info[element];
7403 /* This is needed to fix a problem that was caused by a bugfix in function
7404 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7405 corrects the behaviour when a custom element changes to another custom
7406 element with a higher element number that has change actions defined.
7407 Normally, only one change per frame is allowed for custom elements.
7408 Therefore, it is checked if a custom element already changed in the
7409 current frame; if it did, subsequent changes are suppressed.
7410 Unfortunately, this is only checked for element changes, but not for
7411 change actions, which are still executed. As the function above loops
7412 through all custom elements from lower to higher, an element change
7413 resulting in a lower CE number won't be checked again, while a target
7414 element with a higher number will also be checked, and potential change
7415 actions will get executed for this CE, too (which is wrong), while
7416 further changes are ignored (which is correct). As this bugfix breaks
7417 Zelda II (but no other levels), allow the previous, incorrect behaviour
7418 for this outstanding level set to not break the game or existing tapes */
7420 if (strncmp(leveldir_current->identifier, "zelda2", 6) == 0 ||
7421 strncmp(ei->description, "scanline - row 1", 16) == 0)
7422 level->use_action_after_change_bug = TRUE;
7426 /* not centering level after relocating player was default only in 3.2.3 */
7427 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
7428 level->shifted_relocation = TRUE;
7430 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
7431 if (level->game_version < VERSION_IDENT(3,2,6,0))
7432 level->em_explodes_by_fire = TRUE;
7435 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
7439 /* map custom element change events that have changed in newer versions
7440 (these following values were accidentally changed in version 3.0.1)
7441 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
7442 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7444 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7446 int element = EL_CUSTOM_START + i;
7448 /* order of checking and copying events to be mapped is important */
7449 /* (do not change the start and end value -- they are constant) */
7450 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7452 if (HAS_CHANGE_EVENT(element, j - 2))
7454 SET_CHANGE_EVENT(element, j - 2, FALSE);
7455 SET_CHANGE_EVENT(element, j, TRUE);
7459 /* order of checking and copying events to be mapped is important */
7460 /* (do not change the start and end value -- they are constant) */
7461 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7463 if (HAS_CHANGE_EVENT(element, j - 1))
7465 SET_CHANGE_EVENT(element, j - 1, FALSE);
7466 SET_CHANGE_EVENT(element, j, TRUE);
7472 /* initialize "can_change" field for old levels with only one change page */
7473 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7475 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7477 int element = EL_CUSTOM_START + i;
7479 if (CAN_CHANGE(element))
7480 element_info[element].change->can_change = TRUE;
7484 /* correct custom element values (for old levels without these options) */
7485 if (level->game_version < VERSION_IDENT(3,1,1,0))
7487 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7489 int element = EL_CUSTOM_START + i;
7490 struct ElementInfo *ei = &element_info[element];
7492 if (ei->access_direction == MV_NO_DIRECTION)
7493 ei->access_direction = MV_ALL_DIRECTIONS;
7497 /* correct custom element values (fix invalid values for all versions) */
7500 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7502 int element = EL_CUSTOM_START + i;
7503 struct ElementInfo *ei = &element_info[element];
7505 for (j = 0; j < ei->num_change_pages; j++)
7507 struct ElementChangeInfo *change = &ei->change_page[j];
7509 if (change->trigger_player == CH_PLAYER_NONE)
7510 change->trigger_player = CH_PLAYER_ANY;
7512 if (change->trigger_side == CH_SIDE_NONE)
7513 change->trigger_side = CH_SIDE_ANY;
7518 /* initialize "can_explode" field for old levels which did not store this */
7519 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
7520 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7522 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7524 int element = EL_CUSTOM_START + i;
7526 if (EXPLODES_1X1_OLD(element))
7527 element_info[element].explosion_type = EXPLODES_1X1;
7529 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7530 EXPLODES_SMASHED(element) ||
7531 EXPLODES_IMPACT(element)));
7535 /* correct previously hard-coded move delay values for maze runner style */
7536 if (level->game_version < VERSION_IDENT(3,1,1,0))
7538 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7540 int element = EL_CUSTOM_START + i;
7542 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7544 /* previously hard-coded and therefore ignored */
7545 element_info[element].move_delay_fixed = 9;
7546 element_info[element].move_delay_random = 0;
7551 /* map elements that have changed in newer versions */
7552 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7553 level->game_version);
7554 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7555 for (x = 0; x < 3; x++)
7556 for (y = 0; y < 3; y++)
7557 level->yamyam_content[i].e[x][y] =
7558 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7559 level->game_version);
7561 /* initialize element properties for level editor etc. */
7562 InitElementPropertiesEngine(level->game_version);
7563 InitElementPropertiesAfterLoading(level->game_version);
7564 InitElementPropertiesGfxElement();
7567 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
7571 /* map elements that have changed in newer versions */
7572 for (y = 0; y < level->fieldy; y++)
7573 for (x = 0; x < level->fieldx; x++)
7574 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7575 level->game_version);
7577 /* copy elements to runtime playfield array */
7578 for (x = 0; x < MAX_LEV_FIELDX; x++)
7579 for (y = 0; y < MAX_LEV_FIELDY; y++)
7580 Feld[x][y] = level->field[x][y];
7582 /* initialize level size variables for faster access */
7583 lev_fieldx = level->fieldx;
7584 lev_fieldy = level->fieldy;
7586 /* determine border element for this level */
7587 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7588 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
7593 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
7595 struct LevelFileInfo *level_file_info = &level->file_info;
7597 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7598 CopyNativeLevel_RND_to_Native(level);
7601 void LoadLevelTemplate(int nr)
7605 setLevelFileInfo(&level_template.file_info, nr);
7606 filename = level_template.file_info.filename;
7608 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7610 LoadLevel_InitVersion(&level_template, filename);
7611 LoadLevel_InitElements(&level_template, filename);
7613 ActivateLevelTemplate();
7616 void LoadLevel(int nr)
7620 setLevelFileInfo(&level.file_info, nr);
7621 filename = level.file_info.filename;
7623 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7625 if (level.use_custom_template)
7626 LoadLevelTemplate(-1);
7628 LoadLevel_InitVersion(&level, filename);
7629 LoadLevel_InitElements(&level, filename);
7630 LoadLevel_InitPlayfield(&level, filename);
7632 LoadLevel_InitNativeEngines(&level, filename);
7635 void LoadLevelInfoOnly(int nr)
7641 setLevelFileInfo(&level.file_info, nr);
7643 filename = level.file_info.filename;
7646 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7649 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7653 chunk_size += putFileVersion(file, level->file_version);
7654 chunk_size += putFileVersion(file, level->game_version);
7659 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7663 chunk_size += putFile16BitBE(file, level->creation_date.year);
7664 chunk_size += putFile8Bit(file, level->creation_date.month);
7665 chunk_size += putFile8Bit(file, level->creation_date.day);
7671 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7675 putFile8Bit(file, level->fieldx);
7676 putFile8Bit(file, level->fieldy);
7678 putFile16BitBE(file, level->time);
7679 putFile16BitBE(file, level->gems_needed);
7681 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7682 putFile8Bit(file, level->name[i]);
7684 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7685 putFile8Bit(file, level->score[i]);
7687 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7688 for (y = 0; y < 3; y++)
7689 for (x = 0; x < 3; x++)
7690 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7691 level->yamyam_content[i].e[x][y]));
7692 putFile8Bit(file, level->amoeba_speed);
7693 putFile8Bit(file, level->time_magic_wall);
7694 putFile8Bit(file, level->time_wheel);
7695 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7696 level->amoeba_content));
7697 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7698 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7699 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7700 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7702 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7704 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7705 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7706 putFile32BitBE(file, level->can_move_into_acid_bits);
7707 putFile8Bit(file, level->dont_collide_with_bits);
7709 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7710 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7712 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7713 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7714 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7716 putFile8Bit(file, level->game_engine_type);
7718 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7722 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7727 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7728 chunk_size += putFile8Bit(file, level->name[i]);
7733 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7738 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7739 chunk_size += putFile8Bit(file, level->author[i]);
7745 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7750 for (y = 0; y < level->fieldy; y++)
7751 for (x = 0; x < level->fieldx; x++)
7752 if (level->encoding_16bit_field)
7753 chunk_size += putFile16BitBE(file, level->field[x][y]);
7755 chunk_size += putFile8Bit(file, level->field[x][y]);
7761 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7766 for (y = 0; y < level->fieldy; y++)
7767 for (x = 0; x < level->fieldx; x++)
7768 chunk_size += putFile16BitBE(file, level->field[x][y]);
7774 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7778 putFile8Bit(file, EL_YAMYAM);
7779 putFile8Bit(file, level->num_yamyam_contents);
7780 putFile8Bit(file, 0);
7781 putFile8Bit(file, 0);
7783 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7784 for (y = 0; y < 3; y++)
7785 for (x = 0; x < 3; x++)
7786 if (level->encoding_16bit_field)
7787 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7789 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7794 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7797 int num_contents, content_xsize, content_ysize;
7798 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7800 if (element == EL_YAMYAM)
7802 num_contents = level->num_yamyam_contents;
7806 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7807 for (y = 0; y < 3; y++)
7808 for (x = 0; x < 3; x++)
7809 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7811 else if (element == EL_BD_AMOEBA)
7817 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7818 for (y = 0; y < 3; y++)
7819 for (x = 0; x < 3; x++)
7820 content_array[i][x][y] = EL_EMPTY;
7821 content_array[0][0][0] = level->amoeba_content;
7825 /* chunk header already written -- write empty chunk data */
7826 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7828 Error(ERR_WARN, "cannot save content for element '%d'", element);
7832 putFile16BitBE(file, element);
7833 putFile8Bit(file, num_contents);
7834 putFile8Bit(file, content_xsize);
7835 putFile8Bit(file, content_ysize);
7837 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7839 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7840 for (y = 0; y < 3; y++)
7841 for (x = 0; x < 3; x++)
7842 putFile16BitBE(file, content_array[i][x][y]);
7847 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7849 int envelope_nr = element - EL_ENVELOPE_1;
7850 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7854 chunk_size += putFile16BitBE(file, element);
7855 chunk_size += putFile16BitBE(file, envelope_len);
7856 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7857 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7859 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7860 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7862 for (i = 0; i < envelope_len; i++)
7863 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7870 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7871 int num_changed_custom_elements)
7875 putFile16BitBE(file, num_changed_custom_elements);
7877 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7879 int element = EL_CUSTOM_START + i;
7881 struct ElementInfo *ei = &element_info[element];
7883 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7885 if (check < num_changed_custom_elements)
7887 putFile16BitBE(file, element);
7888 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7895 if (check != num_changed_custom_elements) /* should not happen */
7896 Error(ERR_WARN, "inconsistent number of custom element properties");
7901 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7902 int num_changed_custom_elements)
7906 putFile16BitBE(file, num_changed_custom_elements);
7908 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7910 int element = EL_CUSTOM_START + i;
7912 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7914 if (check < num_changed_custom_elements)
7916 putFile16BitBE(file, element);
7917 putFile16BitBE(file, element_info[element].change->target_element);
7924 if (check != num_changed_custom_elements) /* should not happen */
7925 Error(ERR_WARN, "inconsistent number of custom target elements");
7930 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7931 int num_changed_custom_elements)
7933 int i, j, x, y, check = 0;
7935 putFile16BitBE(file, num_changed_custom_elements);
7937 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7939 int element = EL_CUSTOM_START + i;
7940 struct ElementInfo *ei = &element_info[element];
7942 if (ei->modified_settings)
7944 if (check < num_changed_custom_elements)
7946 putFile16BitBE(file, element);
7948 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7949 putFile8Bit(file, ei->description[j]);
7951 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7953 /* some free bytes for future properties and padding */
7954 WriteUnusedBytesToFile(file, 7);
7956 putFile8Bit(file, ei->use_gfx_element);
7957 putFile16BitBE(file, ei->gfx_element_initial);
7959 putFile8Bit(file, ei->collect_score_initial);
7960 putFile8Bit(file, ei->collect_count_initial);
7962 putFile16BitBE(file, ei->push_delay_fixed);
7963 putFile16BitBE(file, ei->push_delay_random);
7964 putFile16BitBE(file, ei->move_delay_fixed);
7965 putFile16BitBE(file, ei->move_delay_random);
7967 putFile16BitBE(file, ei->move_pattern);
7968 putFile8Bit(file, ei->move_direction_initial);
7969 putFile8Bit(file, ei->move_stepsize);
7971 for (y = 0; y < 3; y++)
7972 for (x = 0; x < 3; x++)
7973 putFile16BitBE(file, ei->content.e[x][y]);
7975 putFile32BitBE(file, ei->change->events);
7977 putFile16BitBE(file, ei->change->target_element);
7979 putFile16BitBE(file, ei->change->delay_fixed);
7980 putFile16BitBE(file, ei->change->delay_random);
7981 putFile16BitBE(file, ei->change->delay_frames);
7983 putFile16BitBE(file, ei->change->initial_trigger_element);
7985 putFile8Bit(file, ei->change->explode);
7986 putFile8Bit(file, ei->change->use_target_content);
7987 putFile8Bit(file, ei->change->only_if_complete);
7988 putFile8Bit(file, ei->change->use_random_replace);
7990 putFile8Bit(file, ei->change->random_percentage);
7991 putFile8Bit(file, ei->change->replace_when);
7993 for (y = 0; y < 3; y++)
7994 for (x = 0; x < 3; x++)
7995 putFile16BitBE(file, ei->change->content.e[x][y]);
7997 putFile8Bit(file, ei->slippery_type);
7999 /* some free bytes for future properties and padding */
8000 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8007 if (check != num_changed_custom_elements) /* should not happen */
8008 Error(ERR_WARN, "inconsistent number of custom element properties");
8013 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8015 struct ElementInfo *ei = &element_info[element];
8018 /* ---------- custom element base property values (96 bytes) ------------- */
8020 putFile16BitBE(file, element);
8022 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8023 putFile8Bit(file, ei->description[i]);
8025 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8027 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
8029 putFile8Bit(file, ei->num_change_pages);
8031 putFile16BitBE(file, ei->ce_value_fixed_initial);
8032 putFile16BitBE(file, ei->ce_value_random_initial);
8033 putFile8Bit(file, ei->use_last_ce_value);
8035 putFile8Bit(file, ei->use_gfx_element);
8036 putFile16BitBE(file, ei->gfx_element_initial);
8038 putFile8Bit(file, ei->collect_score_initial);
8039 putFile8Bit(file, ei->collect_count_initial);
8041 putFile8Bit(file, ei->drop_delay_fixed);
8042 putFile8Bit(file, ei->push_delay_fixed);
8043 putFile8Bit(file, ei->drop_delay_random);
8044 putFile8Bit(file, ei->push_delay_random);
8045 putFile16BitBE(file, ei->move_delay_fixed);
8046 putFile16BitBE(file, ei->move_delay_random);
8048 /* bits 0 - 15 of "move_pattern" ... */
8049 putFile16BitBE(file, ei->move_pattern & 0xffff);
8050 putFile8Bit(file, ei->move_direction_initial);
8051 putFile8Bit(file, ei->move_stepsize);
8053 putFile8Bit(file, ei->slippery_type);
8055 for (y = 0; y < 3; y++)
8056 for (x = 0; x < 3; x++)
8057 putFile16BitBE(file, ei->content.e[x][y]);
8059 putFile16BitBE(file, ei->move_enter_element);
8060 putFile16BitBE(file, ei->move_leave_element);
8061 putFile8Bit(file, ei->move_leave_type);
8063 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
8064 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8066 putFile8Bit(file, ei->access_direction);
8068 putFile8Bit(file, ei->explosion_delay);
8069 putFile8Bit(file, ei->ignition_delay);
8070 putFile8Bit(file, ei->explosion_type);
8072 /* some free bytes for future custom property values and padding */
8073 WriteUnusedBytesToFile(file, 1);
8075 /* ---------- change page property values (48 bytes) --------------------- */
8077 for (i = 0; i < ei->num_change_pages; i++)
8079 struct ElementChangeInfo *change = &ei->change_page[i];
8080 unsigned int event_bits;
8082 /* bits 0 - 31 of "has_event[]" ... */
8084 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8085 if (change->has_event[j])
8086 event_bits |= (1 << j);
8087 putFile32BitBE(file, event_bits);
8089 putFile16BitBE(file, change->target_element);
8091 putFile16BitBE(file, change->delay_fixed);
8092 putFile16BitBE(file, change->delay_random);
8093 putFile16BitBE(file, change->delay_frames);
8095 putFile16BitBE(file, change->initial_trigger_element);
8097 putFile8Bit(file, change->explode);
8098 putFile8Bit(file, change->use_target_content);
8099 putFile8Bit(file, change->only_if_complete);
8100 putFile8Bit(file, change->use_random_replace);
8102 putFile8Bit(file, change->random_percentage);
8103 putFile8Bit(file, change->replace_when);
8105 for (y = 0; y < 3; y++)
8106 for (x = 0; x < 3; x++)
8107 putFile16BitBE(file, change->target_content.e[x][y]);
8109 putFile8Bit(file, change->can_change);
8111 putFile8Bit(file, change->trigger_side);
8113 putFile8Bit(file, change->trigger_player);
8114 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8115 log_2(change->trigger_page)));
8117 putFile8Bit(file, change->has_action);
8118 putFile8Bit(file, change->action_type);
8119 putFile8Bit(file, change->action_mode);
8120 putFile16BitBE(file, change->action_arg);
8122 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
8124 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8125 if (change->has_event[j])
8126 event_bits |= (1 << (j - 32));
8127 putFile8Bit(file, event_bits);
8133 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8135 struct ElementInfo *ei = &element_info[element];
8136 struct ElementGroupInfo *group = ei->group;
8139 putFile16BitBE(file, element);
8141 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8142 putFile8Bit(file, ei->description[i]);
8144 putFile8Bit(file, group->num_elements);
8146 putFile8Bit(file, ei->use_gfx_element);
8147 putFile16BitBE(file, ei->gfx_element_initial);
8149 putFile8Bit(file, group->choice_mode);
8151 /* some free bytes for future values and padding */
8152 WriteUnusedBytesToFile(file, 3);
8154 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8155 putFile16BitBE(file, group->element[i]);
8159 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8160 boolean write_element)
8162 int save_type = entry->save_type;
8163 int data_type = entry->data_type;
8164 int conf_type = entry->conf_type;
8165 int byte_mask = conf_type & CONF_MASK_BYTES;
8166 int element = entry->element;
8167 int default_value = entry->default_value;
8169 boolean modified = FALSE;
8171 if (byte_mask != CONF_MASK_MULTI_BYTES)
8173 void *value_ptr = entry->value;
8174 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8177 /* check if any settings have been modified before saving them */
8178 if (value != default_value)
8181 /* do not save if explicitly told or if unmodified default settings */
8182 if ((save_type == SAVE_CONF_NEVER) ||
8183 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8187 num_bytes += putFile16BitBE(file, element);
8189 num_bytes += putFile8Bit(file, conf_type);
8190 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8191 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8192 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8195 else if (data_type == TYPE_STRING)
8197 char *default_string = entry->default_string;
8198 char *string = (char *)(entry->value);
8199 int string_length = strlen(string);
8202 /* check if any settings have been modified before saving them */
8203 if (!strEqual(string, default_string))
8206 /* do not save if explicitly told or if unmodified default settings */
8207 if ((save_type == SAVE_CONF_NEVER) ||
8208 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8212 num_bytes += putFile16BitBE(file, element);
8214 num_bytes += putFile8Bit(file, conf_type);
8215 num_bytes += putFile16BitBE(file, string_length);
8217 for (i = 0; i < string_length; i++)
8218 num_bytes += putFile8Bit(file, string[i]);
8220 else if (data_type == TYPE_ELEMENT_LIST)
8222 int *element_array = (int *)(entry->value);
8223 int num_elements = *(int *)(entry->num_entities);
8226 /* check if any settings have been modified before saving them */
8227 for (i = 0; i < num_elements; i++)
8228 if (element_array[i] != default_value)
8231 /* do not save if explicitly told or if unmodified default settings */
8232 if ((save_type == SAVE_CONF_NEVER) ||
8233 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8237 num_bytes += putFile16BitBE(file, element);
8239 num_bytes += putFile8Bit(file, conf_type);
8240 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8242 for (i = 0; i < num_elements; i++)
8243 num_bytes += putFile16BitBE(file, element_array[i]);
8245 else if (data_type == TYPE_CONTENT_LIST)
8247 struct Content *content = (struct Content *)(entry->value);
8248 int num_contents = *(int *)(entry->num_entities);
8251 /* check if any settings have been modified before saving them */
8252 for (i = 0; i < num_contents; i++)
8253 for (y = 0; y < 3; y++)
8254 for (x = 0; x < 3; x++)
8255 if (content[i].e[x][y] != default_value)
8258 /* do not save if explicitly told or if unmodified default settings */
8259 if ((save_type == SAVE_CONF_NEVER) ||
8260 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8264 num_bytes += putFile16BitBE(file, element);
8266 num_bytes += putFile8Bit(file, conf_type);
8267 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8269 for (i = 0; i < num_contents; i++)
8270 for (y = 0; y < 3; y++)
8271 for (x = 0; x < 3; x++)
8272 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8278 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8283 li = *level; /* copy level data into temporary buffer */
8285 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8286 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8291 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8296 li = *level; /* copy level data into temporary buffer */
8298 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8299 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8304 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8306 int envelope_nr = element - EL_ENVELOPE_1;
8310 chunk_size += putFile16BitBE(file, element);
8312 /* copy envelope data into temporary buffer */
8313 xx_envelope = level->envelope[envelope_nr];
8315 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8316 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8321 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8323 struct ElementInfo *ei = &element_info[element];
8327 chunk_size += putFile16BitBE(file, element);
8329 xx_ei = *ei; /* copy element data into temporary buffer */
8331 /* set default description string for this specific element */
8332 strcpy(xx_default_description, getDefaultElementDescription(ei));
8335 /* set (fixed) number of content areas (may be wrong by broken level file) */
8336 /* (this is now directly corrected for broken level files after loading) */
8337 xx_num_contents = 1;
8340 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8341 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8343 for (i = 0; i < ei->num_change_pages; i++)
8345 struct ElementChangeInfo *change = &ei->change_page[i];
8347 xx_current_change_page = i;
8349 xx_change = *change; /* copy change data into temporary buffer */
8352 setEventBitsFromEventFlags(change);
8354 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8355 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8362 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8364 struct ElementInfo *ei = &element_info[element];
8365 struct ElementGroupInfo *group = ei->group;
8369 chunk_size += putFile16BitBE(file, element);
8371 xx_ei = *ei; /* copy element data into temporary buffer */
8372 xx_group = *group; /* copy group data into temporary buffer */
8374 /* set default description string for this specific element */
8375 strcpy(xx_default_description, getDefaultElementDescription(ei));
8377 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8378 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8383 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
8389 if (!(file = fopen(filename, MODE_WRITE)))
8391 Error(ERR_WARN, "cannot save level file '%s'", filename);
8395 level->file_version = FILE_VERSION_ACTUAL;
8396 level->game_version = GAME_VERSION_ACTUAL;
8398 level->creation_date = getCurrentDate();
8400 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8401 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8403 chunk_size = SaveLevel_VERS(NULL, level);
8404 putFileChunkBE(file, "VERS", chunk_size);
8405 SaveLevel_VERS(file, level);
8407 chunk_size = SaveLevel_DATE(NULL, level);
8408 putFileChunkBE(file, "DATE", chunk_size);
8409 SaveLevel_DATE(file, level);
8411 chunk_size = SaveLevel_NAME(NULL, level);
8412 putFileChunkBE(file, "NAME", chunk_size);
8413 SaveLevel_NAME(file, level);
8415 chunk_size = SaveLevel_AUTH(NULL, level);
8416 putFileChunkBE(file, "AUTH", chunk_size);
8417 SaveLevel_AUTH(file, level);
8419 chunk_size = SaveLevel_INFO(NULL, level);
8420 putFileChunkBE(file, "INFO", chunk_size);
8421 SaveLevel_INFO(file, level);
8423 chunk_size = SaveLevel_BODY(NULL, level);
8424 putFileChunkBE(file, "BODY", chunk_size);
8425 SaveLevel_BODY(file, level);
8427 chunk_size = SaveLevel_ELEM(NULL, level);
8428 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
8430 putFileChunkBE(file, "ELEM", chunk_size);
8431 SaveLevel_ELEM(file, level);
8434 for (i = 0; i < NUM_ENVELOPES; i++)
8436 int element = EL_ENVELOPE_1 + i;
8438 chunk_size = SaveLevel_NOTE(NULL, level, element);
8439 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
8441 putFileChunkBE(file, "NOTE", chunk_size);
8442 SaveLevel_NOTE(file, level, element);
8446 /* if not using template level, check for non-default custom/group elements */
8447 if (!level->use_custom_template)
8449 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8451 int element = EL_CUSTOM_START + i;
8453 chunk_size = SaveLevel_CUSX(NULL, level, element);
8454 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
8456 putFileChunkBE(file, "CUSX", chunk_size);
8457 SaveLevel_CUSX(file, level, element);
8461 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8463 int element = EL_GROUP_START + i;
8465 chunk_size = SaveLevel_GRPX(NULL, level, element);
8466 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
8468 putFileChunkBE(file, "GRPX", chunk_size);
8469 SaveLevel_GRPX(file, level, element);
8476 SetFilePermissions(filename, PERMS_PRIVATE);
8479 void SaveLevel(int nr)
8481 char *filename = getDefaultLevelFilename(nr);
8483 SaveLevelFromFilename(&level, filename);
8486 void SaveLevelTemplate()
8488 char *filename = getDefaultLevelFilename(-1);
8490 SaveLevelFromFilename(&level, filename);
8493 boolean SaveLevelChecked(int nr)
8495 char *filename = getDefaultLevelFilename(nr);
8496 boolean new_level = !fileExists(filename);
8497 boolean level_saved = FALSE;
8499 if (new_level || Request("Save this level and kill the old ?", REQ_ASK))
8504 Request("Level saved !", REQ_CONFIRM);
8512 void DumpLevel(struct LevelInfo *level)
8514 if (level->no_valid_file)
8516 Error(ERR_WARN, "cannot dump -- no valid level file found");
8521 printf_line("-", 79);
8522 printf("Level xxx (file version %08d, game version %08d)\n",
8523 level->file_version, level->game_version);
8524 printf_line("-", 79);
8526 printf("Level author: '%s'\n", level->author);
8527 printf("Level title: '%s'\n", level->name);
8529 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8531 printf("Level time: %d seconds\n", level->time);
8532 printf("Gems needed: %d\n", level->gems_needed);
8534 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
8535 printf("Time for wheel: %d seconds\n", level->time_wheel);
8536 printf("Time for light: %d seconds\n", level->time_light);
8537 printf("Time for timegate: %d seconds\n", level->time_timegate);
8539 printf("Amoeba speed: %d\n", level->amoeba_speed);
8542 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8543 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8544 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8545 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8546 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8548 printf_line("-", 79);
8552 /* ========================================================================= */
8553 /* tape file functions */
8554 /* ========================================================================= */
8556 static void setTapeInfoToDefaults()
8560 /* always start with reliable default values (empty tape) */
8563 /* default values (also for pre-1.2 tapes) with only the first player */
8564 tape.player_participates[0] = TRUE;
8565 for (i = 1; i < MAX_PLAYERS; i++)
8566 tape.player_participates[i] = FALSE;
8568 /* at least one (default: the first) player participates in every tape */
8569 tape.num_participating_players = 1;
8571 tape.level_nr = level_nr;
8573 tape.changed = FALSE;
8575 tape.recording = FALSE;
8576 tape.playing = FALSE;
8577 tape.pausing = FALSE;
8579 tape.no_valid_file = FALSE;
8582 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
8584 tape->file_version = getFileVersion(file);
8585 tape->game_version = getFileVersion(file);
8590 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
8594 tape->random_seed = getFile32BitBE(file);
8595 tape->date = getFile32BitBE(file);
8596 tape->length = getFile32BitBE(file);
8598 /* read header fields that are new since version 1.2 */
8599 if (tape->file_version >= FILE_VERSION_1_2)
8601 byte store_participating_players = getFile8Bit(file);
8604 /* since version 1.2, tapes store which players participate in the tape */
8605 tape->num_participating_players = 0;
8606 for (i = 0; i < MAX_PLAYERS; i++)
8608 tape->player_participates[i] = FALSE;
8610 if (store_participating_players & (1 << i))
8612 tape->player_participates[i] = TRUE;
8613 tape->num_participating_players++;
8617 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
8619 engine_version = getFileVersion(file);
8620 if (engine_version > 0)
8621 tape->engine_version = engine_version;
8623 tape->engine_version = tape->game_version;
8629 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
8631 int level_identifier_size;
8634 level_identifier_size = getFile16BitBE(file);
8636 tape->level_identifier =
8637 checked_realloc(tape->level_identifier, level_identifier_size);
8639 for (i = 0; i < level_identifier_size; i++)
8640 tape->level_identifier[i] = getFile8Bit(file);
8642 tape->level_nr = getFile16BitBE(file);
8644 chunk_size = 2 + level_identifier_size + 2;
8649 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
8652 int chunk_size_expected =
8653 (tape->num_participating_players + 1) * tape->length;
8655 if (chunk_size_expected != chunk_size)
8657 ReadUnusedBytesFromFile(file, chunk_size);
8658 return chunk_size_expected;
8661 for (i = 0; i < tape->length; i++)
8663 if (i >= MAX_TAPE_LEN)
8666 for (j = 0; j < MAX_PLAYERS; j++)
8668 tape->pos[i].action[j] = MV_NONE;
8670 if (tape->player_participates[j])
8671 tape->pos[i].action[j] = getFile8Bit(file);
8674 tape->pos[i].delay = getFile8Bit(file);
8676 if (tape->file_version == FILE_VERSION_1_0)
8678 /* eliminate possible diagonal moves in old tapes */
8679 /* this is only for backward compatibility */
8681 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8682 byte action = tape->pos[i].action[0];
8683 int k, num_moves = 0;
8685 for (k = 0; k<4; k++)
8687 if (action & joy_dir[k])
8689 tape->pos[i + num_moves].action[0] = joy_dir[k];
8691 tape->pos[i + num_moves].delay = 0;
8700 tape->length += num_moves;
8703 else if (tape->file_version < FILE_VERSION_2_0)
8705 /* convert pre-2.0 tapes to new tape format */
8707 if (tape->pos[i].delay > 1)
8710 tape->pos[i + 1] = tape->pos[i];
8711 tape->pos[i + 1].delay = 1;
8714 for (j = 0; j < MAX_PLAYERS; j++)
8715 tape->pos[i].action[j] = MV_NONE;
8716 tape->pos[i].delay--;
8727 if (i != tape->length)
8728 chunk_size = (tape->num_participating_players + 1) * i;
8733 void LoadTape_SokobanSolution(char *filename)
8736 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8738 if (!(file = fopen(filename, MODE_READ)))
8740 tape.no_valid_file = TRUE;
8747 unsigned char c = fgetc(file);
8756 tape.pos[tape.length].action[0] = MV_UP;
8757 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8763 tape.pos[tape.length].action[0] = MV_DOWN;
8764 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8770 tape.pos[tape.length].action[0] = MV_LEFT;
8771 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8777 tape.pos[tape.length].action[0] = MV_RIGHT;
8778 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8786 /* ignore white-space characters */
8790 tape.no_valid_file = TRUE;
8792 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
8800 if (tape.no_valid_file)
8803 tape.length_seconds = GetTapeLength();
8806 void LoadTapeFromFilename(char *filename)
8808 char cookie[MAX_LINE_LEN];
8809 char chunk_name[CHUNK_ID_LEN + 1];
8813 /* always start with reliable default values */
8814 setTapeInfoToDefaults();
8816 if (strSuffix(filename, ".sln"))
8818 LoadTape_SokobanSolution(filename);
8823 if (!(file = fopen(filename, MODE_READ)))
8825 tape.no_valid_file = TRUE;
8830 getFileChunkBE(file, chunk_name, NULL);
8831 if (strEqual(chunk_name, "RND1"))
8833 getFile32BitBE(file); /* not used */
8835 getFileChunkBE(file, chunk_name, NULL);
8836 if (!strEqual(chunk_name, "TAPE"))
8838 tape.no_valid_file = TRUE;
8840 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
8845 else /* check for pre-2.0 file format with cookie string */
8847 strcpy(cookie, chunk_name);
8848 if (fgets(&cookie[4], MAX_LINE_LEN - 4, file) == NULL)
8850 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8851 cookie[strlen(cookie) - 1] = '\0';
8853 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8855 tape.no_valid_file = TRUE;
8857 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
8862 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8864 tape.no_valid_file = TRUE;
8866 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
8872 /* pre-2.0 tape files have no game version, so use file version here */
8873 tape.game_version = tape.file_version;
8876 if (tape.file_version < FILE_VERSION_1_2)
8878 /* tape files from versions before 1.2.0 without chunk structure */
8879 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8880 LoadTape_BODY(file, 2 * tape.length, &tape);
8888 int (*loader)(FILE *, int, struct TapeInfo *);
8892 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8893 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8894 { "INFO", -1, LoadTape_INFO },
8895 { "BODY", -1, LoadTape_BODY },
8899 while (getFileChunkBE(file, chunk_name, &chunk_size))
8903 while (chunk_info[i].name != NULL &&
8904 !strEqual(chunk_name, chunk_info[i].name))
8907 if (chunk_info[i].name == NULL)
8909 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
8910 chunk_name, filename);
8911 ReadUnusedBytesFromFile(file, chunk_size);
8913 else if (chunk_info[i].size != -1 &&
8914 chunk_info[i].size != chunk_size)
8916 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
8917 chunk_size, chunk_name, filename);
8918 ReadUnusedBytesFromFile(file, chunk_size);
8922 /* call function to load this tape chunk */
8923 int chunk_size_expected =
8924 (chunk_info[i].loader)(file, chunk_size, &tape);
8926 /* the size of some chunks cannot be checked before reading other
8927 chunks first (like "HEAD" and "BODY") that contain some header
8928 information, so check them here */
8929 if (chunk_size_expected != chunk_size)
8931 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
8932 chunk_size, chunk_name, filename);
8940 tape.length_seconds = GetTapeLength();
8943 printf("::: tape file version: %d\n", tape.file_version);
8944 printf("::: tape game version: %d\n", tape.game_version);
8945 printf("::: tape engine version: %d\n", tape.engine_version);
8949 void LoadTape(int nr)
8951 char *filename = getTapeFilename(nr);
8953 LoadTapeFromFilename(filename);
8956 void LoadSolutionTape(int nr)
8958 char *filename = getSolutionTapeFilename(nr);
8960 LoadTapeFromFilename(filename);
8963 if (TAPE_IS_EMPTY(tape) &&
8964 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8965 level.native_sp_level->demo.is_available)
8966 CopyNativeTape_SP_to_RND(&level);
8970 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8972 putFileVersion(file, tape->file_version);
8973 putFileVersion(file, tape->game_version);
8976 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8979 byte store_participating_players = 0;
8981 /* set bits for participating players for compact storage */
8982 for (i = 0; i < MAX_PLAYERS; i++)
8983 if (tape->player_participates[i])
8984 store_participating_players |= (1 << i);
8986 putFile32BitBE(file, tape->random_seed);
8987 putFile32BitBE(file, tape->date);
8988 putFile32BitBE(file, tape->length);
8990 putFile8Bit(file, store_participating_players);
8992 /* unused bytes not at the end here for 4-byte alignment of engine_version */
8993 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8995 putFileVersion(file, tape->engine_version);
8998 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9000 int level_identifier_size = strlen(tape->level_identifier) + 1;
9003 putFile16BitBE(file, level_identifier_size);
9005 for (i = 0; i < level_identifier_size; i++)
9006 putFile8Bit(file, tape->level_identifier[i]);
9008 putFile16BitBE(file, tape->level_nr);
9011 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9015 for (i = 0; i < tape->length; i++)
9017 for (j = 0; j < MAX_PLAYERS; j++)
9018 if (tape->player_participates[j])
9019 putFile8Bit(file, tape->pos[i].action[j]);
9021 putFile8Bit(file, tape->pos[i].delay);
9025 void SaveTape(int nr)
9027 char *filename = getTapeFilename(nr);
9030 boolean new_tape = TRUE;
9032 int num_participating_players = 0;
9033 int info_chunk_size;
9034 int body_chunk_size;
9037 InitTapeDirectory(leveldir_current->subdir);
9040 /* if a tape still exists, ask to overwrite it */
9041 if (fileExists(filename))
9044 if (!Request("Replace old tape ?", REQ_ASK))
9049 if (!(file = fopen(filename, MODE_WRITE)))
9051 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
9055 tape.file_version = FILE_VERSION_ACTUAL;
9056 tape.game_version = GAME_VERSION_ACTUAL;
9058 /* count number of participating players */
9059 for (i = 0; i < MAX_PLAYERS; i++)
9060 if (tape.player_participates[i])
9061 num_participating_players++;
9063 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9064 body_chunk_size = (num_participating_players + 1) * tape.length;
9066 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9067 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9069 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9070 SaveTape_VERS(file, &tape);
9072 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9073 SaveTape_HEAD(file, &tape);
9075 putFileChunkBE(file, "INFO", info_chunk_size);
9076 SaveTape_INFO(file, &tape);
9078 putFileChunkBE(file, "BODY", body_chunk_size);
9079 SaveTape_BODY(file, &tape);
9083 SetFilePermissions(filename, PERMS_PRIVATE);
9085 tape.changed = FALSE;
9089 Request("Tape saved !", REQ_CONFIRM);
9093 boolean SaveTapeChecked(int nr)
9095 char *filename = getTapeFilename(nr);
9096 boolean new_tape = !fileExists(filename);
9097 boolean tape_saved = FALSE;
9099 if (new_tape || Request("Replace old tape ?", REQ_ASK))
9104 Request("Tape saved !", REQ_CONFIRM);
9112 void DumpTape(struct TapeInfo *tape)
9114 int tape_frame_counter;
9117 if (tape->no_valid_file)
9119 Error(ERR_WARN, "cannot dump -- no valid tape file found");
9124 printf_line("-", 79);
9125 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
9126 tape->level_nr, tape->file_version, tape->game_version);
9127 printf(" (effective engine version %08d)\n",
9128 tape->engine_version);
9129 printf("Level series identifier: '%s'\n", tape->level_identifier);
9130 printf_line("-", 79);
9132 tape_frame_counter = 0;
9134 for (i = 0; i < tape->length; i++)
9136 if (i >= MAX_TAPE_LEN)
9139 printf("%04d: ", i);
9141 for (j = 0; j < MAX_PLAYERS; j++)
9143 if (tape->player_participates[j])
9145 int action = tape->pos[i].action[j];
9147 printf("%d:%02x ", j, action);
9148 printf("[%c%c%c%c|%c%c] - ",
9149 (action & JOY_LEFT ? '<' : ' '),
9150 (action & JOY_RIGHT ? '>' : ' '),
9151 (action & JOY_UP ? '^' : ' '),
9152 (action & JOY_DOWN ? 'v' : ' '),
9153 (action & JOY_BUTTON_1 ? '1' : ' '),
9154 (action & JOY_BUTTON_2 ? '2' : ' '));
9158 printf("(%03d) ", tape->pos[i].delay);
9159 printf("[%05d]\n", tape_frame_counter);
9161 tape_frame_counter += tape->pos[i].delay;
9164 printf_line("-", 79);
9168 /* ========================================================================= */
9169 /* score file functions */
9170 /* ========================================================================= */
9172 void LoadScore(int nr)
9175 char *filename = getScoreFilename(nr);
9176 char cookie[MAX_LINE_LEN];
9177 char line[MAX_LINE_LEN];
9181 /* always start with reliable default values */
9182 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9184 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
9185 highscore[i].Score = 0;
9188 if (!(file = fopen(filename, MODE_READ)))
9191 /* check file identifier */
9192 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9194 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9195 cookie[strlen(cookie) - 1] = '\0';
9197 if (!checkCookieString(cookie, SCORE_COOKIE))
9199 Error(ERR_WARN, "unknown format of score file '%s'", filename);
9204 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9206 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
9207 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
9208 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9211 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9212 line[strlen(line) - 1] = '\0';
9214 for (line_ptr = line; *line_ptr; line_ptr++)
9216 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9218 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
9219 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
9228 void SaveScore(int nr)
9231 char *filename = getScoreFilename(nr);
9234 InitScoreDirectory(leveldir_current->subdir);
9236 if (!(file = fopen(filename, MODE_WRITE)))
9238 Error(ERR_WARN, "cannot save score for level %d", nr);
9242 fprintf(file, "%s\n\n", SCORE_COOKIE);
9244 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9245 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
9249 SetFilePermissions(filename, PERMS_PUBLIC);
9253 /* ========================================================================= */
9254 /* setup file functions */
9255 /* ========================================================================= */
9257 #define TOKEN_STR_PLAYER_PREFIX "player_"
9260 #define SETUP_TOKEN_PLAYER_NAME 0
9261 #define SETUP_TOKEN_SOUND 1
9262 #define SETUP_TOKEN_SOUND_LOOPS 2
9263 #define SETUP_TOKEN_SOUND_MUSIC 3
9264 #define SETUP_TOKEN_SOUND_SIMPLE 4
9265 #define SETUP_TOKEN_TOONS 5
9266 #define SETUP_TOKEN_SCROLL_DELAY 6
9267 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
9268 #define SETUP_TOKEN_SOFT_SCROLLING 8
9269 #define SETUP_TOKEN_FADE_SCREENS 9
9270 #define SETUP_TOKEN_AUTORECORD 10
9271 #define SETUP_TOKEN_SHOW_TITLESCREEN 11
9272 #define SETUP_TOKEN_QUICK_DOORS 12
9273 #define SETUP_TOKEN_TEAM_MODE 13
9274 #define SETUP_TOKEN_HANDICAP 14
9275 #define SETUP_TOKEN_SKIP_LEVELS 15
9276 #define SETUP_TOKEN_TIME_LIMIT 16
9277 #define SETUP_TOKEN_FULLSCREEN 17
9278 #define SETUP_TOKEN_FULLSCREEN_MODE 18
9279 #define SETUP_TOKEN_ASK_ON_ESCAPE 19
9280 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 20
9281 #define SETUP_TOKEN_QUICK_SWITCH 21
9282 #define SETUP_TOKEN_INPUT_ON_FOCUS 22
9283 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 23
9284 #define SETUP_TOKEN_GAME_FRAME_DELAY 24
9285 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 25
9286 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 26
9287 #define SETUP_TOKEN_GRAPHICS_SET 27
9288 #define SETUP_TOKEN_SOUNDS_SET 28
9289 #define SETUP_TOKEN_MUSIC_SET 29
9290 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 30
9291 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 31
9292 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 32
9293 #define SETUP_TOKEN_VOLUME_SIMPLE 33
9294 #define SETUP_TOKEN_VOLUME_LOOPS 34
9295 #define SETUP_TOKEN_VOLUME_MUSIC 35
9297 #define NUM_GLOBAL_SETUP_TOKENS 36
9300 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
9301 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
9302 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
9303 #define SETUP_TOKEN_EDITOR_EL_MORE 3
9304 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 4
9305 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 5
9306 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 6
9307 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 7
9308 #define SETUP_TOKEN_EDITOR_EL_CHARS 8
9309 #define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS 9
9310 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 10
9311 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 11
9312 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 12
9313 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 13
9314 #define SETUP_TOKEN_EDITOR_EL_BY_GAME 14
9315 #define SETUP_TOKEN_EDITOR_EL_BY_TYPE 15
9316 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 16
9318 #define NUM_EDITOR_SETUP_TOKENS 17
9320 /* editor cascade setup */
9321 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
9322 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
9323 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
9324 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
9325 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
9326 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
9327 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
9328 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
9329 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
9330 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
9331 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
9332 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
9333 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
9334 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
9335 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
9337 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
9339 /* shortcut setup */
9340 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
9341 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
9342 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
9343 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
9344 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
9345 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
9346 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
9347 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
9348 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
9349 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
9350 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
9351 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
9352 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
9353 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
9354 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
9355 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
9356 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
9357 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
9358 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
9359 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
9360 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
9362 #define NUM_SHORTCUT_SETUP_TOKENS 21
9365 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
9366 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
9367 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
9368 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
9369 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
9370 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
9371 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
9372 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
9373 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
9374 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
9375 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
9376 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
9377 #define SETUP_TOKEN_PLAYER_KEY_UP 12
9378 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
9379 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
9380 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
9382 #define NUM_PLAYER_SETUP_TOKENS 16
9385 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
9386 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
9387 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
9389 #define NUM_SYSTEM_SETUP_TOKENS 3
9392 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
9394 #define NUM_OPTIONS_SETUP_TOKENS 1
9397 static struct SetupInfo si;
9398 static struct SetupEditorInfo sei;
9399 static struct SetupEditorCascadeInfo seci;
9400 static struct SetupShortcutInfo ssi;
9401 static struct SetupInputInfo sii;
9402 static struct SetupSystemInfo syi;
9403 static struct OptionInfo soi;
9405 static struct TokenInfo global_setup_tokens[] =
9407 { TYPE_STRING, &si.player_name, "player_name" },
9408 { TYPE_SWITCH, &si.sound, "sound" },
9409 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
9410 { TYPE_SWITCH, &si.sound_music, "background_music" },
9411 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
9412 { TYPE_SWITCH, &si.toons, "toons" },
9413 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
9414 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
9415 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
9416 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
9417 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
9418 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
9419 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
9420 { TYPE_SWITCH, &si.team_mode, "team_mode" },
9421 { TYPE_SWITCH, &si.handicap, "handicap" },
9422 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
9423 { TYPE_SWITCH, &si.time_limit, "time_limit" },
9424 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
9425 { TYPE_STRING, &si.fullscreen_mode, "fullscreen_mode" },
9426 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
9427 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
9428 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
9429 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
9430 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
9431 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
9432 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
9433 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
9434 { TYPE_STRING, &si.graphics_set, "graphics_set" },
9435 { TYPE_STRING, &si.sounds_set, "sounds_set" },
9436 { TYPE_STRING, &si.music_set, "music_set" },
9437 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
9438 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
9439 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
9440 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
9441 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
9442 { TYPE_INTEGER,&si.volume_music, "volume_music" },
9445 static boolean not_used = FALSE;
9446 static struct TokenInfo editor_setup_tokens[] =
9449 { TYPE_SWITCH, ¬_used, "editor.el_boulderdash" },
9450 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine" },
9451 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine_club" },
9452 { TYPE_SWITCH, ¬_used, "editor.el_more" },
9453 { TYPE_SWITCH, ¬_used, "editor.el_sokoban" },
9454 { TYPE_SWITCH, ¬_used, "editor.el_supaplex" },
9455 { TYPE_SWITCH, ¬_used, "editor.el_diamond_caves" },
9456 { TYPE_SWITCH, ¬_used, "editor.el_dx_boulderdash" },
9458 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
9459 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
9460 { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
9461 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
9462 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
9463 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
9464 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
9465 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
9467 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
9468 { TYPE_SWITCH, &sei.el_steel_chars, "editor.el_steel_chars" },
9469 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
9471 { TYPE_SWITCH, ¬_used, "editor.el_headlines" },
9473 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
9475 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
9476 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
9477 { TYPE_SWITCH, &sei.el_by_game, "editor.el_by_game" },
9478 { TYPE_SWITCH, &sei.el_by_type, "editor.el_by_type" },
9479 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
9482 static struct TokenInfo editor_cascade_setup_tokens[] =
9484 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
9485 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
9486 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
9487 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
9488 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
9489 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
9490 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
9491 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
9492 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
9493 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
9494 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
9495 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
9496 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
9497 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
9498 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
9501 static struct TokenInfo shortcut_setup_tokens[] =
9503 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
9504 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
9505 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
9506 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
9507 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
9508 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
9509 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
9510 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
9511 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
9512 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
9513 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
9514 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
9515 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
9516 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
9517 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
9518 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
9519 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
9520 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
9521 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
9522 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
9523 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
9526 static struct TokenInfo player_setup_tokens[] =
9528 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
9529 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
9530 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
9531 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
9532 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
9533 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
9534 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
9535 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
9536 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
9537 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
9538 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
9539 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
9540 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
9541 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
9542 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
9543 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
9546 static struct TokenInfo system_setup_tokens[] =
9548 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
9549 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
9550 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
9553 static struct TokenInfo options_setup_tokens[] =
9555 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
9558 static char *get_corrected_login_name(char *login_name)
9560 /* needed because player name must be a fixed length string */
9561 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
9563 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
9564 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
9566 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
9567 if (strchr(login_name_new, ' '))
9568 *strchr(login_name_new, ' ') = '\0';
9570 return login_name_new;
9573 static void setSetupInfoToDefaults(struct SetupInfo *si)
9577 si->player_name = get_corrected_login_name(getLoginName());
9580 si->sound_loops = TRUE;
9581 si->sound_music = TRUE;
9582 si->sound_simple = TRUE;
9584 si->scroll_delay = TRUE;
9585 si->scroll_delay_value = STD_SCROLL_DELAY;
9586 si->soft_scrolling = TRUE;
9587 si->fade_screens = TRUE;
9588 si->autorecord = TRUE;
9589 si->show_titlescreen = TRUE;
9590 si->quick_doors = FALSE;
9591 si->team_mode = FALSE;
9592 si->handicap = TRUE;
9593 si->skip_levels = TRUE;
9594 si->time_limit = TRUE;
9595 si->fullscreen = FALSE;
9596 si->fullscreen_mode = getStringCopy(DEFAULT_FULLSCREEN_MODE);
9597 si->ask_on_escape = TRUE;
9598 si->ask_on_escape_editor = TRUE;
9599 si->quick_switch = FALSE;
9600 si->input_on_focus = FALSE;
9601 si->prefer_aga_graphics = TRUE;
9602 si->game_frame_delay = GAME_FRAME_DELAY;
9603 si->sp_show_border_elements = FALSE;
9604 si->small_game_graphics = FALSE;
9606 si->graphics_set = getStringCopy(GFX_DEFAULT_SUBDIR);
9607 si->sounds_set = getStringCopy(SND_DEFAULT_SUBDIR);
9608 si->music_set = getStringCopy(MUS_DEFAULT_SUBDIR);
9609 si->override_level_graphics = FALSE;
9610 si->override_level_sounds = FALSE;
9611 si->override_level_music = FALSE;
9613 si->volume_simple = 100; /* percent */
9614 si->volume_loops = 100; /* percent */
9615 si->volume_music = 100; /* percent */
9617 si->editor.el_boulderdash = TRUE;
9618 si->editor.el_emerald_mine = TRUE;
9619 si->editor.el_emerald_mine_club = TRUE;
9620 si->editor.el_more = TRUE;
9621 si->editor.el_sokoban = TRUE;
9622 si->editor.el_supaplex = TRUE;
9623 si->editor.el_diamond_caves = TRUE;
9624 si->editor.el_dx_boulderdash = TRUE;
9625 si->editor.el_chars = TRUE;
9626 si->editor.el_steel_chars = TRUE;
9627 si->editor.el_custom = TRUE;
9629 si->editor.el_headlines = TRUE;
9630 si->editor.el_user_defined = FALSE;
9631 si->editor.el_dynamic = TRUE;
9633 si->editor.show_element_token = FALSE;
9635 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
9636 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
9637 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
9639 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
9640 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
9641 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
9642 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
9643 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9645 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
9646 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
9647 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
9648 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
9649 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
9650 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
9652 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
9653 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
9654 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
9656 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
9657 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
9658 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
9659 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
9661 for (i = 0; i < MAX_PLAYERS; i++)
9663 si->input[i].use_joystick = FALSE;
9664 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9665 si->input[i].joy.xleft = JOYSTICK_XLEFT;
9666 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9667 si->input[i].joy.xright = JOYSTICK_XRIGHT;
9668 si->input[i].joy.yupper = JOYSTICK_YUPPER;
9669 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9670 si->input[i].joy.ylower = JOYSTICK_YLOWER;
9671 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
9672 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
9673 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
9674 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9675 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
9676 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
9677 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
9678 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
9681 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9682 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9683 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9685 si->options.verbose = FALSE;
9687 #if defined(CREATE_SPECIAL_EDITION_RND_JUE)
9689 si->handicap = FALSE;
9690 si->fullscreen = TRUE;
9691 si->override_level_graphics = AUTO;
9692 si->override_level_sounds = AUTO;
9693 si->override_level_music = AUTO;
9697 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9699 si->editor_cascade.el_bd = TRUE;
9700 si->editor_cascade.el_em = TRUE;
9701 si->editor_cascade.el_emc = TRUE;
9702 si->editor_cascade.el_rnd = TRUE;
9703 si->editor_cascade.el_sb = TRUE;
9704 si->editor_cascade.el_sp = TRUE;
9705 si->editor_cascade.el_dc = TRUE;
9706 si->editor_cascade.el_dx = TRUE;
9708 si->editor_cascade.el_chars = FALSE;
9709 si->editor_cascade.el_steel_chars = FALSE;
9710 si->editor_cascade.el_ce = FALSE;
9711 si->editor_cascade.el_ge = FALSE;
9712 si->editor_cascade.el_ref = FALSE;
9713 si->editor_cascade.el_user = FALSE;
9714 si->editor_cascade.el_dynamic = FALSE;
9717 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9721 if (!setup_file_hash)
9726 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9727 setSetupInfo(global_setup_tokens, i,
9728 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
9733 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9734 setSetupInfo(editor_setup_tokens, i,
9735 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
9738 /* shortcut setup */
9739 ssi = setup.shortcut;
9740 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9741 setSetupInfo(shortcut_setup_tokens, i,
9742 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
9743 setup.shortcut = ssi;
9746 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9750 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9752 sii = setup.input[pnr];
9753 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9755 char full_token[100];
9757 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9758 setSetupInfo(player_setup_tokens, i,
9759 getHashEntry(setup_file_hash, full_token));
9761 setup.input[pnr] = sii;
9766 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9767 setSetupInfo(system_setup_tokens, i,
9768 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
9772 soi = setup.options;
9773 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9774 setSetupInfo(options_setup_tokens, i,
9775 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
9776 setup.options = soi;
9779 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9783 if (!setup_file_hash)
9786 /* editor cascade setup */
9787 seci = setup.editor_cascade;
9788 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9789 setSetupInfo(editor_cascade_setup_tokens, i,
9790 getHashEntry(setup_file_hash,
9791 editor_cascade_setup_tokens[i].text));
9792 setup.editor_cascade = seci;
9797 char *filename = getSetupFilename();
9798 SetupFileHash *setup_file_hash = NULL;
9800 /* always start with reliable default values */
9801 setSetupInfoToDefaults(&setup);
9803 setup_file_hash = loadSetupFileHash(filename);
9805 if (setup_file_hash)
9807 char *player_name_new;
9809 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
9810 decodeSetupFileHash(setup_file_hash);
9812 freeSetupFileHash(setup_file_hash);
9814 /* needed to work around problems with fixed length strings */
9815 player_name_new = get_corrected_login_name(setup.player_name);
9816 free(setup.player_name);
9817 setup.player_name = player_name_new;
9819 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9820 if (setup.scroll_delay == FALSE)
9822 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9823 setup.scroll_delay = TRUE; /* now always "on" */
9826 /* make sure that scroll delay value stays inside valid range */
9827 setup.scroll_delay_value =
9828 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9831 Error(ERR_WARN, "using default setup values");
9834 void LoadSetup_EditorCascade()
9836 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9837 SetupFileHash *setup_file_hash = NULL;
9839 /* always start with reliable default values */
9840 setSetupInfoToDefaults_EditorCascade(&setup);
9842 setup_file_hash = loadSetupFileHash(filename);
9844 if (setup_file_hash)
9846 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
9847 decodeSetupFileHash_EditorCascade(setup_file_hash);
9849 freeSetupFileHash(setup_file_hash);
9857 char *filename = getSetupFilename();
9861 InitUserDataDirectory();
9863 if (!(file = fopen(filename, MODE_WRITE)))
9865 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9869 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
9870 getCookie("SETUP")));
9871 fprintf(file, "\n");
9875 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9877 /* just to make things nicer :) */
9878 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9879 i == SETUP_TOKEN_GRAPHICS_SET ||
9880 i == SETUP_TOKEN_VOLUME_SIMPLE)
9881 fprintf(file, "\n");
9883 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9888 fprintf(file, "\n");
9889 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9890 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9892 /* shortcut setup */
9893 ssi = setup.shortcut;
9894 fprintf(file, "\n");
9895 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9896 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9899 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9903 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9904 fprintf(file, "\n");
9906 sii = setup.input[pnr];
9907 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9908 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9913 fprintf(file, "\n");
9914 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9915 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9918 soi = setup.options;
9919 fprintf(file, "\n");
9920 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9921 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9925 SetFilePermissions(filename, PERMS_PRIVATE);
9928 void SaveSetup_EditorCascade()
9930 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9934 InitUserDataDirectory();
9936 if (!(file = fopen(filename, MODE_WRITE)))
9938 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9943 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
9944 getCookie("SETUP")));
9945 fprintf(file, "\n");
9947 seci = setup.editor_cascade;
9948 fprintf(file, "\n");
9949 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9950 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9954 SetFilePermissions(filename, PERMS_PRIVATE);
9959 void LoadCustomElementDescriptions()
9961 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9962 SetupFileHash *setup_file_hash;
9965 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9967 if (element_info[i].custom_description != NULL)
9969 free(element_info[i].custom_description);
9970 element_info[i].custom_description = NULL;
9974 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9977 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9979 char *token = getStringCat2(element_info[i].token_name, ".name");
9980 char *value = getHashEntry(setup_file_hash, token);
9983 element_info[i].custom_description = getStringCopy(value);
9988 freeSetupFileHash(setup_file_hash);
9991 static int getElementFromToken(char *token)
9994 char *value = getHashEntry(element_token_hash, token);
10001 /* !!! OPTIMIZE THIS BY USING HASH !!! */
10002 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10003 if (strEqual(token, element_info[i].token_name))
10007 Error(ERR_WARN, "unknown element token '%s'", token);
10009 return EL_UNDEFINED;
10012 static int get_token_parameter_value(char *token, char *value_raw)
10016 if (token == NULL || value_raw == NULL)
10017 return ARG_UNDEFINED_VALUE;
10019 suffix = strrchr(token, '.');
10020 if (suffix == NULL)
10024 if (strEqual(suffix, ".element"))
10025 return getElementFromToken(value_raw);
10029 if (strncmp(suffix, ".font", 5) == 0)
10033 /* !!! OPTIMIZE THIS BY USING HASH !!! */
10034 for (i = 0; i < NUM_FONTS; i++)
10035 if (strEqual(value_raw, font_info[i].token_name))
10038 /* if font not found, use reliable default value */
10039 return FONT_INITIAL_1;
10043 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
10044 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10047 void InitMenuDesignSettings_Static()
10050 static SetupFileHash *image_config_hash = NULL;
10055 if (image_config_hash == NULL)
10057 image_config_hash = newSetupFileHash();
10059 for (i = 0; image_config[i].token != NULL; i++)
10060 setHashEntry(image_config_hash,
10061 image_config[i].token,
10062 image_config[i].value);
10067 /* always start with reliable default values from static default config */
10068 for (i = 0; image_config_vars[i].token != NULL; i++)
10070 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10073 *image_config_vars[i].value =
10074 get_token_parameter_value(image_config_vars[i].token, value);
10081 /* always start with reliable default values from static default config */
10082 for (i = 0; image_config_vars[i].token != NULL; i++)
10083 for (j = 0; image_config[j].token != NULL; j++)
10084 if (strEqual(image_config_vars[i].token, image_config[j].token))
10085 *image_config_vars[i].value =
10086 get_token_parameter_value(image_config_vars[i].token,
10087 image_config[j].value);
10091 static void InitMenuDesignSettings_SpecialPreProcessing()
10095 /* the following initializes hierarchical values from static configuration */
10097 /* special case: initialize "ARG_DEFAULT" values in static default config */
10098 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
10099 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
10100 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10101 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10102 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
10103 titlemessage_default.fade_mode = title_default.fade_mode;
10104 titlemessage_default.fade_delay = title_default.fade_delay;
10105 titlemessage_default.post_delay = title_default.post_delay;
10106 titlemessage_default.auto_delay = title_default.auto_delay;
10108 /* special case: initialize "ARG_DEFAULT" values in static default config */
10109 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
10110 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10112 titlemessage_initial[i] = titlemessage_initial_default;
10113 titlemessage[i] = titlemessage_default;
10116 /* special case: initialize "ARG_DEFAULT" values in static default config */
10117 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
10118 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10120 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
10121 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
10124 /* special case: initialize "ARG_DEFAULT" values in static default config */
10125 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
10126 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10128 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
10129 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
10130 if (i != GFX_SPECIAL_ARG_EDITOR) /* editor value already initialized */
10131 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
10135 static void InitMenuDesignSettings_SpecialPostProcessing()
10137 /* special case: initialize later added SETUP list size from LEVELS value */
10138 if (menu.list_size[GAME_MODE_SETUP] == -1)
10139 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
10142 static void LoadMenuDesignSettingsFromFilename(char *filename)
10144 static struct TitleMessageInfo tmi;
10145 static struct TokenInfo titlemessage_tokens[] =
10147 { TYPE_INTEGER, &tmi.x, ".x" },
10148 { TYPE_INTEGER, &tmi.y, ".y" },
10149 { TYPE_INTEGER, &tmi.width, ".width" },
10150 { TYPE_INTEGER, &tmi.height, ".height" },
10151 { TYPE_INTEGER, &tmi.chars, ".chars" },
10152 { TYPE_INTEGER, &tmi.lines, ".lines" },
10153 { TYPE_INTEGER, &tmi.align, ".align" },
10154 { TYPE_INTEGER, &tmi.valign, ".valign" },
10155 { TYPE_INTEGER, &tmi.font, ".font" },
10156 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
10157 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
10158 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
10159 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
10160 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
10161 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
10162 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
10163 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
10169 struct TitleMessageInfo *array;
10172 titlemessage_arrays[] =
10174 { titlemessage_initial, "[titlemessage_initial]" },
10175 { titlemessage, "[titlemessage]" },
10179 SetupFileHash *setup_file_hash;
10183 printf("LoadMenuDesignSettings from file '%s' ...\n", filename);
10186 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10189 /* the following initializes hierarchical values from dynamic configuration */
10191 /* special case: initialize with default values that may be overwritten */
10192 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
10193 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10195 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
10196 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
10197 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
10199 if (value_1 != NULL)
10200 menu.draw_xoffset[i] = get_integer_from_string(value_1);
10201 if (value_2 != NULL)
10202 menu.draw_yoffset[i] = get_integer_from_string(value_2);
10203 if (value_3 != NULL)
10204 menu.list_size[i] = get_integer_from_string(value_3);
10207 /* special case: initialize with default values that may be overwritten */
10208 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
10209 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
10211 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
10212 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
10214 if (value_1 != NULL)
10215 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
10216 if (value_2 != NULL)
10217 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
10220 /* special case: initialize with default values that may be overwritten */
10221 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
10222 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
10224 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
10225 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
10227 if (value_1 != NULL)
10228 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
10229 if (value_2 != NULL)
10230 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
10233 /* special case: initialize with default values that may be overwritten */
10234 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
10235 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10237 char *token_1 = "menu.enter_screen.fade_mode";
10238 char *token_2 = "menu.enter_screen.fade_delay";
10239 char *token_3 = "menu.enter_screen.post_delay";
10240 char *token_4 = "menu.leave_screen.fade_mode";
10241 char *token_5 = "menu.leave_screen.fade_delay";
10242 char *token_6 = "menu.leave_screen.post_delay";
10243 char *value_1 = getHashEntry(setup_file_hash, token_1);
10244 char *value_2 = getHashEntry(setup_file_hash, token_2);
10245 char *value_3 = getHashEntry(setup_file_hash, token_3);
10246 char *value_4 = getHashEntry(setup_file_hash, token_4);
10247 char *value_5 = getHashEntry(setup_file_hash, token_5);
10248 char *value_6 = getHashEntry(setup_file_hash, token_6);
10250 if (value_1 != NULL)
10251 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
10253 if (value_2 != NULL)
10254 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
10256 if (value_3 != NULL)
10257 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
10259 if (value_4 != NULL)
10260 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
10262 if (value_5 != NULL)
10263 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
10265 if (value_6 != NULL)
10266 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
10270 /* special case: initialize with default values that may be overwritten */
10271 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
10272 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10274 char *token_1 = "viewport.playfield.x";
10275 char *token_2 = "viewport.playfield.y";
10276 char *token_3 = "viewport.playfield.width";
10277 char *token_4 = "viewport.playfield.height";
10278 char *token_5 = "viewport.playfield.border_size";
10279 char *token_6 = "viewport.door_1.x";
10280 char *token_7 = "viewport.door_1.y";
10281 char *token_8 = "viewport.door_2.x";
10282 char *token_9 = "viewport.door_2.y";
10283 char *value_1 = getHashEntry(setup_file_hash, token_1);
10284 char *value_2 = getHashEntry(setup_file_hash, token_2);
10285 char *value_3 = getHashEntry(setup_file_hash, token_3);
10286 char *value_4 = getHashEntry(setup_file_hash, token_4);
10287 char *value_5 = getHashEntry(setup_file_hash, token_5);
10288 char *value_6 = getHashEntry(setup_file_hash, token_6);
10289 char *value_7 = getHashEntry(setup_file_hash, token_7);
10290 char *value_8 = getHashEntry(setup_file_hash, token_8);
10291 char *value_9 = getHashEntry(setup_file_hash, token_9);
10293 if (value_1 != NULL)
10294 viewport.playfield[i].x = get_token_parameter_value(token_1, value_1);
10295 if (value_2 != NULL)
10296 viewport.playfield[i].y = get_token_parameter_value(token_2, value_2);
10297 if (value_3 != NULL)
10298 viewport.playfield[i].width = get_token_parameter_value(token_3, value_3);
10299 if (value_4 != NULL)
10300 viewport.playfield[i].height = get_token_parameter_value(token_4,value_4);
10301 if (value_5 != NULL)
10302 viewport.playfield[i].border_size = get_token_parameter_value(token_5,
10304 if (value_6 != NULL)
10305 viewport.door_1[i].x = get_token_parameter_value(token_6, value_6);
10306 if (value_7 != NULL)
10307 viewport.door_1[i].y = get_token_parameter_value(token_7, value_7);
10308 if (value_8 != NULL)
10309 viewport.door_2[i].x = get_token_parameter_value(token_8, value_8);
10310 if (value_9 != NULL)
10311 viewport.door_2[i].y = get_token_parameter_value(token_9, value_9);
10314 /* special case: initialize with default values that may be overwritten */
10315 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
10316 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
10318 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
10319 char *base_token = titlemessage_arrays[i].text;
10321 for (j = 0; titlemessage_tokens[j].type != -1; j++)
10323 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
10324 char *value = getHashEntry(setup_file_hash, token);
10328 int parameter_value = get_token_parameter_value(token, value);
10330 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
10334 if (titlemessage_tokens[j].type == TYPE_INTEGER)
10335 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
10337 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
10347 /* read (and overwrite with) values that may be specified in config file */
10348 for (i = 0; image_config_vars[i].token != NULL; i++)
10350 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
10352 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
10353 if (value != NULL && !strEqual(value, ARG_DEFAULT))
10354 *image_config_vars[i].value =
10355 get_token_parameter_value(image_config_vars[i].token, value);
10358 freeSetupFileHash(setup_file_hash);
10361 void LoadMenuDesignSettings()
10363 char *filename_base = UNDEFINED_FILENAME, *filename_local;
10365 InitMenuDesignSettings_Static();
10366 InitMenuDesignSettings_SpecialPreProcessing();
10369 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
10371 if (!SETUP_OVERRIDE_ARTWORK(setup, ARTWORK_TYPE_GRAPHICS))
10374 /* first look for special settings configured in level series config */
10375 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
10377 if (fileExists(filename_base))
10378 LoadMenuDesignSettingsFromFilename(filename_base);
10381 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10383 if (filename_local != NULL && !strEqual(filename_base, filename_local))
10384 LoadMenuDesignSettingsFromFilename(filename_local);
10386 InitMenuDesignSettings_SpecialPostProcessing();
10389 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
10391 char *filename = getEditorSetupFilename();
10392 SetupFileList *setup_file_list, *list;
10393 SetupFileHash *element_hash;
10394 int num_unknown_tokens = 0;
10397 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
10400 element_hash = newSetupFileHash();
10402 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10403 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10405 /* determined size may be larger than needed (due to unknown elements) */
10407 for (list = setup_file_list; list != NULL; list = list->next)
10410 /* add space for up to 3 more elements for padding that may be needed */
10411 *num_elements += 3;
10413 /* free memory for old list of elements, if needed */
10414 checked_free(*elements);
10416 /* allocate memory for new list of elements */
10417 *elements = checked_malloc(*num_elements * sizeof(int));
10420 for (list = setup_file_list; list != NULL; list = list->next)
10422 char *value = getHashEntry(element_hash, list->token);
10424 if (value == NULL) /* try to find obsolete token mapping */
10426 char *mapped_token = get_mapped_token(list->token);
10428 if (mapped_token != NULL)
10430 value = getHashEntry(element_hash, mapped_token);
10432 free(mapped_token);
10438 (*elements)[(*num_elements)++] = atoi(value);
10442 if (num_unknown_tokens == 0)
10444 Error(ERR_INFO_LINE, "-");
10445 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10446 Error(ERR_INFO, "- config file: '%s'", filename);
10448 num_unknown_tokens++;
10451 Error(ERR_INFO, "- token: '%s'", list->token);
10455 if (num_unknown_tokens > 0)
10456 Error(ERR_INFO_LINE, "-");
10458 while (*num_elements % 4) /* pad with empty elements, if needed */
10459 (*elements)[(*num_elements)++] = EL_EMPTY;
10461 freeSetupFileList(setup_file_list);
10462 freeSetupFileHash(element_hash);
10465 for (i = 0; i < *num_elements; i++)
10466 printf("editor: element '%s' [%d]\n",
10467 element_info[(*elements)[i]].token_name, (*elements)[i]);
10471 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10474 SetupFileHash *setup_file_hash = NULL;
10475 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10476 char *filename_music, *filename_prefix, *filename_info;
10482 token_to_value_ptr[] =
10484 { "title_header", &tmp_music_file_info.title_header },
10485 { "artist_header", &tmp_music_file_info.artist_header },
10486 { "album_header", &tmp_music_file_info.album_header },
10487 { "year_header", &tmp_music_file_info.year_header },
10489 { "title", &tmp_music_file_info.title },
10490 { "artist", &tmp_music_file_info.artist },
10491 { "album", &tmp_music_file_info.album },
10492 { "year", &tmp_music_file_info.year },
10498 filename_music = (is_sound ? getCustomSoundFilename(basename) :
10499 getCustomMusicFilename(basename));
10501 if (filename_music == NULL)
10504 /* ---------- try to replace file extension ---------- */
10506 filename_prefix = getStringCopy(filename_music);
10507 if (strrchr(filename_prefix, '.') != NULL)
10508 *strrchr(filename_prefix, '.') = '\0';
10509 filename_info = getStringCat2(filename_prefix, ".txt");
10512 printf("trying to load file '%s'...\n", filename_info);
10515 if (fileExists(filename_info))
10516 setup_file_hash = loadSetupFileHash(filename_info);
10518 free(filename_prefix);
10519 free(filename_info);
10521 if (setup_file_hash == NULL)
10523 /* ---------- try to add file extension ---------- */
10525 filename_prefix = getStringCopy(filename_music);
10526 filename_info = getStringCat2(filename_prefix, ".txt");
10529 printf("trying to load file '%s'...\n", filename_info);
10532 if (fileExists(filename_info))
10533 setup_file_hash = loadSetupFileHash(filename_info);
10535 free(filename_prefix);
10536 free(filename_info);
10539 if (setup_file_hash == NULL)
10542 /* ---------- music file info found ---------- */
10544 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10546 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10548 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10550 *token_to_value_ptr[i].value_ptr =
10551 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10554 tmp_music_file_info.basename = getStringCopy(basename);
10555 tmp_music_file_info.music = music;
10556 tmp_music_file_info.is_sound = is_sound;
10558 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10559 *new_music_file_info = tmp_music_file_info;
10561 return new_music_file_info;
10564 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10566 return get_music_file_info_ext(basename, music, FALSE);
10569 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10571 return get_music_file_info_ext(basename, sound, TRUE);
10574 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10575 char *basename, boolean is_sound)
10577 for (; list != NULL; list = list->next)
10578 if (list->is_sound == is_sound && strEqual(list->basename, basename))
10584 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10586 return music_info_listed_ext(list, basename, FALSE);
10589 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10591 return music_info_listed_ext(list, basename, TRUE);
10596 void LoadMusicInfo()
10598 char *music_directory = getCustomMusicDirectory();
10599 int num_music = getMusicListSize();
10600 int num_music_noconf = 0;
10601 int num_sounds = getSoundListSize();
10603 DirectoryEntry *dir_entry;
10604 struct FileInfo *music, *sound;
10605 struct MusicFileInfo *next, **new;
10608 while (music_file_info != NULL)
10610 next = music_file_info->next;
10612 checked_free(music_file_info->basename);
10614 checked_free(music_file_info->title_header);
10615 checked_free(music_file_info->artist_header);
10616 checked_free(music_file_info->album_header);
10617 checked_free(music_file_info->year_header);
10619 checked_free(music_file_info->title);
10620 checked_free(music_file_info->artist);
10621 checked_free(music_file_info->album);
10622 checked_free(music_file_info->year);
10624 free(music_file_info);
10626 music_file_info = next;
10629 new = &music_file_info;
10631 for (i = 0; i < num_music; i++)
10633 music = getMusicListEntry(i);
10635 if (music->filename == NULL)
10638 if (strEqual(music->filename, UNDEFINED_FILENAME))
10641 /* a configured file may be not recognized as music */
10642 if (!FileIsMusic(music->filename))
10646 printf("::: -> '%s' (configured)\n", music->filename);
10649 if (!music_info_listed(music_file_info, music->filename))
10651 *new = get_music_file_info(music->filename, i);
10654 printf(":1: adding '%s' ['%s'] ...\n", (*new)->title, music->filename);
10657 new = &(*new)->next;
10661 if ((dir = openDirectory(music_directory)) == NULL)
10663 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10667 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
10669 char *basename = dir_entry->basename;
10670 boolean music_already_used = FALSE;
10673 /* skip all music files that are configured in music config file */
10674 for (i = 0; i < num_music; i++)
10676 music = getMusicListEntry(i);
10678 if (music->filename == NULL)
10681 if (strEqual(basename, music->filename))
10683 music_already_used = TRUE;
10688 if (music_already_used)
10691 if (!FileIsMusic(basename))
10695 printf("::: -> '%s' (found in directory)\n", basename);
10698 if (!music_info_listed(music_file_info, basename))
10700 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10703 printf(":2: adding '%s' ['%s'] ...\n", (*new)->title, basename);
10706 new = &(*new)->next;
10709 num_music_noconf++;
10712 closeDirectory(dir);
10714 for (i = 0; i < num_sounds; i++)
10716 sound = getSoundListEntry(i);
10718 if (sound->filename == NULL)
10721 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10724 /* a configured file may be not recognized as sound */
10725 if (!FileIsSound(sound->filename))
10729 printf("::: -> '%s' (configured)\n", sound->filename);
10732 if (!sound_info_listed(music_file_info, sound->filename))
10734 *new = get_sound_file_info(sound->filename, i);
10736 new = &(*new)->next;
10741 for (next = music_file_info; next != NULL; next = next->next)
10742 printf("::: title == '%s'\n", next->title);
10748 void LoadMusicInfo()
10750 char *music_directory = getCustomMusicDirectory();
10751 int num_music = getMusicListSize();
10752 int num_music_noconf = 0;
10753 int num_sounds = getSoundListSize();
10755 struct dirent *dir_entry;
10756 struct FileInfo *music, *sound;
10757 struct MusicFileInfo *next, **new;
10760 while (music_file_info != NULL)
10762 next = music_file_info->next;
10764 checked_free(music_file_info->basename);
10766 checked_free(music_file_info->title_header);
10767 checked_free(music_file_info->artist_header);
10768 checked_free(music_file_info->album_header);
10769 checked_free(music_file_info->year_header);
10771 checked_free(music_file_info->title);
10772 checked_free(music_file_info->artist);
10773 checked_free(music_file_info->album);
10774 checked_free(music_file_info->year);
10776 free(music_file_info);
10778 music_file_info = next;
10781 new = &music_file_info;
10783 for (i = 0; i < num_music; i++)
10785 music = getMusicListEntry(i);
10787 if (music->filename == NULL)
10790 if (strEqual(music->filename, UNDEFINED_FILENAME))
10793 /* a configured file may be not recognized as music */
10794 if (!FileIsMusic(music->filename))
10798 printf("::: -> '%s' (configured)\n", music->filename);
10801 if (!music_info_listed(music_file_info, music->filename))
10803 *new = get_music_file_info(music->filename, i);
10806 printf(":1: adding '%s' ['%s'] ...\n", (*new)->title, music->filename);
10809 new = &(*new)->next;
10813 if ((dir = opendir(music_directory)) == NULL)
10815 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10819 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
10821 char *basename = dir_entry->d_name;
10822 boolean music_already_used = FALSE;
10825 /* skip all music files that are configured in music config file */
10826 for (i = 0; i < num_music; i++)
10828 music = getMusicListEntry(i);
10830 if (music->filename == NULL)
10833 if (strEqual(basename, music->filename))
10835 music_already_used = TRUE;
10840 if (music_already_used)
10843 if (!FileIsMusic(basename))
10847 printf("::: -> '%s' (found in directory)\n", basename);
10850 if (!music_info_listed(music_file_info, basename))
10852 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10855 printf(":2: adding '%s' ['%s'] ...\n", (*new)->title, basename);
10858 new = &(*new)->next;
10861 num_music_noconf++;
10866 for (i = 0; i < num_sounds; i++)
10868 sound = getSoundListEntry(i);
10870 if (sound->filename == NULL)
10873 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10876 /* a configured file may be not recognized as sound */
10877 if (!FileIsSound(sound->filename))
10881 printf("::: -> '%s' (configured)\n", sound->filename);
10884 if (!sound_info_listed(music_file_info, sound->filename))
10886 *new = get_sound_file_info(sound->filename, i);
10888 new = &(*new)->next;
10893 for (next = music_file_info; next != NULL; next = next->next)
10894 printf("::: title == '%s'\n", next->title);
10900 void add_helpanim_entry(int element, int action, int direction, int delay,
10901 int *num_list_entries)
10903 struct HelpAnimInfo *new_list_entry;
10904 (*num_list_entries)++;
10907 checked_realloc(helpanim_info,
10908 *num_list_entries * sizeof(struct HelpAnimInfo));
10909 new_list_entry = &helpanim_info[*num_list_entries - 1];
10911 new_list_entry->element = element;
10912 new_list_entry->action = action;
10913 new_list_entry->direction = direction;
10914 new_list_entry->delay = delay;
10917 void print_unknown_token(char *filename, char *token, int token_nr)
10921 Error(ERR_INFO_LINE, "-");
10922 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10923 Error(ERR_INFO, "- config file: '%s'", filename);
10926 Error(ERR_INFO, "- token: '%s'", token);
10929 void print_unknown_token_end(int token_nr)
10932 Error(ERR_INFO_LINE, "-");
10935 void LoadHelpAnimInfo()
10937 char *filename = getHelpAnimFilename();
10938 SetupFileList *setup_file_list = NULL, *list;
10939 SetupFileHash *element_hash, *action_hash, *direction_hash;
10940 int num_list_entries = 0;
10941 int num_unknown_tokens = 0;
10944 if (fileExists(filename))
10945 setup_file_list = loadSetupFileList(filename);
10947 if (setup_file_list == NULL)
10949 /* use reliable default values from static configuration */
10950 SetupFileList *insert_ptr;
10952 insert_ptr = setup_file_list =
10953 newSetupFileList(helpanim_config[0].token,
10954 helpanim_config[0].value);
10956 for (i = 1; helpanim_config[i].token; i++)
10957 insert_ptr = addListEntry(insert_ptr,
10958 helpanim_config[i].token,
10959 helpanim_config[i].value);
10962 element_hash = newSetupFileHash();
10963 action_hash = newSetupFileHash();
10964 direction_hash = newSetupFileHash();
10966 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10967 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10969 for (i = 0; i < NUM_ACTIONS; i++)
10970 setHashEntry(action_hash, element_action_info[i].suffix,
10971 i_to_a(element_action_info[i].value));
10973 /* do not store direction index (bit) here, but direction value! */
10974 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10975 setHashEntry(direction_hash, element_direction_info[i].suffix,
10976 i_to_a(1 << element_direction_info[i].value));
10978 for (list = setup_file_list; list != NULL; list = list->next)
10980 char *element_token, *action_token, *direction_token;
10981 char *element_value, *action_value, *direction_value;
10982 int delay = atoi(list->value);
10984 if (strEqual(list->token, "end"))
10986 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10991 /* first try to break element into element/action/direction parts;
10992 if this does not work, also accept combined "element[.act][.dir]"
10993 elements (like "dynamite.active"), which are unique elements */
10995 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10997 element_value = getHashEntry(element_hash, list->token);
10998 if (element_value != NULL) /* element found */
10999 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11000 &num_list_entries);
11003 /* no further suffixes found -- this is not an element */
11004 print_unknown_token(filename, list->token, num_unknown_tokens++);
11010 /* token has format "<prefix>.<something>" */
11012 action_token = strchr(list->token, '.'); /* suffix may be action ... */
11013 direction_token = action_token; /* ... or direction */
11015 element_token = getStringCopy(list->token);
11016 *strchr(element_token, '.') = '\0';
11018 element_value = getHashEntry(element_hash, element_token);
11020 if (element_value == NULL) /* this is no element */
11022 element_value = getHashEntry(element_hash, list->token);
11023 if (element_value != NULL) /* combined element found */
11024 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11025 &num_list_entries);
11027 print_unknown_token(filename, list->token, num_unknown_tokens++);
11029 free(element_token);
11034 action_value = getHashEntry(action_hash, action_token);
11036 if (action_value != NULL) /* action found */
11038 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
11039 &num_list_entries);
11041 free(element_token);
11046 direction_value = getHashEntry(direction_hash, direction_token);
11048 if (direction_value != NULL) /* direction found */
11050 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
11051 &num_list_entries);
11053 free(element_token);
11058 if (strchr(action_token + 1, '.') == NULL)
11060 /* no further suffixes found -- this is not an action nor direction */
11062 element_value = getHashEntry(element_hash, list->token);
11063 if (element_value != NULL) /* combined element found */
11064 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11065 &num_list_entries);
11067 print_unknown_token(filename, list->token, num_unknown_tokens++);
11069 free(element_token);
11074 /* token has format "<prefix>.<suffix>.<something>" */
11076 direction_token = strchr(action_token + 1, '.');
11078 action_token = getStringCopy(action_token);
11079 *strchr(action_token + 1, '.') = '\0';
11081 action_value = getHashEntry(action_hash, action_token);
11083 if (action_value == NULL) /* this is no action */
11085 element_value = getHashEntry(element_hash, list->token);
11086 if (element_value != NULL) /* combined element found */
11087 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11088 &num_list_entries);
11090 print_unknown_token(filename, list->token, num_unknown_tokens++);
11092 free(element_token);
11093 free(action_token);
11098 direction_value = getHashEntry(direction_hash, direction_token);
11100 if (direction_value != NULL) /* direction found */
11102 add_helpanim_entry(atoi(element_value), atoi(action_value),
11103 atoi(direction_value), delay, &num_list_entries);
11105 free(element_token);
11106 free(action_token);
11111 /* this is no direction */
11113 element_value = getHashEntry(element_hash, list->token);
11114 if (element_value != NULL) /* combined element found */
11115 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11116 &num_list_entries);
11118 print_unknown_token(filename, list->token, num_unknown_tokens++);
11120 free(element_token);
11121 free(action_token);
11124 print_unknown_token_end(num_unknown_tokens);
11126 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11127 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
11129 freeSetupFileList(setup_file_list);
11130 freeSetupFileHash(element_hash);
11131 freeSetupFileHash(action_hash);
11132 freeSetupFileHash(direction_hash);
11135 for (i = 0; i < num_list_entries; i++)
11136 printf("::: '%s': %d, %d, %d => %d\n",
11137 EL_NAME(helpanim_info[i].element),
11138 helpanim_info[i].element,
11139 helpanim_info[i].action,
11140 helpanim_info[i].direction,
11141 helpanim_info[i].delay);
11145 void LoadHelpTextInfo()
11147 char *filename = getHelpTextFilename();
11150 if (helptext_info != NULL)
11152 freeSetupFileHash(helptext_info);
11153 helptext_info = NULL;
11156 if (fileExists(filename))
11157 helptext_info = loadSetupFileHash(filename);
11159 if (helptext_info == NULL)
11161 /* use reliable default values from static configuration */
11162 helptext_info = newSetupFileHash();
11164 for (i = 0; helptext_config[i].token; i++)
11165 setHashEntry(helptext_info,
11166 helptext_config[i].token,
11167 helptext_config[i].value);
11171 BEGIN_HASH_ITERATION(helptext_info, itr)
11173 printf("::: '%s' => '%s'\n",
11174 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
11176 END_HASH_ITERATION(hash, itr)
11181 /* ------------------------------------------------------------------------- */
11182 /* convert levels */
11183 /* ------------------------------------------------------------------------- */
11185 #define MAX_NUM_CONVERT_LEVELS 1000
11187 void ConvertLevels()
11189 static LevelDirTree *convert_leveldir = NULL;
11190 static int convert_level_nr = -1;
11191 static int num_levels_handled = 0;
11192 static int num_levels_converted = 0;
11193 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
11196 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
11197 global.convert_leveldir);
11199 if (convert_leveldir == NULL)
11200 Error(ERR_EXIT, "no such level identifier: '%s'",
11201 global.convert_leveldir);
11203 leveldir_current = convert_leveldir;
11205 if (global.convert_level_nr != -1)
11207 convert_leveldir->first_level = global.convert_level_nr;
11208 convert_leveldir->last_level = global.convert_level_nr;
11211 convert_level_nr = convert_leveldir->first_level;
11213 printf_line("=", 79);
11214 printf("Converting levels\n");
11215 printf_line("-", 79);
11216 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
11217 printf("Level series name: '%s'\n", convert_leveldir->name);
11218 printf("Level series author: '%s'\n", convert_leveldir->author);
11219 printf("Number of levels: %d\n", convert_leveldir->levels);
11220 printf_line("=", 79);
11223 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11224 levels_failed[i] = FALSE;
11226 while (convert_level_nr <= convert_leveldir->last_level)
11228 char *level_filename;
11231 level_nr = convert_level_nr++;
11233 printf("Level %03d: ", level_nr);
11235 LoadLevel(level_nr);
11236 if (level.no_valid_file)
11238 printf("(no level)\n");
11242 printf("converting level ... ");
11244 level_filename = getDefaultLevelFilename(level_nr);
11245 new_level = !fileExists(level_filename);
11249 SaveLevel(level_nr);
11251 num_levels_converted++;
11253 printf("converted.\n");
11257 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
11258 levels_failed[level_nr] = TRUE;
11260 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
11263 num_levels_handled++;
11267 printf_line("=", 79);
11268 printf("Number of levels handled: %d\n", num_levels_handled);
11269 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
11270 (num_levels_handled ?
11271 num_levels_converted * 100 / num_levels_handled : 0));
11272 printf_line("-", 79);
11273 printf("Summary (for automatic parsing by scripts):\n");
11274 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
11275 convert_leveldir->identifier, num_levels_converted,
11276 num_levels_handled,
11277 (num_levels_handled ?
11278 num_levels_converted * 100 / num_levels_handled : 0));
11280 if (num_levels_handled != num_levels_converted)
11282 printf(", FAILED:");
11283 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11284 if (levels_failed[i])
11285 printf(" %03d", i);
11289 printf_line("=", 79);
11291 CloseAllAndExit(0);
11295 /* ------------------------------------------------------------------------- */
11296 /* create and save images for use in level sketches (raw BMP format) */
11297 /* ------------------------------------------------------------------------- */
11299 void CreateLevelSketchImages()
11301 #if defined(TARGET_SDL)
11306 InitElementPropertiesGfxElement();
11308 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
11309 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
11311 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11313 Bitmap *src_bitmap;
11315 int element = getMappedElement(i);
11316 int graphic = el2edimg(element);
11317 char basename1[16];
11318 char basename2[16];
11322 sprintf(basename1, "%03d.bmp", i);
11323 sprintf(basename2, "%03ds.bmp", i);
11325 filename1 = getPath2(global.create_images_dir, basename1);
11326 filename2 = getPath2(global.create_images_dir, basename2);
11328 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
11329 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
11332 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
11333 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
11335 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
11336 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
11338 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
11339 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
11345 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
11348 FreeBitmap(bitmap1);
11349 FreeBitmap(bitmap2);
11354 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
11356 CloseAllAndExit(0);
11361 /* ------------------------------------------------------------------------- */
11362 /* create and save images for custom and group elements (raw BMP format) */
11363 /* ------------------------------------------------------------------------- */
11365 void CreateCustomElementImages()
11367 #if defined(TARGET_SDL)
11368 char *filename = "graphics.classic/RocksCE.bmp";
11370 Bitmap *src_bitmap;
11371 int dummy_graphic = IMG_CUSTOM_99;
11372 int yoffset_ce = 0;
11373 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
11377 bitmap = CreateBitmap(TILEX * 16 * 2,
11378 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
11381 getFixedGraphicSource(dummy_graphic, 0, &src_bitmap, &src_x, &src_y);
11383 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11390 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11391 TILEX * x, TILEY * y + yoffset_ce);
11393 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11395 TILEX * x + TILEX * 16,
11396 TILEY * y + yoffset_ce);
11398 for (j = 2; j >= 0; j--)
11402 BlitBitmap(src_bitmap, bitmap,
11403 TILEX + c * 7, 0, 6, 10,
11404 TILEX * x + 6 + j * 7,
11405 TILEY * y + 11 + yoffset_ce);
11407 BlitBitmap(src_bitmap, bitmap,
11408 TILEX + c * 8, TILEY, 6, 10,
11409 TILEX * 16 + TILEX * x + 6 + j * 8,
11410 TILEY * y + 10 + yoffset_ce);
11416 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
11423 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11424 TILEX * x, TILEY * y + yoffset_ge);
11426 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11428 TILEX * x + TILEX * 16,
11429 TILEY * y + yoffset_ge);
11431 for (j = 1; j >= 0; j--)
11435 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
11436 TILEX * x + 6 + j * 10,
11437 TILEY * y + 11 + yoffset_ge);
11439 BlitBitmap(src_bitmap, bitmap,
11440 TILEX + c * 8, TILEY + 12, 6, 10,
11441 TILEX * 16 + TILEX * x + 10 + j * 8,
11442 TILEY * y + 10 + yoffset_ge);
11448 if (SDL_SaveBMP(bitmap->surface, filename) != 0)
11449 Error(ERR_EXIT, "cannot save CE graphics file '%s'", filename);
11451 FreeBitmap(bitmap);
11453 CloseAllAndExit(0);
11458 void CreateLevelSketchImages_TEST()
11460 void CreateCustomElementImages()