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
246 static struct LevelFileConfigInfo chunk_config_ELEM[] =
248 /* (these values are the same for each player) */
251 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
252 &li.block_last_field, FALSE /* default case for EM levels */
256 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
257 &li.sp_block_last_field, TRUE /* default case for SP levels */
261 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
262 &li.instant_relocation, FALSE
266 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
267 &li.can_pass_to_walkable, FALSE
271 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
272 &li.block_snap_field, TRUE
276 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
277 &li.continuous_snapping, TRUE
281 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
282 &li.shifted_relocation, FALSE
285 /* (these values are different for each player) */
288 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
289 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
293 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
294 &li.initial_player_gravity[0], FALSE
298 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
299 &li.use_start_element[0], FALSE
303 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
304 &li.start_element[0], EL_PLAYER_1
308 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
309 &li.use_artwork_element[0], FALSE
313 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
314 &li.artwork_element[0], EL_PLAYER_1
318 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
319 &li.use_explosion_element[0], FALSE
323 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
324 &li.explosion_element[0], EL_PLAYER_1
328 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
329 &li.use_initial_inventory[0], FALSE
333 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
334 &li.initial_inventory_size[0], 1
338 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
339 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
340 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
345 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
346 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
350 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
351 &li.initial_player_gravity[1], FALSE
355 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
356 &li.use_start_element[1], FALSE
360 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
361 &li.start_element[1], EL_PLAYER_2
365 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
366 &li.use_artwork_element[1], FALSE
370 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
371 &li.artwork_element[1], EL_PLAYER_2
375 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
376 &li.use_explosion_element[1], FALSE
380 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
381 &li.explosion_element[1], EL_PLAYER_2
385 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
386 &li.use_initial_inventory[1], FALSE
390 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
391 &li.initial_inventory_size[1], 1
395 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
396 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
397 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
402 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
403 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
408 &li.initial_player_gravity[2], FALSE
412 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
413 &li.use_start_element[2], FALSE
417 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
418 &li.start_element[2], EL_PLAYER_3
422 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
423 &li.use_artwork_element[2], FALSE
427 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
428 &li.artwork_element[2], EL_PLAYER_3
432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
433 &li.use_explosion_element[2], FALSE
437 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
438 &li.explosion_element[2], EL_PLAYER_3
442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
443 &li.use_initial_inventory[2], FALSE
447 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
448 &li.initial_inventory_size[2], 1
452 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
453 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
454 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
459 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
460 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
464 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
465 &li.initial_player_gravity[3], FALSE
469 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
470 &li.use_start_element[3], FALSE
474 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
475 &li.start_element[3], EL_PLAYER_4
479 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
480 &li.use_artwork_element[3], FALSE
484 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
485 &li.artwork_element[3], EL_PLAYER_4
489 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
490 &li.use_explosion_element[3], FALSE
494 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
495 &li.explosion_element[3], EL_PLAYER_4
499 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
500 &li.use_initial_inventory[3], FALSE
504 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
505 &li.initial_inventory_size[3], 1
509 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
510 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
511 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
516 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
517 &li.score[SC_EMERALD], 10
522 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
523 &li.score[SC_DIAMOND], 10
528 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
529 &li.score[SC_BUG], 10
534 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
535 &li.score[SC_SPACESHIP], 10
540 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
541 &li.score[SC_PACMAN], 10
546 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
547 &li.score[SC_NUT], 10
552 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
553 &li.score[SC_DYNAMITE], 10
558 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
559 &li.score[SC_KEY], 10
564 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
565 &li.score[SC_PEARL], 10
570 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
571 &li.score[SC_CRYSTAL], 10
576 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
577 &li.amoeba_content, EL_DIAMOND
581 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
586 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
587 &li.grow_into_diggable, TRUE
592 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
593 &li.yamyam_content, EL_ROCK, NULL,
594 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
598 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
599 &li.score[SC_YAMYAM], 10
604 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
605 &li.score[SC_ROBOT], 10
609 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
615 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
621 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
622 &li.time_magic_wall, 10
627 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
628 &li.game_of_life[0], 2
632 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
633 &li.game_of_life[1], 3
637 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
638 &li.game_of_life[2], 3
642 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
643 &li.game_of_life[3], 3
648 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
653 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
658 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
663 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
668 EL_TIMEGATE_SWITCH, -1,
669 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
670 &li.time_timegate, 10
674 EL_LIGHT_SWITCH_ACTIVE, -1,
675 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
680 EL_SHIELD_NORMAL, -1,
681 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
682 &li.shield_normal_time, 10
685 EL_SHIELD_NORMAL, -1,
686 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
687 &li.score[SC_SHIELD], 10
691 EL_SHIELD_DEADLY, -1,
692 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
693 &li.shield_deadly_time, 10
696 EL_SHIELD_DEADLY, -1,
697 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
698 &li.score[SC_SHIELD], 10
703 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
708 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
709 &li.extra_time_score, 10
713 EL_TIME_ORB_FULL, -1,
714 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
715 &li.time_orb_time, 10
718 EL_TIME_ORB_FULL, -1,
719 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
720 &li.use_time_orb_bug, FALSE
725 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
726 &li.use_spring_bug, FALSE
731 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
732 &li.android_move_time, 10
736 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
737 &li.android_clone_time, 10
741 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
742 &li.android_clone_element[0], EL_EMPTY, NULL,
743 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
748 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
753 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
758 EL_EMC_MAGNIFIER, -1,
759 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
760 &li.magnify_score, 10
763 EL_EMC_MAGNIFIER, -1,
764 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
769 EL_EMC_MAGIC_BALL, -1,
770 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
774 EL_EMC_MAGIC_BALL, -1,
775 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
776 &li.ball_random, FALSE
779 EL_EMC_MAGIC_BALL, -1,
780 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
781 &li.ball_state_initial, FALSE
784 EL_EMC_MAGIC_BALL, -1,
785 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
786 &li.ball_content, EL_EMPTY, NULL,
787 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
790 /* ---------- unused values ----------------------------------------------- */
793 EL_UNKNOWN, SAVE_CONF_NEVER,
794 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
795 &li.score[SC_UNKNOWN_14], 10
798 EL_UNKNOWN, SAVE_CONF_NEVER,
799 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
800 &li.score[SC_UNKNOWN_15], 10
810 static struct LevelFileConfigInfo chunk_config_NOTE[] =
814 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
815 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
819 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
820 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
825 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
826 &xx_envelope.autowrap, FALSE
830 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
831 &xx_envelope.centered, FALSE
836 TYPE_STRING, CONF_VALUE_BYTES(1),
837 &xx_envelope.text, -1, NULL,
838 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
839 &xx_default_string_empty[0]
849 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
853 TYPE_STRING, CONF_VALUE_BYTES(1),
854 &xx_ei.description[0], -1,
855 &yy_ei.description[0],
856 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
857 &xx_default_description[0]
862 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
863 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
864 &yy_ei.properties[EP_BITFIELD_BASE_NR]
870 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
871 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
872 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
878 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
879 &xx_ei.use_gfx_element, FALSE,
880 &yy_ei.use_gfx_element
884 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
885 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
886 &yy_ei.gfx_element_initial
891 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
892 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
893 &yy_ei.access_direction
898 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
899 &xx_ei.collect_score_initial, 10,
900 &yy_ei.collect_score_initial
904 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
905 &xx_ei.collect_count_initial, 1,
906 &yy_ei.collect_count_initial
911 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
912 &xx_ei.ce_value_fixed_initial, 0,
913 &yy_ei.ce_value_fixed_initial
917 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
918 &xx_ei.ce_value_random_initial, 0,
919 &yy_ei.ce_value_random_initial
923 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
924 &xx_ei.use_last_ce_value, FALSE,
925 &yy_ei.use_last_ce_value
930 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
931 &xx_ei.push_delay_fixed, 8,
932 &yy_ei.push_delay_fixed
936 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
937 &xx_ei.push_delay_random, 8,
938 &yy_ei.push_delay_random
942 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
943 &xx_ei.drop_delay_fixed, 0,
944 &yy_ei.drop_delay_fixed
948 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
949 &xx_ei.drop_delay_random, 0,
950 &yy_ei.drop_delay_random
954 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
955 &xx_ei.move_delay_fixed, 0,
956 &yy_ei.move_delay_fixed
960 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
961 &xx_ei.move_delay_random, 0,
962 &yy_ei.move_delay_random
967 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
968 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
973 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
974 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
975 &yy_ei.move_direction_initial
979 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
980 &xx_ei.move_stepsize, TILEX / 8,
986 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
987 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
988 &yy_ei.move_enter_element
992 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
993 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
994 &yy_ei.move_leave_element
998 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
999 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1000 &yy_ei.move_leave_type
1005 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1006 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1007 &yy_ei.slippery_type
1012 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1013 &xx_ei.explosion_type, EXPLODES_3X3,
1014 &yy_ei.explosion_type
1018 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1019 &xx_ei.explosion_delay, 16,
1020 &yy_ei.explosion_delay
1024 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1025 &xx_ei.ignition_delay, 8,
1026 &yy_ei.ignition_delay
1031 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1032 &xx_ei.content, EL_EMPTY_SPACE,
1034 &xx_num_contents, 1, 1
1037 /* ---------- "num_change_pages" must be the last entry ------------------- */
1040 -1, SAVE_CONF_ALWAYS,
1041 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1042 &xx_ei.num_change_pages, 1,
1043 &yy_ei.num_change_pages
1054 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1056 /* ---------- "current_change_page" must be the first entry --------------- */
1059 -1, SAVE_CONF_ALWAYS,
1060 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1061 &xx_current_change_page, -1
1064 /* ---------- (the remaining entries can be in any order) ----------------- */
1068 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1069 &xx_change.can_change, FALSE
1074 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1075 &xx_event_bits[0], 0
1079 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1080 &xx_event_bits[1], 0
1085 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1086 &xx_change.trigger_player, CH_PLAYER_ANY
1090 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1091 &xx_change.trigger_side, CH_SIDE_ANY
1095 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1096 &xx_change.trigger_page, CH_PAGE_ANY
1101 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1102 &xx_change.target_element, EL_EMPTY_SPACE
1107 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1108 &xx_change.delay_fixed, 0
1112 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1113 &xx_change.delay_random, 0
1117 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1118 &xx_change.delay_frames, FRAMES_PER_SECOND
1123 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1124 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1129 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1130 &xx_change.explode, FALSE
1134 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1135 &xx_change.use_target_content, FALSE
1139 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1140 &xx_change.only_if_complete, FALSE
1144 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1145 &xx_change.use_random_replace, FALSE
1149 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1150 &xx_change.random_percentage, 100
1154 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1155 &xx_change.replace_when, CP_WHEN_EMPTY
1160 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1161 &xx_change.has_action, FALSE
1165 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1166 &xx_change.action_type, CA_NO_ACTION
1170 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1171 &xx_change.action_mode, CA_MODE_UNDEFINED
1175 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1176 &xx_change.action_arg, CA_ARG_UNDEFINED
1181 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1182 &xx_change.action_element, EL_EMPTY_SPACE
1187 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1188 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1189 &xx_num_contents, 1, 1
1199 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1203 TYPE_STRING, CONF_VALUE_BYTES(1),
1204 &xx_ei.description[0], -1, NULL,
1205 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1206 &xx_default_description[0]
1211 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1212 &xx_ei.use_gfx_element, FALSE
1216 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1217 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1222 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1223 &xx_group.choice_mode, ANIM_RANDOM
1228 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1229 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1230 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1240 static struct LevelFileConfigInfo chunk_config_CONF[] = /* (OBSOLETE) */
1244 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1245 &li.block_snap_field, TRUE
1249 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1250 &li.continuous_snapping, TRUE
1254 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1255 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1259 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1260 &li.use_start_element[0], FALSE
1264 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1265 &li.start_element[0], EL_PLAYER_1
1269 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1270 &li.use_artwork_element[0], FALSE
1274 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1275 &li.artwork_element[0], EL_PLAYER_1
1279 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1280 &li.use_explosion_element[0], FALSE
1284 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1285 &li.explosion_element[0], EL_PLAYER_1
1300 filetype_id_list[] =
1302 { LEVEL_FILE_TYPE_RND, "RND" },
1303 { LEVEL_FILE_TYPE_BD, "BD" },
1304 { LEVEL_FILE_TYPE_EM, "EM" },
1305 { LEVEL_FILE_TYPE_SP, "SP" },
1306 { LEVEL_FILE_TYPE_DX, "DX" },
1307 { LEVEL_FILE_TYPE_SB, "SB" },
1308 { LEVEL_FILE_TYPE_DC, "DC" },
1313 /* ========================================================================= */
1314 /* level file functions */
1315 /* ========================================================================= */
1317 static struct DateInfo getCurrentDate()
1319 time_t epoch_seconds = time(NULL);
1320 struct tm *now = localtime(&epoch_seconds);
1321 struct DateInfo date;
1323 date.year = now->tm_year + 1900;
1324 date.month = now->tm_mon + 1;
1325 date.day = now->tm_mday;
1330 static void resetEventFlags(struct ElementChangeInfo *change)
1334 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1335 change->has_event[i] = FALSE;
1338 static void resetEventBits()
1342 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1343 xx_event_bits[i] = 0;
1346 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1350 /* important: only change event flag if corresponding event bit is set
1351 (this is because all xx_event_bits[] values are loaded separately,
1352 and all xx_event_bits[] values are set back to zero before loading
1353 another value xx_event_bits[x] (each value representing 32 flags)) */
1355 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1356 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1357 change->has_event[i] = TRUE;
1360 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1364 /* in contrast to the above function setEventFlagsFromEventBits(), it
1365 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1366 depending on the corresponding change->has_event[i] values here, as
1367 all xx_event_bits[] values are reset in resetEventBits() before */
1369 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1370 if (change->has_event[i])
1371 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1374 static char *getDefaultElementDescription(struct ElementInfo *ei)
1376 static char description[MAX_ELEMENT_NAME_LEN + 1];
1377 char *default_description = (ei->custom_description != NULL ?
1378 ei->custom_description :
1379 ei->editor_description);
1382 /* always start with reliable default values */
1383 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1384 description[i] = '\0';
1386 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1387 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1389 return &description[0];
1392 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1394 char *default_description = getDefaultElementDescription(ei);
1397 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1398 ei->description[i] = default_description[i];
1401 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1405 for (i = 0; conf[i].data_type != -1; i++)
1407 int default_value = conf[i].default_value;
1408 int data_type = conf[i].data_type;
1409 int conf_type = conf[i].conf_type;
1410 int byte_mask = conf_type & CONF_MASK_BYTES;
1412 if (byte_mask == CONF_MASK_MULTI_BYTES)
1414 int default_num_entities = conf[i].default_num_entities;
1415 int max_num_entities = conf[i].max_num_entities;
1417 *(int *)(conf[i].num_entities) = default_num_entities;
1419 if (data_type == TYPE_STRING)
1421 char *default_string = conf[i].default_string;
1422 char *string = (char *)(conf[i].value);
1424 strncpy(string, default_string, max_num_entities);
1426 else if (data_type == TYPE_ELEMENT_LIST)
1428 int *element_array = (int *)(conf[i].value);
1431 for (j = 0; j < max_num_entities; j++)
1432 element_array[j] = default_value;
1434 else if (data_type == TYPE_CONTENT_LIST)
1436 struct Content *content = (struct Content *)(conf[i].value);
1439 for (c = 0; c < max_num_entities; c++)
1440 for (y = 0; y < 3; y++)
1441 for (x = 0; x < 3; x++)
1442 content[c].e[x][y] = default_value;
1445 else /* constant size configuration data (1, 2 or 4 bytes) */
1447 if (data_type == TYPE_BOOLEAN)
1448 *(boolean *)(conf[i].value) = default_value;
1450 *(int *) (conf[i].value) = default_value;
1455 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1459 for (i = 0; conf[i].data_type != -1; i++)
1461 int data_type = conf[i].data_type;
1462 int conf_type = conf[i].conf_type;
1463 int byte_mask = conf_type & CONF_MASK_BYTES;
1465 if (byte_mask == CONF_MASK_MULTI_BYTES)
1467 int max_num_entities = conf[i].max_num_entities;
1469 if (data_type == TYPE_STRING)
1471 char *string = (char *)(conf[i].value);
1472 char *string_copy = (char *)(conf[i].value_copy);
1474 strncpy(string_copy, string, max_num_entities);
1476 else if (data_type == TYPE_ELEMENT_LIST)
1478 int *element_array = (int *)(conf[i].value);
1479 int *element_array_copy = (int *)(conf[i].value_copy);
1482 for (j = 0; j < max_num_entities; j++)
1483 element_array_copy[j] = element_array[j];
1485 else if (data_type == TYPE_CONTENT_LIST)
1487 struct Content *content = (struct Content *)(conf[i].value);
1488 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1491 for (c = 0; c < max_num_entities; c++)
1492 for (y = 0; y < 3; y++)
1493 for (x = 0; x < 3; x++)
1494 content_copy[c].e[x][y] = content[c].e[x][y];
1497 else /* constant size configuration data (1, 2 or 4 bytes) */
1499 if (data_type == TYPE_BOOLEAN)
1500 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1502 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1507 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1511 xx_ei = *ei_from; /* copy element data into temporary buffer */
1512 yy_ei = *ei_to; /* copy element data into temporary buffer */
1514 copyConfigFromConfigList(chunk_config_CUSX_base);
1519 /* ---------- reinitialize and copy change pages ---------- */
1521 ei_to->num_change_pages = ei_from->num_change_pages;
1522 ei_to->current_change_page = ei_from->current_change_page;
1524 setElementChangePages(ei_to, ei_to->num_change_pages);
1526 for (i = 0; i < ei_to->num_change_pages; i++)
1527 ei_to->change_page[i] = ei_from->change_page[i];
1529 /* ---------- copy group element info ---------- */
1530 if (ei_from->group != NULL && ei_to->group != NULL) /* group or internal */
1531 *ei_to->group = *ei_from->group;
1533 /* mark this custom element as modified */
1534 ei_to->modified_settings = TRUE;
1537 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1539 int change_page_size = sizeof(struct ElementChangeInfo);
1541 ei->num_change_pages = MAX(1, change_pages);
1544 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1546 if (ei->current_change_page >= ei->num_change_pages)
1547 ei->current_change_page = ei->num_change_pages - 1;
1549 ei->change = &ei->change_page[ei->current_change_page];
1552 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1554 xx_change = *change; /* copy change data into temporary buffer */
1557 /* (not needed; set by setConfigToDefaultsFromConfigList()) */
1558 xx_num_contents = 1;
1561 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1563 *change = xx_change;
1565 resetEventFlags(change);
1567 change->direct_action = 0;
1568 change->other_action = 0;
1570 change->pre_change_function = NULL;
1571 change->change_function = NULL;
1572 change->post_change_function = NULL;
1575 static void setLevelInfoToDefaults(struct LevelInfo *level)
1577 static boolean clipboard_elements_initialized = FALSE;
1580 InitElementPropertiesStatic();
1582 li = *level; /* copy level data into temporary buffer */
1584 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1585 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1587 *level = li; /* copy temporary buffer back to level data */
1589 setLevelInfoToDefaults_EM();
1590 setLevelInfoToDefaults_SP();
1592 level->native_em_level = &native_em_level;
1593 level->native_sp_level = &native_sp_level;
1595 level->file_version = FILE_VERSION_ACTUAL;
1596 level->game_version = GAME_VERSION_ACTUAL;
1598 level->creation_date = getCurrentDate();
1600 level->encoding_16bit_field = TRUE;
1601 level->encoding_16bit_yamyam = TRUE;
1602 level->encoding_16bit_amoeba = TRUE;
1604 for (x = 0; x < MAX_LEV_FIELDX; x++)
1605 for (y = 0; y < MAX_LEV_FIELDY; y++)
1606 level->field[x][y] = EL_SAND;
1608 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1609 level->name[i] = '\0';
1610 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1611 level->author[i] = '\0';
1613 strcpy(level->name, NAMELESS_LEVEL_NAME);
1614 strcpy(level->author, ANONYMOUS_NAME);
1616 level->field[0][0] = EL_PLAYER_1;
1617 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1619 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1622 struct ElementInfo *ei = &element_info[element];
1624 /* never initialize clipboard elements after the very first time */
1625 /* (to be able to use clipboard elements between several levels) */
1626 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1629 if (IS_ENVELOPE(element))
1631 int envelope_nr = element - EL_ENVELOPE_1;
1633 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1635 level->envelope[envelope_nr] = xx_envelope;
1638 if (IS_CUSTOM_ELEMENT(element) ||
1639 IS_GROUP_ELEMENT(element) ||
1640 IS_INTERNAL_ELEMENT(element))
1642 xx_ei = *ei; /* copy element data into temporary buffer */
1644 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1649 setElementChangePages(ei, 1);
1650 setElementChangeInfoToDefaults(ei->change);
1652 if (IS_CUSTOM_ELEMENT(element) ||
1653 IS_GROUP_ELEMENT(element) ||
1654 IS_INTERNAL_ELEMENT(element))
1656 setElementDescriptionToDefault(ei);
1658 ei->modified_settings = FALSE;
1661 if (IS_CUSTOM_ELEMENT(element) ||
1662 IS_INTERNAL_ELEMENT(element))
1664 /* internal values used in level editor */
1666 ei->access_type = 0;
1667 ei->access_layer = 0;
1668 ei->access_protected = 0;
1669 ei->walk_to_action = 0;
1670 ei->smash_targets = 0;
1673 ei->can_explode_by_fire = FALSE;
1674 ei->can_explode_smashed = FALSE;
1675 ei->can_explode_impact = FALSE;
1677 ei->current_change_page = 0;
1680 if (IS_GROUP_ELEMENT(element) ||
1681 IS_INTERNAL_ELEMENT(element))
1683 struct ElementGroupInfo *group;
1685 /* initialize memory for list of elements in group */
1686 if (ei->group == NULL)
1687 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1691 xx_group = *group; /* copy group data into temporary buffer */
1693 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1699 clipboard_elements_initialized = TRUE;
1701 BorderElement = EL_STEELWALL;
1703 level->no_valid_file = FALSE;
1705 level->changed = FALSE;
1707 /* set all bug compatibility flags to "false" => do not emulate this bug */
1708 level->use_action_after_change_bug = FALSE;
1710 if (leveldir_current == NULL) /* only when dumping level */
1713 /* try to determine better author name than 'anonymous' */
1714 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1716 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1717 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1721 switch (LEVELCLASS(leveldir_current))
1723 case LEVELCLASS_TUTORIAL:
1724 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1727 case LEVELCLASS_CONTRIB:
1728 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1729 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1732 case LEVELCLASS_PRIVATE:
1733 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1734 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1738 /* keep default value */
1744 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1746 level_file_info->nr = 0;
1747 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1748 level_file_info->packed = FALSE;
1749 level_file_info->basename = NULL;
1750 level_file_info->filename = NULL;
1753 static void ActivateLevelTemplate()
1757 /* Currently there is no special action needed to activate the template
1758 data, because 'element_info' property settings overwrite the original
1759 level data, while all other variables do not change. */
1761 /* Exception: 'from_level_template' elements in the original level playfield
1762 are overwritten with the corresponding elements at the same position in
1763 playfield from the level template. */
1765 for (x = 0; x < level.fieldx; x++)
1766 for (y = 0; y < level.fieldy; y++)
1767 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1769 level.field[x][y] = level_template.field[x][y];
1772 printf("::: found EL_FROM_LEVEL_TEMPLATE at %d, %d\n", x, y);
1777 static char *getLevelFilenameFromBasename(char *basename)
1779 static char *filename = NULL;
1781 checked_free(filename);
1783 filename = getPath2(getCurrentLevelDir(), basename);
1788 static int getFileTypeFromBasename(char *basename)
1790 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1792 static char *filename = NULL;
1793 struct stat file_status;
1795 /* ---------- try to determine file type from filename ---------- */
1797 /* check for typical filename of a Supaplex level package file */
1799 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1800 return LEVEL_FILE_TYPE_SP;
1802 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
1803 strncmp(basename, "LEVELS.D", 8) == 0))
1804 return LEVEL_FILE_TYPE_SP;
1807 /* check for typical filename of a Diamond Caves II level package file */
1808 if (strSuffixLower(basename, ".dc") ||
1809 strSuffixLower(basename, ".dc2"))
1810 return LEVEL_FILE_TYPE_DC;
1812 /* check for typical filename of a Sokoban level package file */
1813 if (strSuffixLower(basename, ".xsb") &&
1814 strchr(basename, '%') == NULL)
1815 return LEVEL_FILE_TYPE_SB;
1817 /* ---------- try to determine file type from filesize ---------- */
1819 checked_free(filename);
1820 filename = getPath2(getCurrentLevelDir(), basename);
1822 if (stat(filename, &file_status) == 0)
1824 /* check for typical filesize of a Supaplex level package file */
1825 if (file_status.st_size == 170496)
1826 return LEVEL_FILE_TYPE_SP;
1829 return LEVEL_FILE_TYPE_UNKNOWN;
1832 static boolean checkForPackageFromBasename(char *basename)
1834 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
1835 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
1837 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
1840 static char *getSingleLevelBasename(int nr)
1842 static char basename[MAX_FILENAME_LEN];
1845 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
1847 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
1852 static char *getPackedLevelBasename(int type)
1854 static char basename[MAX_FILENAME_LEN];
1855 char *directory = getCurrentLevelDir();
1857 struct dirent *dir_entry;
1859 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
1861 if ((dir = opendir(directory)) == NULL)
1863 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
1868 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1870 char *entry_basename = dir_entry->d_name;
1871 int entry_type = getFileTypeFromBasename(entry_basename);
1873 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
1875 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
1878 strcpy(basename, entry_basename);
1890 static char *getSingleLevelFilename(int nr)
1892 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
1896 static char *getPackedLevelFilename(int type)
1898 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
1902 char *getDefaultLevelFilename(int nr)
1904 return getSingleLevelFilename(nr);
1908 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
1912 lfi->packed = FALSE;
1913 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
1914 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1918 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
1919 int type, char *format, ...)
1921 static char basename[MAX_FILENAME_LEN];
1924 va_start(ap, format);
1925 vsprintf(basename, format, ap);
1929 lfi->packed = FALSE;
1930 lfi->basename = basename;
1931 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1934 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
1939 lfi->basename = getPackedLevelBasename(lfi->type);
1940 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1943 static int getFiletypeFromID(char *filetype_id)
1945 char *filetype_id_lower;
1946 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
1949 if (filetype_id == NULL)
1950 return LEVEL_FILE_TYPE_UNKNOWN;
1952 filetype_id_lower = getStringToLower(filetype_id);
1954 for (i = 0; filetype_id_list[i].id != NULL; i++)
1956 char *id_lower = getStringToLower(filetype_id_list[i].id);
1958 if (strEqual(filetype_id_lower, id_lower))
1959 filetype = filetype_id_list[i].filetype;
1963 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
1967 free(filetype_id_lower);
1972 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
1976 /* special case: level number is negative => check for level template file */
1980 /* global variable "leveldir_current" must be modified in the loop below */
1981 LevelDirTree *leveldir_current_last = leveldir_current;
1983 /* check for template level in path from current to topmost tree node */
1985 while (leveldir_current != NULL)
1987 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
1988 "template.%s", LEVELFILE_EXTENSION);
1990 if (fileExists(lfi->filename))
1993 leveldir_current = leveldir_current->node_parent;
1996 /* restore global variable "leveldir_current" modified in above loop */
1997 leveldir_current = leveldir_current_last;
2001 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2002 "template.%s", LEVELFILE_EXTENSION);
2006 /* no fallback if template file not existing */
2010 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2011 if (leveldir_current->level_filename != NULL)
2013 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2015 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2016 leveldir_current->level_filename, nr);
2018 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2020 if (fileExists(lfi->filename))
2024 /* check for native Rocks'n'Diamonds level file */
2025 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2026 "%03d.%s", nr, LEVELFILE_EXTENSION);
2027 if (fileExists(lfi->filename))
2030 /* check for Emerald Mine level file (V1) */
2031 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2032 'a' + (nr / 10) % 26, '0' + nr % 10);
2033 if (fileExists(lfi->filename))
2035 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2036 'A' + (nr / 10) % 26, '0' + nr % 10);
2037 if (fileExists(lfi->filename))
2040 /* check for Emerald Mine level file (V2 to V5) */
2041 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2042 if (fileExists(lfi->filename))
2045 /* check for Emerald Mine level file (V6 / single mode) */
2046 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2047 if (fileExists(lfi->filename))
2049 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2050 if (fileExists(lfi->filename))
2053 /* check for Emerald Mine level file (V6 / teamwork mode) */
2054 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2055 if (fileExists(lfi->filename))
2057 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2058 if (fileExists(lfi->filename))
2061 /* check for various packed level file formats */
2062 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2063 if (fileExists(lfi->filename))
2066 /* no known level file found -- use default values (and fail later) */
2067 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2068 "%03d.%s", nr, LEVELFILE_EXTENSION);
2071 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2073 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2074 lfi->type = getFileTypeFromBasename(lfi->basename);
2077 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2079 /* always start with reliable default values */
2080 setFileInfoToDefaults(level_file_info);
2082 level_file_info->nr = nr; /* set requested level number */
2084 determineLevelFileInfo_Filename(level_file_info);
2085 determineLevelFileInfo_Filetype(level_file_info);
2088 /* ------------------------------------------------------------------------- */
2089 /* functions for loading R'n'D level */
2090 /* ------------------------------------------------------------------------- */
2092 int getMappedElement(int element)
2094 /* remap some (historic, now obsolete) elements */
2098 case EL_PLAYER_OBSOLETE:
2099 element = EL_PLAYER_1;
2102 case EL_KEY_OBSOLETE:
2106 case EL_EM_KEY_1_FILE_OBSOLETE:
2107 element = EL_EM_KEY_1;
2110 case EL_EM_KEY_2_FILE_OBSOLETE:
2111 element = EL_EM_KEY_2;
2114 case EL_EM_KEY_3_FILE_OBSOLETE:
2115 element = EL_EM_KEY_3;
2118 case EL_EM_KEY_4_FILE_OBSOLETE:
2119 element = EL_EM_KEY_4;
2122 case EL_ENVELOPE_OBSOLETE:
2123 element = EL_ENVELOPE_1;
2131 if (element >= NUM_FILE_ELEMENTS)
2133 Error(ERR_WARN, "invalid level element %d", element);
2135 element = EL_UNKNOWN;
2143 int getMappedElementByVersion(int element, int game_version)
2145 /* remap some elements due to certain game version */
2147 if (game_version <= VERSION_IDENT(2,2,0,0))
2149 /* map game font elements */
2150 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2151 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2152 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2153 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2156 if (game_version < VERSION_IDENT(3,0,0,0))
2158 /* map Supaplex gravity tube elements */
2159 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2160 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2161 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2162 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2169 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
2171 level->file_version = getFileVersion(file);
2172 level->game_version = getFileVersion(file);
2177 static int LoadLevel_DATE(FILE *file, int chunk_size, struct LevelInfo *level)
2179 level->creation_date.year = getFile16BitBE(file);
2180 level->creation_date.month = getFile8Bit(file);
2181 level->creation_date.day = getFile8Bit(file);
2186 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
2188 int initial_player_stepsize;
2189 int initial_player_gravity;
2192 level->fieldx = getFile8Bit(file);
2193 level->fieldy = getFile8Bit(file);
2195 level->time = getFile16BitBE(file);
2196 level->gems_needed = getFile16BitBE(file);
2198 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2199 level->name[i] = getFile8Bit(file);
2200 level->name[MAX_LEVEL_NAME_LEN] = 0;
2202 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2203 level->score[i] = getFile8Bit(file);
2205 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2206 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2207 for (y = 0; y < 3; y++)
2208 for (x = 0; x < 3; x++)
2209 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2211 level->amoeba_speed = getFile8Bit(file);
2212 level->time_magic_wall = getFile8Bit(file);
2213 level->time_wheel = getFile8Bit(file);
2214 level->amoeba_content = getMappedElement(getFile8Bit(file));
2216 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2219 for (i = 0; i < MAX_PLAYERS; i++)
2220 level->initial_player_stepsize[i] = initial_player_stepsize;
2222 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2224 for (i = 0; i < MAX_PLAYERS; i++)
2225 level->initial_player_gravity[i] = initial_player_gravity;
2227 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2228 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2230 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2232 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2233 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2234 level->can_move_into_acid_bits = getFile32BitBE(file);
2235 level->dont_collide_with_bits = getFile8Bit(file);
2237 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2238 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2240 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2241 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2242 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2244 level->game_engine_type = getFile8Bit(file);
2246 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2251 static int LoadLevel_NAME(FILE *file, int chunk_size, struct LevelInfo *level)
2255 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2256 level->name[i] = getFile8Bit(file);
2257 level->name[MAX_LEVEL_NAME_LEN] = 0;
2262 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
2266 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2267 level->author[i] = getFile8Bit(file);
2268 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2273 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
2276 int chunk_size_expected = level->fieldx * level->fieldy;
2278 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2279 stored with 16-bit encoding (and should be twice as big then).
2280 Even worse, playfield data was stored 16-bit when only yamyam content
2281 contained 16-bit elements and vice versa. */
2283 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2284 chunk_size_expected *= 2;
2286 if (chunk_size_expected != chunk_size)
2288 ReadUnusedBytesFromFile(file, chunk_size);
2289 return chunk_size_expected;
2292 for (y = 0; y < level->fieldy; y++)
2293 for (x = 0; x < level->fieldx; x++)
2294 level->field[x][y] =
2295 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2300 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
2303 int header_size = 4;
2304 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2305 int chunk_size_expected = header_size + content_size;
2307 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2308 stored with 16-bit encoding (and should be twice as big then).
2309 Even worse, playfield data was stored 16-bit when only yamyam content
2310 contained 16-bit elements and vice versa. */
2312 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2313 chunk_size_expected += content_size;
2315 if (chunk_size_expected != chunk_size)
2317 ReadUnusedBytesFromFile(file, chunk_size);
2318 return chunk_size_expected;
2322 level->num_yamyam_contents = getFile8Bit(file);
2326 /* correct invalid number of content fields -- should never happen */
2327 if (level->num_yamyam_contents < 1 ||
2328 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2329 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2331 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2332 for (y = 0; y < 3; y++)
2333 for (x = 0; x < 3; x++)
2334 level->yamyam_content[i].e[x][y] =
2335 getMappedElement(level->encoding_16bit_field ?
2336 getFile16BitBE(file) : getFile8Bit(file));
2340 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
2344 int num_contents, content_xsize, content_ysize;
2345 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2347 element = getMappedElement(getFile16BitBE(file));
2348 num_contents = getFile8Bit(file);
2349 content_xsize = getFile8Bit(file);
2350 content_ysize = getFile8Bit(file);
2352 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2354 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2355 for (y = 0; y < 3; y++)
2356 for (x = 0; x < 3; x++)
2357 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2359 /* correct invalid number of content fields -- should never happen */
2360 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2361 num_contents = STD_ELEMENT_CONTENTS;
2363 if (element == EL_YAMYAM)
2365 level->num_yamyam_contents = num_contents;
2367 for (i = 0; i < num_contents; i++)
2368 for (y = 0; y < 3; y++)
2369 for (x = 0; x < 3; x++)
2370 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2372 else if (element == EL_BD_AMOEBA)
2374 level->amoeba_content = content_array[0][0][0];
2378 Error(ERR_WARN, "cannot load content for element '%d'", element);
2384 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
2390 int chunk_size_expected;
2392 element = getMappedElement(getFile16BitBE(file));
2393 if (!IS_ENVELOPE(element))
2394 element = EL_ENVELOPE_1;
2396 envelope_nr = element - EL_ENVELOPE_1;
2398 envelope_len = getFile16BitBE(file);
2400 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2401 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2403 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2405 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2406 if (chunk_size_expected != chunk_size)
2408 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2409 return chunk_size_expected;
2412 for (i = 0; i < envelope_len; i++)
2413 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2418 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
2420 int num_changed_custom_elements = getFile16BitBE(file);
2421 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2424 if (chunk_size_expected != chunk_size)
2426 ReadUnusedBytesFromFile(file, chunk_size - 2);
2427 return chunk_size_expected;
2430 for (i = 0; i < num_changed_custom_elements; i++)
2432 int element = getMappedElement(getFile16BitBE(file));
2433 int properties = getFile32BitBE(file);
2435 if (IS_CUSTOM_ELEMENT(element))
2436 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2438 Error(ERR_WARN, "invalid custom element number %d", element);
2440 /* older game versions that wrote level files with CUS1 chunks used
2441 different default push delay values (not yet stored in level file) */
2442 element_info[element].push_delay_fixed = 2;
2443 element_info[element].push_delay_random = 8;
2449 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
2451 int num_changed_custom_elements = getFile16BitBE(file);
2452 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2455 if (chunk_size_expected != chunk_size)
2457 ReadUnusedBytesFromFile(file, chunk_size - 2);
2458 return chunk_size_expected;
2461 for (i = 0; i < num_changed_custom_elements; i++)
2463 int element = getMappedElement(getFile16BitBE(file));
2464 int custom_target_element = getMappedElement(getFile16BitBE(file));
2466 if (IS_CUSTOM_ELEMENT(element))
2467 element_info[element].change->target_element = custom_target_element;
2469 Error(ERR_WARN, "invalid custom element number %d", element);
2475 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
2477 int num_changed_custom_elements = getFile16BitBE(file);
2478 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2481 if (chunk_size_expected != chunk_size)
2483 ReadUnusedBytesFromFile(file, chunk_size - 2);
2484 return chunk_size_expected;
2487 for (i = 0; i < num_changed_custom_elements; i++)
2489 int element = getMappedElement(getFile16BitBE(file));
2490 struct ElementInfo *ei = &element_info[element];
2491 unsigned int event_bits;
2493 if (!IS_CUSTOM_ELEMENT(element))
2495 Error(ERR_WARN, "invalid custom element number %d", element);
2497 element = EL_INTERNAL_DUMMY;
2500 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2501 ei->description[j] = getFile8Bit(file);
2502 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2504 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2506 /* some free bytes for future properties and padding */
2507 ReadUnusedBytesFromFile(file, 7);
2509 ei->use_gfx_element = getFile8Bit(file);
2510 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2512 ei->collect_score_initial = getFile8Bit(file);
2513 ei->collect_count_initial = getFile8Bit(file);
2515 ei->push_delay_fixed = getFile16BitBE(file);
2516 ei->push_delay_random = getFile16BitBE(file);
2517 ei->move_delay_fixed = getFile16BitBE(file);
2518 ei->move_delay_random = getFile16BitBE(file);
2520 ei->move_pattern = getFile16BitBE(file);
2521 ei->move_direction_initial = getFile8Bit(file);
2522 ei->move_stepsize = getFile8Bit(file);
2524 for (y = 0; y < 3; y++)
2525 for (x = 0; x < 3; x++)
2526 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2528 event_bits = getFile32BitBE(file);
2529 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2530 if (event_bits & (1 << j))
2531 ei->change->has_event[j] = TRUE;
2533 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2535 ei->change->delay_fixed = getFile16BitBE(file);
2536 ei->change->delay_random = getFile16BitBE(file);
2537 ei->change->delay_frames = getFile16BitBE(file);
2539 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2541 ei->change->explode = getFile8Bit(file);
2542 ei->change->use_target_content = getFile8Bit(file);
2543 ei->change->only_if_complete = getFile8Bit(file);
2544 ei->change->use_random_replace = getFile8Bit(file);
2546 ei->change->random_percentage = getFile8Bit(file);
2547 ei->change->replace_when = getFile8Bit(file);
2549 for (y = 0; y < 3; y++)
2550 for (x = 0; x < 3; x++)
2551 ei->change->target_content.e[x][y] =
2552 getMappedElement(getFile16BitBE(file));
2554 ei->slippery_type = getFile8Bit(file);
2556 /* some free bytes for future properties and padding */
2557 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2559 /* mark that this custom element has been modified */
2560 ei->modified_settings = TRUE;
2566 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
2568 struct ElementInfo *ei;
2569 int chunk_size_expected;
2573 /* ---------- custom element base property values (96 bytes) ------------- */
2575 element = getMappedElement(getFile16BitBE(file));
2577 if (!IS_CUSTOM_ELEMENT(element))
2579 Error(ERR_WARN, "invalid custom element number %d", element);
2581 ReadUnusedBytesFromFile(file, chunk_size - 2);
2585 ei = &element_info[element];
2587 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2588 ei->description[i] = getFile8Bit(file);
2589 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2591 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2593 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2595 ei->num_change_pages = getFile8Bit(file);
2597 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2598 if (chunk_size_expected != chunk_size)
2600 ReadUnusedBytesFromFile(file, chunk_size - 43);
2601 return chunk_size_expected;
2604 ei->ce_value_fixed_initial = getFile16BitBE(file);
2605 ei->ce_value_random_initial = getFile16BitBE(file);
2606 ei->use_last_ce_value = getFile8Bit(file);
2608 ei->use_gfx_element = getFile8Bit(file);
2609 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2611 ei->collect_score_initial = getFile8Bit(file);
2612 ei->collect_count_initial = getFile8Bit(file);
2614 ei->drop_delay_fixed = getFile8Bit(file);
2615 ei->push_delay_fixed = getFile8Bit(file);
2616 ei->drop_delay_random = getFile8Bit(file);
2617 ei->push_delay_random = getFile8Bit(file);
2618 ei->move_delay_fixed = getFile16BitBE(file);
2619 ei->move_delay_random = getFile16BitBE(file);
2621 /* bits 0 - 15 of "move_pattern" ... */
2622 ei->move_pattern = getFile16BitBE(file);
2623 ei->move_direction_initial = getFile8Bit(file);
2624 ei->move_stepsize = getFile8Bit(file);
2626 ei->slippery_type = getFile8Bit(file);
2628 for (y = 0; y < 3; y++)
2629 for (x = 0; x < 3; x++)
2630 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2632 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2633 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2634 ei->move_leave_type = getFile8Bit(file);
2636 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2637 ei->move_pattern |= (getFile16BitBE(file) << 16);
2639 ei->access_direction = getFile8Bit(file);
2641 ei->explosion_delay = getFile8Bit(file);
2642 ei->ignition_delay = getFile8Bit(file);
2643 ei->explosion_type = getFile8Bit(file);
2645 /* some free bytes for future custom property values and padding */
2646 ReadUnusedBytesFromFile(file, 1);
2648 /* ---------- change page property values (48 bytes) --------------------- */
2650 setElementChangePages(ei, ei->num_change_pages);
2652 for (i = 0; i < ei->num_change_pages; i++)
2654 struct ElementChangeInfo *change = &ei->change_page[i];
2655 unsigned int event_bits;
2657 /* always start with reliable default values */
2658 setElementChangeInfoToDefaults(change);
2660 /* bits 0 - 31 of "has_event[]" ... */
2661 event_bits = getFile32BitBE(file);
2662 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2663 if (event_bits & (1 << j))
2664 change->has_event[j] = TRUE;
2666 change->target_element = getMappedElement(getFile16BitBE(file));
2668 change->delay_fixed = getFile16BitBE(file);
2669 change->delay_random = getFile16BitBE(file);
2670 change->delay_frames = getFile16BitBE(file);
2672 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2674 change->explode = getFile8Bit(file);
2675 change->use_target_content = getFile8Bit(file);
2676 change->only_if_complete = getFile8Bit(file);
2677 change->use_random_replace = getFile8Bit(file);
2679 change->random_percentage = getFile8Bit(file);
2680 change->replace_when = getFile8Bit(file);
2682 for (y = 0; y < 3; y++)
2683 for (x = 0; x < 3; x++)
2684 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2686 change->can_change = getFile8Bit(file);
2688 change->trigger_side = getFile8Bit(file);
2690 change->trigger_player = getFile8Bit(file);
2691 change->trigger_page = getFile8Bit(file);
2693 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2694 CH_PAGE_ANY : (1 << change->trigger_page));
2696 change->has_action = getFile8Bit(file);
2697 change->action_type = getFile8Bit(file);
2698 change->action_mode = getFile8Bit(file);
2699 change->action_arg = getFile16BitBE(file);
2701 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2702 event_bits = getFile8Bit(file);
2703 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2704 if (event_bits & (1 << (j - 32)))
2705 change->has_event[j] = TRUE;
2708 /* mark this custom element as modified */
2709 ei->modified_settings = TRUE;
2714 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
2716 struct ElementInfo *ei;
2717 struct ElementGroupInfo *group;
2721 element = getMappedElement(getFile16BitBE(file));
2723 if (!IS_GROUP_ELEMENT(element))
2725 Error(ERR_WARN, "invalid group element number %d", element);
2727 ReadUnusedBytesFromFile(file, chunk_size - 2);
2731 ei = &element_info[element];
2733 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2734 ei->description[i] = getFile8Bit(file);
2735 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2737 group = element_info[element].group;
2739 group->num_elements = getFile8Bit(file);
2741 ei->use_gfx_element = getFile8Bit(file);
2742 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2744 group->choice_mode = getFile8Bit(file);
2746 /* some free bytes for future values and padding */
2747 ReadUnusedBytesFromFile(file, 3);
2749 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2750 group->element[i] = getMappedElement(getFile16BitBE(file));
2752 /* mark this group element as modified */
2753 element_info[element].modified_settings = TRUE;
2758 static int LoadLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *conf,
2759 int element, int real_element)
2761 int micro_chunk_size = 0;
2762 int conf_type = getFile8Bit(file);
2763 int byte_mask = conf_type & CONF_MASK_BYTES;
2764 boolean element_found = FALSE;
2767 micro_chunk_size += 1;
2769 if (byte_mask == CONF_MASK_MULTI_BYTES)
2771 int num_bytes = getFile16BitBE(file);
2772 byte *buffer = checked_malloc(num_bytes);
2774 ReadBytesFromFile(file, buffer, num_bytes);
2776 for (i = 0; conf[i].data_type != -1; i++)
2778 if (conf[i].element == element &&
2779 conf[i].conf_type == conf_type)
2781 int data_type = conf[i].data_type;
2782 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2783 int max_num_entities = conf[i].max_num_entities;
2785 if (num_entities > max_num_entities)
2788 "truncating number of entities for element %d from %d to %d",
2789 element, num_entities, max_num_entities);
2791 num_entities = max_num_entities;
2794 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
2795 data_type == TYPE_CONTENT_LIST))
2797 /* for element and content lists, zero entities are not allowed */
2798 Error(ERR_WARN, "found empty list of entities for element %d",
2801 /* do not set "num_entities" here to prevent reading behind buffer */
2803 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
2807 *(int *)(conf[i].num_entities) = num_entities;
2810 element_found = TRUE;
2812 if (data_type == TYPE_STRING)
2814 char *string = (char *)(conf[i].value);
2817 for (j = 0; j < max_num_entities; j++)
2818 string[j] = (j < num_entities ? buffer[j] : '\0');
2820 else if (data_type == TYPE_ELEMENT_LIST)
2822 int *element_array = (int *)(conf[i].value);
2825 for (j = 0; j < num_entities; j++)
2827 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
2829 else if (data_type == TYPE_CONTENT_LIST)
2831 struct Content *content= (struct Content *)(conf[i].value);
2834 for (c = 0; c < num_entities; c++)
2835 for (y = 0; y < 3; y++)
2836 for (x = 0; x < 3; x++)
2837 content[c].e[x][y] =
2838 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
2841 element_found = FALSE;
2847 checked_free(buffer);
2849 micro_chunk_size += 2 + num_bytes;
2851 else /* constant size configuration data (1, 2 or 4 bytes) */
2853 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
2854 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
2855 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
2857 for (i = 0; conf[i].data_type != -1; i++)
2859 if (conf[i].element == element &&
2860 conf[i].conf_type == conf_type)
2862 int data_type = conf[i].data_type;
2864 if (data_type == TYPE_ELEMENT)
2865 value = getMappedElement(value);
2867 if (data_type == TYPE_BOOLEAN)
2868 *(boolean *)(conf[i].value) = value;
2870 *(int *) (conf[i].value) = value;
2872 element_found = TRUE;
2878 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
2883 char *error_conf_chunk_bytes =
2884 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
2885 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
2886 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
2887 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
2888 int error_element = real_element;
2890 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
2891 error_conf_chunk_bytes, error_conf_chunk_token,
2892 error_element, EL_NAME(error_element));
2895 return micro_chunk_size;
2898 static int LoadLevel_INFO(FILE *file, int chunk_size, struct LevelInfo *level)
2900 int real_chunk_size = 0;
2902 li = *level; /* copy level data into temporary buffer */
2906 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
2908 if (real_chunk_size >= chunk_size)
2912 *level = li; /* copy temporary buffer back to level data */
2914 return real_chunk_size;
2917 static int LoadLevel_CONF(FILE *file, int chunk_size, struct LevelInfo *level)
2919 int real_chunk_size = 0;
2921 li = *level; /* copy level data into temporary buffer */
2925 int element = getMappedElement(getFile16BitBE(file));
2927 real_chunk_size += 2;
2928 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
2930 if (real_chunk_size >= chunk_size)
2934 *level = li; /* copy temporary buffer back to level data */
2936 return real_chunk_size;
2939 static int LoadLevel_ELEM(FILE *file, int chunk_size, struct LevelInfo *level)
2941 int real_chunk_size = 0;
2943 li = *level; /* copy level data into temporary buffer */
2947 int element = getMappedElement(getFile16BitBE(file));
2949 real_chunk_size += 2;
2950 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
2952 if (real_chunk_size >= chunk_size)
2956 *level = li; /* copy temporary buffer back to level data */
2958 return real_chunk_size;
2961 static int LoadLevel_NOTE(FILE *file, int chunk_size, struct LevelInfo *level)
2963 int element = getMappedElement(getFile16BitBE(file));
2964 int envelope_nr = element - EL_ENVELOPE_1;
2965 int real_chunk_size = 2;
2969 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
2972 if (real_chunk_size >= chunk_size)
2976 level->envelope[envelope_nr] = xx_envelope;
2978 return real_chunk_size;
2981 static int LoadLevel_CUSX(FILE *file, int chunk_size, struct LevelInfo *level)
2983 int element = getMappedElement(getFile16BitBE(file));
2984 int real_chunk_size = 2;
2985 struct ElementInfo *ei = &element_info[element];
2988 xx_ei = *ei; /* copy element data into temporary buffer */
2990 xx_ei.num_change_pages = -1;
2994 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
2996 if (xx_ei.num_change_pages != -1)
2999 if (real_chunk_size >= chunk_size)
3005 if (ei->num_change_pages == -1)
3007 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3010 ei->num_change_pages = 1;
3012 setElementChangePages(ei, 1);
3013 setElementChangeInfoToDefaults(ei->change);
3015 return real_chunk_size;
3018 /* initialize number of change pages stored for this custom element */
3019 setElementChangePages(ei, ei->num_change_pages);
3020 for (i = 0; i < ei->num_change_pages; i++)
3021 setElementChangeInfoToDefaults(&ei->change_page[i]);
3023 /* start with reading properties for the first change page */
3024 xx_current_change_page = 0;
3028 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3030 xx_change = *change; /* copy change data into temporary buffer */
3032 resetEventBits(); /* reset bits; change page might have changed */
3034 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3037 *change = xx_change;
3039 setEventFlagsFromEventBits(change);
3041 if (real_chunk_size >= chunk_size)
3045 return real_chunk_size;
3048 static int LoadLevel_GRPX(FILE *file, int chunk_size, struct LevelInfo *level)
3050 int element = getMappedElement(getFile16BitBE(file));
3051 int real_chunk_size = 2;
3052 struct ElementInfo *ei = &element_info[element];
3053 struct ElementGroupInfo *group = ei->group;
3055 xx_ei = *ei; /* copy element data into temporary buffer */
3056 xx_group = *group; /* copy group data into temporary buffer */
3060 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3063 if (real_chunk_size >= chunk_size)
3070 return real_chunk_size;
3073 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3074 struct LevelFileInfo *level_file_info)
3076 char *filename = level_file_info->filename;
3077 char cookie[MAX_LINE_LEN];
3078 char chunk_name[CHUNK_ID_LEN + 1];
3082 if (!(file = fopen(filename, MODE_READ)))
3084 level->no_valid_file = TRUE;
3087 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3089 if (level != &level_template)
3090 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3096 getFileChunkBE(file, chunk_name, NULL);
3097 if (strEqual(chunk_name, "RND1"))
3099 getFile32BitBE(file); /* not used */
3101 getFileChunkBE(file, chunk_name, NULL);
3102 if (!strEqual(chunk_name, "CAVE"))
3104 level->no_valid_file = TRUE;
3106 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3111 else /* check for pre-2.0 file format with cookie string */
3113 strcpy(cookie, chunk_name);
3114 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
3115 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3116 cookie[strlen(cookie) - 1] = '\0';
3118 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3120 level->no_valid_file = TRUE;
3122 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3127 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3129 level->no_valid_file = TRUE;
3131 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3136 /* pre-2.0 level files have no game version, so use file version here */
3137 level->game_version = level->file_version;
3140 if (level->file_version < FILE_VERSION_1_2)
3142 /* level files from versions before 1.2.0 without chunk structure */
3143 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3144 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3152 int (*loader)(FILE *, int, struct LevelInfo *);
3156 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3157 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3158 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3159 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3160 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3161 { "INFO", -1, LoadLevel_INFO },
3162 { "BODY", -1, LoadLevel_BODY },
3163 { "CONT", -1, LoadLevel_CONT },
3164 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3165 { "CNT3", -1, LoadLevel_CNT3 },
3166 { "CUS1", -1, LoadLevel_CUS1 },
3167 { "CUS2", -1, LoadLevel_CUS2 },
3168 { "CUS3", -1, LoadLevel_CUS3 },
3169 { "CUS4", -1, LoadLevel_CUS4 },
3170 { "GRP1", -1, LoadLevel_GRP1 },
3171 { "CONF", -1, LoadLevel_CONF },
3172 { "ELEM", -1, LoadLevel_ELEM },
3173 { "NOTE", -1, LoadLevel_NOTE },
3174 { "CUSX", -1, LoadLevel_CUSX },
3175 { "GRPX", -1, LoadLevel_GRPX },
3180 while (getFileChunkBE(file, chunk_name, &chunk_size))
3184 while (chunk_info[i].name != NULL &&
3185 !strEqual(chunk_name, chunk_info[i].name))
3188 if (chunk_info[i].name == NULL)
3190 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3191 chunk_name, filename);
3192 ReadUnusedBytesFromFile(file, chunk_size);
3194 else if (chunk_info[i].size != -1 &&
3195 chunk_info[i].size != chunk_size)
3197 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3198 chunk_size, chunk_name, filename);
3199 ReadUnusedBytesFromFile(file, chunk_size);
3203 /* call function to load this level chunk */
3204 int chunk_size_expected =
3205 (chunk_info[i].loader)(file, chunk_size, level);
3207 /* the size of some chunks cannot be checked before reading other
3208 chunks first (like "HEAD" and "BODY") that contain some header
3209 information, so check them here */
3210 if (chunk_size_expected != chunk_size)
3212 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3213 chunk_size, chunk_name, filename);
3222 /* ------------------------------------------------------------------------- */
3223 /* functions for loading EM level */
3224 /* ------------------------------------------------------------------------- */
3228 static int map_em_element_yam(int element)
3232 case 0x00: return EL_EMPTY;
3233 case 0x01: return EL_EMERALD;
3234 case 0x02: return EL_DIAMOND;
3235 case 0x03: return EL_ROCK;
3236 case 0x04: return EL_ROBOT;
3237 case 0x05: return EL_SPACESHIP_UP;
3238 case 0x06: return EL_BOMB;
3239 case 0x07: return EL_BUG_UP;
3240 case 0x08: return EL_AMOEBA_DROP;
3241 case 0x09: return EL_NUT;
3242 case 0x0a: return EL_YAMYAM;
3243 case 0x0b: return EL_QUICKSAND_FULL;
3244 case 0x0c: return EL_SAND;
3245 case 0x0d: return EL_WALL_SLIPPERY;
3246 case 0x0e: return EL_STEELWALL;
3247 case 0x0f: return EL_WALL;
3248 case 0x10: return EL_EM_KEY_1;
3249 case 0x11: return EL_EM_KEY_2;
3250 case 0x12: return EL_EM_KEY_4;
3251 case 0x13: return EL_EM_KEY_3;
3252 case 0x14: return EL_MAGIC_WALL;
3253 case 0x15: return EL_ROBOT_WHEEL;
3254 case 0x16: return EL_DYNAMITE;
3256 case 0x17: return EL_EM_KEY_1; /* EMC */
3257 case 0x18: return EL_BUG_UP; /* EMC */
3258 case 0x1a: return EL_DIAMOND; /* EMC */
3259 case 0x1b: return EL_EMERALD; /* EMC */
3260 case 0x25: return EL_NUT; /* EMC */
3261 case 0x80: return EL_EMPTY; /* EMC */
3262 case 0x85: return EL_EM_KEY_1; /* EMC */
3263 case 0x86: return EL_EM_KEY_2; /* EMC */
3264 case 0x87: return EL_EM_KEY_4; /* EMC */
3265 case 0x88: return EL_EM_KEY_3; /* EMC */
3266 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
3267 case 0x9a: return EL_AMOEBA_WET; /* EMC */
3268 case 0xaf: return EL_DYNAMITE; /* EMC */
3269 case 0xbd: return EL_SAND; /* EMC */
3272 Error(ERR_WARN, "invalid level element %d", element);
3277 static int map_em_element_field(int element)
3279 if (element >= 0xc8 && element <= 0xe1)
3280 return EL_CHAR_A + (element - 0xc8);
3281 else if (element >= 0xe2 && element <= 0xeb)
3282 return EL_CHAR_0 + (element - 0xe2);
3286 case 0x00: return EL_ROCK;
3287 case 0x01: return EL_ROCK; /* EMC */
3288 case 0x02: return EL_DIAMOND;
3289 case 0x03: return EL_DIAMOND;
3290 case 0x04: return EL_ROBOT;
3291 case 0x05: return EL_ROBOT; /* EMC */
3292 case 0x06: return EL_EMPTY_SPACE; /* EMC */
3293 case 0x07: return EL_EMPTY_SPACE; /* EMC */
3294 case 0x08: return EL_SPACESHIP_UP;
3295 case 0x09: return EL_SPACESHIP_RIGHT;
3296 case 0x0a: return EL_SPACESHIP_DOWN;
3297 case 0x0b: return EL_SPACESHIP_LEFT;
3298 case 0x0c: return EL_SPACESHIP_UP;
3299 case 0x0d: return EL_SPACESHIP_RIGHT;
3300 case 0x0e: return EL_SPACESHIP_DOWN;
3301 case 0x0f: return EL_SPACESHIP_LEFT;
3303 case 0x10: return EL_BOMB;
3304 case 0x11: return EL_BOMB; /* EMC */
3305 case 0x12: return EL_EMERALD;
3306 case 0x13: return EL_EMERALD;
3307 case 0x14: return EL_BUG_UP;
3308 case 0x15: return EL_BUG_RIGHT;
3309 case 0x16: return EL_BUG_DOWN;
3310 case 0x17: return EL_BUG_LEFT;
3311 case 0x18: return EL_BUG_UP;
3312 case 0x19: return EL_BUG_RIGHT;
3313 case 0x1a: return EL_BUG_DOWN;
3314 case 0x1b: return EL_BUG_LEFT;
3315 case 0x1c: return EL_AMOEBA_DROP;
3316 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
3317 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
3318 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
3320 case 0x20: return EL_ROCK;
3321 case 0x21: return EL_BOMB; /* EMC */
3322 case 0x22: return EL_DIAMOND; /* EMC */
3323 case 0x23: return EL_EMERALD; /* EMC */
3324 case 0x24: return EL_MAGIC_WALL;
3325 case 0x25: return EL_NUT;
3326 case 0x26: return EL_NUT; /* EMC */
3327 case 0x27: return EL_NUT; /* EMC */
3329 /* looks like magic wheel, but is _always_ activated */
3330 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
3332 case 0x29: return EL_YAMYAM; /* up */
3333 case 0x2a: return EL_YAMYAM; /* down */
3334 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
3335 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
3336 case 0x2d: return EL_QUICKSAND_FULL;
3337 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
3338 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
3340 case 0x30: return EL_EMPTY_SPACE; /* EMC */
3341 case 0x31: return EL_SAND; /* EMC */
3342 case 0x32: return EL_SAND; /* EMC */
3343 case 0x33: return EL_SAND; /* EMC */
3344 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
3345 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
3346 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
3347 case 0x37: return EL_SAND; /* EMC */
3348 case 0x38: return EL_ROCK; /* EMC */
3349 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
3350 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
3351 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
3352 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
3353 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
3354 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
3355 case 0x3f: return EL_ACID_POOL_BOTTOM;
3357 case 0x40: return EL_EXIT_OPEN; /* 1 */
3358 case 0x41: return EL_EXIT_OPEN; /* 2 */
3359 case 0x42: return EL_EXIT_OPEN; /* 3 */
3360 case 0x43: return EL_BALLOON; /* EMC */
3361 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
3362 case 0x45: return EL_SPRING; /* EMC */
3363 case 0x46: return EL_SPRING; /* falling */ /* EMC */
3364 case 0x47: return EL_SPRING; /* left */ /* EMC */
3365 case 0x48: return EL_SPRING; /* right */ /* EMC */
3366 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
3367 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
3368 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
3369 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
3370 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
3371 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
3372 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
3374 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
3375 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
3376 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
3377 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
3378 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
3379 case 0x55: return EL_EMPTY_SPACE; /* EMC */
3380 case 0x56: return EL_EMPTY_SPACE; /* EMC */
3381 case 0x57: return EL_EMPTY_SPACE; /* EMC */
3382 case 0x58: return EL_EMPTY_SPACE; /* EMC */
3383 case 0x59: return EL_EMPTY_SPACE; /* EMC */
3384 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
3385 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
3386 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
3387 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
3388 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
3389 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
3391 case 0x60: return EL_EMPTY_SPACE; /* EMC */
3392 case 0x61: return EL_EMPTY_SPACE; /* EMC */
3393 case 0x62: return EL_EMPTY_SPACE; /* EMC */
3394 case 0x63: return EL_SPRING; /* left */ /* EMC */
3395 case 0x64: return EL_SPRING; /* right */ /* EMC */
3396 case 0x65: return EL_ACID; /* 1 */ /* EMC */
3397 case 0x66: return EL_ACID; /* 2 */ /* EMC */
3398 case 0x67: return EL_ACID; /* 3 */ /* EMC */
3399 case 0x68: return EL_ACID; /* 4 */ /* EMC */
3400 case 0x69: return EL_ACID; /* 5 */ /* EMC */
3401 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
3402 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
3403 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
3404 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
3405 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
3406 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
3408 case 0x70: return EL_EMPTY_SPACE; /* EMC */
3409 case 0x71: return EL_EMPTY_SPACE; /* EMC */
3410 case 0x72: return EL_NUT; /* left */ /* EMC */
3411 case 0x73: return EL_SAND; /* EMC (? "nut") */
3412 case 0x74: return EL_STEELWALL;
3413 case 0x75: return EL_EMPTY_SPACE; /* EMC */
3414 case 0x76: return EL_EMPTY_SPACE; /* EMC */
3415 case 0x77: return EL_BOMB; /* left */ /* EMC */
3416 case 0x78: return EL_BOMB; /* right */ /* EMC */
3417 case 0x79: return EL_ROCK; /* left */ /* EMC */
3418 case 0x7a: return EL_ROCK; /* right */ /* EMC */
3419 case 0x7b: return EL_ACID; /* (? EMC "blank") */
3420 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
3421 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
3422 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
3423 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
3425 case 0x80: return EL_EMPTY;
3426 case 0x81: return EL_WALL_SLIPPERY;
3427 case 0x82: return EL_SAND;
3428 case 0x83: return EL_STEELWALL;
3429 case 0x84: return EL_WALL;
3430 case 0x85: return EL_EM_KEY_1;
3431 case 0x86: return EL_EM_KEY_2;
3432 case 0x87: return EL_EM_KEY_4;
3433 case 0x88: return EL_EM_KEY_3;
3434 case 0x89: return EL_EM_GATE_1;
3435 case 0x8a: return EL_EM_GATE_2;
3436 case 0x8b: return EL_EM_GATE_4;
3437 case 0x8c: return EL_EM_GATE_3;
3438 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
3439 case 0x8e: return EL_EM_GATE_1_GRAY;
3440 case 0x8f: return EL_EM_GATE_2_GRAY;
3442 case 0x90: return EL_EM_GATE_4_GRAY;
3443 case 0x91: return EL_EM_GATE_3_GRAY;
3444 case 0x92: return EL_MAGIC_WALL;
3445 case 0x93: return EL_ROBOT_WHEEL;
3446 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
3447 case 0x95: return EL_ACID_POOL_TOPLEFT;
3448 case 0x96: return EL_ACID_POOL_TOPRIGHT;
3449 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
3450 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
3451 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
3452 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
3453 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
3454 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
3455 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
3456 case 0x9e: return EL_EXIT_CLOSED;
3457 case 0x9f: return EL_CHAR_LESS; /* arrow left */
3459 /* looks like normal sand, but behaves like wall */
3460 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
3461 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
3462 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
3463 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
3464 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
3465 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
3466 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
3467 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
3468 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
3469 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
3470 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
3471 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
3472 case 0xac: return EL_CHAR_COMMA; /* EMC */
3473 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
3474 case 0xae: return EL_CHAR_MINUS; /* EMC */
3475 case 0xaf: return EL_DYNAMITE;
3477 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
3478 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
3479 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
3480 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
3481 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
3482 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
3483 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
3484 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
3485 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
3486 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
3487 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
3488 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
3489 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
3490 case 0xbd: return EL_SAND; /* EMC ("dirt") */
3491 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
3492 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
3494 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
3495 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
3496 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
3497 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
3498 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
3499 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
3500 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
3501 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
3503 /* characters: see above */
3505 case 0xec: return EL_CHAR_PERIOD;
3506 case 0xed: return EL_CHAR_EXCLAM;
3507 case 0xee: return EL_CHAR_COLON;
3508 case 0xef: return EL_CHAR_QUESTION;
3510 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
3511 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
3512 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
3513 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
3514 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
3515 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
3516 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
3517 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
3519 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
3520 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
3521 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
3522 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
3523 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
3524 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
3526 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
3527 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
3530 /* should never happen (all 8-bit value cases should be handled) */
3531 Error(ERR_WARN, "invalid level element %d", element);
3536 #define EM_LEVEL_SIZE 2106
3537 #define EM_LEVEL_XSIZE 64
3538 #define EM_LEVEL_YSIZE 32
3540 static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
3541 struct LevelFileInfo *level_file_info)
3543 char *filename = level_file_info->filename;
3545 unsigned char leveldata[EM_LEVEL_SIZE];
3546 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
3547 int nr = level_file_info->nr;
3550 if (!(file = fopen(filename, MODE_READ)))
3552 level->no_valid_file = TRUE;
3554 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3559 for (i = 0; i < EM_LEVEL_SIZE; i++)
3560 leveldata[i] = fgetc(file);
3564 /* check if level data is crypted by testing against known starting bytes
3565 of the few existing crypted level files (from Emerald Mine 1 + 2) */
3567 if ((leveldata[0] == 0xf1 ||
3568 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
3570 unsigned char code0 = 0x65;
3571 unsigned char code1 = 0x11;
3573 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
3574 leveldata[0] = 0xf1;
3576 /* decode crypted level data */
3578 for (i = 0; i < EM_LEVEL_SIZE; i++)
3580 leveldata[i] ^= code0;
3581 leveldata[i] -= code1;
3583 code0 = (code0 + 7) & 0xff;
3587 level->fieldx = EM_LEVEL_XSIZE;
3588 level->fieldy = EM_LEVEL_YSIZE;
3590 level->time = header[46] * 10;
3591 level->gems_needed = header[47];
3593 /* The original Emerald Mine levels have their level number stored
3594 at the second byte of the level file...
3595 Do not trust this information at other level files, e.g. EMC,
3596 but correct it anyway (normally the first row is completely
3597 steel wall, so the correction does not hurt anyway). */
3599 if (leveldata[1] == nr)
3600 leveldata[1] = leveldata[2]; /* correct level number field */
3602 sprintf(level->name, "Level %d", nr); /* set level name */
3604 level->score[SC_EMERALD] = header[36];
3605 level->score[SC_DIAMOND] = header[37];
3606 level->score[SC_ROBOT] = header[38];
3607 level->score[SC_SPACESHIP] = header[39];
3608 level->score[SC_BUG] = header[40];
3609 level->score[SC_YAMYAM] = header[41];
3610 level->score[SC_NUT] = header[42];
3611 level->score[SC_DYNAMITE] = header[43];
3612 level->score[SC_TIME_BONUS] = header[44];
3614 level->num_yamyam_contents = 4;
3616 for (i = 0; i < level->num_yamyam_contents; i++)
3617 for (y = 0; y < 3; y++)
3618 for (x = 0; x < 3; x++)
3619 level->yamyam_content[i].e[x][y] =
3620 map_em_element_yam(header[i * 9 + y * 3 + x]);
3622 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
3623 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
3624 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
3625 level->amoeba_content = EL_DIAMOND;
3627 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3629 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
3631 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
3632 new_element = EL_AMOEBA_WET;
3634 level->field[x][y] = new_element;
3637 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
3638 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
3639 level->field[x][y] = EL_PLAYER_1;
3641 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
3642 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
3643 level->field[x][y] = EL_PLAYER_2;
3648 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3650 static int ball_xy[8][2] =
3661 struct LevelInfo_EM *level_em = level->native_em_level;
3662 struct LEVEL *lev = level_em->lev;
3663 struct PLAYER **ply = level_em->ply;
3666 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3667 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3669 lev->time_seconds = level->time;
3670 lev->required_initial = level->gems_needed;
3672 lev->emerald_score = level->score[SC_EMERALD];
3673 lev->diamond_score = level->score[SC_DIAMOND];
3674 lev->alien_score = level->score[SC_ROBOT];
3675 lev->tank_score = level->score[SC_SPACESHIP];
3676 lev->bug_score = level->score[SC_BUG];
3677 lev->eater_score = level->score[SC_YAMYAM];
3678 lev->nut_score = level->score[SC_NUT];
3679 lev->dynamite_score = level->score[SC_DYNAMITE];
3680 lev->key_score = level->score[SC_KEY];
3681 lev->exit_score = level->score[SC_TIME_BONUS];
3683 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3684 for (y = 0; y < 3; y++)
3685 for (x = 0; x < 3; x++)
3686 lev->eater_array[i][y * 3 + x] =
3687 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3689 lev->amoeba_time = level->amoeba_speed;
3690 lev->wonderwall_time_initial = level->time_magic_wall;
3691 lev->wheel_time = level->time_wheel;
3693 lev->android_move_time = level->android_move_time;
3694 lev->android_clone_time = level->android_clone_time;
3695 lev->ball_random = level->ball_random;
3696 lev->ball_state_initial = level->ball_state_initial;
3697 lev->ball_time = level->ball_time;
3698 lev->num_ball_arrays = level->num_ball_contents;
3700 lev->lenses_score = level->lenses_score;
3701 lev->magnify_score = level->magnify_score;
3702 lev->slurp_score = level->slurp_score;
3704 lev->lenses_time = level->lenses_time;
3705 lev->magnify_time = level->magnify_time;
3707 lev->wind_direction_initial =
3708 map_direction_RND_to_EM(level->wind_direction_initial);
3709 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3710 lev->wind_time : 0);
3712 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3713 for (j = 0; j < 8; j++)
3714 lev->ball_array[i][j] =
3715 map_element_RND_to_EM(level->
3716 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3718 map_android_clone_elements_RND_to_EM(level);
3720 /* first fill the complete playfield with the default border element */
3721 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3722 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3723 level_em->cave[x][y] = ZBORDER;
3725 if (BorderElement == EL_STEELWALL)
3727 for (y = 0; y < lev->height + 2; y++)
3728 for (x = 0; x < lev->width + 2; x++)
3729 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3732 /* then copy the real level contents from level file into the playfield */
3733 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3735 int new_element = map_element_RND_to_EM(level->field[x][y]);
3736 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3737 int xx = x + 1 + offset;
3738 int yy = y + 1 + offset;
3740 if (level->field[x][y] == EL_AMOEBA_DEAD)
3741 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3743 level_em->cave[xx][yy] = new_element;
3746 for (i = 0; i < MAX_PLAYERS; i++)
3748 ply[i]->x_initial = 0;
3749 ply[i]->y_initial = 0;
3752 /* initialize player positions and delete players from the playfield */
3753 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3755 if (ELEM_IS_PLAYER(level->field[x][y]))
3757 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3758 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3759 int xx = x + 1 + offset;
3760 int yy = y + 1 + offset;
3762 ply[player_nr]->x_initial = xx;
3763 ply[player_nr]->y_initial = yy;
3765 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3769 if (BorderElement == EL_STEELWALL)
3776 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3778 static int ball_xy[8][2] =
3789 struct LevelInfo_EM *level_em = level->native_em_level;
3790 struct LEVEL *lev = level_em->lev;
3791 struct PLAYER **ply = level_em->ply;
3794 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3795 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3797 level->time = lev->time_seconds;
3798 level->gems_needed = lev->required_initial;
3800 sprintf(level->name, "Level %d", level->file_info.nr);
3802 level->score[SC_EMERALD] = lev->emerald_score;
3803 level->score[SC_DIAMOND] = lev->diamond_score;
3804 level->score[SC_ROBOT] = lev->alien_score;
3805 level->score[SC_SPACESHIP] = lev->tank_score;
3806 level->score[SC_BUG] = lev->bug_score;
3807 level->score[SC_YAMYAM] = lev->eater_score;
3808 level->score[SC_NUT] = lev->nut_score;
3809 level->score[SC_DYNAMITE] = lev->dynamite_score;
3810 level->score[SC_KEY] = lev->key_score;
3811 level->score[SC_TIME_BONUS] = lev->exit_score;
3813 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3815 for (i = 0; i < level->num_yamyam_contents; i++)
3816 for (y = 0; y < 3; y++)
3817 for (x = 0; x < 3; x++)
3818 level->yamyam_content[i].e[x][y] =
3819 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3821 level->amoeba_speed = lev->amoeba_time;
3822 level->time_magic_wall = lev->wonderwall_time_initial;
3823 level->time_wheel = lev->wheel_time;
3825 level->android_move_time = lev->android_move_time;
3826 level->android_clone_time = lev->android_clone_time;
3827 level->ball_random = lev->ball_random;
3828 level->ball_state_initial = lev->ball_state_initial;
3829 level->ball_time = lev->ball_time;
3830 level->num_ball_contents = lev->num_ball_arrays;
3832 level->lenses_score = lev->lenses_score;
3833 level->magnify_score = lev->magnify_score;
3834 level->slurp_score = lev->slurp_score;
3836 level->lenses_time = lev->lenses_time;
3837 level->magnify_time = lev->magnify_time;
3839 level->wind_direction_initial =
3840 map_direction_EM_to_RND(lev->wind_direction_initial);
3842 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3843 for (j = 0; j < 8; j++)
3844 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3845 map_element_EM_to_RND(lev->ball_array[i][j]);
3847 map_android_clone_elements_EM_to_RND(level);
3849 /* convert the playfield (some elements need special treatment) */
3850 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3852 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3854 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3855 new_element = EL_AMOEBA_DEAD;
3857 level->field[x][y] = new_element;
3860 for (i = 0; i < MAX_PLAYERS; i++)
3862 /* in case of all players set to the same field, use the first player */
3863 int nr = MAX_PLAYERS - i - 1;
3864 int jx = ply[nr]->x_initial - 1;
3865 int jy = ply[nr]->y_initial - 1;
3867 if (jx != -1 && jy != -1)
3868 level->field[jx][jy] = EL_PLAYER_1 + nr;
3873 /* ------------------------------------------------------------------------- */
3874 /* functions for loading SP level */
3875 /* ------------------------------------------------------------------------- */
3879 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
3880 #define SP_LEVEL_SIZE 1536
3881 #define SP_LEVEL_XSIZE 60
3882 #define SP_LEVEL_YSIZE 24
3883 #define SP_LEVEL_NAME_LEN 23
3885 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
3888 int initial_player_gravity;
3889 int num_special_ports;
3892 /* for details of the Supaplex level format, see Herman Perk's Supaplex
3893 documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
3895 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
3896 for (y = 0; y < SP_LEVEL_YSIZE; y++)
3898 for (x = 0; x < SP_LEVEL_XSIZE; x++)
3900 int element_old = fgetc(file);
3903 if (element_old <= 0x27)
3904 element_new = getMappedElement(EL_SP_START + element_old);
3905 else if (element_old == 0x28)
3906 element_new = EL_INVISIBLE_WALL;
3909 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
3910 Error(ERR_WARN, "invalid level element %d", element_old);
3912 element_new = EL_UNKNOWN;
3915 level->field[x][y] = element_new;
3919 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
3921 /* initial gravity: 1 == "on", anything else (0) == "off" */
3922 initial_player_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
3924 for (i = 0; i < MAX_PLAYERS; i++)
3925 level->initial_player_gravity[i] = initial_player_gravity;
3927 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
3929 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
3930 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3931 level->name[i] = fgetc(file);
3932 level->name[SP_LEVEL_NAME_LEN] = '\0';
3934 /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
3935 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
3937 /* number of infotrons needed; 0 means that Supaplex will count the total
3938 amount of infotrons in the level and use the low byte of that number
3939 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
3940 level->gems_needed = fgetc(file);
3942 /* number of special ("gravity") port entries below (maximum 10 allowed) */
3943 num_special_ports = fgetc(file);
3945 /* database of properties of up to 10 special ports (6 bytes per port) */
3946 for (i = 0; i < 10; i++)
3948 int port_location, port_x, port_y, port_element;
3951 /* high and low byte of the location of a special port; if (x, y) are the
3952 coordinates of a port in the field and (0, 0) is the top-left corner,
3953 the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
3954 of what may be expected: Supaplex works with a game field in memory
3955 which is 2 bytes per tile) */
3956 port_location = getFile16BitBE(file);
3958 /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
3959 gravity = fgetc(file);
3961 /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
3962 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
3964 /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
3965 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
3967 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
3969 if (i >= num_special_ports)
3972 port_x = (port_location / 2) % SP_LEVEL_XSIZE;
3973 port_y = (port_location / 2) / SP_LEVEL_XSIZE;
3975 if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
3976 port_y < 0 || port_y >= SP_LEVEL_YSIZE)
3978 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3984 port_element = level->field[port_x][port_y];
3986 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3987 port_element > EL_SP_GRAVITY_PORT_UP)
3989 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3994 /* change previous (wrong) gravity inverting special port to either
3995 gravity enabling special port or gravity disabling special port */
3996 level->field[port_x][port_y] +=
3997 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3998 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4001 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
4003 /* change special gravity ports without database entries to normal ports */
4004 for (y = 0; y < SP_LEVEL_YSIZE; y++)
4005 for (x = 0; x < SP_LEVEL_XSIZE; x++)
4006 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4007 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4008 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4010 /* auto-determine number of infotrons if it was stored as "0" -- see above */
4011 if (level->gems_needed == 0)
4013 for (y = 0; y < SP_LEVEL_YSIZE; y++)
4014 for (x = 0; x < SP_LEVEL_XSIZE; x++)
4015 if (level->field[x][y] == EL_SP_INFOTRON)
4016 level->gems_needed++;
4018 level->gems_needed &= 0xff; /* only use low byte -- see above */
4021 level->fieldx = SP_LEVEL_XSIZE;
4022 level->fieldy = SP_LEVEL_YSIZE;
4024 level->time = 0; /* no time limit */
4025 level->amoeba_speed = 0;
4026 level->time_magic_wall = 0;
4027 level->time_wheel = 0;
4028 level->amoeba_content = EL_EMPTY;
4031 /* original Supaplex does not use score values -- use default values */
4033 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4034 level->score[i] = 0;
4037 /* there are no yamyams in supaplex levels */
4038 for (i = 0; i < level->num_yamyam_contents; i++)
4039 for (y = 0; y < 3; y++)
4040 for (x = 0; x < 3; x++)
4041 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4044 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
4045 struct LevelFileInfo *level_file_info)
4047 char *filename = level_file_info->filename;
4049 int nr = level_file_info->nr - leveldir_current->first_level;
4051 char name_first, name_last;
4052 struct LevelInfo multipart_level;
4053 int multipart_xpos, multipart_ypos;
4054 boolean is_multipart_level;
4055 boolean is_first_part;
4056 boolean reading_multipart_level = FALSE;
4057 boolean use_empty_level = FALSE;
4059 if (!(file = fopen(filename, MODE_READ)))
4061 level->no_valid_file = TRUE;
4063 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4068 /* position file stream to the requested level inside the level package */
4069 if (level_file_info->packed &&
4070 fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
4072 level->no_valid_file = TRUE;
4074 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
4079 /* there exist Supaplex level package files with multi-part levels which
4080 can be detected as follows: instead of leading and trailing dashes ('-')
4081 to pad the level name, they have leading and trailing numbers which are
4082 the x and y coordinations of the current part of the multi-part level;
4083 if there are '?' characters instead of numbers on the left or right side
4084 of the level name, the multi-part level consists of only horizontal or
4087 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
4089 LoadLevelFromFileStream_SP(file, level, l);
4091 /* check if this level is a part of a bigger multi-part level */
4093 name_first = level->name[0];
4094 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
4096 is_multipart_level =
4097 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
4098 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
4101 ((name_first == '?' || name_first == '1') &&
4102 (name_last == '?' || name_last == '1'));
4104 /* correct leading multipart level meta information in level name */
4105 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
4106 level->name[i] = '-';
4108 /* correct trailing multipart level meta information in level name */
4109 for (i = SP_LEVEL_NAME_LEN - 1; i >= 0 && level->name[i] == name_last; i--)
4110 level->name[i] = '-';
4112 /* ---------- check for normal single level ---------- */
4114 if (!reading_multipart_level && !is_multipart_level)
4116 /* the current level is simply a normal single-part level, and we are
4117 not reading a multi-part level yet, so return the level as it is */
4122 /* ---------- check for empty level (unused multi-part) ---------- */
4124 if (!reading_multipart_level && is_multipart_level && !is_first_part)
4126 /* this is a part of a multi-part level, but not the first part
4127 (and we are not already reading parts of a multi-part level);
4128 in this case, use an empty level instead of the single part */
4130 use_empty_level = TRUE;
4135 /* ---------- check for finished multi-part level ---------- */
4137 if (reading_multipart_level &&
4138 (!is_multipart_level ||
4139 !strEqual(level->name, multipart_level.name)))
4141 /* we are already reading parts of a multi-part level, but this level is
4142 either not a multi-part level, or a part of a different multi-part
4143 level; in both cases, the multi-part level seems to be complete */
4148 /* ---------- here we have one part of a multi-part level ---------- */
4150 reading_multipart_level = TRUE;
4152 if (is_first_part) /* start with first part of new multi-part level */
4154 /* copy level info structure from first part */
4155 multipart_level = *level;
4157 /* clear playfield of new multi-part level */
4158 for (y = 0; y < MAX_LEV_FIELDY; y++)
4159 for (x = 0; x < MAX_LEV_FIELDX; x++)
4160 multipart_level.field[x][y] = EL_EMPTY;
4163 if (name_first == '?')
4165 if (name_last == '?')
4168 multipart_xpos = (int)(name_first - '0');
4169 multipart_ypos = (int)(name_last - '0');
4172 printf("----------> part (%d/%d) of multi-part level '%s'\n",
4173 multipart_xpos, multipart_ypos, multipart_level.name);
4176 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
4177 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
4179 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
4184 multipart_level.fieldx = MAX(multipart_level.fieldx,
4185 multipart_xpos * SP_LEVEL_XSIZE);
4186 multipart_level.fieldy = MAX(multipart_level.fieldy,
4187 multipart_ypos * SP_LEVEL_YSIZE);
4189 /* copy level part at the right position of multi-part level */
4190 for (y = 0; y < SP_LEVEL_YSIZE; y++)
4192 for (x = 0; x < SP_LEVEL_XSIZE; x++)
4194 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
4195 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
4197 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
4204 if (use_empty_level)
4206 setLevelInfoToDefaults(level);
4208 level->fieldx = SP_LEVEL_XSIZE;
4209 level->fieldy = SP_LEVEL_YSIZE;
4211 for (y = 0; y < SP_LEVEL_YSIZE; y++)
4212 for (x = 0; x < SP_LEVEL_XSIZE; x++)
4213 level->field[x][y] = EL_EMPTY;
4215 strcpy(level->name, "-------- EMPTY --------");
4217 Error(ERR_WARN, "single part of multi-part level -- using empty level");
4220 if (reading_multipart_level)
4221 *level = multipart_level;
4226 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4228 struct LevelInfo_SP *level_sp = level->native_sp_level;
4229 LevelInfoType *header = &level_sp->header;
4232 level_sp->width = level->fieldx;
4233 level_sp->height = level->fieldy;
4235 for (x = 0; x < level->fieldx; x++)
4237 for (y = 0; y < level->fieldy; y++)
4239 int element_old = level->field[x][y];
4242 if (element_old >= EL_SP_START &&
4243 element_old <= EL_SP_END)
4244 element_new = element_old - EL_SP_START;
4245 else if (element_old == EL_EMPTY_SPACE)
4247 else if (element_old == EL_INVISIBLE_WALL)
4250 element_new = 0x20; /* map unknown elements to yellow "hardware" */
4252 level_sp->playfield[x][y] = element_new;
4256 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4258 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4259 header->LevelTitle[i] = level->name[i];
4260 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
4262 header->InfotronsNeeded = level->gems_needed;
4264 header->SpecialPortCount = 0;
4266 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4268 boolean gravity_port_found = FALSE;
4269 boolean gravity_port_valid = FALSE;
4270 int gravity_port_flag;
4271 int gravity_port_base_element;
4272 int element = level->field[x][y];
4274 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4275 element <= EL_SP_GRAVITY_ON_PORT_UP)
4277 gravity_port_found = TRUE;
4278 gravity_port_valid = TRUE;
4279 gravity_port_flag = 1;
4280 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4282 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4283 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4285 gravity_port_found = TRUE;
4286 gravity_port_valid = TRUE;
4287 gravity_port_flag = 0;
4288 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4290 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4291 element <= EL_SP_GRAVITY_PORT_UP)
4293 /* change R'n'D style gravity inverting special port to normal port
4294 (there are no gravity inverting ports in native Supaplex engine) */
4296 gravity_port_found = TRUE;
4297 gravity_port_valid = FALSE;
4298 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4301 if (gravity_port_found)
4303 if (gravity_port_valid &&
4304 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4306 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4308 port->PortLocation = (y * level->fieldx + x) * 2;
4309 port->Gravity = gravity_port_flag;
4311 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4313 header->SpecialPortCount++;
4317 /* change special gravity port to normal port */
4319 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4322 level_sp->playfield[x][y] = element - EL_SP_START;
4327 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4329 struct LevelInfo_SP *level_sp = level->native_sp_level;
4330 LevelInfoType *header = &level_sp->header;
4333 level->fieldx = level_sp->width;
4334 level->fieldy = level_sp->height;
4336 for (x = 0; x < level->fieldx; x++)
4338 for (y = 0; y < level->fieldy; y++)
4340 int element_old = level_sp->playfield[x][y];
4343 if (element_old <= 0x27)
4344 element_new = getMappedElement(EL_SP_START + element_old);
4345 else if (element_old == 0x28)
4346 element_new = EL_INVISIBLE_WALL;
4349 Error(ERR_WARN, "invalid element %d at position %d, %d",
4352 element_new = EL_UNKNOWN;
4355 level->field[x][y] = element_new;
4359 for (i = 0; i < MAX_PLAYERS; i++)
4360 level->initial_player_gravity[i] =
4361 (header->InitialGravity == 1 ? TRUE : FALSE);
4363 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4364 level->name[i] = header->LevelTitle[i];
4365 level->name[SP_LEVEL_NAME_LEN] = '\0';
4367 level->gems_needed = header->InfotronsNeeded;
4369 for (i = 0; i < header->SpecialPortCount; i++)
4371 SpecialPortType *port = &header->SpecialPort[i];
4372 int port_location = port->PortLocation;
4373 int gravity = port->Gravity;
4374 int port_x, port_y, port_element;
4376 port_x = (port_location / 2) % level->fieldx;
4377 port_y = (port_location / 2) / level->fieldx;
4379 if (port_x < 0 || port_x >= level->fieldx ||
4380 port_y < 0 || port_y >= level->fieldy)
4382 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
4388 port_element = level->field[port_x][port_y];
4390 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4391 port_element > EL_SP_GRAVITY_PORT_UP)
4393 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
4398 /* change previous (wrong) gravity inverting special port to either
4399 gravity enabling special port or gravity disabling special port */
4400 level->field[port_x][port_y] +=
4401 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4402 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4405 /* change special gravity ports without database entries to normal ports */
4406 for (x = 0; x < level->fieldx; x++)
4407 for (y = 0; y < level->fieldy; y++)
4408 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4409 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4410 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4412 level->time = 0; /* no time limit */
4413 level->amoeba_speed = 0;
4414 level->time_magic_wall = 0;
4415 level->time_wheel = 0;
4416 level->amoeba_content = EL_EMPTY;
4419 /* original Supaplex does not use score values -- use default values */
4421 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4422 level->score[i] = 0;
4425 /* there are no yamyams in supaplex levels */
4426 for (i = 0; i < level->num_yamyam_contents; i++)
4427 for (x = 0; x < 3; x++)
4428 for (y = 0; y < 3; y++)
4429 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4432 static void setTapeInfoToDefaults();
4434 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4436 struct LevelInfo_SP *level_sp = level->native_sp_level;
4437 struct DemoInfo_SP *demo = &level_sp->demo;
4440 /* always start with reliable default values */
4441 setTapeInfoToDefaults();
4443 tape.level_nr = demo->level_nr; /* (currently not used) */
4444 tape.length = demo->length - 1; /* without "end of demo" byte */
4445 tape.random_seed = level_sp->header.DemoRandomSeed;
4447 // tape.date = <SET FROM FILE DATE OF *.SP FILE>
4449 for (i = 0; i < demo->length - 1; i++)
4451 int demo_action = demo->data[i] & 0x0f;
4452 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4454 tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
4455 tape.pos[i].delay = demo_repeat + 1;
4458 tape.length_seconds = GetTapeLength();
4462 /* ------------------------------------------------------------------------- */
4463 /* functions for loading DC level */
4464 /* ------------------------------------------------------------------------- */
4466 #define DC_LEVEL_HEADER_SIZE 344
4468 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
4470 static int last_data_encoded;
4474 int diff_hi, diff_lo;
4475 int data_hi, data_lo;
4476 unsigned short data_decoded;
4480 last_data_encoded = 0;
4487 diff = data_encoded - last_data_encoded;
4488 diff_hi = diff & ~0xff;
4489 diff_lo = diff & 0xff;
4493 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4494 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4495 data_hi = data_hi & 0xff00;
4497 data_decoded = data_hi | data_lo;
4499 last_data_encoded = data_encoded;
4501 offset1 = (offset1 + 1) % 31;
4502 offset2 = offset2 & 0xff;
4504 return data_decoded;
4507 int getMappedElement_DC(int element)
4515 /* 0x0117 - 0x036e: (?) */
4518 /* 0x042d - 0x0684: (?) */
4534 element = EL_CRYSTAL;
4537 case 0x0e77: /* quicksand (boulder) */
4538 element = EL_QUICKSAND_FAST_FULL;
4541 case 0x0e99: /* slow quicksand (boulder) */
4542 element = EL_QUICKSAND_FULL;
4546 element = EL_EM_EXIT_OPEN;
4550 element = EL_EM_EXIT_CLOSED;
4554 element = EL_EM_STEEL_EXIT_OPEN;
4558 element = EL_EM_STEEL_EXIT_CLOSED;
4561 case 0x0f4f: /* dynamite (lit 1) */
4562 element = EL_EM_DYNAMITE_ACTIVE;
4565 case 0x0f57: /* dynamite (lit 2) */
4566 element = EL_EM_DYNAMITE_ACTIVE;
4569 case 0x0f5f: /* dynamite (lit 3) */
4570 element = EL_EM_DYNAMITE_ACTIVE;
4573 case 0x0f67: /* dynamite (lit 4) */
4574 element = EL_EM_DYNAMITE_ACTIVE;
4581 element = EL_AMOEBA_WET;
4585 element = EL_AMOEBA_DROP;
4589 element = EL_DC_MAGIC_WALL;
4593 element = EL_SPACESHIP_UP;
4597 element = EL_SPACESHIP_DOWN;
4601 element = EL_SPACESHIP_LEFT;
4605 element = EL_SPACESHIP_RIGHT;
4609 element = EL_BUG_UP;
4613 element = EL_BUG_DOWN;
4617 element = EL_BUG_LEFT;
4621 element = EL_BUG_RIGHT;
4625 element = EL_MOLE_UP;
4629 element = EL_MOLE_DOWN;
4633 element = EL_MOLE_LEFT;
4637 element = EL_MOLE_RIGHT;
4645 element = EL_YAMYAM;
4649 element = EL_SWITCHGATE_OPEN;
4653 element = EL_SWITCHGATE_CLOSED;
4657 element = EL_DC_SWITCHGATE_SWITCH_UP;
4661 element = EL_TIMEGATE_CLOSED;
4664 case 0x144c: /* conveyor belt switch (green) */
4665 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4668 case 0x144f: /* conveyor belt switch (red) */
4669 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4672 case 0x1452: /* conveyor belt switch (blue) */
4673 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4677 element = EL_CONVEYOR_BELT_3_MIDDLE;
4681 element = EL_CONVEYOR_BELT_3_LEFT;
4685 element = EL_CONVEYOR_BELT_3_RIGHT;
4689 element = EL_CONVEYOR_BELT_1_MIDDLE;
4693 element = EL_CONVEYOR_BELT_1_LEFT;
4697 element = EL_CONVEYOR_BELT_1_RIGHT;
4701 element = EL_CONVEYOR_BELT_4_MIDDLE;
4705 element = EL_CONVEYOR_BELT_4_LEFT;
4709 element = EL_CONVEYOR_BELT_4_RIGHT;
4713 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4717 element = EL_EXPANDABLE_WALL_VERTICAL;
4721 element = EL_EXPANDABLE_WALL_ANY;
4724 case 0x14ce: /* growing steel wall (left/right) */
4725 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4728 case 0x14df: /* growing steel wall (up/down) */
4729 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4732 case 0x14e8: /* growing steel wall (up/down/left/right) */
4733 element = EL_EXPANDABLE_STEELWALL_ANY;
4737 element = EL_SHIELD_DEADLY;
4741 element = EL_EXTRA_TIME;
4749 element = EL_EMPTY_SPACE;
4752 case 0x1578: /* quicksand (empty) */
4753 element = EL_QUICKSAND_FAST_EMPTY;
4756 case 0x1579: /* slow quicksand (empty) */
4757 element = EL_QUICKSAND_EMPTY;
4760 /* 0x157c - 0x158b: */
4763 /* 0x1590 - 0x159f: */
4764 /* EL_DC_LANDMINE */
4767 element = EL_EM_DYNAMITE;
4770 case 0x15a1: /* key (red) */
4771 element = EL_EM_KEY_1;
4774 case 0x15a2: /* key (yellow) */
4775 element = EL_EM_KEY_2;
4778 case 0x15a3: /* key (blue) */
4779 element = EL_EM_KEY_4;
4782 case 0x15a4: /* key (green) */
4783 element = EL_EM_KEY_3;
4786 case 0x15a5: /* key (white) */
4787 element = EL_DC_KEY_WHITE;
4791 element = EL_WALL_SLIPPERY;
4798 case 0x15a8: /* wall (not round) */
4802 case 0x15a9: /* (blue) */
4803 element = EL_CHAR_A;
4806 case 0x15aa: /* (blue) */
4807 element = EL_CHAR_B;
4810 case 0x15ab: /* (blue) */
4811 element = EL_CHAR_C;
4814 case 0x15ac: /* (blue) */
4815 element = EL_CHAR_D;
4818 case 0x15ad: /* (blue) */
4819 element = EL_CHAR_E;
4822 case 0x15ae: /* (blue) */
4823 element = EL_CHAR_F;
4826 case 0x15af: /* (blue) */
4827 element = EL_CHAR_G;
4830 case 0x15b0: /* (blue) */
4831 element = EL_CHAR_H;
4834 case 0x15b1: /* (blue) */
4835 element = EL_CHAR_I;
4838 case 0x15b2: /* (blue) */
4839 element = EL_CHAR_J;
4842 case 0x15b3: /* (blue) */
4843 element = EL_CHAR_K;
4846 case 0x15b4: /* (blue) */
4847 element = EL_CHAR_L;
4850 case 0x15b5: /* (blue) */
4851 element = EL_CHAR_M;
4854 case 0x15b6: /* (blue) */
4855 element = EL_CHAR_N;
4858 case 0x15b7: /* (blue) */
4859 element = EL_CHAR_O;
4862 case 0x15b8: /* (blue) */
4863 element = EL_CHAR_P;
4866 case 0x15b9: /* (blue) */
4867 element = EL_CHAR_Q;
4870 case 0x15ba: /* (blue) */
4871 element = EL_CHAR_R;
4874 case 0x15bb: /* (blue) */
4875 element = EL_CHAR_S;
4878 case 0x15bc: /* (blue) */
4879 element = EL_CHAR_T;
4882 case 0x15bd: /* (blue) */
4883 element = EL_CHAR_U;
4886 case 0x15be: /* (blue) */
4887 element = EL_CHAR_V;
4890 case 0x15bf: /* (blue) */
4891 element = EL_CHAR_W;
4894 case 0x15c0: /* (blue) */
4895 element = EL_CHAR_X;
4898 case 0x15c1: /* (blue) */
4899 element = EL_CHAR_Y;
4902 case 0x15c2: /* (blue) */
4903 element = EL_CHAR_Z;
4906 case 0x15c3: /* (blue) */
4907 element = EL_CHAR_AUMLAUT;
4910 case 0x15c4: /* (blue) */
4911 element = EL_CHAR_OUMLAUT;
4914 case 0x15c5: /* (blue) */
4915 element = EL_CHAR_UUMLAUT;
4918 case 0x15c6: /* (blue) */
4919 element = EL_CHAR_0;
4922 case 0x15c7: /* (blue) */
4923 element = EL_CHAR_1;
4926 case 0x15c8: /* (blue) */
4927 element = EL_CHAR_2;
4930 case 0x15c9: /* (blue) */
4931 element = EL_CHAR_3;
4934 case 0x15ca: /* (blue) */
4935 element = EL_CHAR_4;
4938 case 0x15cb: /* (blue) */
4939 element = EL_CHAR_5;
4942 case 0x15cc: /* (blue) */
4943 element = EL_CHAR_6;
4946 case 0x15cd: /* (blue) */
4947 element = EL_CHAR_7;
4950 case 0x15ce: /* (blue) */
4951 element = EL_CHAR_8;
4954 case 0x15cf: /* (blue) */
4955 element = EL_CHAR_9;
4958 case 0x15d0: /* (blue) */
4959 element = EL_CHAR_PERIOD;
4962 case 0x15d1: /* (blue) */
4963 element = EL_CHAR_EXCLAM;
4966 case 0x15d2: /* (blue) */
4967 element = EL_CHAR_COLON;
4970 case 0x15d3: /* (blue) */
4971 element = EL_CHAR_LESS;
4974 case 0x15d4: /* (blue) */
4975 element = EL_CHAR_GREATER;
4978 case 0x15d5: /* (blue) */
4979 element = EL_CHAR_QUESTION;
4982 case 0x15d6: /* (blue) */
4983 element = EL_CHAR_COPYRIGHT;
4986 case 0x15d7: /* (blue) */
4987 element = EL_CHAR_UP;
4990 case 0x15d8: /* (blue) */
4991 element = EL_CHAR_DOWN;
4994 case 0x15d9: /* (blue) */
4995 element = EL_CHAR_BUTTON;
4998 case 0x15da: /* (blue) */
4999 element = EL_CHAR_PLUS;
5002 case 0x15db: /* (blue) */
5003 element = EL_CHAR_MINUS;
5006 case 0x15dc: /* (blue) */
5007 element = EL_CHAR_APOSTROPHE;
5010 case 0x15dd: /* (blue) */
5011 element = EL_CHAR_PARENLEFT;
5014 case 0x15de: /* (blue) */
5015 element = EL_CHAR_PARENRIGHT;
5018 case 0x15df: /* (green) */
5019 element = EL_CHAR_A;
5022 case 0x15e0: /* (green) */
5023 element = EL_CHAR_B;
5026 case 0x15e1: /* (green) */
5027 element = EL_CHAR_C;
5030 case 0x15e2: /* (green) */
5031 element = EL_CHAR_D;
5034 case 0x15e3: /* (green) */
5035 element = EL_CHAR_E;
5038 case 0x15e4: /* (green) */
5039 element = EL_CHAR_F;
5042 case 0x15e5: /* (green) */
5043 element = EL_CHAR_G;
5046 case 0x15e6: /* (green) */
5047 element = EL_CHAR_H;
5050 case 0x15e7: /* (green) */
5051 element = EL_CHAR_I;
5054 case 0x15e8: /* (green) */
5055 element = EL_CHAR_J;
5058 case 0x15e9: /* (green) */
5059 element = EL_CHAR_K;
5062 case 0x15ea: /* (green) */
5063 element = EL_CHAR_L;
5066 case 0x15eb: /* (green) */
5067 element = EL_CHAR_M;
5070 case 0x15ec: /* (green) */
5071 element = EL_CHAR_N;
5074 case 0x15ed: /* (green) */
5075 element = EL_CHAR_O;
5078 case 0x15ee: /* (green) */
5079 element = EL_CHAR_P;
5082 case 0x15ef: /* (green) */
5083 element = EL_CHAR_Q;
5086 case 0x15f0: /* (green) */
5087 element = EL_CHAR_R;
5090 case 0x15f1: /* (green) */
5091 element = EL_CHAR_S;
5094 case 0x15f2: /* (green) */
5095 element = EL_CHAR_T;
5098 case 0x15f3: /* (green) */
5099 element = EL_CHAR_U;
5102 case 0x15f4: /* (green) */
5103 element = EL_CHAR_V;
5106 case 0x15f5: /* (green) */
5107 element = EL_CHAR_W;
5110 case 0x15f6: /* (green) */
5111 element = EL_CHAR_X;
5114 case 0x15f7: /* (green) */
5115 element = EL_CHAR_Y;
5118 case 0x15f8: /* (green) */
5119 element = EL_CHAR_Z;
5122 case 0x15f9: /* (green) */
5123 element = EL_CHAR_AUMLAUT;
5126 case 0x15fa: /* (green) */
5127 element = EL_CHAR_OUMLAUT;
5130 case 0x15fb: /* (green) */
5131 element = EL_CHAR_UUMLAUT;
5134 case 0x15fc: /* (green) */
5135 element = EL_CHAR_0;
5138 case 0x15fd: /* (green) */
5139 element = EL_CHAR_1;
5142 case 0x15fe: /* (green) */
5143 element = EL_CHAR_2;
5146 case 0x15ff: /* (green) */
5147 element = EL_CHAR_3;
5150 case 0x1600: /* (green) */
5151 element = EL_CHAR_4;
5154 case 0x1601: /* (green) */
5155 element = EL_CHAR_5;
5158 case 0x1602: /* (green) */
5159 element = EL_CHAR_6;
5162 case 0x1603: /* (green) */
5163 element = EL_CHAR_7;
5166 case 0x1604: /* (green) */
5167 element = EL_CHAR_8;
5170 case 0x1605: /* (green) */
5171 element = EL_CHAR_9;
5174 case 0x1606: /* (green) */
5175 element = EL_CHAR_PERIOD;
5178 case 0x1607: /* (green) */
5179 element = EL_CHAR_EXCLAM;
5182 case 0x1608: /* (green) */
5183 element = EL_CHAR_COLON;
5186 case 0x1609: /* (green) */
5187 element = EL_CHAR_LESS;
5190 case 0x160a: /* (green) */
5191 element = EL_CHAR_GREATER;
5194 case 0x160b: /* (green) */
5195 element = EL_CHAR_QUESTION;
5198 case 0x160c: /* (green) */
5199 element = EL_CHAR_COPYRIGHT;
5202 case 0x160d: /* (green) */
5203 element = EL_CHAR_UP;
5206 case 0x160e: /* (green) */
5207 element = EL_CHAR_DOWN;
5210 case 0x160f: /* (green) */
5211 element = EL_CHAR_BUTTON;
5214 case 0x1610: /* (green) */
5215 element = EL_CHAR_PLUS;
5218 case 0x1611: /* (green) */
5219 element = EL_CHAR_MINUS;
5222 case 0x1612: /* (green) */
5223 element = EL_CHAR_APOSTROPHE;
5226 case 0x1613: /* (green) */
5227 element = EL_CHAR_PARENLEFT;
5230 case 0x1614: /* (green) */
5231 element = EL_CHAR_PARENRIGHT;
5234 case 0x1615: /* (blue steel) */
5235 element = EL_STEEL_CHAR_A;
5238 case 0x1616: /* (blue steel) */
5239 element = EL_STEEL_CHAR_B;
5242 case 0x1617: /* (blue steel) */
5243 element = EL_STEEL_CHAR_C;
5246 case 0x1618: /* (blue steel) */
5247 element = EL_STEEL_CHAR_D;
5250 case 0x1619: /* (blue steel) */
5251 element = EL_STEEL_CHAR_E;
5254 case 0x161a: /* (blue steel) */
5255 element = EL_STEEL_CHAR_F;
5258 case 0x161b: /* (blue steel) */
5259 element = EL_STEEL_CHAR_G;
5262 case 0x161c: /* (blue steel) */
5263 element = EL_STEEL_CHAR_H;
5266 case 0x161d: /* (blue steel) */
5267 element = EL_STEEL_CHAR_I;
5270 case 0x161e: /* (blue steel) */
5271 element = EL_STEEL_CHAR_J;
5274 case 0x161f: /* (blue steel) */
5275 element = EL_STEEL_CHAR_K;
5278 case 0x1620: /* (blue steel) */
5279 element = EL_STEEL_CHAR_L;
5282 case 0x1621: /* (blue steel) */
5283 element = EL_STEEL_CHAR_M;
5286 case 0x1622: /* (blue steel) */
5287 element = EL_STEEL_CHAR_N;
5290 case 0x1623: /* (blue steel) */
5291 element = EL_STEEL_CHAR_O;
5294 case 0x1624: /* (blue steel) */
5295 element = EL_STEEL_CHAR_P;
5298 case 0x1625: /* (blue steel) */
5299 element = EL_STEEL_CHAR_Q;
5302 case 0x1626: /* (blue steel) */
5303 element = EL_STEEL_CHAR_R;
5306 case 0x1627: /* (blue steel) */
5307 element = EL_STEEL_CHAR_S;
5310 case 0x1628: /* (blue steel) */
5311 element = EL_STEEL_CHAR_T;
5314 case 0x1629: /* (blue steel) */
5315 element = EL_STEEL_CHAR_U;
5318 case 0x162a: /* (blue steel) */
5319 element = EL_STEEL_CHAR_V;
5322 case 0x162b: /* (blue steel) */
5323 element = EL_STEEL_CHAR_W;
5326 case 0x162c: /* (blue steel) */
5327 element = EL_STEEL_CHAR_X;
5330 case 0x162d: /* (blue steel) */
5331 element = EL_STEEL_CHAR_Y;
5334 case 0x162e: /* (blue steel) */
5335 element = EL_STEEL_CHAR_Z;
5338 case 0x162f: /* (blue steel) */
5339 element = EL_STEEL_CHAR_AUMLAUT;
5342 case 0x1630: /* (blue steel) */
5343 element = EL_STEEL_CHAR_OUMLAUT;
5346 case 0x1631: /* (blue steel) */
5347 element = EL_STEEL_CHAR_UUMLAUT;
5350 case 0x1632: /* (blue steel) */
5351 element = EL_STEEL_CHAR_0;
5354 case 0x1633: /* (blue steel) */
5355 element = EL_STEEL_CHAR_1;
5358 case 0x1634: /* (blue steel) */
5359 element = EL_STEEL_CHAR_2;
5362 case 0x1635: /* (blue steel) */
5363 element = EL_STEEL_CHAR_3;
5366 case 0x1636: /* (blue steel) */
5367 element = EL_STEEL_CHAR_4;
5370 case 0x1637: /* (blue steel) */
5371 element = EL_STEEL_CHAR_5;
5374 case 0x1638: /* (blue steel) */
5375 element = EL_STEEL_CHAR_6;
5378 case 0x1639: /* (blue steel) */
5379 element = EL_STEEL_CHAR_7;
5382 case 0x163a: /* (blue steel) */
5383 element = EL_STEEL_CHAR_8;
5386 case 0x163b: /* (blue steel) */
5387 element = EL_STEEL_CHAR_9;
5390 case 0x163c: /* (blue steel) */
5391 element = EL_STEEL_CHAR_PERIOD;
5394 case 0x163d: /* (blue steel) */
5395 element = EL_STEEL_CHAR_EXCLAM;
5398 case 0x163e: /* (blue steel) */
5399 element = EL_STEEL_CHAR_COLON;
5402 case 0x163f: /* (blue steel) */
5403 element = EL_STEEL_CHAR_LESS;
5406 case 0x1640: /* (blue steel) */
5407 element = EL_STEEL_CHAR_GREATER;
5410 case 0x1641: /* (blue steel) */
5411 element = EL_STEEL_CHAR_QUESTION;
5414 case 0x1642: /* (blue steel) */
5415 element = EL_STEEL_CHAR_COPYRIGHT;
5418 case 0x1643: /* (blue steel) */
5419 element = EL_STEEL_CHAR_UP;
5422 case 0x1644: /* (blue steel) */
5423 element = EL_STEEL_CHAR_DOWN;
5426 case 0x1645: /* (blue steel) */
5427 element = EL_STEEL_CHAR_BUTTON;
5430 case 0x1646: /* (blue steel) */
5431 element = EL_STEEL_CHAR_PLUS;
5434 case 0x1647: /* (blue steel) */
5435 element = EL_STEEL_CHAR_MINUS;
5438 case 0x1648: /* (blue steel) */
5439 element = EL_STEEL_CHAR_APOSTROPHE;
5442 case 0x1649: /* (blue steel) */
5443 element = EL_STEEL_CHAR_PARENLEFT;
5446 case 0x164a: /* (blue steel) */
5447 element = EL_STEEL_CHAR_PARENRIGHT;
5450 case 0x164b: /* (green steel) */
5451 element = EL_STEEL_CHAR_A;
5454 case 0x164c: /* (green steel) */
5455 element = EL_STEEL_CHAR_B;
5458 case 0x164d: /* (green steel) */
5459 element = EL_STEEL_CHAR_C;
5462 case 0x164e: /* (green steel) */
5463 element = EL_STEEL_CHAR_D;
5466 case 0x164f: /* (green steel) */
5467 element = EL_STEEL_CHAR_E;
5470 case 0x1650: /* (green steel) */
5471 element = EL_STEEL_CHAR_F;
5474 case 0x1651: /* (green steel) */
5475 element = EL_STEEL_CHAR_G;
5478 case 0x1652: /* (green steel) */
5479 element = EL_STEEL_CHAR_H;
5482 case 0x1653: /* (green steel) */
5483 element = EL_STEEL_CHAR_I;
5486 case 0x1654: /* (green steel) */
5487 element = EL_STEEL_CHAR_J;
5490 case 0x1655: /* (green steel) */
5491 element = EL_STEEL_CHAR_K;
5494 case 0x1656: /* (green steel) */
5495 element = EL_STEEL_CHAR_L;
5498 case 0x1657: /* (green steel) */
5499 element = EL_STEEL_CHAR_M;
5502 case 0x1658: /* (green steel) */
5503 element = EL_STEEL_CHAR_N;
5506 case 0x1659: /* (green steel) */
5507 element = EL_STEEL_CHAR_O;
5510 case 0x165a: /* (green steel) */
5511 element = EL_STEEL_CHAR_P;
5514 case 0x165b: /* (green steel) */
5515 element = EL_STEEL_CHAR_Q;
5518 case 0x165c: /* (green steel) */
5519 element = EL_STEEL_CHAR_R;
5522 case 0x165d: /* (green steel) */
5523 element = EL_STEEL_CHAR_S;
5526 case 0x165e: /* (green steel) */
5527 element = EL_STEEL_CHAR_T;
5530 case 0x165f: /* (green steel) */
5531 element = EL_STEEL_CHAR_U;
5534 case 0x1660: /* (green steel) */
5535 element = EL_STEEL_CHAR_V;
5538 case 0x1661: /* (green steel) */
5539 element = EL_STEEL_CHAR_W;
5542 case 0x1662: /* (green steel) */
5543 element = EL_STEEL_CHAR_X;
5546 case 0x1663: /* (green steel) */
5547 element = EL_STEEL_CHAR_Y;
5550 case 0x1664: /* (green steel) */
5551 element = EL_STEEL_CHAR_Z;
5554 case 0x1665: /* (green steel) */
5555 element = EL_STEEL_CHAR_AUMLAUT;
5558 case 0x1666: /* (green steel) */
5559 element = EL_STEEL_CHAR_OUMLAUT;
5562 case 0x1667: /* (green steel) */
5563 element = EL_STEEL_CHAR_UUMLAUT;
5566 case 0x1668: /* (green steel) */
5567 element = EL_STEEL_CHAR_0;
5570 case 0x1669: /* (green steel) */
5571 element = EL_STEEL_CHAR_1;
5574 case 0x166a: /* (green steel) */
5575 element = EL_STEEL_CHAR_2;
5578 case 0x166b: /* (green steel) */
5579 element = EL_STEEL_CHAR_3;
5582 case 0x166c: /* (green steel) */
5583 element = EL_STEEL_CHAR_4;
5586 case 0x166d: /* (green steel) */
5587 element = EL_STEEL_CHAR_5;
5590 case 0x166e: /* (green steel) */
5591 element = EL_STEEL_CHAR_6;
5594 case 0x166f: /* (green steel) */
5595 element = EL_STEEL_CHAR_7;
5598 case 0x1670: /* (green steel) */
5599 element = EL_STEEL_CHAR_8;
5602 case 0x1671: /* (green steel) */
5603 element = EL_STEEL_CHAR_9;
5606 case 0x1672: /* (green steel) */
5607 element = EL_STEEL_CHAR_PERIOD;
5610 case 0x1673: /* (green steel) */
5611 element = EL_STEEL_CHAR_EXCLAM;
5614 case 0x1674: /* (green steel) */
5615 element = EL_STEEL_CHAR_COLON;
5618 case 0x1675: /* (green steel) */
5619 element = EL_STEEL_CHAR_LESS;
5622 case 0x1676: /* (green steel) */
5623 element = EL_STEEL_CHAR_GREATER;
5626 case 0x1677: /* (green steel) */
5627 element = EL_STEEL_CHAR_QUESTION;
5630 case 0x1678: /* (green steel) */
5631 element = EL_STEEL_CHAR_COPYRIGHT;
5634 case 0x1679: /* (green steel) */
5635 element = EL_STEEL_CHAR_UP;
5638 case 0x167a: /* (green steel) */
5639 element = EL_STEEL_CHAR_DOWN;
5642 case 0x167b: /* (green steel) */
5643 element = EL_STEEL_CHAR_BUTTON;
5646 case 0x167c: /* (green steel) */
5647 element = EL_STEEL_CHAR_PLUS;
5650 case 0x167d: /* (green steel) */
5651 element = EL_STEEL_CHAR_MINUS;
5654 case 0x167e: /* (green steel) */
5655 element = EL_STEEL_CHAR_APOSTROPHE;
5658 case 0x167f: /* (green steel) */
5659 element = EL_STEEL_CHAR_PARENLEFT;
5662 case 0x1680: /* (green steel) */
5663 element = EL_STEEL_CHAR_PARENRIGHT;
5666 case 0x1681: /* gate (red) */
5667 element = EL_EM_GATE_1;
5670 case 0x1682: /* secret gate (red) */
5671 element = EL_GATE_1_GRAY;
5674 case 0x1683: /* gate (yellow) */
5675 element = EL_EM_GATE_2;
5678 case 0x1684: /* secret gate (yellow) */
5679 element = EL_GATE_2_GRAY;
5682 case 0x1685: /* gate (blue) */
5683 element = EL_EM_GATE_4;
5686 case 0x1686: /* secret gate (blue) */
5687 element = EL_GATE_4_GRAY;
5690 case 0x1687: /* gate (green) */
5691 element = EL_EM_GATE_3;
5694 case 0x1688: /* secret gate (green) */
5695 element = EL_GATE_3_GRAY;
5698 case 0x1689: /* gate (white) */
5699 element = EL_DC_GATE_WHITE;
5702 case 0x168a: /* secret gate (white) */
5703 element = EL_DC_GATE_WHITE_GRAY;
5706 case 0x168b: /* secret gate (no key) */
5707 element = EL_DC_GATE_FAKE_GRAY;
5711 element = EL_ROBOT_WHEEL;
5715 element = EL_DC_TIMEGATE_SWITCH;
5719 element = EL_ACID_POOL_BOTTOM;
5723 element = EL_ACID_POOL_TOPLEFT;
5727 element = EL_ACID_POOL_TOPRIGHT;
5731 element = EL_ACID_POOL_BOTTOMLEFT;
5735 element = EL_ACID_POOL_BOTTOMRIGHT;
5739 element = EL_STEELWALL;
5743 element = EL_STEELWALL_SLIPPERY;
5746 case 0x1695: /* steel wall (not round) */
5747 element = EL_STEELWALL;
5750 case 0x1696: /* steel wall (left) */
5751 element = EL_DC_STEELWALL_1_LEFT;
5754 case 0x1697: /* steel wall (bottom) */
5755 element = EL_DC_STEELWALL_1_BOTTOM;
5758 case 0x1698: /* steel wall (right) */
5759 element = EL_DC_STEELWALL_1_RIGHT;
5762 case 0x1699: /* steel wall (top) */
5763 element = EL_DC_STEELWALL_1_TOP;
5766 case 0x169a: /* steel wall (left/bottom) */
5767 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5770 case 0x169b: /* steel wall (right/bottom) */
5771 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5774 case 0x169c: /* steel wall (right/top) */
5775 element = EL_DC_STEELWALL_1_TOPRIGHT;
5778 case 0x169d: /* steel wall (left/top) */
5779 element = EL_DC_STEELWALL_1_TOPLEFT;
5782 case 0x169e: /* steel wall (right/bottom small) */
5783 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5786 case 0x169f: /* steel wall (left/bottom small) */
5787 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5790 case 0x16a0: /* steel wall (right/top small) */
5791 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5794 case 0x16a1: /* steel wall (left/top small) */
5795 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5798 case 0x16a2: /* steel wall (left/right) */
5799 element = EL_DC_STEELWALL_1_VERTICAL;
5802 case 0x16a3: /* steel wall (top/bottom) */
5803 element = EL_DC_STEELWALL_1_HORIZONTAL;
5806 case 0x16a4: /* steel wall 2 (left end) */
5807 element = EL_DC_STEELWALL_2_LEFT;
5810 case 0x16a5: /* steel wall 2 (right end) */
5811 element = EL_DC_STEELWALL_2_RIGHT;
5814 case 0x16a6: /* steel wall 2 (top end) */
5815 element = EL_DC_STEELWALL_2_TOP;
5818 case 0x16a7: /* steel wall 2 (bottom end) */
5819 element = EL_DC_STEELWALL_2_BOTTOM;
5822 case 0x16a8: /* steel wall 2 (left/right) */
5823 element = EL_DC_STEELWALL_2_HORIZONTAL;
5826 case 0x16a9: /* steel wall 2 (up/down) */
5827 element = EL_DC_STEELWALL_2_VERTICAL;
5830 case 0x16aa: /* steel wall 2 (mid) */
5831 element = EL_DC_STEELWALL_2_MIDDLE;
5835 element = EL_SIGN_EXCLAMATION;
5839 element = EL_SIGN_RADIOACTIVITY;
5843 element = EL_SIGN_STOP;
5847 element = EL_SIGN_WHEELCHAIR;
5851 element = EL_SIGN_PARKING;
5855 element = EL_SIGN_NO_ENTRY;
5859 element = EL_SIGN_HEART;
5863 element = EL_SIGN_GIVE_WAY;
5867 element = EL_SIGN_ENTRY_FORBIDDEN;
5871 element = EL_SIGN_EMERGENCY_EXIT;
5875 element = EL_SIGN_YIN_YANG;
5879 element = EL_WALL_EMERALD;
5883 element = EL_WALL_DIAMOND;
5887 element = EL_WALL_PEARL;
5891 element = EL_WALL_CRYSTAL;
5895 element = EL_INVISIBLE_WALL;
5899 element = EL_INVISIBLE_STEELWALL;
5902 /* 0x16bc - 0x16cb: */
5903 /* EL_INVISIBLE_SAND */
5906 element = EL_LIGHT_SWITCH;
5910 element = EL_ENVELOPE_1;
5914 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5915 element = EL_DIAMOND;
5916 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5917 element = EL_EMERALD;
5918 else if (element >= 0x157c && element <= 0x158b)
5920 else if (element >= 0x1590 && element <= 0x159f)
5921 element = EL_DC_LANDMINE;
5922 else if (element >= 0x16bc && element <= 0x16cb)
5923 element = EL_INVISIBLE_SAND;
5926 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5927 element = EL_UNKNOWN;
5932 return getMappedElement(element);
5937 static void LoadLevelFromFileStream_DC(FILE *file, struct LevelInfo *level,
5940 byte header[DC_LEVEL_HEADER_SIZE];
5942 int envelope_header_pos = 62;
5943 int envelope_content_pos = 94;
5944 int level_name_pos = 251;
5945 int level_author_pos = 292;
5946 int envelope_header_len;
5947 int envelope_content_len;
5949 int level_author_len;
5951 int num_yamyam_contents;
5954 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5956 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5958 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5960 header[i * 2 + 0] = header_word >> 8;
5961 header[i * 2 + 1] = header_word & 0xff;
5964 /* read some values from level header to check level decoding integrity */
5965 fieldx = header[6] | (header[7] << 8);
5966 fieldy = header[8] | (header[9] << 8);
5967 num_yamyam_contents = header[60] | (header[61] << 8);
5969 /* do some simple sanity checks to ensure that level was correctly decoded */
5970 if (fieldx < 1 || fieldx > 256 ||
5971 fieldy < 1 || fieldy > 256 ||
5972 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5974 level->no_valid_file = TRUE;
5976 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5981 /* maximum envelope header size is 31 bytes */
5982 envelope_header_len = header[envelope_header_pos];
5983 /* maximum envelope content size is 110 (156?) bytes */
5984 envelope_content_len = header[envelope_content_pos];
5986 /* maximum level title size is 40 bytes */
5987 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5988 /* maximum level author size is 30 (51?) bytes */
5989 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5993 for (i = 0; i < envelope_header_len; i++)
5994 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5995 level->envelope[0].text[envelope_size++] =
5996 header[envelope_header_pos + 1 + i];
5998 if (envelope_header_len > 0 && envelope_content_len > 0)
6000 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6001 level->envelope[0].text[envelope_size++] = '\n';
6002 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6003 level->envelope[0].text[envelope_size++] = '\n';
6006 for (i = 0; i < envelope_content_len; i++)
6007 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6008 level->envelope[0].text[envelope_size++] =
6009 header[envelope_content_pos + 1 + i];
6011 level->envelope[0].text[envelope_size] = '\0';
6013 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6014 level->envelope[0].ysize = 10;
6015 level->envelope[0].autowrap = TRUE;
6016 level->envelope[0].centered = TRUE;
6018 for (i = 0; i < level_name_len; i++)
6019 level->name[i] = header[level_name_pos + 1 + i];
6020 level->name[level_name_len] = '\0';
6022 for (i = 0; i < level_author_len; i++)
6023 level->author[i] = header[level_author_pos + 1 + i];
6024 level->author[level_author_len] = '\0';
6026 num_yamyam_contents = header[60] | (header[61] << 8);
6027 level->num_yamyam_contents =
6028 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6030 for (i = 0; i < num_yamyam_contents; i++)
6032 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6034 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6036 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6038 int element_dc = word;
6041 if (i < MAX_ELEMENT_CONTENTS)
6042 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6046 fieldx = header[6] | (header[7] << 8);
6047 fieldy = header[8] | (header[9] << 8);
6048 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6049 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6051 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6053 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6055 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6057 int element_dc = word;
6060 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6061 level->field[x][y] = getMappedElement_DC(element_dc);
6064 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6065 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6066 level->field[x][y] = EL_PLAYER_1;
6068 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6069 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6070 level->field[x][y] = EL_PLAYER_2;
6072 level->gems_needed = header[18] | (header[19] << 8);
6074 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6075 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6076 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6077 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6078 level->score[SC_NUT] = header[28] | (header[29] << 8);
6079 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6080 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6081 level->score[SC_BUG] = header[34] | (header[35] << 8);
6082 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6083 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6084 level->score[SC_KEY] = header[40] | (header[41] << 8);
6085 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6087 level->time = header[44] | (header[45] << 8);
6089 level->amoeba_speed = header[46] | (header[47] << 8);
6090 level->time_light = header[48] | (header[49] << 8);
6091 level->time_timegate = header[50] | (header[51] << 8);
6092 level->time_wheel = header[52] | (header[53] << 8);
6093 level->time_magic_wall = header[54] | (header[55] << 8);
6094 level->extra_time = header[56] | (header[57] << 8);
6095 level->shield_normal_time = header[58] | (header[59] << 8);
6097 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6098 can slip down from flat walls, like normal walls and steel walls */
6099 level->em_slippery_gems = TRUE;
6102 /* Diamond Caves II levels are always surrounded by indestructible wall, but
6103 not necessarily in a rectangular way -- fill with invisible steel wall */
6105 /* !!! not always true !!! keep level and set BorderElement instead !!! */
6107 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6110 if ((x == 0 || x == level->fieldx - 1 ||
6111 y == 0 || y == level->fieldy - 1) &&
6112 level->field[x][y] == EL_EMPTY)
6113 level->field[x][y] = EL_INVISIBLE_STEELWALL;
6115 if ((x == 0 || x == level->fieldx - 1 ||
6116 y == 0 || y == level->fieldy - 1) &&
6117 level->field[x][y] == EL_EMPTY)
6118 FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
6119 level->field, level->fieldx, level->fieldy);
6125 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6126 struct LevelFileInfo *level_file_info)
6128 char *filename = level_file_info->filename;
6130 int num_magic_bytes = 8;
6131 char magic_bytes[num_magic_bytes + 1];
6132 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6134 if (!(file = fopen(filename, MODE_READ)))
6136 level->no_valid_file = TRUE;
6138 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6143 // fseek(file, 0x0000, SEEK_SET);
6145 if (level_file_info->packed)
6147 /* read "magic bytes" from start of file */
6148 fgets(magic_bytes, num_magic_bytes + 1, file);
6150 /* check "magic bytes" for correct file format */
6151 if (!strPrefix(magic_bytes, "DC2"))
6153 level->no_valid_file = TRUE;
6155 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
6161 if (strPrefix(magic_bytes, "DC2Win95") ||
6162 strPrefix(magic_bytes, "DC2Win98"))
6164 int position_first_level = 0x00fa;
6165 int extra_bytes = 4;
6168 /* advance file stream to first level inside the level package */
6169 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6171 /* each block of level data is followed by block of non-level data */
6172 num_levels_to_skip *= 2;
6174 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
6175 while (num_levels_to_skip >= 0)
6177 /* advance file stream to next level inside the level package */
6178 if (fseek(file, skip_bytes, SEEK_CUR) != 0)
6180 level->no_valid_file = TRUE;
6182 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
6188 /* skip apparently unused extra bytes following each level */
6189 ReadUnusedBytesFromFile(file, extra_bytes);
6191 /* read size of next level in level package */
6192 skip_bytes = getFile32BitLE(file);
6194 num_levels_to_skip--;
6199 level->no_valid_file = TRUE;
6201 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
6208 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
6215 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6216 struct LevelFileInfo *level_file_info)
6218 char *filename = level_file_info->filename;
6221 int nr = level_file_info->nr - leveldir_current->first_level;
6223 byte header[DC_LEVEL_HEADER_SIZE];
6225 int envelope_header_pos = 62;
6226 int envelope_content_pos = 94;
6227 int level_name_pos = 251;
6228 int level_author_pos = 292;
6229 int envelope_header_len;
6230 int envelope_content_len;
6232 int level_author_len;
6234 int num_yamyam_contents;
6237 if (!(file = fopen(filename, MODE_READ)))
6239 level->no_valid_file = TRUE;
6241 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6247 /* position file stream to the requested level inside the level package */
6248 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
6250 level->no_valid_file = TRUE;
6252 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
6258 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
6260 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6262 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6264 header[i * 2 + 0] = header_word >> 8;
6265 header[i * 2 + 1] = header_word & 0xff;
6268 /* read some values from level header to check level decoding integrity */
6269 fieldx = header[6] | (header[7] << 8);
6270 fieldy = header[8] | (header[9] << 8);
6271 num_yamyam_contents = header[60] | (header[61] << 8);
6273 /* do some simple sanity checks to ensure that level was correctly decoded */
6274 if (fieldx < 1 || fieldx > 256 ||
6275 fieldy < 1 || fieldy > 256 ||
6276 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6278 level->no_valid_file = TRUE;
6280 Error(ERR_WARN, "cannot read level from file '%s' -- using empty level",
6286 /* maximum envelope header size is 31 bytes */
6287 envelope_header_len = header[envelope_header_pos];
6288 /* maximum envelope content size is 110 (156?) bytes */
6289 envelope_content_len = header[envelope_content_pos];
6291 /* maximum level title size is 40 bytes */
6292 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6293 /* maximum level author size is 30 (51?) bytes */
6294 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6298 for (i = 0; i < envelope_header_len; i++)
6299 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6300 level->envelope[0].text[envelope_size++] =
6301 header[envelope_header_pos + 1 + i];
6303 if (envelope_header_len > 0 && envelope_content_len > 0)
6305 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6306 level->envelope[0].text[envelope_size++] = '\n';
6307 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6308 level->envelope[0].text[envelope_size++] = '\n';
6311 for (i = 0; i < envelope_content_len; i++)
6312 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6313 level->envelope[0].text[envelope_size++] =
6314 header[envelope_content_pos + 1 + i];
6316 level->envelope[0].text[envelope_size] = '\0';
6318 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6319 level->envelope[0].ysize = 10;
6320 level->envelope[0].autowrap = TRUE;
6321 level->envelope[0].centered = TRUE;
6323 for (i = 0; i < level_name_len; i++)
6324 level->name[i] = header[level_name_pos + 1 + i];
6325 level->name[level_name_len] = '\0';
6327 for (i = 0; i < level_author_len; i++)
6328 level->author[i] = header[level_author_pos + 1 + i];
6329 level->author[level_author_len] = '\0';
6331 num_yamyam_contents = header[60] | (header[61] << 8);
6332 level->num_yamyam_contents =
6333 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6335 for (i = 0; i < num_yamyam_contents; i++)
6337 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6339 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6341 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6343 int element_dc = word;
6346 if (i < MAX_ELEMENT_CONTENTS)
6347 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6351 fieldx = header[6] | (header[7] << 8);
6352 fieldy = header[8] | (header[9] << 8);
6353 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6354 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6356 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6358 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6360 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6362 int element_dc = word;
6365 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6366 level->field[x][y] = getMappedElement_DC(element_dc);
6369 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6370 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6371 level->field[x][y] = EL_PLAYER_1;
6373 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6374 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6375 level->field[x][y] = EL_PLAYER_2;
6377 level->gems_needed = header[18] | (header[19] << 8);
6379 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6380 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6381 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6382 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6383 level->score[SC_NUT] = header[28] | (header[29] << 8);
6384 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6385 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6386 level->score[SC_BUG] = header[34] | (header[35] << 8);
6387 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6388 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6389 level->score[SC_KEY] = header[40] | (header[41] << 8);
6390 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6392 level->time = header[44] | (header[45] << 8);
6394 level->amoeba_speed = header[46] | (header[47] << 8);
6395 level->time_light = header[48] | (header[49] << 8);
6396 level->time_timegate = header[50] | (header[51] << 8);
6397 level->time_wheel = header[52] | (header[53] << 8);
6398 level->time_magic_wall = header[54] | (header[55] << 8);
6399 level->extra_time = header[56] | (header[57] << 8);
6400 level->shield_normal_time = header[58] | (header[59] << 8);
6404 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6405 can slip down from flat walls, like normal walls and steel walls */
6406 level->em_slippery_gems = TRUE;
6409 /* Diamond Caves II levels are always surrounded by indestructible wall, but
6410 not necessarily in a rectangular way -- fill with invisible steel wall */
6412 /* !!! not always true !!! keep level and set BorderElement instead !!! */
6414 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6417 if ((x == 0 || x == level->fieldx - 1 ||
6418 y == 0 || y == level->fieldy - 1) &&
6419 level->field[x][y] == EL_EMPTY)
6420 level->field[x][y] = EL_INVISIBLE_STEELWALL;
6422 if ((x == 0 || x == level->fieldx - 1 ||
6423 y == 0 || y == level->fieldy - 1) &&
6424 level->field[x][y] == EL_EMPTY)
6425 FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
6426 level->field, level->fieldx, level->fieldy);
6435 /* ------------------------------------------------------------------------- */
6436 /* functions for loading SB level */
6437 /* ------------------------------------------------------------------------- */
6441 static boolean check_special_flags(char *flag)
6444 printf("::: '%s', '%s', '%s'\n",
6446 options.special_flags,
6447 leveldir_current->special_flags);
6450 if (strEqual(options.special_flags, flag) ||
6451 strEqual(leveldir_current->special_flags, flag))
6459 #define SPECIAL_FLAG_LOAD_XSB_TO_CES (1 << 0)
6461 static unsigned long get_special_flags(char *flags_string)
6463 unsigned long flags_value = 0;
6465 if (strEqual(flags_string, "load_xsb_to_ces"))
6466 flags_value = SPECIAL_FLAG_LOAD_XSB_TO_CES;
6473 int getMappedElement_SB(int element_ascii, boolean use_ces)
6481 sb_element_mapping[] =
6483 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
6484 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
6485 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
6486 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
6487 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
6488 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
6489 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
6491 { '_', EL_INVISIBLE_STEELWALL, EL_CUSTOM_8 }, /* floor beyond border */
6493 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
6501 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6502 if (element_ascii == sb_element_mapping[i].ascii)
6503 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6505 return EL_UNDEFINED;
6508 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6509 struct LevelFileInfo *level_file_info)
6511 char *filename = level_file_info->filename;
6512 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6513 char last_comment[MAX_LINE_LEN];
6514 char level_name[MAX_LINE_LEN];
6517 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6518 boolean read_continued_line = FALSE;
6519 boolean reading_playfield = FALSE;
6520 boolean got_valid_playfield_line = FALSE;
6521 boolean invalid_playfield_char = FALSE;
6523 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6525 boolean load_xsb_to_ces = options.special_flags & SPECIAL_FLAG_LOAD_XSB_TO_CES;
6527 int file_level_nr = 0;
6532 printf("::: looking for level number %d [%d]\n",
6533 level_file_info->nr, num_levels_to_skip);
6536 last_comment[0] = '\0';
6537 level_name[0] = '\0';
6539 if (!(file = fopen(filename, MODE_READ)))
6541 level->no_valid_file = TRUE;
6543 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6550 /* level successfully read, but next level may follow here */
6551 if (!got_valid_playfield_line && reading_playfield)
6554 printf("::: read complete playfield\n");
6557 /* read playfield from single level file -- skip remaining file */
6558 if (!level_file_info->packed)
6561 if (file_level_nr >= num_levels_to_skip)
6566 last_comment[0] = '\0';
6567 level_name[0] = '\0';
6569 reading_playfield = FALSE;
6572 got_valid_playfield_line = FALSE;
6574 /* read next line of input file */
6575 if (!fgets(line, MAX_LINE_LEN, file))
6578 /* check if line was completely read and is terminated by line break */
6579 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6582 /* cut trailing line break (this can be newline and/or carriage return) */
6583 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6584 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6587 /* copy raw input line for later use (mainly debugging output) */
6588 strcpy(line_raw, line);
6590 if (read_continued_line)
6592 /* append new line to existing line, if there is enough space */
6593 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6594 strcat(previous_line, line_ptr);
6596 strcpy(line, previous_line); /* copy storage buffer to line */
6598 read_continued_line = FALSE;
6601 /* if the last character is '\', continue at next line */
6602 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6604 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
6605 strcpy(previous_line, line); /* copy line to storage buffer */
6607 read_continued_line = TRUE;
6612 /* skip empty lines */
6613 if (line[0] == '\0')
6616 /* extract comment text from comment line */
6619 for (line_ptr = line; *line_ptr; line_ptr++)
6620 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6623 strcpy(last_comment, line_ptr);
6626 printf("::: found comment '%s' in line %d\n", last_comment, line_nr);
6632 /* extract level title text from line containing level title */
6633 if (line[0] == '\'')
6635 strcpy(level_name, &line[1]);
6637 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6638 level_name[strlen(level_name) - 1] = '\0';
6641 printf("::: found level name '%s' in line %d\n", level_name, line_nr);
6647 /* skip lines containing only spaces (or empty lines) */
6648 for (line_ptr = line; *line_ptr; line_ptr++)
6649 if (*line_ptr != ' ')
6651 if (*line_ptr == '\0')
6654 /* at this point, we have found a line containing part of a playfield */
6657 printf("::: found playfield row in line %d\n", line_nr);
6660 got_valid_playfield_line = TRUE;
6662 if (!reading_playfield)
6664 reading_playfield = TRUE;
6665 invalid_playfield_char = FALSE;
6667 for (x = 0; x < MAX_LEV_FIELDX; x++)
6668 for (y = 0; y < MAX_LEV_FIELDY; y++)
6669 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6674 /* start with topmost tile row */
6678 /* skip playfield line if larger row than allowed */
6679 if (y >= MAX_LEV_FIELDY)
6682 /* start with leftmost tile column */
6685 /* read playfield elements from line */
6686 for (line_ptr = line; *line_ptr; line_ptr++)
6688 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6690 /* stop parsing playfield line if larger column than allowed */
6691 if (x >= MAX_LEV_FIELDX)
6694 if (mapped_sb_element == EL_UNDEFINED)
6696 invalid_playfield_char = TRUE;
6701 level->field[x][y] = mapped_sb_element;
6703 /* continue with next tile column */
6706 level->fieldx = MAX(x, level->fieldx);
6709 if (invalid_playfield_char)
6711 /* if first playfield line, treat invalid lines as comment lines */
6713 reading_playfield = FALSE;
6718 /* continue with next tile row */
6726 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6727 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6729 if (!reading_playfield)
6731 level->no_valid_file = TRUE;
6733 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6738 if (*level_name != '\0')
6740 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6741 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6744 printf(":1: level name: '%s'\n", level->name);
6747 else if (*last_comment != '\0')
6749 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6750 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6753 printf(":2: level name: '%s'\n", level->name);
6758 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6761 /* set all empty fields beyond the border walls to invisible steel wall */
6762 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6764 if ((x == 0 || x == level->fieldx - 1 ||
6765 y == 0 || y == level->fieldy - 1) &&
6766 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6767 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6768 level->field, level->fieldx, level->fieldy);
6771 /* set special level settings for Sokoban levels */
6774 level->use_step_counter = TRUE;
6776 if (load_xsb_to_ces)
6778 level->initial_player_stepsize[0] = STEPSIZE_SLOW;
6780 /* fill smaller playfields with padding "beyond border wall" elements */
6781 if (level->fieldx < SCR_FIELDX ||
6782 level->fieldy < SCR_FIELDY)
6784 short field[level->fieldx][level->fieldy];
6785 int new_fieldx = MAX(level->fieldx, SCR_FIELDX);
6786 int new_fieldy = MAX(level->fieldy, SCR_FIELDY);
6787 int pos_fieldx = (new_fieldx - level->fieldx) / 2;
6788 int pos_fieldy = (new_fieldy - level->fieldy) / 2;
6790 /* copy old playfield (which is smaller than the visible area) */
6791 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6792 field[x][y] = level->field[x][y];
6794 /* fill new, larger playfield with "beyond border wall" elements */
6795 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
6796 level->field[x][y] = getMappedElement_SB('_', load_xsb_to_ces);
6798 /* copy the old playfield to the middle of the new playfield */
6799 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6800 level->field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
6802 level->fieldx = new_fieldx;
6803 level->fieldy = new_fieldy;
6806 level->use_custom_template = TRUE;
6811 /* ------------------------------------------------------------------------- */
6812 /* functions for handling native levels */
6813 /* ------------------------------------------------------------------------- */
6815 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6816 struct LevelFileInfo *level_file_info)
6818 if (!LoadNativeLevel_EM(level_file_info->filename))
6819 level->no_valid_file = TRUE;
6822 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6823 struct LevelFileInfo *level_file_info)
6827 /* determine position of requested level inside level package */
6828 if (level_file_info->packed)
6829 pos = level_file_info->nr - leveldir_current->first_level;
6831 if (!LoadNativeLevel_SP(level_file_info->filename, pos))
6832 level->no_valid_file = TRUE;
6835 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6837 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6838 CopyNativeLevel_RND_to_EM(level);
6839 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6840 CopyNativeLevel_RND_to_SP(level);
6843 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6845 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6846 CopyNativeLevel_EM_to_RND(level);
6847 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6848 CopyNativeLevel_SP_to_RND(level);
6852 /* ------------------------------------------------------------------------- */
6853 /* functions for loading generic level */
6854 /* ------------------------------------------------------------------------- */
6856 void LoadLevelFromFileInfo(struct LevelInfo *level,
6857 struct LevelFileInfo *level_file_info)
6859 /* always start with reliable default values */
6860 setLevelInfoToDefaults(level);
6862 switch (level_file_info->type)
6864 case LEVEL_FILE_TYPE_RND:
6865 LoadLevelFromFileInfo_RND(level, level_file_info);
6868 case LEVEL_FILE_TYPE_EM:
6869 LoadLevelFromFileInfo_EM(level, level_file_info);
6870 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6873 case LEVEL_FILE_TYPE_SP:
6874 LoadLevelFromFileInfo_SP(level, level_file_info);
6875 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6878 case LEVEL_FILE_TYPE_DC:
6879 LoadLevelFromFileInfo_DC(level, level_file_info);
6882 case LEVEL_FILE_TYPE_SB:
6883 LoadLevelFromFileInfo_SB(level, level_file_info);
6887 LoadLevelFromFileInfo_RND(level, level_file_info);
6891 /* if level file is invalid, restore level structure to default values */
6892 if (level->no_valid_file)
6893 setLevelInfoToDefaults(level);
6895 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6896 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6898 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6899 CopyNativeLevel_Native_to_RND(level);
6902 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6904 static struct LevelFileInfo level_file_info;
6906 /* always start with reliable default values */
6907 setFileInfoToDefaults(&level_file_info);
6909 level_file_info.nr = 0; /* unknown level number */
6910 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
6911 level_file_info.filename = filename;
6913 LoadLevelFromFileInfo(level, &level_file_info);
6916 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
6920 if (leveldir_current == NULL) /* only when dumping level */
6923 /* all engine modifications also valid for levels which use latest engine */
6924 if (level->game_version < VERSION_IDENT(3,2,0,5))
6926 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6927 level->score[SC_TIME_BONUS] /= 10;
6931 leveldir_current->latest_engine = TRUE; /* !!! TEST ONLY !!! */
6934 if (leveldir_current->latest_engine)
6936 /* ---------- use latest game engine ----------------------------------- */
6938 /* For all levels which are forced to use the latest game engine version
6939 (normally all but user contributed, private and undefined levels), set
6940 the game engine version to the actual version; this allows for actual
6941 corrections in the game engine to take effect for existing, converted
6942 levels (from "classic" or other existing games) to make the emulation
6943 of the corresponding game more accurate, while (hopefully) not breaking
6944 existing levels created from other players. */
6946 level->game_version = GAME_VERSION_ACTUAL;
6948 /* Set special EM style gems behaviour: EM style gems slip down from
6949 normal, steel and growing wall. As this is a more fundamental change,
6950 it seems better to set the default behaviour to "off" (as it is more
6951 natural) and make it configurable in the level editor (as a property
6952 of gem style elements). Already existing converted levels (neither
6953 private nor contributed levels) are changed to the new behaviour. */
6955 if (level->file_version < FILE_VERSION_2_0)
6956 level->em_slippery_gems = TRUE;
6961 /* ---------- use game engine the level was created with ----------------- */
6963 /* For all levels which are not forced to use the latest game engine
6964 version (normally user contributed, private and undefined levels),
6965 use the version of the game engine the levels were created for.
6967 Since 2.0.1, the game engine version is now directly stored
6968 in the level file (chunk "VERS"), so there is no need anymore
6969 to set the game version from the file version (except for old,
6970 pre-2.0 levels, where the game version is still taken from the
6971 file format version used to store the level -- see above). */
6973 /* player was faster than enemies in 1.0.0 and before */
6974 if (level->file_version == FILE_VERSION_1_0)
6975 for (i = 0; i < MAX_PLAYERS; i++)
6976 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6978 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6979 if (level->game_version == VERSION_IDENT(2,0,1,0))
6980 level->em_slippery_gems = TRUE;
6982 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6983 if (level->game_version < VERSION_IDENT(2,2,0,0))
6984 level->use_spring_bug = TRUE;
6986 if (level->game_version < VERSION_IDENT(3,2,0,5))
6988 /* time orb caused limited time in endless time levels before 3.2.0-5 */
6989 level->use_time_orb_bug = TRUE;
6991 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6992 level->block_snap_field = FALSE;
6994 /* extra time score was same value as time left score before 3.2.0-5 */
6995 level->extra_time_score = level->score[SC_TIME_BONUS];
6998 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6999 level->score[SC_TIME_BONUS] /= 10;
7003 if (level->game_version < VERSION_IDENT(3,2,0,7))
7005 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
7006 level->continuous_snapping = FALSE;
7009 /* only few elements were able to actively move into acid before 3.1.0 */
7010 /* trigger settings did not exist before 3.1.0; set to default "any" */
7011 if (level->game_version < VERSION_IDENT(3,1,0,0))
7013 /* correct "can move into acid" settings (all zero in old levels) */
7015 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
7016 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
7018 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7019 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7020 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7021 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7023 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7024 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7026 /* correct trigger settings (stored as zero == "none" in old levels) */
7028 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7030 int element = EL_CUSTOM_START + i;
7031 struct ElementInfo *ei = &element_info[element];
7033 for (j = 0; j < ei->num_change_pages; j++)
7035 struct ElementChangeInfo *change = &ei->change_page[j];
7037 change->trigger_player = CH_PLAYER_ANY;
7038 change->trigger_page = CH_PAGE_ANY;
7043 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
7045 int element = EL_CUSTOM_256;
7046 struct ElementInfo *ei = &element_info[element];
7047 struct ElementChangeInfo *change = &ei->change_page[0];
7049 /* This is needed to fix a problem that was caused by a bugfix in function
7050 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7051 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7052 not replace walkable elements, but instead just placed the player on it,
7053 without placing the Sokoban field under the player). Unfortunately, this
7054 breaks "Snake Bite" style levels when the snake is halfway through a door
7055 that just closes (the snake head is still alive and can be moved in this
7056 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7057 player (without Sokoban element) which then gets killed as designed). */
7059 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7060 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7061 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7062 change->target_element = EL_PLAYER_1;
7066 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
7067 if (level->game_version < VERSION_IDENT(3,2,5,0))
7069 /* This is needed to fix a problem that was caused by a bugfix in function
7070 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7071 corrects the behaviour when a custom element changes to another custom
7072 element with a higher element number that has change actions defined.
7073 Normally, only one change per frame is allowed for custom elements.
7074 Therefore, it is checked if a custom element already changed in the
7075 current frame; if it did, subsequent changes are suppressed.
7076 Unfortunately, this is only checked for element changes, but not for
7077 change actions, which are still executed. As the function above loops
7078 through all custom elements from lower to higher, an element change
7079 resulting in a lower CE number won't be checked again, while a target
7080 element with a higher number will also be checked, and potential change
7081 actions will get executed for this CE, too (which is wrong), while
7082 further changes are ignored (which is correct). As this bugfix breaks
7083 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7084 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7085 behaviour for existing levels and tapes that make use of this bug */
7087 level->use_action_after_change_bug = TRUE;
7090 /* !!! THIS DOES NOT FIX "Zelda I" (GRAPHICALLY) AND "Alan's FMV" LEVELS */
7091 /* try to detect and fix "Zelda II" levels, which are broken with 3.2.5 */
7093 int element = EL_CUSTOM_16;
7094 struct ElementInfo *ei = &element_info[element];
7096 /* This is needed to fix a problem that was caused by a bugfix in function
7097 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7098 corrects the behaviour when a custom element changes to another custom
7099 element with a higher element number that has change actions defined.
7100 Normally, only one change per frame is allowed for custom elements.
7101 Therefore, it is checked if a custom element already changed in the
7102 current frame; if it did, subsequent changes are suppressed.
7103 Unfortunately, this is only checked for element changes, but not for
7104 change actions, which are still executed. As the function above loops
7105 through all custom elements from lower to higher, an element change
7106 resulting in a lower CE number won't be checked again, while a target
7107 element with a higher number will also be checked, and potential change
7108 actions will get executed for this CE, too (which is wrong), while
7109 further changes are ignored (which is correct). As this bugfix breaks
7110 Zelda II (but no other levels), allow the previous, incorrect behaviour
7111 for this outstanding level set to not break the game or existing tapes */
7113 if (strncmp(leveldir_current->identifier, "zelda2", 6) == 0 ||
7114 strncmp(ei->description, "scanline - row 1", 16) == 0)
7115 level->use_action_after_change_bug = TRUE;
7119 /* not centering level after relocating player was default only in 3.2.3 */
7120 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
7121 level->shifted_relocation = TRUE;
7123 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
7124 if (level->game_version < VERSION_IDENT(3,2,6,0))
7125 level->em_explodes_by_fire = TRUE;
7128 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
7132 /* map custom element change events that have changed in newer versions
7133 (these following values were accidentally changed in version 3.0.1)
7134 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
7135 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7137 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7139 int element = EL_CUSTOM_START + i;
7141 /* order of checking and copying events to be mapped is important */
7142 /* (do not change the start and end value -- they are constant) */
7143 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7145 if (HAS_CHANGE_EVENT(element, j - 2))
7147 SET_CHANGE_EVENT(element, j - 2, FALSE);
7148 SET_CHANGE_EVENT(element, j, TRUE);
7152 /* order of checking and copying events to be mapped is important */
7153 /* (do not change the start and end value -- they are constant) */
7154 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7156 if (HAS_CHANGE_EVENT(element, j - 1))
7158 SET_CHANGE_EVENT(element, j - 1, FALSE);
7159 SET_CHANGE_EVENT(element, j, TRUE);
7165 /* initialize "can_change" field for old levels with only one change page */
7166 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7168 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7170 int element = EL_CUSTOM_START + i;
7172 if (CAN_CHANGE(element))
7173 element_info[element].change->can_change = TRUE;
7177 /* correct custom element values (for old levels without these options) */
7178 if (level->game_version < VERSION_IDENT(3,1,1,0))
7180 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7182 int element = EL_CUSTOM_START + i;
7183 struct ElementInfo *ei = &element_info[element];
7185 if (ei->access_direction == MV_NO_DIRECTION)
7186 ei->access_direction = MV_ALL_DIRECTIONS;
7190 /* correct custom element values (fix invalid values for all versions) */
7193 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7195 int element = EL_CUSTOM_START + i;
7196 struct ElementInfo *ei = &element_info[element];
7198 for (j = 0; j < ei->num_change_pages; j++)
7200 struct ElementChangeInfo *change = &ei->change_page[j];
7202 if (change->trigger_player == CH_PLAYER_NONE)
7203 change->trigger_player = CH_PLAYER_ANY;
7205 if (change->trigger_side == CH_SIDE_NONE)
7206 change->trigger_side = CH_SIDE_ANY;
7211 /* initialize "can_explode" field for old levels which did not store this */
7212 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
7213 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7215 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7217 int element = EL_CUSTOM_START + i;
7219 if (EXPLODES_1X1_OLD(element))
7220 element_info[element].explosion_type = EXPLODES_1X1;
7222 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7223 EXPLODES_SMASHED(element) ||
7224 EXPLODES_IMPACT(element)));
7228 /* correct previously hard-coded move delay values for maze runner style */
7229 if (level->game_version < VERSION_IDENT(3,1,1,0))
7231 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7233 int element = EL_CUSTOM_START + i;
7235 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7237 /* previously hard-coded and therefore ignored */
7238 element_info[element].move_delay_fixed = 9;
7239 element_info[element].move_delay_random = 0;
7244 /* map elements that have changed in newer versions */
7245 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7246 level->game_version);
7247 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7248 for (x = 0; x < 3; x++)
7249 for (y = 0; y < 3; y++)
7250 level->yamyam_content[i].e[x][y] =
7251 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7252 level->game_version);
7254 /* initialize element properties for level editor etc. */
7255 InitElementPropertiesEngine(level->game_version);
7256 InitElementPropertiesAfterLoading(level->game_version);
7257 InitElementPropertiesGfxElement();
7260 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
7264 /* map elements that have changed in newer versions */
7265 for (y = 0; y < level->fieldy; y++)
7266 for (x = 0; x < level->fieldx; x++)
7267 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7268 level->game_version);
7270 /* copy elements to runtime playfield array */
7271 for (x = 0; x < MAX_LEV_FIELDX; x++)
7272 for (y = 0; y < MAX_LEV_FIELDY; y++)
7273 Feld[x][y] = level->field[x][y];
7275 /* initialize level size variables for faster access */
7276 lev_fieldx = level->fieldx;
7277 lev_fieldy = level->fieldy;
7279 /* determine border element for this level */
7280 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7281 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
7286 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
7288 struct LevelFileInfo *level_file_info = &level->file_info;
7290 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7291 CopyNativeLevel_RND_to_Native(level);
7294 void LoadLevelTemplate(int nr)
7298 setLevelFileInfo(&level_template.file_info, nr);
7299 filename = level_template.file_info.filename;
7301 LoadLevelFromFileInfo(&level_template, &level_template.file_info);
7303 LoadLevel_InitVersion(&level_template, filename);
7304 LoadLevel_InitElements(&level_template, filename);
7306 ActivateLevelTemplate();
7309 void LoadLevel(int nr)
7313 setLevelFileInfo(&level.file_info, nr);
7314 filename = level.file_info.filename;
7316 LoadLevelFromFileInfo(&level, &level.file_info);
7318 if (level.use_custom_template)
7319 LoadLevelTemplate(-1);
7321 LoadLevel_InitVersion(&level, filename);
7322 LoadLevel_InitElements(&level, filename);
7323 LoadLevel_InitPlayfield(&level, filename);
7325 LoadLevel_InitNativeEngines(&level, filename);
7328 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7332 chunk_size += putFileVersion(file, level->file_version);
7333 chunk_size += putFileVersion(file, level->game_version);
7338 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7342 chunk_size += putFile16BitBE(file, level->creation_date.year);
7343 chunk_size += putFile8Bit(file, level->creation_date.month);
7344 chunk_size += putFile8Bit(file, level->creation_date.day);
7350 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7354 putFile8Bit(file, level->fieldx);
7355 putFile8Bit(file, level->fieldy);
7357 putFile16BitBE(file, level->time);
7358 putFile16BitBE(file, level->gems_needed);
7360 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7361 putFile8Bit(file, level->name[i]);
7363 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7364 putFile8Bit(file, level->score[i]);
7366 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7367 for (y = 0; y < 3; y++)
7368 for (x = 0; x < 3; x++)
7369 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7370 level->yamyam_content[i].e[x][y]));
7371 putFile8Bit(file, level->amoeba_speed);
7372 putFile8Bit(file, level->time_magic_wall);
7373 putFile8Bit(file, level->time_wheel);
7374 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7375 level->amoeba_content));
7376 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7377 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7378 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7379 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7381 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7383 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7384 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7385 putFile32BitBE(file, level->can_move_into_acid_bits);
7386 putFile8Bit(file, level->dont_collide_with_bits);
7388 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7389 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7391 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7392 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7393 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7395 putFile8Bit(file, level->game_engine_type);
7397 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7401 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7406 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7407 chunk_size += putFile8Bit(file, level->name[i]);
7412 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7417 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7418 chunk_size += putFile8Bit(file, level->author[i]);
7424 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7429 for (y = 0; y < level->fieldy; y++)
7430 for (x = 0; x < level->fieldx; x++)
7431 if (level->encoding_16bit_field)
7432 chunk_size += putFile16BitBE(file, level->field[x][y]);
7434 chunk_size += putFile8Bit(file, level->field[x][y]);
7440 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7445 for (y = 0; y < level->fieldy; y++)
7446 for (x = 0; x < level->fieldx; x++)
7447 chunk_size += putFile16BitBE(file, level->field[x][y]);
7453 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7457 putFile8Bit(file, EL_YAMYAM);
7458 putFile8Bit(file, level->num_yamyam_contents);
7459 putFile8Bit(file, 0);
7460 putFile8Bit(file, 0);
7462 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7463 for (y = 0; y < 3; y++)
7464 for (x = 0; x < 3; x++)
7465 if (level->encoding_16bit_field)
7466 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7468 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7473 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7476 int num_contents, content_xsize, content_ysize;
7477 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7479 if (element == EL_YAMYAM)
7481 num_contents = level->num_yamyam_contents;
7485 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7486 for (y = 0; y < 3; y++)
7487 for (x = 0; x < 3; x++)
7488 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7490 else if (element == EL_BD_AMOEBA)
7496 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7497 for (y = 0; y < 3; y++)
7498 for (x = 0; x < 3; x++)
7499 content_array[i][x][y] = EL_EMPTY;
7500 content_array[0][0][0] = level->amoeba_content;
7504 /* chunk header already written -- write empty chunk data */
7505 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7507 Error(ERR_WARN, "cannot save content for element '%d'", element);
7511 putFile16BitBE(file, element);
7512 putFile8Bit(file, num_contents);
7513 putFile8Bit(file, content_xsize);
7514 putFile8Bit(file, content_ysize);
7516 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7518 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7519 for (y = 0; y < 3; y++)
7520 for (x = 0; x < 3; x++)
7521 putFile16BitBE(file, content_array[i][x][y]);
7526 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7528 int envelope_nr = element - EL_ENVELOPE_1;
7529 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7533 chunk_size += putFile16BitBE(file, element);
7534 chunk_size += putFile16BitBE(file, envelope_len);
7535 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7536 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7538 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7539 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7541 for (i = 0; i < envelope_len; i++)
7542 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7549 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7550 int num_changed_custom_elements)
7554 putFile16BitBE(file, num_changed_custom_elements);
7556 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7558 int element = EL_CUSTOM_START + i;
7560 struct ElementInfo *ei = &element_info[element];
7562 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7564 if (check < num_changed_custom_elements)
7566 putFile16BitBE(file, element);
7567 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7574 if (check != num_changed_custom_elements) /* should not happen */
7575 Error(ERR_WARN, "inconsistent number of custom element properties");
7580 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7581 int num_changed_custom_elements)
7585 putFile16BitBE(file, num_changed_custom_elements);
7587 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7589 int element = EL_CUSTOM_START + i;
7591 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7593 if (check < num_changed_custom_elements)
7595 putFile16BitBE(file, element);
7596 putFile16BitBE(file, element_info[element].change->target_element);
7603 if (check != num_changed_custom_elements) /* should not happen */
7604 Error(ERR_WARN, "inconsistent number of custom target elements");
7609 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7610 int num_changed_custom_elements)
7612 int i, j, x, y, check = 0;
7614 putFile16BitBE(file, num_changed_custom_elements);
7616 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7618 int element = EL_CUSTOM_START + i;
7619 struct ElementInfo *ei = &element_info[element];
7621 if (ei->modified_settings)
7623 if (check < num_changed_custom_elements)
7625 putFile16BitBE(file, element);
7627 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7628 putFile8Bit(file, ei->description[j]);
7630 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7632 /* some free bytes for future properties and padding */
7633 WriteUnusedBytesToFile(file, 7);
7635 putFile8Bit(file, ei->use_gfx_element);
7636 putFile16BitBE(file, ei->gfx_element_initial);
7638 putFile8Bit(file, ei->collect_score_initial);
7639 putFile8Bit(file, ei->collect_count_initial);
7641 putFile16BitBE(file, ei->push_delay_fixed);
7642 putFile16BitBE(file, ei->push_delay_random);
7643 putFile16BitBE(file, ei->move_delay_fixed);
7644 putFile16BitBE(file, ei->move_delay_random);
7646 putFile16BitBE(file, ei->move_pattern);
7647 putFile8Bit(file, ei->move_direction_initial);
7648 putFile8Bit(file, ei->move_stepsize);
7650 for (y = 0; y < 3; y++)
7651 for (x = 0; x < 3; x++)
7652 putFile16BitBE(file, ei->content.e[x][y]);
7654 putFile32BitBE(file, ei->change->events);
7656 putFile16BitBE(file, ei->change->target_element);
7658 putFile16BitBE(file, ei->change->delay_fixed);
7659 putFile16BitBE(file, ei->change->delay_random);
7660 putFile16BitBE(file, ei->change->delay_frames);
7662 putFile16BitBE(file, ei->change->initial_trigger_element);
7664 putFile8Bit(file, ei->change->explode);
7665 putFile8Bit(file, ei->change->use_target_content);
7666 putFile8Bit(file, ei->change->only_if_complete);
7667 putFile8Bit(file, ei->change->use_random_replace);
7669 putFile8Bit(file, ei->change->random_percentage);
7670 putFile8Bit(file, ei->change->replace_when);
7672 for (y = 0; y < 3; y++)
7673 for (x = 0; x < 3; x++)
7674 putFile16BitBE(file, ei->change->content.e[x][y]);
7676 putFile8Bit(file, ei->slippery_type);
7678 /* some free bytes for future properties and padding */
7679 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7686 if (check != num_changed_custom_elements) /* should not happen */
7687 Error(ERR_WARN, "inconsistent number of custom element properties");
7692 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7694 struct ElementInfo *ei = &element_info[element];
7697 /* ---------- custom element base property values (96 bytes) ------------- */
7699 putFile16BitBE(file, element);
7701 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7702 putFile8Bit(file, ei->description[i]);
7704 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7706 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
7708 putFile8Bit(file, ei->num_change_pages);
7710 putFile16BitBE(file, ei->ce_value_fixed_initial);
7711 putFile16BitBE(file, ei->ce_value_random_initial);
7712 putFile8Bit(file, ei->use_last_ce_value);
7714 putFile8Bit(file, ei->use_gfx_element);
7715 putFile16BitBE(file, ei->gfx_element_initial);
7717 putFile8Bit(file, ei->collect_score_initial);
7718 putFile8Bit(file, ei->collect_count_initial);
7720 putFile8Bit(file, ei->drop_delay_fixed);
7721 putFile8Bit(file, ei->push_delay_fixed);
7722 putFile8Bit(file, ei->drop_delay_random);
7723 putFile8Bit(file, ei->push_delay_random);
7724 putFile16BitBE(file, ei->move_delay_fixed);
7725 putFile16BitBE(file, ei->move_delay_random);
7727 /* bits 0 - 15 of "move_pattern" ... */
7728 putFile16BitBE(file, ei->move_pattern & 0xffff);
7729 putFile8Bit(file, ei->move_direction_initial);
7730 putFile8Bit(file, ei->move_stepsize);
7732 putFile8Bit(file, ei->slippery_type);
7734 for (y = 0; y < 3; y++)
7735 for (x = 0; x < 3; x++)
7736 putFile16BitBE(file, ei->content.e[x][y]);
7738 putFile16BitBE(file, ei->move_enter_element);
7739 putFile16BitBE(file, ei->move_leave_element);
7740 putFile8Bit(file, ei->move_leave_type);
7742 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
7743 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7745 putFile8Bit(file, ei->access_direction);
7747 putFile8Bit(file, ei->explosion_delay);
7748 putFile8Bit(file, ei->ignition_delay);
7749 putFile8Bit(file, ei->explosion_type);
7751 /* some free bytes for future custom property values and padding */
7752 WriteUnusedBytesToFile(file, 1);
7754 /* ---------- change page property values (48 bytes) --------------------- */
7756 for (i = 0; i < ei->num_change_pages; i++)
7758 struct ElementChangeInfo *change = &ei->change_page[i];
7759 unsigned int event_bits;
7761 /* bits 0 - 31 of "has_event[]" ... */
7763 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7764 if (change->has_event[j])
7765 event_bits |= (1 << j);
7766 putFile32BitBE(file, event_bits);
7768 putFile16BitBE(file, change->target_element);
7770 putFile16BitBE(file, change->delay_fixed);
7771 putFile16BitBE(file, change->delay_random);
7772 putFile16BitBE(file, change->delay_frames);
7774 putFile16BitBE(file, change->initial_trigger_element);
7776 putFile8Bit(file, change->explode);
7777 putFile8Bit(file, change->use_target_content);
7778 putFile8Bit(file, change->only_if_complete);
7779 putFile8Bit(file, change->use_random_replace);
7781 putFile8Bit(file, change->random_percentage);
7782 putFile8Bit(file, change->replace_when);
7784 for (y = 0; y < 3; y++)
7785 for (x = 0; x < 3; x++)
7786 putFile16BitBE(file, change->target_content.e[x][y]);
7788 putFile8Bit(file, change->can_change);
7790 putFile8Bit(file, change->trigger_side);
7792 putFile8Bit(file, change->trigger_player);
7793 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7794 log_2(change->trigger_page)));
7796 putFile8Bit(file, change->has_action);
7797 putFile8Bit(file, change->action_type);
7798 putFile8Bit(file, change->action_mode);
7799 putFile16BitBE(file, change->action_arg);
7801 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7803 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7804 if (change->has_event[j])
7805 event_bits |= (1 << (j - 32));
7806 putFile8Bit(file, event_bits);
7812 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7814 struct ElementInfo *ei = &element_info[element];
7815 struct ElementGroupInfo *group = ei->group;
7818 putFile16BitBE(file, element);
7820 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7821 putFile8Bit(file, ei->description[i]);
7823 putFile8Bit(file, group->num_elements);
7825 putFile8Bit(file, ei->use_gfx_element);
7826 putFile16BitBE(file, ei->gfx_element_initial);
7828 putFile8Bit(file, group->choice_mode);
7830 /* some free bytes for future values and padding */
7831 WriteUnusedBytesToFile(file, 3);
7833 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7834 putFile16BitBE(file, group->element[i]);
7838 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7839 boolean write_element)
7841 int save_type = entry->save_type;
7842 int data_type = entry->data_type;
7843 int conf_type = entry->conf_type;
7844 int byte_mask = conf_type & CONF_MASK_BYTES;
7845 int element = entry->element;
7846 int default_value = entry->default_value;
7848 boolean modified = FALSE;
7850 if (byte_mask != CONF_MASK_MULTI_BYTES)
7852 void *value_ptr = entry->value;
7853 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7856 /* check if any settings have been modified before saving them */
7857 if (value != default_value)
7860 /* do not save if explicitly told or if unmodified default settings */
7861 if ((save_type == SAVE_CONF_NEVER) ||
7862 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7866 num_bytes += putFile16BitBE(file, element);
7868 num_bytes += putFile8Bit(file, conf_type);
7869 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7870 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7871 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7874 else if (data_type == TYPE_STRING)
7876 char *default_string = entry->default_string;
7877 char *string = (char *)(entry->value);
7878 int string_length = strlen(string);
7881 /* check if any settings have been modified before saving them */
7882 if (!strEqual(string, default_string))
7885 /* do not save if explicitly told or if unmodified default settings */
7886 if ((save_type == SAVE_CONF_NEVER) ||
7887 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7891 num_bytes += putFile16BitBE(file, element);
7893 num_bytes += putFile8Bit(file, conf_type);
7894 num_bytes += putFile16BitBE(file, string_length);
7896 for (i = 0; i < string_length; i++)
7897 num_bytes += putFile8Bit(file, string[i]);
7899 else if (data_type == TYPE_ELEMENT_LIST)
7901 int *element_array = (int *)(entry->value);
7902 int num_elements = *(int *)(entry->num_entities);
7905 /* check if any settings have been modified before saving them */
7906 for (i = 0; i < num_elements; i++)
7907 if (element_array[i] != default_value)
7910 /* do not save if explicitly told or if unmodified default settings */
7911 if ((save_type == SAVE_CONF_NEVER) ||
7912 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7916 num_bytes += putFile16BitBE(file, element);
7918 num_bytes += putFile8Bit(file, conf_type);
7919 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7921 for (i = 0; i < num_elements; i++)
7922 num_bytes += putFile16BitBE(file, element_array[i]);
7924 else if (data_type == TYPE_CONTENT_LIST)
7926 struct Content *content = (struct Content *)(entry->value);
7927 int num_contents = *(int *)(entry->num_entities);
7930 /* check if any settings have been modified before saving them */
7931 for (i = 0; i < num_contents; i++)
7932 for (y = 0; y < 3; y++)
7933 for (x = 0; x < 3; x++)
7934 if (content[i].e[x][y] != default_value)
7937 /* do not save if explicitly told or if unmodified default settings */
7938 if ((save_type == SAVE_CONF_NEVER) ||
7939 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7943 num_bytes += putFile16BitBE(file, element);
7945 num_bytes += putFile8Bit(file, conf_type);
7946 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7948 for (i = 0; i < num_contents; i++)
7949 for (y = 0; y < 3; y++)
7950 for (x = 0; x < 3; x++)
7951 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7957 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7962 li = *level; /* copy level data into temporary buffer */
7964 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7965 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7970 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7975 li = *level; /* copy level data into temporary buffer */
7977 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7978 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7983 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7985 int envelope_nr = element - EL_ENVELOPE_1;
7989 chunk_size += putFile16BitBE(file, element);
7991 /* copy envelope data into temporary buffer */
7992 xx_envelope = level->envelope[envelope_nr];
7994 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7995 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8000 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8002 struct ElementInfo *ei = &element_info[element];
8006 chunk_size += putFile16BitBE(file, element);
8008 xx_ei = *ei; /* copy element data into temporary buffer */
8010 /* set default description string for this specific element */
8011 strcpy(xx_default_description, getDefaultElementDescription(ei));
8014 /* set (fixed) number of content areas (may be wrong by broken level file) */
8015 /* (this is now directly corrected for broken level files after loading) */
8016 xx_num_contents = 1;
8019 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8020 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8022 for (i = 0; i < ei->num_change_pages; i++)
8024 struct ElementChangeInfo *change = &ei->change_page[i];
8026 xx_current_change_page = i;
8028 xx_change = *change; /* copy change data into temporary buffer */
8031 setEventBitsFromEventFlags(change);
8033 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8034 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8041 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8043 struct ElementInfo *ei = &element_info[element];
8044 struct ElementGroupInfo *group = ei->group;
8048 chunk_size += putFile16BitBE(file, element);
8050 xx_ei = *ei; /* copy element data into temporary buffer */
8051 xx_group = *group; /* copy group data into temporary buffer */
8053 /* set default description string for this specific element */
8054 strcpy(xx_default_description, getDefaultElementDescription(ei));
8056 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8057 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8062 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
8068 if (!(file = fopen(filename, MODE_WRITE)))
8070 Error(ERR_WARN, "cannot save level file '%s'", filename);
8074 level->file_version = FILE_VERSION_ACTUAL;
8075 level->game_version = GAME_VERSION_ACTUAL;
8077 level->creation_date = getCurrentDate();
8079 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8080 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8082 chunk_size = SaveLevel_VERS(NULL, level);
8083 putFileChunkBE(file, "VERS", chunk_size);
8084 SaveLevel_VERS(file, level);
8086 chunk_size = SaveLevel_DATE(NULL, level);
8087 putFileChunkBE(file, "DATE", chunk_size);
8088 SaveLevel_DATE(file, level);
8090 chunk_size = SaveLevel_NAME(NULL, level);
8091 putFileChunkBE(file, "NAME", chunk_size);
8092 SaveLevel_NAME(file, level);
8094 chunk_size = SaveLevel_AUTH(NULL, level);
8095 putFileChunkBE(file, "AUTH", chunk_size);
8096 SaveLevel_AUTH(file, level);
8098 chunk_size = SaveLevel_INFO(NULL, level);
8099 putFileChunkBE(file, "INFO", chunk_size);
8100 SaveLevel_INFO(file, level);
8102 chunk_size = SaveLevel_BODY(NULL, level);
8103 putFileChunkBE(file, "BODY", chunk_size);
8104 SaveLevel_BODY(file, level);
8106 chunk_size = SaveLevel_ELEM(NULL, level);
8107 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
8109 putFileChunkBE(file, "ELEM", chunk_size);
8110 SaveLevel_ELEM(file, level);
8113 for (i = 0; i < NUM_ENVELOPES; i++)
8115 int element = EL_ENVELOPE_1 + i;
8117 chunk_size = SaveLevel_NOTE(NULL, level, element);
8118 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
8120 putFileChunkBE(file, "NOTE", chunk_size);
8121 SaveLevel_NOTE(file, level, element);
8125 /* if not using template level, check for non-default custom/group elements */
8126 if (!level->use_custom_template)
8128 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8130 int element = EL_CUSTOM_START + i;
8132 chunk_size = SaveLevel_CUSX(NULL, level, element);
8133 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
8135 putFileChunkBE(file, "CUSX", chunk_size);
8136 SaveLevel_CUSX(file, level, element);
8140 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8142 int element = EL_GROUP_START + i;
8144 chunk_size = SaveLevel_GRPX(NULL, level, element);
8145 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
8147 putFileChunkBE(file, "GRPX", chunk_size);
8148 SaveLevel_GRPX(file, level, element);
8155 SetFilePermissions(filename, PERMS_PRIVATE);
8158 void SaveLevel(int nr)
8160 char *filename = getDefaultLevelFilename(nr);
8162 SaveLevelFromFilename(&level, filename);
8165 void SaveLevelTemplate()
8167 char *filename = getDefaultLevelFilename(-1);
8169 SaveLevelFromFilename(&level, filename);
8172 boolean SaveLevelChecked(int nr)
8174 char *filename = getDefaultLevelFilename(nr);
8175 boolean new_level = !fileExists(filename);
8176 boolean level_saved = FALSE;
8178 if (new_level || Request("Save this level and kill the old ?", REQ_ASK))
8183 Request("Level saved !", REQ_CONFIRM);
8191 void DumpLevel(struct LevelInfo *level)
8193 if (level->no_valid_file)
8195 Error(ERR_WARN, "cannot dump -- no valid level file found");
8200 printf_line("-", 79);
8201 printf("Level xxx (file version %08d, game version %08d)\n",
8202 level->file_version, level->game_version);
8203 printf_line("-", 79);
8205 printf("Level author: '%s'\n", level->author);
8206 printf("Level title: '%s'\n", level->name);
8208 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8210 printf("Level time: %d seconds\n", level->time);
8211 printf("Gems needed: %d\n", level->gems_needed);
8213 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
8214 printf("Time for wheel: %d seconds\n", level->time_wheel);
8215 printf("Time for light: %d seconds\n", level->time_light);
8216 printf("Time for timegate: %d seconds\n", level->time_timegate);
8218 printf("Amoeba speed: %d\n", level->amoeba_speed);
8221 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8222 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8223 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8224 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8225 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8227 printf_line("-", 79);
8231 /* ========================================================================= */
8232 /* tape file functions */
8233 /* ========================================================================= */
8235 static void setTapeInfoToDefaults()
8239 /* always start with reliable default values (empty tape) */
8242 /* default values (also for pre-1.2 tapes) with only the first player */
8243 tape.player_participates[0] = TRUE;
8244 for (i = 1; i < MAX_PLAYERS; i++)
8245 tape.player_participates[i] = FALSE;
8247 /* at least one (default: the first) player participates in every tape */
8248 tape.num_participating_players = 1;
8250 tape.level_nr = level_nr;
8252 tape.changed = FALSE;
8254 tape.recording = FALSE;
8255 tape.playing = FALSE;
8256 tape.pausing = FALSE;
8258 tape.no_valid_file = FALSE;
8261 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
8263 tape->file_version = getFileVersion(file);
8264 tape->game_version = getFileVersion(file);
8269 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
8273 tape->random_seed = getFile32BitBE(file);
8274 tape->date = getFile32BitBE(file);
8275 tape->length = getFile32BitBE(file);
8277 /* read header fields that are new since version 1.2 */
8278 if (tape->file_version >= FILE_VERSION_1_2)
8280 byte store_participating_players = getFile8Bit(file);
8283 /* since version 1.2, tapes store which players participate in the tape */
8284 tape->num_participating_players = 0;
8285 for (i = 0; i < MAX_PLAYERS; i++)
8287 tape->player_participates[i] = FALSE;
8289 if (store_participating_players & (1 << i))
8291 tape->player_participates[i] = TRUE;
8292 tape->num_participating_players++;
8296 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
8298 engine_version = getFileVersion(file);
8299 if (engine_version > 0)
8300 tape->engine_version = engine_version;
8302 tape->engine_version = tape->game_version;
8308 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
8310 int level_identifier_size;
8313 level_identifier_size = getFile16BitBE(file);
8315 tape->level_identifier =
8316 checked_realloc(tape->level_identifier, level_identifier_size);
8318 for (i = 0; i < level_identifier_size; i++)
8319 tape->level_identifier[i] = getFile8Bit(file);
8321 tape->level_nr = getFile16BitBE(file);
8323 chunk_size = 2 + level_identifier_size + 2;
8328 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
8331 int chunk_size_expected =
8332 (tape->num_participating_players + 1) * tape->length;
8334 if (chunk_size_expected != chunk_size)
8336 ReadUnusedBytesFromFile(file, chunk_size);
8337 return chunk_size_expected;
8340 for (i = 0; i < tape->length; i++)
8342 if (i >= MAX_TAPE_LEN)
8345 for (j = 0; j < MAX_PLAYERS; j++)
8347 tape->pos[i].action[j] = MV_NONE;
8349 if (tape->player_participates[j])
8350 tape->pos[i].action[j] = getFile8Bit(file);
8353 tape->pos[i].delay = getFile8Bit(file);
8355 if (tape->file_version == FILE_VERSION_1_0)
8357 /* eliminate possible diagonal moves in old tapes */
8358 /* this is only for backward compatibility */
8360 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8361 byte action = tape->pos[i].action[0];
8362 int k, num_moves = 0;
8364 for (k = 0; k<4; k++)
8366 if (action & joy_dir[k])
8368 tape->pos[i + num_moves].action[0] = joy_dir[k];
8370 tape->pos[i + num_moves].delay = 0;
8379 tape->length += num_moves;
8382 else if (tape->file_version < FILE_VERSION_2_0)
8384 /* convert pre-2.0 tapes to new tape format */
8386 if (tape->pos[i].delay > 1)
8389 tape->pos[i + 1] = tape->pos[i];
8390 tape->pos[i + 1].delay = 1;
8393 for (j = 0; j < MAX_PLAYERS; j++)
8394 tape->pos[i].action[j] = MV_NONE;
8395 tape->pos[i].delay--;
8406 if (i != tape->length)
8407 chunk_size = (tape->num_participating_players + 1) * i;
8412 void LoadTapeFromFilename(char *filename)
8414 char cookie[MAX_LINE_LEN];
8415 char chunk_name[CHUNK_ID_LEN + 1];
8419 /* always start with reliable default values */
8420 setTapeInfoToDefaults();
8422 if (!(file = fopen(filename, MODE_READ)))
8424 tape.no_valid_file = TRUE;
8429 getFileChunkBE(file, chunk_name, NULL);
8430 if (strEqual(chunk_name, "RND1"))
8432 getFile32BitBE(file); /* not used */
8434 getFileChunkBE(file, chunk_name, NULL);
8435 if (!strEqual(chunk_name, "TAPE"))
8437 tape.no_valid_file = TRUE;
8439 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
8444 else /* check for pre-2.0 file format with cookie string */
8446 strcpy(cookie, chunk_name);
8447 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
8448 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8449 cookie[strlen(cookie) - 1] = '\0';
8451 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8453 tape.no_valid_file = TRUE;
8455 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
8460 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8462 tape.no_valid_file = TRUE;
8464 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
8469 /* pre-2.0 tape files have no game version, so use file version here */
8470 tape.game_version = tape.file_version;
8473 if (tape.file_version < FILE_VERSION_1_2)
8475 /* tape files from versions before 1.2.0 without chunk structure */
8476 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8477 LoadTape_BODY(file, 2 * tape.length, &tape);
8485 int (*loader)(FILE *, int, struct TapeInfo *);
8489 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8490 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8491 { "INFO", -1, LoadTape_INFO },
8492 { "BODY", -1, LoadTape_BODY },
8496 while (getFileChunkBE(file, chunk_name, &chunk_size))
8500 while (chunk_info[i].name != NULL &&
8501 !strEqual(chunk_name, chunk_info[i].name))
8504 if (chunk_info[i].name == NULL)
8506 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
8507 chunk_name, filename);
8508 ReadUnusedBytesFromFile(file, chunk_size);
8510 else if (chunk_info[i].size != -1 &&
8511 chunk_info[i].size != chunk_size)
8513 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
8514 chunk_size, chunk_name, filename);
8515 ReadUnusedBytesFromFile(file, chunk_size);
8519 /* call function to load this tape chunk */
8520 int chunk_size_expected =
8521 (chunk_info[i].loader)(file, chunk_size, &tape);
8523 /* the size of some chunks cannot be checked before reading other
8524 chunks first (like "HEAD" and "BODY") that contain some header
8525 information, so check them here */
8526 if (chunk_size_expected != chunk_size)
8528 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
8529 chunk_size, chunk_name, filename);
8537 tape.length_seconds = GetTapeLength();
8540 printf("::: tape file version: %d\n", tape.file_version);
8541 printf("::: tape game version: %d\n", tape.game_version);
8542 printf("::: tape engine version: %d\n", tape.engine_version);
8546 void LoadTape(int nr)
8548 char *filename = getTapeFilename(nr);
8550 LoadTapeFromFilename(filename);
8553 void LoadSolutionTape(int nr)
8555 char *filename = getSolutionTapeFilename(nr);
8557 LoadTapeFromFilename(filename);
8560 if (TAPE_IS_EMPTY(tape) &&
8561 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8562 level.native_sp_level->demo.is_available)
8563 CopyNativeTape_SP_to_RND(&level);
8567 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8569 putFileVersion(file, tape->file_version);
8570 putFileVersion(file, tape->game_version);
8573 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8576 byte store_participating_players = 0;
8578 /* set bits for participating players for compact storage */
8579 for (i = 0; i < MAX_PLAYERS; i++)
8580 if (tape->player_participates[i])
8581 store_participating_players |= (1 << i);
8583 putFile32BitBE(file, tape->random_seed);
8584 putFile32BitBE(file, tape->date);
8585 putFile32BitBE(file, tape->length);
8587 putFile8Bit(file, store_participating_players);
8589 /* unused bytes not at the end here for 4-byte alignment of engine_version */
8590 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8592 putFileVersion(file, tape->engine_version);
8595 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8597 int level_identifier_size = strlen(tape->level_identifier) + 1;
8600 putFile16BitBE(file, level_identifier_size);
8602 for (i = 0; i < level_identifier_size; i++)
8603 putFile8Bit(file, tape->level_identifier[i]);
8605 putFile16BitBE(file, tape->level_nr);
8608 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8612 for (i = 0; i < tape->length; i++)
8614 for (j = 0; j < MAX_PLAYERS; j++)
8615 if (tape->player_participates[j])
8616 putFile8Bit(file, tape->pos[i].action[j]);
8618 putFile8Bit(file, tape->pos[i].delay);
8622 void SaveTape(int nr)
8624 char *filename = getTapeFilename(nr);
8627 boolean new_tape = TRUE;
8629 int num_participating_players = 0;
8630 int info_chunk_size;
8631 int body_chunk_size;
8634 InitTapeDirectory(leveldir_current->subdir);
8637 /* if a tape still exists, ask to overwrite it */
8638 if (fileExists(filename))
8641 if (!Request("Replace old tape ?", REQ_ASK))
8646 if (!(file = fopen(filename, MODE_WRITE)))
8648 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8652 tape.file_version = FILE_VERSION_ACTUAL;
8653 tape.game_version = GAME_VERSION_ACTUAL;
8655 /* count number of participating players */
8656 for (i = 0; i < MAX_PLAYERS; i++)
8657 if (tape.player_participates[i])
8658 num_participating_players++;
8660 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8661 body_chunk_size = (num_participating_players + 1) * tape.length;
8663 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8664 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8666 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8667 SaveTape_VERS(file, &tape);
8669 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8670 SaveTape_HEAD(file, &tape);
8672 putFileChunkBE(file, "INFO", info_chunk_size);
8673 SaveTape_INFO(file, &tape);
8675 putFileChunkBE(file, "BODY", body_chunk_size);
8676 SaveTape_BODY(file, &tape);
8680 SetFilePermissions(filename, PERMS_PRIVATE);
8682 tape.changed = FALSE;
8686 Request("Tape saved !", REQ_CONFIRM);
8690 boolean SaveTapeChecked(int nr)
8692 char *filename = getTapeFilename(nr);
8693 boolean new_tape = !fileExists(filename);
8694 boolean tape_saved = FALSE;
8696 if (new_tape || Request("Replace old tape ?", REQ_ASK))
8701 Request("Tape saved !", REQ_CONFIRM);
8709 void DumpTape(struct TapeInfo *tape)
8711 int tape_frame_counter;
8714 if (tape->no_valid_file)
8716 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8721 printf_line("-", 79);
8722 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
8723 tape->level_nr, tape->file_version, tape->game_version);
8724 printf(" (effective engine version %08d)\n",
8725 tape->engine_version);
8726 printf("Level series identifier: '%s'\n", tape->level_identifier);
8727 printf_line("-", 79);
8729 tape_frame_counter = 0;
8731 for (i = 0; i < tape->length; i++)
8733 if (i >= MAX_TAPE_LEN)
8736 printf("%04d: ", i);
8738 for (j = 0; j < MAX_PLAYERS; j++)
8740 if (tape->player_participates[j])
8742 int action = tape->pos[i].action[j];
8744 printf("%d:%02x ", j, action);
8745 printf("[%c%c%c%c|%c%c] - ",
8746 (action & JOY_LEFT ? '<' : ' '),
8747 (action & JOY_RIGHT ? '>' : ' '),
8748 (action & JOY_UP ? '^' : ' '),
8749 (action & JOY_DOWN ? 'v' : ' '),
8750 (action & JOY_BUTTON_1 ? '1' : ' '),
8751 (action & JOY_BUTTON_2 ? '2' : ' '));
8755 printf("(%03d) ", tape->pos[i].delay);
8756 printf("[%05d]\n", tape_frame_counter);
8758 tape_frame_counter += tape->pos[i].delay;
8761 printf_line("-", 79);
8765 /* ========================================================================= */
8766 /* score file functions */
8767 /* ========================================================================= */
8769 void LoadScore(int nr)
8772 char *filename = getScoreFilename(nr);
8773 char cookie[MAX_LINE_LEN];
8774 char line[MAX_LINE_LEN];
8778 /* always start with reliable default values */
8779 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8781 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8782 highscore[i].Score = 0;
8785 if (!(file = fopen(filename, MODE_READ)))
8788 /* check file identifier */
8789 fgets(cookie, MAX_LINE_LEN, file);
8790 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8791 cookie[strlen(cookie) - 1] = '\0';
8793 if (!checkCookieString(cookie, SCORE_COOKIE))
8795 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8800 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8802 fscanf(file, "%d", &highscore[i].Score);
8803 fgets(line, MAX_LINE_LEN, file);
8805 if (line[strlen(line) - 1] == '\n')
8806 line[strlen(line) - 1] = '\0';
8808 for (line_ptr = line; *line_ptr; line_ptr++)
8810 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8812 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8813 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8822 void SaveScore(int nr)
8825 char *filename = getScoreFilename(nr);
8828 InitScoreDirectory(leveldir_current->subdir);
8830 if (!(file = fopen(filename, MODE_WRITE)))
8832 Error(ERR_WARN, "cannot save score for level %d", nr);
8836 fprintf(file, "%s\n\n", SCORE_COOKIE);
8838 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8839 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8843 SetFilePermissions(filename, PERMS_PUBLIC);
8847 /* ========================================================================= */
8848 /* setup file functions */
8849 /* ========================================================================= */
8851 #define TOKEN_STR_PLAYER_PREFIX "player_"
8854 #define SETUP_TOKEN_PLAYER_NAME 0
8855 #define SETUP_TOKEN_SOUND 1
8856 #define SETUP_TOKEN_SOUND_LOOPS 2
8857 #define SETUP_TOKEN_SOUND_MUSIC 3
8858 #define SETUP_TOKEN_SOUND_SIMPLE 4
8859 #define SETUP_TOKEN_TOONS 5
8860 #define SETUP_TOKEN_SCROLL_DELAY 6
8861 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
8862 #define SETUP_TOKEN_SOFT_SCROLLING 8
8863 #define SETUP_TOKEN_FADE_SCREENS 9
8864 #define SETUP_TOKEN_AUTORECORD 10
8865 #define SETUP_TOKEN_SHOW_TITLESCREEN 11
8866 #define SETUP_TOKEN_QUICK_DOORS 12
8867 #define SETUP_TOKEN_TEAM_MODE 13
8868 #define SETUP_TOKEN_HANDICAP 14
8869 #define SETUP_TOKEN_SKIP_LEVELS 15
8870 #define SETUP_TOKEN_TIME_LIMIT 16
8871 #define SETUP_TOKEN_FULLSCREEN 17
8872 #define SETUP_TOKEN_FULLSCREEN_MODE 18
8873 #define SETUP_TOKEN_ASK_ON_ESCAPE 19
8874 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 20
8875 #define SETUP_TOKEN_QUICK_SWITCH 21
8876 #define SETUP_TOKEN_INPUT_ON_FOCUS 22
8877 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 23
8878 #define SETUP_TOKEN_GAME_FRAME_DELAY 24
8879 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 25
8880 #define SETUP_TOKEN_GRAPHICS_SET 26
8881 #define SETUP_TOKEN_SOUNDS_SET 27
8882 #define SETUP_TOKEN_MUSIC_SET 28
8883 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 29
8884 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 30
8885 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 31
8887 #define NUM_GLOBAL_SETUP_TOKENS 32
8890 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
8891 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
8892 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
8893 #define SETUP_TOKEN_EDITOR_EL_MORE 3
8894 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 4
8895 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 5
8896 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 6
8897 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 7
8898 #define SETUP_TOKEN_EDITOR_EL_CHARS 8
8899 #define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS 9
8900 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 10
8901 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 11
8902 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 12
8903 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 13
8904 #define SETUP_TOKEN_EDITOR_EL_BY_GAME 14
8905 #define SETUP_TOKEN_EDITOR_EL_BY_TYPE 15
8906 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 16
8908 #define NUM_EDITOR_SETUP_TOKENS 17
8910 /* editor cascade setup */
8911 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
8912 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
8913 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
8914 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
8915 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
8916 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
8917 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
8918 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
8919 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
8920 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
8921 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
8922 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
8923 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
8924 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
8925 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
8927 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
8929 /* shortcut setup */
8930 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
8931 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
8932 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
8933 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
8934 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
8935 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
8936 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
8937 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
8939 #define NUM_SHORTCUT_SETUP_TOKENS 8
8942 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
8943 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
8944 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
8945 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
8946 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
8947 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
8948 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
8949 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
8950 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
8951 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
8952 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
8953 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
8954 #define SETUP_TOKEN_PLAYER_KEY_UP 12
8955 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
8956 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8957 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8959 #define NUM_PLAYER_SETUP_TOKENS 16
8962 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8963 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8964 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8966 #define NUM_SYSTEM_SETUP_TOKENS 3
8969 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8971 #define NUM_OPTIONS_SETUP_TOKENS 1
8974 static struct SetupInfo si;
8975 static struct SetupEditorInfo sei;
8976 static struct SetupEditorCascadeInfo seci;
8977 static struct SetupShortcutInfo ssi;
8978 static struct SetupInputInfo sii;
8979 static struct SetupSystemInfo syi;
8980 static struct OptionInfo soi;
8982 static struct TokenInfo global_setup_tokens[] =
8984 { TYPE_STRING, &si.player_name, "player_name" },
8985 { TYPE_SWITCH, &si.sound, "sound" },
8986 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8987 { TYPE_SWITCH, &si.sound_music, "background_music" },
8988 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8989 { TYPE_SWITCH, &si.toons, "toons" },
8990 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8991 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8992 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
8993 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8994 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8995 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8996 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8997 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8998 { TYPE_SWITCH, &si.handicap, "handicap" },
8999 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
9000 { TYPE_SWITCH, &si.time_limit, "time_limit" },
9001 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
9002 { TYPE_STRING, &si.fullscreen_mode, "fullscreen_mode" },
9003 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
9004 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
9005 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
9006 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
9007 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
9008 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
9009 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
9010 { TYPE_STRING, &si.graphics_set, "graphics_set" },
9011 { TYPE_STRING, &si.sounds_set, "sounds_set" },
9012 { TYPE_STRING, &si.music_set, "music_set" },
9013 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
9014 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
9015 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
9018 static boolean not_used = FALSE;
9019 static struct TokenInfo editor_setup_tokens[] =
9022 { TYPE_SWITCH, ¬_used, "editor.el_boulderdash" },
9023 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine" },
9024 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine_club" },
9025 { TYPE_SWITCH, ¬_used, "editor.el_more" },
9026 { TYPE_SWITCH, ¬_used, "editor.el_sokoban" },
9027 { TYPE_SWITCH, ¬_used, "editor.el_supaplex" },
9028 { TYPE_SWITCH, ¬_used, "editor.el_diamond_caves" },
9029 { TYPE_SWITCH, ¬_used, "editor.el_dx_boulderdash" },
9031 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
9032 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
9033 { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
9034 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
9035 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
9036 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
9037 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
9038 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
9040 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
9041 { TYPE_SWITCH, &sei.el_steel_chars, "editor.el_steel_chars" },
9042 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
9044 { TYPE_SWITCH, ¬_used, "editor.el_headlines" },
9046 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
9048 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
9049 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
9050 { TYPE_SWITCH, &sei.el_by_game, "editor.el_by_game" },
9051 { TYPE_SWITCH, &sei.el_by_type, "editor.el_by_type" },
9052 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
9055 static struct TokenInfo editor_cascade_setup_tokens[] =
9057 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
9058 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
9059 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
9060 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
9061 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
9062 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
9063 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
9064 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
9065 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
9066 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
9067 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
9068 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
9069 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
9070 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
9071 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
9074 static struct TokenInfo shortcut_setup_tokens[] =
9076 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
9077 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
9078 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
9079 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
9080 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
9081 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
9082 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
9083 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
9086 static struct TokenInfo player_setup_tokens[] =
9088 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
9089 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
9090 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
9091 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
9092 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
9093 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
9094 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
9095 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
9096 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
9097 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
9098 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
9099 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
9100 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
9101 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
9102 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
9103 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
9106 static struct TokenInfo system_setup_tokens[] =
9108 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
9109 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
9110 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
9113 static struct TokenInfo options_setup_tokens[] =
9115 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
9118 static char *get_corrected_login_name(char *login_name)
9120 /* needed because player name must be a fixed length string */
9121 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
9123 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
9124 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
9126 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
9127 if (strchr(login_name_new, ' '))
9128 *strchr(login_name_new, ' ') = '\0';
9130 return login_name_new;
9133 static void setSetupInfoToDefaults(struct SetupInfo *si)
9137 si->player_name = get_corrected_login_name(getLoginName());
9140 si->sound_loops = TRUE;
9141 si->sound_music = TRUE;
9142 si->sound_simple = TRUE;
9144 si->scroll_delay = TRUE;
9145 si->scroll_delay_value = STD_SCROLL_DELAY;
9146 si->soft_scrolling = TRUE;
9147 si->fade_screens = TRUE;
9148 si->autorecord = TRUE;
9149 si->show_titlescreen = TRUE;
9150 si->quick_doors = FALSE;
9151 si->team_mode = FALSE;
9152 si->handicap = TRUE;
9153 si->skip_levels = TRUE;
9154 si->time_limit = TRUE;
9155 si->fullscreen = FALSE;
9156 si->fullscreen_mode = getStringCopy(DEFAULT_FULLSCREEN_MODE);
9157 si->ask_on_escape = TRUE;
9158 si->ask_on_escape_editor = TRUE;
9159 si->quick_switch = FALSE;
9160 si->input_on_focus = FALSE;
9161 si->prefer_aga_graphics = TRUE;
9162 si->game_frame_delay = GAME_FRAME_DELAY;
9163 si->sp_show_border_elements = FALSE;
9165 si->graphics_set = getStringCopy(GFX_DEFAULT_SUBDIR);
9166 si->sounds_set = getStringCopy(SND_DEFAULT_SUBDIR);
9167 si->music_set = getStringCopy(MUS_DEFAULT_SUBDIR);
9168 si->override_level_graphics = FALSE;
9169 si->override_level_sounds = FALSE;
9170 si->override_level_music = FALSE;
9172 si->editor.el_boulderdash = TRUE;
9173 si->editor.el_emerald_mine = TRUE;
9174 si->editor.el_emerald_mine_club = TRUE;
9175 si->editor.el_more = TRUE;
9176 si->editor.el_sokoban = TRUE;
9177 si->editor.el_supaplex = TRUE;
9178 si->editor.el_diamond_caves = TRUE;
9179 si->editor.el_dx_boulderdash = TRUE;
9180 si->editor.el_chars = TRUE;
9181 si->editor.el_steel_chars = TRUE;
9182 si->editor.el_custom = TRUE;
9184 si->editor.el_headlines = TRUE;
9185 si->editor.el_user_defined = FALSE;
9186 si->editor.el_dynamic = TRUE;
9188 si->editor.show_element_token = FALSE;
9190 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
9191 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
9192 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
9194 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
9195 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
9196 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
9197 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
9198 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9200 for (i = 0; i < MAX_PLAYERS; i++)
9202 si->input[i].use_joystick = FALSE;
9203 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9204 si->input[i].joy.xleft = JOYSTICK_XLEFT;
9205 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9206 si->input[i].joy.xright = JOYSTICK_XRIGHT;
9207 si->input[i].joy.yupper = JOYSTICK_YUPPER;
9208 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9209 si->input[i].joy.ylower = JOYSTICK_YLOWER;
9210 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
9211 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
9212 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
9213 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9214 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
9215 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
9216 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
9217 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
9220 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9221 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9222 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9224 si->options.verbose = FALSE;
9226 #if defined(CREATE_SPECIAL_EDITION_RND_JUE)
9227 si->handicap = FALSE;
9228 si->fullscreen = TRUE;
9229 si->override_level_graphics = AUTO;
9230 si->override_level_sounds = AUTO;
9231 si->override_level_music = AUTO;
9235 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9237 si->editor_cascade.el_bd = TRUE;
9238 si->editor_cascade.el_em = TRUE;
9239 si->editor_cascade.el_emc = TRUE;
9240 si->editor_cascade.el_rnd = TRUE;
9241 si->editor_cascade.el_sb = TRUE;
9242 si->editor_cascade.el_sp = TRUE;
9243 si->editor_cascade.el_dc = TRUE;
9244 si->editor_cascade.el_dx = TRUE;
9246 si->editor_cascade.el_chars = FALSE;
9247 si->editor_cascade.el_steel_chars = FALSE;
9248 si->editor_cascade.el_ce = FALSE;
9249 si->editor_cascade.el_ge = FALSE;
9250 si->editor_cascade.el_ref = FALSE;
9251 si->editor_cascade.el_user = FALSE;
9252 si->editor_cascade.el_dynamic = FALSE;
9255 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9259 if (!setup_file_hash)
9264 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9265 setSetupInfo(global_setup_tokens, i,
9266 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
9271 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9272 setSetupInfo(editor_setup_tokens, i,
9273 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
9276 /* shortcut setup */
9277 ssi = setup.shortcut;
9278 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9279 setSetupInfo(shortcut_setup_tokens, i,
9280 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
9281 setup.shortcut = ssi;
9284 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9288 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9290 sii = setup.input[pnr];
9291 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9293 char full_token[100];
9295 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9296 setSetupInfo(player_setup_tokens, i,
9297 getHashEntry(setup_file_hash, full_token));
9299 setup.input[pnr] = sii;
9304 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9305 setSetupInfo(system_setup_tokens, i,
9306 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
9310 soi = setup.options;
9311 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9312 setSetupInfo(options_setup_tokens, i,
9313 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
9314 setup.options = soi;
9317 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9321 if (!setup_file_hash)
9324 /* editor cascade setup */
9325 seci = setup.editor_cascade;
9326 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9327 setSetupInfo(editor_cascade_setup_tokens, i,
9328 getHashEntry(setup_file_hash,
9329 editor_cascade_setup_tokens[i].text));
9330 setup.editor_cascade = seci;
9335 char *filename = getSetupFilename();
9336 SetupFileHash *setup_file_hash = NULL;
9338 /* always start with reliable default values */
9339 setSetupInfoToDefaults(&setup);
9341 setup_file_hash = loadSetupFileHash(filename);
9343 if (setup_file_hash)
9345 char *player_name_new;
9347 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
9348 decodeSetupFileHash(setup_file_hash);
9350 freeSetupFileHash(setup_file_hash);
9352 /* needed to work around problems with fixed length strings */
9353 player_name_new = get_corrected_login_name(setup.player_name);
9354 free(setup.player_name);
9355 setup.player_name = player_name_new;
9357 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9358 if (setup.scroll_delay == FALSE)
9360 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9361 setup.scroll_delay = TRUE; /* now always "on" */
9364 /* make sure that scroll delay value stays inside valid range */
9365 setup.scroll_delay_value =
9366 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9369 Error(ERR_WARN, "using default setup values");
9372 void LoadSetup_EditorCascade()
9374 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9375 SetupFileHash *setup_file_hash = NULL;
9377 /* always start with reliable default values */
9378 setSetupInfoToDefaults_EditorCascade(&setup);
9380 setup_file_hash = loadSetupFileHash(filename);
9382 if (setup_file_hash)
9384 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
9385 decodeSetupFileHash_EditorCascade(setup_file_hash);
9387 freeSetupFileHash(setup_file_hash);
9395 char *filename = getSetupFilename();
9399 InitUserDataDirectory();
9401 if (!(file = fopen(filename, MODE_WRITE)))
9403 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9407 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
9408 getCookie("SETUP")));
9409 fprintf(file, "\n");
9413 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9415 /* just to make things nicer :) */
9416 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9417 i == SETUP_TOKEN_GRAPHICS_SET)
9418 fprintf(file, "\n");
9420 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9425 fprintf(file, "\n");
9426 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9427 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9429 /* shortcut setup */
9430 ssi = setup.shortcut;
9431 fprintf(file, "\n");
9432 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9433 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9436 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9440 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9441 fprintf(file, "\n");
9443 sii = setup.input[pnr];
9444 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9445 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9450 fprintf(file, "\n");
9451 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9452 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9455 soi = setup.options;
9456 fprintf(file, "\n");
9457 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9458 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9462 SetFilePermissions(filename, PERMS_PRIVATE);
9465 void SaveSetup_EditorCascade()
9467 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9471 InitUserDataDirectory();
9473 if (!(file = fopen(filename, MODE_WRITE)))
9475 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9480 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
9481 getCookie("SETUP")));
9482 fprintf(file, "\n");
9484 seci = setup.editor_cascade;
9485 fprintf(file, "\n");
9486 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9487 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9491 SetFilePermissions(filename, PERMS_PRIVATE);
9496 void LoadCustomElementDescriptions()
9498 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9499 SetupFileHash *setup_file_hash;
9502 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9504 if (element_info[i].custom_description != NULL)
9506 free(element_info[i].custom_description);
9507 element_info[i].custom_description = NULL;
9511 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9514 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9516 char *token = getStringCat2(element_info[i].token_name, ".name");
9517 char *value = getHashEntry(setup_file_hash, token);
9520 element_info[i].custom_description = getStringCopy(value);
9525 freeSetupFileHash(setup_file_hash);
9528 static int getElementFromToken(char *token)
9531 char *value = getHashEntry(element_token_hash, token);
9538 /* !!! OPTIMIZE THIS BY USING HASH !!! */
9539 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
9540 if (strEqual(token, element_info[i].token_name))
9544 Error(ERR_WARN, "unknown element token '%s'", token);
9546 return EL_UNDEFINED;
9549 static int get_token_parameter_value(char *token, char *value_raw)
9553 if (token == NULL || value_raw == NULL)
9554 return ARG_UNDEFINED_VALUE;
9556 suffix = strrchr(token, '.');
9561 if (strEqual(suffix, ".element"))
9562 return getElementFromToken(value_raw);
9566 if (strncmp(suffix, ".font", 5) == 0)
9570 /* !!! OPTIMIZE THIS BY USING HASH !!! */
9571 for (i = 0; i < NUM_FONTS; i++)
9572 if (strEqual(value_raw, font_info[i].token_name))
9575 /* if font not found, use reliable default value */
9576 return FONT_INITIAL_1;
9580 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9581 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9584 void InitMenuDesignSettings_Static()
9587 static SetupFileHash *image_config_hash = NULL;
9592 if (image_config_hash == NULL)
9594 image_config_hash = newSetupFileHash();
9596 for (i = 0; image_config[i].token != NULL; i++)
9597 setHashEntry(image_config_hash,
9598 image_config[i].token,
9599 image_config[i].value);
9604 /* always start with reliable default values from static default config */
9605 for (i = 0; image_config_vars[i].token != NULL; i++)
9607 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9610 *image_config_vars[i].value =
9611 get_token_parameter_value(image_config_vars[i].token, value);
9618 /* always start with reliable default values from static default config */
9619 for (i = 0; image_config_vars[i].token != NULL; i++)
9620 for (j = 0; image_config[j].token != NULL; j++)
9621 if (strEqual(image_config_vars[i].token, image_config[j].token))
9622 *image_config_vars[i].value =
9623 get_token_parameter_value(image_config_vars[i].token,
9624 image_config[j].value);
9628 static void InitMenuDesignSettings_SpecialPreProcessing()
9632 /* the following initializes hierarchical values from static configuration */
9634 /* special case: initialize "ARG_DEFAULT" values in static default config */
9635 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9636 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9637 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9638 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9639 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9640 titlemessage_default.fade_mode = title_default.fade_mode;
9641 titlemessage_default.fade_delay = title_default.fade_delay;
9642 titlemessage_default.post_delay = title_default.post_delay;
9643 titlemessage_default.auto_delay = title_default.auto_delay;
9645 /* special case: initialize "ARG_DEFAULT" values in static default config */
9646 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9647 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9649 titlemessage_initial[i] = titlemessage_initial_default;
9650 titlemessage[i] = titlemessage_default;
9653 /* special case: initialize "ARG_DEFAULT" values in static default config */
9654 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9655 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9657 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9658 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9662 static void InitMenuDesignSettings_SpecialPostProcessing()
9664 /* special case: initialize later added SETUP list size from LEVELS value */
9665 if (menu.list_size[GAME_MODE_SETUP] == -1)
9666 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9669 static void LoadMenuDesignSettingsFromFilename(char *filename)
9671 static struct TitleMessageInfo tmi;
9672 static struct TokenInfo titlemessage_tokens[] =
9674 { TYPE_INTEGER, &tmi.x, ".x" },
9675 { TYPE_INTEGER, &tmi.y, ".y" },
9676 { TYPE_INTEGER, &tmi.width, ".width" },
9677 { TYPE_INTEGER, &tmi.height, ".height" },
9678 { TYPE_INTEGER, &tmi.chars, ".chars" },
9679 { TYPE_INTEGER, &tmi.lines, ".lines" },
9680 { TYPE_INTEGER, &tmi.align, ".align" },
9681 { TYPE_INTEGER, &tmi.valign, ".valign" },
9682 { TYPE_INTEGER, &tmi.font, ".font" },
9683 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9684 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9685 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9686 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9687 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9688 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9689 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9690 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9696 struct TitleMessageInfo *array;
9699 titlemessage_arrays[] =
9701 { titlemessage_initial, "[titlemessage_initial]" },
9702 { titlemessage, "[titlemessage]" },
9706 SetupFileHash *setup_file_hash;
9710 printf("LoadMenuDesignSettings from file '%s' ...\n", filename);
9713 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9716 /* the following initializes hierarchical values from dynamic configuration */
9718 /* special case: initialize with default values that may be overwritten */
9719 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9720 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9722 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9723 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9724 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9726 if (value_1 != NULL)
9727 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9728 if (value_2 != NULL)
9729 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9730 if (value_3 != NULL)
9731 menu.list_size[i] = get_integer_from_string(value_3);
9734 /* special case: initialize with default values that may be overwritten */
9735 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9736 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9738 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9739 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9741 if (value_1 != NULL)
9742 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9743 if (value_2 != NULL)
9744 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9747 /* special case: initialize with default values that may be overwritten */
9748 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9749 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9751 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9752 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9754 if (value_1 != NULL)
9755 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9756 if (value_2 != NULL)
9757 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9760 /* special case: initialize with default values that may be overwritten */
9761 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9762 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9764 char *token_1 = "menu.enter_screen.fade_mode";
9765 char *token_2 = "menu.enter_screen.fade_delay";
9766 char *token_3 = "menu.enter_screen.post_delay";
9767 char *token_4 = "menu.leave_screen.fade_mode";
9768 char *token_5 = "menu.leave_screen.fade_delay";
9769 char *token_6 = "menu.leave_screen.post_delay";
9770 char *value_1 = getHashEntry(setup_file_hash, token_1);
9771 char *value_2 = getHashEntry(setup_file_hash, token_2);
9772 char *value_3 = getHashEntry(setup_file_hash, token_3);
9773 char *value_4 = getHashEntry(setup_file_hash, token_4);
9774 char *value_5 = getHashEntry(setup_file_hash, token_5);
9775 char *value_6 = getHashEntry(setup_file_hash, token_6);
9777 if (value_1 != NULL)
9778 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9780 if (value_2 != NULL)
9781 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9783 if (value_3 != NULL)
9784 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9786 if (value_4 != NULL)
9787 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9789 if (value_5 != NULL)
9790 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9792 if (value_6 != NULL)
9793 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9797 /* special case: initialize with default values that may be overwritten */
9798 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9799 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9801 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9802 char *base_token = titlemessage_arrays[i].text;
9804 for (j = 0; titlemessage_tokens[j].type != -1; j++)
9806 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9807 char *value = getHashEntry(setup_file_hash, token);
9811 int parameter_value = get_token_parameter_value(token, value);
9813 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9817 if (titlemessage_tokens[j].type == TYPE_INTEGER)
9818 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9820 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
9830 /* read (and overwrite with) values that may be specified in config file */
9831 for (i = 0; image_config_vars[i].token != NULL; i++)
9833 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9835 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9836 if (value != NULL && !strEqual(value, ARG_DEFAULT))
9837 *image_config_vars[i].value =
9838 get_token_parameter_value(image_config_vars[i].token, value);
9841 freeSetupFileHash(setup_file_hash);
9844 void LoadMenuDesignSettings()
9846 char *filename_base = UNDEFINED_FILENAME, *filename_local;
9848 InitMenuDesignSettings_Static();
9849 InitMenuDesignSettings_SpecialPreProcessing();
9852 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9854 if (!SETUP_OVERRIDE_ARTWORK(setup, ARTWORK_TYPE_GRAPHICS))
9857 /* first look for special settings configured in level series config */
9858 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9860 if (fileExists(filename_base))
9861 LoadMenuDesignSettingsFromFilename(filename_base);
9864 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9866 if (filename_local != NULL && !strEqual(filename_base, filename_local))
9867 LoadMenuDesignSettingsFromFilename(filename_local);
9869 InitMenuDesignSettings_SpecialPostProcessing();
9872 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9874 char *filename = getEditorSetupFilename();
9875 SetupFileList *setup_file_list, *list;
9876 SetupFileHash *element_hash;
9877 int num_unknown_tokens = 0;
9880 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9883 element_hash = newSetupFileHash();
9885 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9886 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9888 /* determined size may be larger than needed (due to unknown elements) */
9890 for (list = setup_file_list; list != NULL; list = list->next)
9893 /* add space for up to 3 more elements for padding that may be needed */
9896 /* free memory for old list of elements, if needed */
9897 checked_free(*elements);
9899 /* allocate memory for new list of elements */
9900 *elements = checked_malloc(*num_elements * sizeof(int));
9903 for (list = setup_file_list; list != NULL; list = list->next)
9905 char *value = getHashEntry(element_hash, list->token);
9907 if (value == NULL) /* try to find obsolete token mapping */
9909 char *mapped_token = get_mapped_token(list->token);
9911 if (mapped_token != NULL)
9913 value = getHashEntry(element_hash, mapped_token);
9921 (*elements)[(*num_elements)++] = atoi(value);
9925 if (num_unknown_tokens == 0)
9927 Error(ERR_INFO_LINE, "-");
9928 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9929 Error(ERR_INFO, "- config file: '%s'", filename);
9931 num_unknown_tokens++;
9934 Error(ERR_INFO, "- token: '%s'", list->token);
9938 if (num_unknown_tokens > 0)
9939 Error(ERR_INFO_LINE, "-");
9941 while (*num_elements % 4) /* pad with empty elements, if needed */
9942 (*elements)[(*num_elements)++] = EL_EMPTY;
9944 freeSetupFileList(setup_file_list);
9945 freeSetupFileHash(element_hash);
9948 for (i = 0; i < *num_elements; i++)
9949 printf("editor: element '%s' [%d]\n",
9950 element_info[(*elements)[i]].token_name, (*elements)[i]);
9954 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9957 SetupFileHash *setup_file_hash = NULL;
9958 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9959 char *filename_music, *filename_prefix, *filename_info;
9965 token_to_value_ptr[] =
9967 { "title_header", &tmp_music_file_info.title_header },
9968 { "artist_header", &tmp_music_file_info.artist_header },
9969 { "album_header", &tmp_music_file_info.album_header },
9970 { "year_header", &tmp_music_file_info.year_header },
9972 { "title", &tmp_music_file_info.title },
9973 { "artist", &tmp_music_file_info.artist },
9974 { "album", &tmp_music_file_info.album },
9975 { "year", &tmp_music_file_info.year },
9981 filename_music = (is_sound ? getCustomSoundFilename(basename) :
9982 getCustomMusicFilename(basename));
9984 if (filename_music == NULL)
9987 /* ---------- try to replace file extension ---------- */
9989 filename_prefix = getStringCopy(filename_music);
9990 if (strrchr(filename_prefix, '.') != NULL)
9991 *strrchr(filename_prefix, '.') = '\0';
9992 filename_info = getStringCat2(filename_prefix, ".txt");
9995 printf("trying to load file '%s'...\n", filename_info);
9998 if (fileExists(filename_info))
9999 setup_file_hash = loadSetupFileHash(filename_info);
10001 free(filename_prefix);
10002 free(filename_info);
10004 if (setup_file_hash == NULL)
10006 /* ---------- try to add file extension ---------- */
10008 filename_prefix = getStringCopy(filename_music);
10009 filename_info = getStringCat2(filename_prefix, ".txt");
10012 printf("trying to load file '%s'...\n", filename_info);
10015 if (fileExists(filename_info))
10016 setup_file_hash = loadSetupFileHash(filename_info);
10018 free(filename_prefix);
10019 free(filename_info);
10022 if (setup_file_hash == NULL)
10025 /* ---------- music file info found ---------- */
10027 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10029 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10031 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10033 *token_to_value_ptr[i].value_ptr =
10034 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10037 tmp_music_file_info.basename = getStringCopy(basename);
10038 tmp_music_file_info.music = music;
10039 tmp_music_file_info.is_sound = is_sound;
10041 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10042 *new_music_file_info = tmp_music_file_info;
10044 return new_music_file_info;
10047 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10049 return get_music_file_info_ext(basename, music, FALSE);
10052 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10054 return get_music_file_info_ext(basename, sound, TRUE);
10057 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10058 char *basename, boolean is_sound)
10060 for (; list != NULL; list = list->next)
10061 if (list->is_sound == is_sound && strEqual(list->basename, basename))
10067 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10069 return music_info_listed_ext(list, basename, FALSE);
10072 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10074 return music_info_listed_ext(list, basename, TRUE);
10077 void LoadMusicInfo()
10079 char *music_directory = getCustomMusicDirectory();
10080 int num_music = getMusicListSize();
10081 int num_music_noconf = 0;
10082 int num_sounds = getSoundListSize();
10084 struct dirent *dir_entry;
10085 struct FileInfo *music, *sound;
10086 struct MusicFileInfo *next, **new;
10089 while (music_file_info != NULL)
10091 next = music_file_info->next;
10093 checked_free(music_file_info->basename);
10095 checked_free(music_file_info->title_header);
10096 checked_free(music_file_info->artist_header);
10097 checked_free(music_file_info->album_header);
10098 checked_free(music_file_info->year_header);
10100 checked_free(music_file_info->title);
10101 checked_free(music_file_info->artist);
10102 checked_free(music_file_info->album);
10103 checked_free(music_file_info->year);
10105 free(music_file_info);
10107 music_file_info = next;
10110 new = &music_file_info;
10112 for (i = 0; i < num_music; i++)
10114 music = getMusicListEntry(i);
10116 if (music->filename == NULL)
10119 if (strEqual(music->filename, UNDEFINED_FILENAME))
10122 /* a configured file may be not recognized as music */
10123 if (!FileIsMusic(music->filename))
10127 printf("::: -> '%s' (configured)\n", music->filename);
10130 if (!music_info_listed(music_file_info, music->filename))
10132 *new = get_music_file_info(music->filename, i);
10134 new = &(*new)->next;
10138 if ((dir = opendir(music_directory)) == NULL)
10140 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10144 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
10146 char *basename = dir_entry->d_name;
10147 boolean music_already_used = FALSE;
10150 /* skip all music files that are configured in music config file */
10151 for (i = 0; i < num_music; i++)
10153 music = getMusicListEntry(i);
10155 if (music->filename == NULL)
10158 if (strEqual(basename, music->filename))
10160 music_already_used = TRUE;
10165 if (music_already_used)
10168 if (!FileIsMusic(basename))
10172 printf("::: -> '%s' (found in directory)\n", basename);
10175 if (!music_info_listed(music_file_info, basename))
10177 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10179 new = &(*new)->next;
10182 num_music_noconf++;
10187 for (i = 0; i < num_sounds; i++)
10189 sound = getSoundListEntry(i);
10191 if (sound->filename == NULL)
10194 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10197 /* a configured file may be not recognized as sound */
10198 if (!FileIsSound(sound->filename))
10202 printf("::: -> '%s' (configured)\n", sound->filename);
10205 if (!sound_info_listed(music_file_info, sound->filename))
10207 *new = get_sound_file_info(sound->filename, i);
10209 new = &(*new)->next;
10214 for (next = music_file_info; next != NULL; next = next->next)
10215 printf("::: title == '%s'\n", next->title);
10219 void add_helpanim_entry(int element, int action, int direction, int delay,
10220 int *num_list_entries)
10222 struct HelpAnimInfo *new_list_entry;
10223 (*num_list_entries)++;
10226 checked_realloc(helpanim_info,
10227 *num_list_entries * sizeof(struct HelpAnimInfo));
10228 new_list_entry = &helpanim_info[*num_list_entries - 1];
10230 new_list_entry->element = element;
10231 new_list_entry->action = action;
10232 new_list_entry->direction = direction;
10233 new_list_entry->delay = delay;
10236 void print_unknown_token(char *filename, char *token, int token_nr)
10240 Error(ERR_INFO_LINE, "-");
10241 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10242 Error(ERR_INFO, "- config file: '%s'", filename);
10245 Error(ERR_INFO, "- token: '%s'", token);
10248 void print_unknown_token_end(int token_nr)
10251 Error(ERR_INFO_LINE, "-");
10254 void LoadHelpAnimInfo()
10256 char *filename = getHelpAnimFilename();
10257 SetupFileList *setup_file_list = NULL, *list;
10258 SetupFileHash *element_hash, *action_hash, *direction_hash;
10259 int num_list_entries = 0;
10260 int num_unknown_tokens = 0;
10263 if (fileExists(filename))
10264 setup_file_list = loadSetupFileList(filename);
10266 if (setup_file_list == NULL)
10268 /* use reliable default values from static configuration */
10269 SetupFileList *insert_ptr;
10271 insert_ptr = setup_file_list =
10272 newSetupFileList(helpanim_config[0].token,
10273 helpanim_config[0].value);
10275 for (i = 1; helpanim_config[i].token; i++)
10276 insert_ptr = addListEntry(insert_ptr,
10277 helpanim_config[i].token,
10278 helpanim_config[i].value);
10281 element_hash = newSetupFileHash();
10282 action_hash = newSetupFileHash();
10283 direction_hash = newSetupFileHash();
10285 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10286 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10288 for (i = 0; i < NUM_ACTIONS; i++)
10289 setHashEntry(action_hash, element_action_info[i].suffix,
10290 i_to_a(element_action_info[i].value));
10292 /* do not store direction index (bit) here, but direction value! */
10293 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10294 setHashEntry(direction_hash, element_direction_info[i].suffix,
10295 i_to_a(1 << element_direction_info[i].value));
10297 for (list = setup_file_list; list != NULL; list = list->next)
10299 char *element_token, *action_token, *direction_token;
10300 char *element_value, *action_value, *direction_value;
10301 int delay = atoi(list->value);
10303 if (strEqual(list->token, "end"))
10305 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10310 /* first try to break element into element/action/direction parts;
10311 if this does not work, also accept combined "element[.act][.dir]"
10312 elements (like "dynamite.active"), which are unique elements */
10314 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10316 element_value = getHashEntry(element_hash, list->token);
10317 if (element_value != NULL) /* element found */
10318 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10319 &num_list_entries);
10322 /* no further suffixes found -- this is not an element */
10323 print_unknown_token(filename, list->token, num_unknown_tokens++);
10329 /* token has format "<prefix>.<something>" */
10331 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10332 direction_token = action_token; /* ... or direction */
10334 element_token = getStringCopy(list->token);
10335 *strchr(element_token, '.') = '\0';
10337 element_value = getHashEntry(element_hash, element_token);
10339 if (element_value == NULL) /* this is no element */
10341 element_value = getHashEntry(element_hash, list->token);
10342 if (element_value != NULL) /* combined element found */
10343 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10344 &num_list_entries);
10346 print_unknown_token(filename, list->token, num_unknown_tokens++);
10348 free(element_token);
10353 action_value = getHashEntry(action_hash, action_token);
10355 if (action_value != NULL) /* action found */
10357 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10358 &num_list_entries);
10360 free(element_token);
10365 direction_value = getHashEntry(direction_hash, direction_token);
10367 if (direction_value != NULL) /* direction found */
10369 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10370 &num_list_entries);
10372 free(element_token);
10377 if (strchr(action_token + 1, '.') == NULL)
10379 /* no further suffixes found -- this is not an action nor direction */
10381 element_value = getHashEntry(element_hash, list->token);
10382 if (element_value != NULL) /* combined element found */
10383 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10384 &num_list_entries);
10386 print_unknown_token(filename, list->token, num_unknown_tokens++);
10388 free(element_token);
10393 /* token has format "<prefix>.<suffix>.<something>" */
10395 direction_token = strchr(action_token + 1, '.');
10397 action_token = getStringCopy(action_token);
10398 *strchr(action_token + 1, '.') = '\0';
10400 action_value = getHashEntry(action_hash, action_token);
10402 if (action_value == NULL) /* this is no action */
10404 element_value = getHashEntry(element_hash, list->token);
10405 if (element_value != NULL) /* combined element found */
10406 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10407 &num_list_entries);
10409 print_unknown_token(filename, list->token, num_unknown_tokens++);
10411 free(element_token);
10412 free(action_token);
10417 direction_value = getHashEntry(direction_hash, direction_token);
10419 if (direction_value != NULL) /* direction found */
10421 add_helpanim_entry(atoi(element_value), atoi(action_value),
10422 atoi(direction_value), delay, &num_list_entries);
10424 free(element_token);
10425 free(action_token);
10430 /* this is no direction */
10432 element_value = getHashEntry(element_hash, list->token);
10433 if (element_value != NULL) /* combined element found */
10434 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10435 &num_list_entries);
10437 print_unknown_token(filename, list->token, num_unknown_tokens++);
10439 free(element_token);
10440 free(action_token);
10443 print_unknown_token_end(num_unknown_tokens);
10445 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10446 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10448 freeSetupFileList(setup_file_list);
10449 freeSetupFileHash(element_hash);
10450 freeSetupFileHash(action_hash);
10451 freeSetupFileHash(direction_hash);
10454 for (i = 0; i < num_list_entries; i++)
10455 printf("::: '%s': %d, %d, %d => %d\n",
10456 EL_NAME(helpanim_info[i].element),
10457 helpanim_info[i].element,
10458 helpanim_info[i].action,
10459 helpanim_info[i].direction,
10460 helpanim_info[i].delay);
10464 void LoadHelpTextInfo()
10466 char *filename = getHelpTextFilename();
10469 if (helptext_info != NULL)
10471 freeSetupFileHash(helptext_info);
10472 helptext_info = NULL;
10475 if (fileExists(filename))
10476 helptext_info = loadSetupFileHash(filename);
10478 if (helptext_info == NULL)
10480 /* use reliable default values from static configuration */
10481 helptext_info = newSetupFileHash();
10483 for (i = 0; helptext_config[i].token; i++)
10484 setHashEntry(helptext_info,
10485 helptext_config[i].token,
10486 helptext_config[i].value);
10490 BEGIN_HASH_ITERATION(helptext_info, itr)
10492 printf("::: '%s' => '%s'\n",
10493 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10495 END_HASH_ITERATION(hash, itr)
10500 /* ------------------------------------------------------------------------- */
10501 /* convert levels */
10502 /* ------------------------------------------------------------------------- */
10504 #define MAX_NUM_CONVERT_LEVELS 1000
10506 void ConvertLevels()
10508 static LevelDirTree *convert_leveldir = NULL;
10509 static int convert_level_nr = -1;
10510 static int num_levels_handled = 0;
10511 static int num_levels_converted = 0;
10512 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10515 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10516 global.convert_leveldir);
10518 if (convert_leveldir == NULL)
10519 Error(ERR_EXIT, "no such level identifier: '%s'",
10520 global.convert_leveldir);
10522 leveldir_current = convert_leveldir;
10524 if (global.convert_level_nr != -1)
10526 convert_leveldir->first_level = global.convert_level_nr;
10527 convert_leveldir->last_level = global.convert_level_nr;
10530 convert_level_nr = convert_leveldir->first_level;
10532 printf_line("=", 79);
10533 printf("Converting levels\n");
10534 printf_line("-", 79);
10535 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
10536 printf("Level series name: '%s'\n", convert_leveldir->name);
10537 printf("Level series author: '%s'\n", convert_leveldir->author);
10538 printf("Number of levels: %d\n", convert_leveldir->levels);
10539 printf_line("=", 79);
10542 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10543 levels_failed[i] = FALSE;
10545 while (convert_level_nr <= convert_leveldir->last_level)
10547 char *level_filename;
10550 level_nr = convert_level_nr++;
10552 printf("Level %03d: ", level_nr);
10554 LoadLevel(level_nr);
10555 if (level.no_valid_file)
10557 printf("(no level)\n");
10561 printf("converting level ... ");
10563 level_filename = getDefaultLevelFilename(level_nr);
10564 new_level = !fileExists(level_filename);
10568 SaveLevel(level_nr);
10570 num_levels_converted++;
10572 printf("converted.\n");
10576 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10577 levels_failed[level_nr] = TRUE;
10579 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10582 num_levels_handled++;
10586 printf_line("=", 79);
10587 printf("Number of levels handled: %d\n", num_levels_handled);
10588 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10589 (num_levels_handled ?
10590 num_levels_converted * 100 / num_levels_handled : 0));
10591 printf_line("-", 79);
10592 printf("Summary (for automatic parsing by scripts):\n");
10593 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10594 convert_leveldir->identifier, num_levels_converted,
10595 num_levels_handled,
10596 (num_levels_handled ?
10597 num_levels_converted * 100 / num_levels_handled : 0));
10599 if (num_levels_handled != num_levels_converted)
10601 printf(", FAILED:");
10602 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10603 if (levels_failed[i])
10604 printf(" %03d", i);
10608 printf_line("=", 79);
10610 CloseAllAndExit(0);
10614 /* ------------------------------------------------------------------------- */
10615 /* create and save images for use in level sketches (raw BMP format) */
10616 /* ------------------------------------------------------------------------- */
10618 void CreateLevelSketchImages()
10620 #if defined(TARGET_SDL)
10625 InitElementPropertiesGfxElement();
10627 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10628 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10630 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10632 Bitmap *src_bitmap;
10634 int element = getMappedElement(i);
10635 int graphic = el2edimg(element);
10636 char basename1[16];
10637 char basename2[16];
10641 sprintf(basename1, "%03d.bmp", i);
10642 sprintf(basename2, "%03ds.bmp", i);
10644 filename1 = getPath2(global.create_images_dir, basename1);
10645 filename2 = getPath2(global.create_images_dir, basename2);
10647 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10648 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY, 0, 0);
10650 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10651 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10653 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10654 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10656 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10657 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10663 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10666 FreeBitmap(bitmap1);
10667 FreeBitmap(bitmap2);
10672 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10674 CloseAllAndExit(0);
10679 /* ------------------------------------------------------------------------- */
10680 /* create and save images for custom and group elements (raw BMP format) */
10681 /* ------------------------------------------------------------------------- */
10683 void CreateCustomElementImages()
10685 #if defined(TARGET_SDL)
10686 char *filename = "graphics.classic/RocksCE.bmp";
10688 Bitmap *src_bitmap;
10689 int dummy_graphic = IMG_CUSTOM_99;
10690 int yoffset_ce = 0;
10691 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10695 bitmap = CreateBitmap(TILEX * 16 * 2,
10696 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10699 getGraphicSource(dummy_graphic, 0, &src_bitmap, &src_x, &src_y);
10701 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10708 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10709 TILEX * x, TILEY * y + yoffset_ce);
10711 BlitBitmap(src_bitmap, bitmap, 0, TILEY, TILEX, TILEY,
10712 TILEX * x + TILEX * 16, TILEY * y + yoffset_ce);
10714 for (j = 2; j >= 0; j--)
10718 BlitBitmap(src_bitmap, bitmap, TILEX + c * 7, 0, 6, 10,
10719 TILEX * x + 6 + j * 7,
10720 TILEY * y + 11 + yoffset_ce);
10722 BlitBitmap(src_bitmap, bitmap, TILEX + c * 8, TILEY, 6, 10,
10723 TILEX * 16 + TILEX * x + 6 + j * 8,
10724 TILEY * y + 10 + yoffset_ce);
10730 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10737 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10738 TILEX * x, TILEY * y + yoffset_ge);
10740 BlitBitmap(src_bitmap, bitmap, 0, TILEY, TILEX, TILEY,
10741 TILEX * x + TILEX * 16, TILEY * y + yoffset_ge);
10743 for (j = 1; j >= 0; j--)
10747 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10748 TILEX * x + 6 + j * 10,
10749 TILEY * y + 11 + yoffset_ge);
10751 BlitBitmap(src_bitmap, bitmap, TILEX + c * 8, TILEY + 12, 6, 10,
10752 TILEX * 16 + TILEX * x + 10 + j * 8,
10753 TILEY * y + 10 + yoffset_ge);
10759 if (SDL_SaveBMP(bitmap->surface, filename) != 0)
10760 Error(ERR_EXIT, "cannot save CE graphics file '%s'", filename);
10762 FreeBitmap(bitmap);
10764 CloseAllAndExit(0);
10769 void CreateLevelSketchImages_TEST()
10771 void CreateCustomElementImages()