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 boolean check_special_flags(char *flag)
1320 printf("::: '%s', '%s', '%s'\n",
1322 options.special_flags,
1323 leveldir_current->special_flags);
1326 if (strEqual(options.special_flags, flag) ||
1327 strEqual(leveldir_current->special_flags, flag))
1333 static struct DateInfo getCurrentDate()
1335 time_t epoch_seconds = time(NULL);
1336 struct tm *now = localtime(&epoch_seconds);
1337 struct DateInfo date;
1339 date.year = now->tm_year + 1900;
1340 date.month = now->tm_mon + 1;
1341 date.day = now->tm_mday;
1346 static void resetEventFlags(struct ElementChangeInfo *change)
1350 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1351 change->has_event[i] = FALSE;
1354 static void resetEventBits()
1358 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1359 xx_event_bits[i] = 0;
1362 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1366 /* important: only change event flag if corresponding event bit is set
1367 (this is because all xx_event_bits[] values are loaded separately,
1368 and all xx_event_bits[] values are set back to zero before loading
1369 another value xx_event_bits[x] (each value representing 32 flags)) */
1371 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1372 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1373 change->has_event[i] = TRUE;
1376 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1380 /* in contrast to the above function setEventFlagsFromEventBits(), it
1381 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1382 depending on the corresponding change->has_event[i] values here, as
1383 all xx_event_bits[] values are reset in resetEventBits() before */
1385 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1386 if (change->has_event[i])
1387 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1390 static char *getDefaultElementDescription(struct ElementInfo *ei)
1392 static char description[MAX_ELEMENT_NAME_LEN + 1];
1393 char *default_description = (ei->custom_description != NULL ?
1394 ei->custom_description :
1395 ei->editor_description);
1398 /* always start with reliable default values */
1399 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1400 description[i] = '\0';
1402 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1403 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1405 return &description[0];
1408 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1410 char *default_description = getDefaultElementDescription(ei);
1413 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1414 ei->description[i] = default_description[i];
1417 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1421 for (i = 0; conf[i].data_type != -1; i++)
1423 int default_value = conf[i].default_value;
1424 int data_type = conf[i].data_type;
1425 int conf_type = conf[i].conf_type;
1426 int byte_mask = conf_type & CONF_MASK_BYTES;
1428 if (byte_mask == CONF_MASK_MULTI_BYTES)
1430 int default_num_entities = conf[i].default_num_entities;
1431 int max_num_entities = conf[i].max_num_entities;
1433 *(int *)(conf[i].num_entities) = default_num_entities;
1435 if (data_type == TYPE_STRING)
1437 char *default_string = conf[i].default_string;
1438 char *string = (char *)(conf[i].value);
1440 strncpy(string, default_string, max_num_entities);
1442 else if (data_type == TYPE_ELEMENT_LIST)
1444 int *element_array = (int *)(conf[i].value);
1447 for (j = 0; j < max_num_entities; j++)
1448 element_array[j] = default_value;
1450 else if (data_type == TYPE_CONTENT_LIST)
1452 struct Content *content = (struct Content *)(conf[i].value);
1455 for (c = 0; c < max_num_entities; c++)
1456 for (y = 0; y < 3; y++)
1457 for (x = 0; x < 3; x++)
1458 content[c].e[x][y] = default_value;
1461 else /* constant size configuration data (1, 2 or 4 bytes) */
1463 if (data_type == TYPE_BOOLEAN)
1464 *(boolean *)(conf[i].value) = default_value;
1466 *(int *) (conf[i].value) = default_value;
1471 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1475 for (i = 0; conf[i].data_type != -1; i++)
1477 int data_type = conf[i].data_type;
1478 int conf_type = conf[i].conf_type;
1479 int byte_mask = conf_type & CONF_MASK_BYTES;
1481 if (byte_mask == CONF_MASK_MULTI_BYTES)
1483 int max_num_entities = conf[i].max_num_entities;
1485 if (data_type == TYPE_STRING)
1487 char *string = (char *)(conf[i].value);
1488 char *string_copy = (char *)(conf[i].value_copy);
1490 strncpy(string_copy, string, max_num_entities);
1492 else if (data_type == TYPE_ELEMENT_LIST)
1494 int *element_array = (int *)(conf[i].value);
1495 int *element_array_copy = (int *)(conf[i].value_copy);
1498 for (j = 0; j < max_num_entities; j++)
1499 element_array_copy[j] = element_array[j];
1501 else if (data_type == TYPE_CONTENT_LIST)
1503 struct Content *content = (struct Content *)(conf[i].value);
1504 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1507 for (c = 0; c < max_num_entities; c++)
1508 for (y = 0; y < 3; y++)
1509 for (x = 0; x < 3; x++)
1510 content_copy[c].e[x][y] = content[c].e[x][y];
1513 else /* constant size configuration data (1, 2 or 4 bytes) */
1515 if (data_type == TYPE_BOOLEAN)
1516 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1518 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1523 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1527 xx_ei = *ei_from; /* copy element data into temporary buffer */
1528 yy_ei = *ei_to; /* copy element data into temporary buffer */
1530 copyConfigFromConfigList(chunk_config_CUSX_base);
1535 /* ---------- reinitialize and copy change pages ---------- */
1537 ei_to->num_change_pages = ei_from->num_change_pages;
1538 ei_to->current_change_page = ei_from->current_change_page;
1540 setElementChangePages(ei_to, ei_to->num_change_pages);
1542 for (i = 0; i < ei_to->num_change_pages; i++)
1543 ei_to->change_page[i] = ei_from->change_page[i];
1545 /* ---------- copy group element info ---------- */
1546 if (ei_from->group != NULL && ei_to->group != NULL) /* group or internal */
1547 *ei_to->group = *ei_from->group;
1549 /* mark this custom element as modified */
1550 ei_to->modified_settings = TRUE;
1553 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1555 int change_page_size = sizeof(struct ElementChangeInfo);
1557 ei->num_change_pages = MAX(1, change_pages);
1560 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1562 if (ei->current_change_page >= ei->num_change_pages)
1563 ei->current_change_page = ei->num_change_pages - 1;
1565 ei->change = &ei->change_page[ei->current_change_page];
1568 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1570 xx_change = *change; /* copy change data into temporary buffer */
1573 /* (not needed; set by setConfigToDefaultsFromConfigList()) */
1574 xx_num_contents = 1;
1577 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1579 *change = xx_change;
1581 resetEventFlags(change);
1583 change->direct_action = 0;
1584 change->other_action = 0;
1586 change->pre_change_function = NULL;
1587 change->change_function = NULL;
1588 change->post_change_function = NULL;
1591 static void setLevelInfoToDefaults(struct LevelInfo *level)
1593 static boolean clipboard_elements_initialized = FALSE;
1596 InitElementPropertiesStatic();
1598 li = *level; /* copy level data into temporary buffer */
1600 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1601 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1603 *level = li; /* copy temporary buffer back to level data */
1605 setLevelInfoToDefaults_EM();
1606 setLevelInfoToDefaults_SP();
1608 level->native_em_level = &native_em_level;
1609 level->native_sp_level = &native_sp_level;
1611 level->file_version = FILE_VERSION_ACTUAL;
1612 level->game_version = GAME_VERSION_ACTUAL;
1614 level->creation_date = getCurrentDate();
1616 level->encoding_16bit_field = TRUE;
1617 level->encoding_16bit_yamyam = TRUE;
1618 level->encoding_16bit_amoeba = TRUE;
1620 for (x = 0; x < MAX_LEV_FIELDX; x++)
1621 for (y = 0; y < MAX_LEV_FIELDY; y++)
1622 level->field[x][y] = EL_SAND;
1624 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1625 level->name[i] = '\0';
1626 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1627 level->author[i] = '\0';
1629 strcpy(level->name, NAMELESS_LEVEL_NAME);
1630 strcpy(level->author, ANONYMOUS_NAME);
1632 level->field[0][0] = EL_PLAYER_1;
1633 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1635 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1638 struct ElementInfo *ei = &element_info[element];
1640 /* never initialize clipboard elements after the very first time */
1641 /* (to be able to use clipboard elements between several levels) */
1642 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1645 if (IS_ENVELOPE(element))
1647 int envelope_nr = element - EL_ENVELOPE_1;
1649 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1651 level->envelope[envelope_nr] = xx_envelope;
1654 if (IS_CUSTOM_ELEMENT(element) ||
1655 IS_GROUP_ELEMENT(element) ||
1656 IS_INTERNAL_ELEMENT(element))
1658 xx_ei = *ei; /* copy element data into temporary buffer */
1660 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1665 setElementChangePages(ei, 1);
1666 setElementChangeInfoToDefaults(ei->change);
1668 if (IS_CUSTOM_ELEMENT(element) ||
1669 IS_GROUP_ELEMENT(element) ||
1670 IS_INTERNAL_ELEMENT(element))
1672 setElementDescriptionToDefault(ei);
1674 ei->modified_settings = FALSE;
1677 if (IS_CUSTOM_ELEMENT(element) ||
1678 IS_INTERNAL_ELEMENT(element))
1680 /* internal values used in level editor */
1682 ei->access_type = 0;
1683 ei->access_layer = 0;
1684 ei->access_protected = 0;
1685 ei->walk_to_action = 0;
1686 ei->smash_targets = 0;
1689 ei->can_explode_by_fire = FALSE;
1690 ei->can_explode_smashed = FALSE;
1691 ei->can_explode_impact = FALSE;
1693 ei->current_change_page = 0;
1696 if (IS_GROUP_ELEMENT(element) ||
1697 IS_INTERNAL_ELEMENT(element))
1699 struct ElementGroupInfo *group;
1701 /* initialize memory for list of elements in group */
1702 if (ei->group == NULL)
1703 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1707 xx_group = *group; /* copy group data into temporary buffer */
1709 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1715 clipboard_elements_initialized = TRUE;
1717 BorderElement = EL_STEELWALL;
1719 level->no_valid_file = FALSE;
1721 level->changed = FALSE;
1723 /* set all bug compatibility flags to "false" => do not emulate this bug */
1724 level->use_action_after_change_bug = FALSE;
1726 if (leveldir_current == NULL) /* only when dumping level */
1729 /* try to determine better author name than 'anonymous' */
1730 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1732 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1733 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1737 switch (LEVELCLASS(leveldir_current))
1739 case LEVELCLASS_TUTORIAL:
1740 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1743 case LEVELCLASS_CONTRIB:
1744 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1745 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1748 case LEVELCLASS_PRIVATE:
1749 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1750 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1754 /* keep default value */
1760 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1762 level_file_info->nr = 0;
1763 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1764 level_file_info->packed = FALSE;
1765 level_file_info->basename = NULL;
1766 level_file_info->filename = NULL;
1769 static void ActivateLevelTemplate()
1773 /* Currently there is no special action needed to activate the template
1774 data, because 'element_info' property settings overwrite the original
1775 level data, while all other variables do not change. */
1777 /* Exception: 'from_level_template' elements in the original level playfield
1778 are overwritten with the corresponding elements at the same position in
1779 playfield from the level template. */
1781 for (x = 0; x < level.fieldx; x++)
1782 for (y = 0; y < level.fieldy; y++)
1783 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1784 level.field[x][y] = level_template.field[x][y];
1786 if (check_special_flags("load_xsb_to_ces"))
1788 struct LevelInfo level_backup = level;
1790 /* overwrite all individual level settings from template level settings */
1791 level = level_template;
1793 /* restore playfield size */
1794 level.fieldx = level_backup.fieldx;
1795 level.fieldy = level_backup.fieldy;
1797 /* restore playfield content */
1798 for (x = 0; x < level.fieldx; x++)
1799 for (y = 0; y < level.fieldy; y++)
1800 level.field[x][y] = level_backup.field[x][y];
1802 /* restore name and author from individual level */
1803 strcpy(level.name, level_backup.name);
1804 strcpy(level.author, level_backup.author);
1806 /* restore flag "use_custom_template" */
1807 level.use_custom_template = level_backup.use_custom_template;
1811 static char *getLevelFilenameFromBasename(char *basename)
1813 static char *filename = NULL;
1815 checked_free(filename);
1817 filename = getPath2(getCurrentLevelDir(), basename);
1822 static int getFileTypeFromBasename(char *basename)
1824 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1826 static char *filename = NULL;
1827 struct stat file_status;
1829 /* ---------- try to determine file type from filename ---------- */
1831 /* check for typical filename of a Supaplex level package file */
1833 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1834 return LEVEL_FILE_TYPE_SP;
1836 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
1837 strncmp(basename, "LEVELS.D", 8) == 0))
1838 return LEVEL_FILE_TYPE_SP;
1841 /* check for typical filename of a Diamond Caves II level package file */
1842 if (strSuffixLower(basename, ".dc") ||
1843 strSuffixLower(basename, ".dc2"))
1844 return LEVEL_FILE_TYPE_DC;
1846 /* check for typical filename of a Sokoban level package file */
1847 if (strSuffixLower(basename, ".xsb") &&
1848 strchr(basename, '%') == NULL)
1849 return LEVEL_FILE_TYPE_SB;
1851 /* ---------- try to determine file type from filesize ---------- */
1853 checked_free(filename);
1854 filename = getPath2(getCurrentLevelDir(), basename);
1856 if (stat(filename, &file_status) == 0)
1858 /* check for typical filesize of a Supaplex level package file */
1859 if (file_status.st_size == 170496)
1860 return LEVEL_FILE_TYPE_SP;
1863 return LEVEL_FILE_TYPE_UNKNOWN;
1866 static boolean checkForPackageFromBasename(char *basename)
1868 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
1869 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
1871 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
1874 static char *getSingleLevelBasename(int nr)
1876 static char basename[MAX_FILENAME_LEN];
1879 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
1881 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
1886 static char *getPackedLevelBasename(int type)
1888 static char basename[MAX_FILENAME_LEN];
1889 char *directory = getCurrentLevelDir();
1891 struct dirent *dir_entry;
1893 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
1895 if ((dir = opendir(directory)) == NULL)
1897 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
1902 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1904 char *entry_basename = dir_entry->d_name;
1905 int entry_type = getFileTypeFromBasename(entry_basename);
1907 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
1909 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
1912 strcpy(basename, entry_basename);
1924 static char *getSingleLevelFilename(int nr)
1926 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
1930 static char *getPackedLevelFilename(int type)
1932 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
1936 char *getDefaultLevelFilename(int nr)
1938 return getSingleLevelFilename(nr);
1942 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
1946 lfi->packed = FALSE;
1947 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
1948 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1952 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
1953 int type, char *format, ...)
1955 static char basename[MAX_FILENAME_LEN];
1958 va_start(ap, format);
1959 vsprintf(basename, format, ap);
1963 lfi->packed = FALSE;
1964 lfi->basename = basename;
1965 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1968 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
1973 lfi->basename = getPackedLevelBasename(lfi->type);
1974 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1977 static int getFiletypeFromID(char *filetype_id)
1979 char *filetype_id_lower;
1980 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
1983 if (filetype_id == NULL)
1984 return LEVEL_FILE_TYPE_UNKNOWN;
1986 filetype_id_lower = getStringToLower(filetype_id);
1988 for (i = 0; filetype_id_list[i].id != NULL; i++)
1990 char *id_lower = getStringToLower(filetype_id_list[i].id);
1992 if (strEqual(filetype_id_lower, id_lower))
1993 filetype = filetype_id_list[i].filetype;
1997 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2001 free(filetype_id_lower);
2006 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2010 /* special case: level number is negative => check for level template file */
2014 /* global variable "leveldir_current" must be modified in the loop below */
2015 LevelDirTree *leveldir_current_last = leveldir_current;
2017 /* check for template level in path from current to topmost tree node */
2019 while (leveldir_current != NULL)
2021 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2022 "template.%s", LEVELFILE_EXTENSION);
2024 if (fileExists(lfi->filename))
2027 leveldir_current = leveldir_current->node_parent;
2030 /* restore global variable "leveldir_current" modified in above loop */
2031 leveldir_current = leveldir_current_last;
2035 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2036 "template.%s", LEVELFILE_EXTENSION);
2040 /* no fallback if template file not existing */
2044 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2045 if (leveldir_current->level_filename != NULL)
2047 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2049 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2050 leveldir_current->level_filename, nr);
2052 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2054 if (fileExists(lfi->filename))
2058 /* check for native Rocks'n'Diamonds level file */
2059 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2060 "%03d.%s", nr, LEVELFILE_EXTENSION);
2061 if (fileExists(lfi->filename))
2064 /* check for Emerald Mine level file (V1) */
2065 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2066 'a' + (nr / 10) % 26, '0' + nr % 10);
2067 if (fileExists(lfi->filename))
2069 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2070 'A' + (nr / 10) % 26, '0' + nr % 10);
2071 if (fileExists(lfi->filename))
2074 /* check for Emerald Mine level file (V2 to V5) */
2075 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2076 if (fileExists(lfi->filename))
2079 /* check for Emerald Mine level file (V6 / single mode) */
2080 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2081 if (fileExists(lfi->filename))
2083 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2084 if (fileExists(lfi->filename))
2087 /* check for Emerald Mine level file (V6 / teamwork mode) */
2088 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2089 if (fileExists(lfi->filename))
2091 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2092 if (fileExists(lfi->filename))
2095 /* check for various packed level file formats */
2096 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2097 if (fileExists(lfi->filename))
2100 /* no known level file found -- use default values (and fail later) */
2101 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2102 "%03d.%s", nr, LEVELFILE_EXTENSION);
2105 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2107 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2108 lfi->type = getFileTypeFromBasename(lfi->basename);
2111 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2113 /* always start with reliable default values */
2114 setFileInfoToDefaults(level_file_info);
2116 level_file_info->nr = nr; /* set requested level number */
2118 determineLevelFileInfo_Filename(level_file_info);
2119 determineLevelFileInfo_Filetype(level_file_info);
2122 /* ------------------------------------------------------------------------- */
2123 /* functions for loading R'n'D level */
2124 /* ------------------------------------------------------------------------- */
2126 int getMappedElement(int element)
2128 /* remap some (historic, now obsolete) elements */
2132 case EL_PLAYER_OBSOLETE:
2133 element = EL_PLAYER_1;
2136 case EL_KEY_OBSOLETE:
2140 case EL_EM_KEY_1_FILE_OBSOLETE:
2141 element = EL_EM_KEY_1;
2144 case EL_EM_KEY_2_FILE_OBSOLETE:
2145 element = EL_EM_KEY_2;
2148 case EL_EM_KEY_3_FILE_OBSOLETE:
2149 element = EL_EM_KEY_3;
2152 case EL_EM_KEY_4_FILE_OBSOLETE:
2153 element = EL_EM_KEY_4;
2156 case EL_ENVELOPE_OBSOLETE:
2157 element = EL_ENVELOPE_1;
2165 if (element >= NUM_FILE_ELEMENTS)
2167 Error(ERR_WARN, "invalid level element %d", element);
2169 element = EL_UNKNOWN;
2177 int getMappedElementByVersion(int element, int game_version)
2179 /* remap some elements due to certain game version */
2181 if (game_version <= VERSION_IDENT(2,2,0,0))
2183 /* map game font elements */
2184 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2185 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2186 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2187 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2190 if (game_version < VERSION_IDENT(3,0,0,0))
2192 /* map Supaplex gravity tube elements */
2193 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2194 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2195 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2196 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2203 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
2205 level->file_version = getFileVersion(file);
2206 level->game_version = getFileVersion(file);
2211 static int LoadLevel_DATE(FILE *file, int chunk_size, struct LevelInfo *level)
2213 level->creation_date.year = getFile16BitBE(file);
2214 level->creation_date.month = getFile8Bit(file);
2215 level->creation_date.day = getFile8Bit(file);
2220 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
2222 int initial_player_stepsize;
2223 int initial_player_gravity;
2226 level->fieldx = getFile8Bit(file);
2227 level->fieldy = getFile8Bit(file);
2229 level->time = getFile16BitBE(file);
2230 level->gems_needed = getFile16BitBE(file);
2232 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2233 level->name[i] = getFile8Bit(file);
2234 level->name[MAX_LEVEL_NAME_LEN] = 0;
2236 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2237 level->score[i] = getFile8Bit(file);
2239 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2240 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2241 for (y = 0; y < 3; y++)
2242 for (x = 0; x < 3; x++)
2243 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2245 level->amoeba_speed = getFile8Bit(file);
2246 level->time_magic_wall = getFile8Bit(file);
2247 level->time_wheel = getFile8Bit(file);
2248 level->amoeba_content = getMappedElement(getFile8Bit(file));
2250 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2253 for (i = 0; i < MAX_PLAYERS; i++)
2254 level->initial_player_stepsize[i] = initial_player_stepsize;
2256 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2258 for (i = 0; i < MAX_PLAYERS; i++)
2259 level->initial_player_gravity[i] = initial_player_gravity;
2261 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2262 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2264 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2266 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2267 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2268 level->can_move_into_acid_bits = getFile32BitBE(file);
2269 level->dont_collide_with_bits = getFile8Bit(file);
2271 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2272 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2274 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2275 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2276 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2278 level->game_engine_type = getFile8Bit(file);
2280 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2285 static int LoadLevel_NAME(FILE *file, int chunk_size, struct LevelInfo *level)
2289 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2290 level->name[i] = getFile8Bit(file);
2291 level->name[MAX_LEVEL_NAME_LEN] = 0;
2296 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
2300 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2301 level->author[i] = getFile8Bit(file);
2302 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2307 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
2310 int chunk_size_expected = level->fieldx * level->fieldy;
2312 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2313 stored with 16-bit encoding (and should be twice as big then).
2314 Even worse, playfield data was stored 16-bit when only yamyam content
2315 contained 16-bit elements and vice versa. */
2317 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2318 chunk_size_expected *= 2;
2320 if (chunk_size_expected != chunk_size)
2322 ReadUnusedBytesFromFile(file, chunk_size);
2323 return chunk_size_expected;
2326 for (y = 0; y < level->fieldy; y++)
2327 for (x = 0; x < level->fieldx; x++)
2328 level->field[x][y] =
2329 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2334 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
2337 int header_size = 4;
2338 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2339 int chunk_size_expected = header_size + content_size;
2341 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2342 stored with 16-bit encoding (and should be twice as big then).
2343 Even worse, playfield data was stored 16-bit when only yamyam content
2344 contained 16-bit elements and vice versa. */
2346 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2347 chunk_size_expected += content_size;
2349 if (chunk_size_expected != chunk_size)
2351 ReadUnusedBytesFromFile(file, chunk_size);
2352 return chunk_size_expected;
2356 level->num_yamyam_contents = getFile8Bit(file);
2360 /* correct invalid number of content fields -- should never happen */
2361 if (level->num_yamyam_contents < 1 ||
2362 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2363 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2365 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2366 for (y = 0; y < 3; y++)
2367 for (x = 0; x < 3; x++)
2368 level->yamyam_content[i].e[x][y] =
2369 getMappedElement(level->encoding_16bit_field ?
2370 getFile16BitBE(file) : getFile8Bit(file));
2374 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
2378 int num_contents, content_xsize, content_ysize;
2379 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2381 element = getMappedElement(getFile16BitBE(file));
2382 num_contents = getFile8Bit(file);
2383 content_xsize = getFile8Bit(file);
2384 content_ysize = getFile8Bit(file);
2386 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2388 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2389 for (y = 0; y < 3; y++)
2390 for (x = 0; x < 3; x++)
2391 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2393 /* correct invalid number of content fields -- should never happen */
2394 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2395 num_contents = STD_ELEMENT_CONTENTS;
2397 if (element == EL_YAMYAM)
2399 level->num_yamyam_contents = num_contents;
2401 for (i = 0; i < num_contents; i++)
2402 for (y = 0; y < 3; y++)
2403 for (x = 0; x < 3; x++)
2404 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2406 else if (element == EL_BD_AMOEBA)
2408 level->amoeba_content = content_array[0][0][0];
2412 Error(ERR_WARN, "cannot load content for element '%d'", element);
2418 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
2424 int chunk_size_expected;
2426 element = getMappedElement(getFile16BitBE(file));
2427 if (!IS_ENVELOPE(element))
2428 element = EL_ENVELOPE_1;
2430 envelope_nr = element - EL_ENVELOPE_1;
2432 envelope_len = getFile16BitBE(file);
2434 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2435 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2437 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2439 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2440 if (chunk_size_expected != chunk_size)
2442 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2443 return chunk_size_expected;
2446 for (i = 0; i < envelope_len; i++)
2447 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2452 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
2454 int num_changed_custom_elements = getFile16BitBE(file);
2455 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2458 if (chunk_size_expected != chunk_size)
2460 ReadUnusedBytesFromFile(file, chunk_size - 2);
2461 return chunk_size_expected;
2464 for (i = 0; i < num_changed_custom_elements; i++)
2466 int element = getMappedElement(getFile16BitBE(file));
2467 int properties = getFile32BitBE(file);
2469 if (IS_CUSTOM_ELEMENT(element))
2470 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2472 Error(ERR_WARN, "invalid custom element number %d", element);
2474 /* older game versions that wrote level files with CUS1 chunks used
2475 different default push delay values (not yet stored in level file) */
2476 element_info[element].push_delay_fixed = 2;
2477 element_info[element].push_delay_random = 8;
2483 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
2485 int num_changed_custom_elements = getFile16BitBE(file);
2486 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2489 if (chunk_size_expected != chunk_size)
2491 ReadUnusedBytesFromFile(file, chunk_size - 2);
2492 return chunk_size_expected;
2495 for (i = 0; i < num_changed_custom_elements; i++)
2497 int element = getMappedElement(getFile16BitBE(file));
2498 int custom_target_element = getMappedElement(getFile16BitBE(file));
2500 if (IS_CUSTOM_ELEMENT(element))
2501 element_info[element].change->target_element = custom_target_element;
2503 Error(ERR_WARN, "invalid custom element number %d", element);
2509 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
2511 int num_changed_custom_elements = getFile16BitBE(file);
2512 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2515 if (chunk_size_expected != chunk_size)
2517 ReadUnusedBytesFromFile(file, chunk_size - 2);
2518 return chunk_size_expected;
2521 for (i = 0; i < num_changed_custom_elements; i++)
2523 int element = getMappedElement(getFile16BitBE(file));
2524 struct ElementInfo *ei = &element_info[element];
2525 unsigned int event_bits;
2527 if (!IS_CUSTOM_ELEMENT(element))
2529 Error(ERR_WARN, "invalid custom element number %d", element);
2531 element = EL_INTERNAL_DUMMY;
2534 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2535 ei->description[j] = getFile8Bit(file);
2536 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2538 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2540 /* some free bytes for future properties and padding */
2541 ReadUnusedBytesFromFile(file, 7);
2543 ei->use_gfx_element = getFile8Bit(file);
2544 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2546 ei->collect_score_initial = getFile8Bit(file);
2547 ei->collect_count_initial = getFile8Bit(file);
2549 ei->push_delay_fixed = getFile16BitBE(file);
2550 ei->push_delay_random = getFile16BitBE(file);
2551 ei->move_delay_fixed = getFile16BitBE(file);
2552 ei->move_delay_random = getFile16BitBE(file);
2554 ei->move_pattern = getFile16BitBE(file);
2555 ei->move_direction_initial = getFile8Bit(file);
2556 ei->move_stepsize = getFile8Bit(file);
2558 for (y = 0; y < 3; y++)
2559 for (x = 0; x < 3; x++)
2560 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2562 event_bits = getFile32BitBE(file);
2563 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2564 if (event_bits & (1 << j))
2565 ei->change->has_event[j] = TRUE;
2567 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2569 ei->change->delay_fixed = getFile16BitBE(file);
2570 ei->change->delay_random = getFile16BitBE(file);
2571 ei->change->delay_frames = getFile16BitBE(file);
2573 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2575 ei->change->explode = getFile8Bit(file);
2576 ei->change->use_target_content = getFile8Bit(file);
2577 ei->change->only_if_complete = getFile8Bit(file);
2578 ei->change->use_random_replace = getFile8Bit(file);
2580 ei->change->random_percentage = getFile8Bit(file);
2581 ei->change->replace_when = getFile8Bit(file);
2583 for (y = 0; y < 3; y++)
2584 for (x = 0; x < 3; x++)
2585 ei->change->target_content.e[x][y] =
2586 getMappedElement(getFile16BitBE(file));
2588 ei->slippery_type = getFile8Bit(file);
2590 /* some free bytes for future properties and padding */
2591 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2593 /* mark that this custom element has been modified */
2594 ei->modified_settings = TRUE;
2600 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
2602 struct ElementInfo *ei;
2603 int chunk_size_expected;
2607 /* ---------- custom element base property values (96 bytes) ------------- */
2609 element = getMappedElement(getFile16BitBE(file));
2611 if (!IS_CUSTOM_ELEMENT(element))
2613 Error(ERR_WARN, "invalid custom element number %d", element);
2615 ReadUnusedBytesFromFile(file, chunk_size - 2);
2619 ei = &element_info[element];
2621 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2622 ei->description[i] = getFile8Bit(file);
2623 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2625 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2627 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2629 ei->num_change_pages = getFile8Bit(file);
2631 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2632 if (chunk_size_expected != chunk_size)
2634 ReadUnusedBytesFromFile(file, chunk_size - 43);
2635 return chunk_size_expected;
2638 ei->ce_value_fixed_initial = getFile16BitBE(file);
2639 ei->ce_value_random_initial = getFile16BitBE(file);
2640 ei->use_last_ce_value = getFile8Bit(file);
2642 ei->use_gfx_element = getFile8Bit(file);
2643 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2645 ei->collect_score_initial = getFile8Bit(file);
2646 ei->collect_count_initial = getFile8Bit(file);
2648 ei->drop_delay_fixed = getFile8Bit(file);
2649 ei->push_delay_fixed = getFile8Bit(file);
2650 ei->drop_delay_random = getFile8Bit(file);
2651 ei->push_delay_random = getFile8Bit(file);
2652 ei->move_delay_fixed = getFile16BitBE(file);
2653 ei->move_delay_random = getFile16BitBE(file);
2655 /* bits 0 - 15 of "move_pattern" ... */
2656 ei->move_pattern = getFile16BitBE(file);
2657 ei->move_direction_initial = getFile8Bit(file);
2658 ei->move_stepsize = getFile8Bit(file);
2660 ei->slippery_type = getFile8Bit(file);
2662 for (y = 0; y < 3; y++)
2663 for (x = 0; x < 3; x++)
2664 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2666 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2667 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2668 ei->move_leave_type = getFile8Bit(file);
2670 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2671 ei->move_pattern |= (getFile16BitBE(file) << 16);
2673 ei->access_direction = getFile8Bit(file);
2675 ei->explosion_delay = getFile8Bit(file);
2676 ei->ignition_delay = getFile8Bit(file);
2677 ei->explosion_type = getFile8Bit(file);
2679 /* some free bytes for future custom property values and padding */
2680 ReadUnusedBytesFromFile(file, 1);
2682 /* ---------- change page property values (48 bytes) --------------------- */
2684 setElementChangePages(ei, ei->num_change_pages);
2686 for (i = 0; i < ei->num_change_pages; i++)
2688 struct ElementChangeInfo *change = &ei->change_page[i];
2689 unsigned int event_bits;
2691 /* always start with reliable default values */
2692 setElementChangeInfoToDefaults(change);
2694 /* bits 0 - 31 of "has_event[]" ... */
2695 event_bits = getFile32BitBE(file);
2696 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2697 if (event_bits & (1 << j))
2698 change->has_event[j] = TRUE;
2700 change->target_element = getMappedElement(getFile16BitBE(file));
2702 change->delay_fixed = getFile16BitBE(file);
2703 change->delay_random = getFile16BitBE(file);
2704 change->delay_frames = getFile16BitBE(file);
2706 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2708 change->explode = getFile8Bit(file);
2709 change->use_target_content = getFile8Bit(file);
2710 change->only_if_complete = getFile8Bit(file);
2711 change->use_random_replace = getFile8Bit(file);
2713 change->random_percentage = getFile8Bit(file);
2714 change->replace_when = getFile8Bit(file);
2716 for (y = 0; y < 3; y++)
2717 for (x = 0; x < 3; x++)
2718 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2720 change->can_change = getFile8Bit(file);
2722 change->trigger_side = getFile8Bit(file);
2724 change->trigger_player = getFile8Bit(file);
2725 change->trigger_page = getFile8Bit(file);
2727 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2728 CH_PAGE_ANY : (1 << change->trigger_page));
2730 change->has_action = getFile8Bit(file);
2731 change->action_type = getFile8Bit(file);
2732 change->action_mode = getFile8Bit(file);
2733 change->action_arg = getFile16BitBE(file);
2735 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2736 event_bits = getFile8Bit(file);
2737 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2738 if (event_bits & (1 << (j - 32)))
2739 change->has_event[j] = TRUE;
2742 /* mark this custom element as modified */
2743 ei->modified_settings = TRUE;
2748 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
2750 struct ElementInfo *ei;
2751 struct ElementGroupInfo *group;
2755 element = getMappedElement(getFile16BitBE(file));
2757 if (!IS_GROUP_ELEMENT(element))
2759 Error(ERR_WARN, "invalid group element number %d", element);
2761 ReadUnusedBytesFromFile(file, chunk_size - 2);
2765 ei = &element_info[element];
2767 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2768 ei->description[i] = getFile8Bit(file);
2769 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2771 group = element_info[element].group;
2773 group->num_elements = getFile8Bit(file);
2775 ei->use_gfx_element = getFile8Bit(file);
2776 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2778 group->choice_mode = getFile8Bit(file);
2780 /* some free bytes for future values and padding */
2781 ReadUnusedBytesFromFile(file, 3);
2783 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2784 group->element[i] = getMappedElement(getFile16BitBE(file));
2786 /* mark this group element as modified */
2787 element_info[element].modified_settings = TRUE;
2792 static int LoadLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *conf,
2793 int element, int real_element)
2795 int micro_chunk_size = 0;
2796 int conf_type = getFile8Bit(file);
2797 int byte_mask = conf_type & CONF_MASK_BYTES;
2798 boolean element_found = FALSE;
2801 micro_chunk_size += 1;
2803 if (byte_mask == CONF_MASK_MULTI_BYTES)
2805 int num_bytes = getFile16BitBE(file);
2806 byte *buffer = checked_malloc(num_bytes);
2808 ReadBytesFromFile(file, buffer, num_bytes);
2810 for (i = 0; conf[i].data_type != -1; i++)
2812 if (conf[i].element == element &&
2813 conf[i].conf_type == conf_type)
2815 int data_type = conf[i].data_type;
2816 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2817 int max_num_entities = conf[i].max_num_entities;
2819 if (num_entities > max_num_entities)
2822 "truncating number of entities for element %d from %d to %d",
2823 element, num_entities, max_num_entities);
2825 num_entities = max_num_entities;
2828 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
2829 data_type == TYPE_CONTENT_LIST))
2831 /* for element and content lists, zero entities are not allowed */
2832 Error(ERR_WARN, "found empty list of entities for element %d",
2835 /* do not set "num_entities" here to prevent reading behind buffer */
2837 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
2841 *(int *)(conf[i].num_entities) = num_entities;
2844 element_found = TRUE;
2846 if (data_type == TYPE_STRING)
2848 char *string = (char *)(conf[i].value);
2851 for (j = 0; j < max_num_entities; j++)
2852 string[j] = (j < num_entities ? buffer[j] : '\0');
2854 else if (data_type == TYPE_ELEMENT_LIST)
2856 int *element_array = (int *)(conf[i].value);
2859 for (j = 0; j < num_entities; j++)
2861 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
2863 else if (data_type == TYPE_CONTENT_LIST)
2865 struct Content *content= (struct Content *)(conf[i].value);
2868 for (c = 0; c < num_entities; c++)
2869 for (y = 0; y < 3; y++)
2870 for (x = 0; x < 3; x++)
2871 content[c].e[x][y] =
2872 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
2875 element_found = FALSE;
2881 checked_free(buffer);
2883 micro_chunk_size += 2 + num_bytes;
2885 else /* constant size configuration data (1, 2 or 4 bytes) */
2887 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
2888 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
2889 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
2891 for (i = 0; conf[i].data_type != -1; i++)
2893 if (conf[i].element == element &&
2894 conf[i].conf_type == conf_type)
2896 int data_type = conf[i].data_type;
2898 if (data_type == TYPE_ELEMENT)
2899 value = getMappedElement(value);
2901 if (data_type == TYPE_BOOLEAN)
2902 *(boolean *)(conf[i].value) = value;
2904 *(int *) (conf[i].value) = value;
2906 element_found = TRUE;
2912 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
2917 char *error_conf_chunk_bytes =
2918 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
2919 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
2920 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
2921 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
2922 int error_element = real_element;
2924 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
2925 error_conf_chunk_bytes, error_conf_chunk_token,
2926 error_element, EL_NAME(error_element));
2929 return micro_chunk_size;
2932 static int LoadLevel_INFO(FILE *file, int chunk_size, struct LevelInfo *level)
2934 int real_chunk_size = 0;
2936 li = *level; /* copy level data into temporary buffer */
2940 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
2942 if (real_chunk_size >= chunk_size)
2946 *level = li; /* copy temporary buffer back to level data */
2948 return real_chunk_size;
2951 static int LoadLevel_CONF(FILE *file, int chunk_size, struct LevelInfo *level)
2953 int real_chunk_size = 0;
2955 li = *level; /* copy level data into temporary buffer */
2959 int element = getMappedElement(getFile16BitBE(file));
2961 real_chunk_size += 2;
2962 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
2964 if (real_chunk_size >= chunk_size)
2968 *level = li; /* copy temporary buffer back to level data */
2970 return real_chunk_size;
2973 static int LoadLevel_ELEM(FILE *file, int chunk_size, struct LevelInfo *level)
2975 int real_chunk_size = 0;
2977 li = *level; /* copy level data into temporary buffer */
2981 int element = getMappedElement(getFile16BitBE(file));
2983 real_chunk_size += 2;
2984 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
2986 if (real_chunk_size >= chunk_size)
2990 *level = li; /* copy temporary buffer back to level data */
2992 return real_chunk_size;
2995 static int LoadLevel_NOTE(FILE *file, int chunk_size, struct LevelInfo *level)
2997 int element = getMappedElement(getFile16BitBE(file));
2998 int envelope_nr = element - EL_ENVELOPE_1;
2999 int real_chunk_size = 2;
3003 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3006 if (real_chunk_size >= chunk_size)
3010 level->envelope[envelope_nr] = xx_envelope;
3012 return real_chunk_size;
3015 static int LoadLevel_CUSX(FILE *file, int chunk_size, struct LevelInfo *level)
3017 int element = getMappedElement(getFile16BitBE(file));
3018 int real_chunk_size = 2;
3019 struct ElementInfo *ei = &element_info[element];
3022 xx_ei = *ei; /* copy element data into temporary buffer */
3024 xx_ei.num_change_pages = -1;
3028 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3030 if (xx_ei.num_change_pages != -1)
3033 if (real_chunk_size >= chunk_size)
3039 if (ei->num_change_pages == -1)
3041 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3044 ei->num_change_pages = 1;
3046 setElementChangePages(ei, 1);
3047 setElementChangeInfoToDefaults(ei->change);
3049 return real_chunk_size;
3052 /* initialize number of change pages stored for this custom element */
3053 setElementChangePages(ei, ei->num_change_pages);
3054 for (i = 0; i < ei->num_change_pages; i++)
3055 setElementChangeInfoToDefaults(&ei->change_page[i]);
3057 /* start with reading properties for the first change page */
3058 xx_current_change_page = 0;
3062 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3064 xx_change = *change; /* copy change data into temporary buffer */
3066 resetEventBits(); /* reset bits; change page might have changed */
3068 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3071 *change = xx_change;
3073 setEventFlagsFromEventBits(change);
3075 if (real_chunk_size >= chunk_size)
3079 return real_chunk_size;
3082 static int LoadLevel_GRPX(FILE *file, int chunk_size, struct LevelInfo *level)
3084 int element = getMappedElement(getFile16BitBE(file));
3085 int real_chunk_size = 2;
3086 struct ElementInfo *ei = &element_info[element];
3087 struct ElementGroupInfo *group = ei->group;
3089 xx_ei = *ei; /* copy element data into temporary buffer */
3090 xx_group = *group; /* copy group data into temporary buffer */
3094 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3097 if (real_chunk_size >= chunk_size)
3104 return real_chunk_size;
3107 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3108 struct LevelFileInfo *level_file_info)
3110 char *filename = level_file_info->filename;
3111 char cookie[MAX_LINE_LEN];
3112 char chunk_name[CHUNK_ID_LEN + 1];
3116 if (!(file = fopen(filename, MODE_READ)))
3118 level->no_valid_file = TRUE;
3121 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3123 if (level != &level_template)
3124 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3130 getFileChunkBE(file, chunk_name, NULL);
3131 if (strEqual(chunk_name, "RND1"))
3133 getFile32BitBE(file); /* not used */
3135 getFileChunkBE(file, chunk_name, NULL);
3136 if (!strEqual(chunk_name, "CAVE"))
3138 level->no_valid_file = TRUE;
3140 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3145 else /* check for pre-2.0 file format with cookie string */
3147 strcpy(cookie, chunk_name);
3148 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
3149 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3150 cookie[strlen(cookie) - 1] = '\0';
3152 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3154 level->no_valid_file = TRUE;
3156 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3161 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3163 level->no_valid_file = TRUE;
3165 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3170 /* pre-2.0 level files have no game version, so use file version here */
3171 level->game_version = level->file_version;
3174 if (level->file_version < FILE_VERSION_1_2)
3176 /* level files from versions before 1.2.0 without chunk structure */
3177 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3178 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3186 int (*loader)(FILE *, int, struct LevelInfo *);
3190 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3191 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3192 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3193 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3194 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3195 { "INFO", -1, LoadLevel_INFO },
3196 { "BODY", -1, LoadLevel_BODY },
3197 { "CONT", -1, LoadLevel_CONT },
3198 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3199 { "CNT3", -1, LoadLevel_CNT3 },
3200 { "CUS1", -1, LoadLevel_CUS1 },
3201 { "CUS2", -1, LoadLevel_CUS2 },
3202 { "CUS3", -1, LoadLevel_CUS3 },
3203 { "CUS4", -1, LoadLevel_CUS4 },
3204 { "GRP1", -1, LoadLevel_GRP1 },
3205 { "CONF", -1, LoadLevel_CONF },
3206 { "ELEM", -1, LoadLevel_ELEM },
3207 { "NOTE", -1, LoadLevel_NOTE },
3208 { "CUSX", -1, LoadLevel_CUSX },
3209 { "GRPX", -1, LoadLevel_GRPX },
3214 while (getFileChunkBE(file, chunk_name, &chunk_size))
3218 while (chunk_info[i].name != NULL &&
3219 !strEqual(chunk_name, chunk_info[i].name))
3222 if (chunk_info[i].name == NULL)
3224 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3225 chunk_name, filename);
3226 ReadUnusedBytesFromFile(file, chunk_size);
3228 else if (chunk_info[i].size != -1 &&
3229 chunk_info[i].size != chunk_size)
3231 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3232 chunk_size, chunk_name, filename);
3233 ReadUnusedBytesFromFile(file, chunk_size);
3237 /* call function to load this level chunk */
3238 int chunk_size_expected =
3239 (chunk_info[i].loader)(file, chunk_size, level);
3241 /* the size of some chunks cannot be checked before reading other
3242 chunks first (like "HEAD" and "BODY") that contain some header
3243 information, so check them here */
3244 if (chunk_size_expected != chunk_size)
3246 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3247 chunk_size, chunk_name, filename);
3256 /* ------------------------------------------------------------------------- */
3257 /* functions for loading EM level */
3258 /* ------------------------------------------------------------------------- */
3262 static int map_em_element_yam(int element)
3266 case 0x00: return EL_EMPTY;
3267 case 0x01: return EL_EMERALD;
3268 case 0x02: return EL_DIAMOND;
3269 case 0x03: return EL_ROCK;
3270 case 0x04: return EL_ROBOT;
3271 case 0x05: return EL_SPACESHIP_UP;
3272 case 0x06: return EL_BOMB;
3273 case 0x07: return EL_BUG_UP;
3274 case 0x08: return EL_AMOEBA_DROP;
3275 case 0x09: return EL_NUT;
3276 case 0x0a: return EL_YAMYAM;
3277 case 0x0b: return EL_QUICKSAND_FULL;
3278 case 0x0c: return EL_SAND;
3279 case 0x0d: return EL_WALL_SLIPPERY;
3280 case 0x0e: return EL_STEELWALL;
3281 case 0x0f: return EL_WALL;
3282 case 0x10: return EL_EM_KEY_1;
3283 case 0x11: return EL_EM_KEY_2;
3284 case 0x12: return EL_EM_KEY_4;
3285 case 0x13: return EL_EM_KEY_3;
3286 case 0x14: return EL_MAGIC_WALL;
3287 case 0x15: return EL_ROBOT_WHEEL;
3288 case 0x16: return EL_DYNAMITE;
3290 case 0x17: return EL_EM_KEY_1; /* EMC */
3291 case 0x18: return EL_BUG_UP; /* EMC */
3292 case 0x1a: return EL_DIAMOND; /* EMC */
3293 case 0x1b: return EL_EMERALD; /* EMC */
3294 case 0x25: return EL_NUT; /* EMC */
3295 case 0x80: return EL_EMPTY; /* EMC */
3296 case 0x85: return EL_EM_KEY_1; /* EMC */
3297 case 0x86: return EL_EM_KEY_2; /* EMC */
3298 case 0x87: return EL_EM_KEY_4; /* EMC */
3299 case 0x88: return EL_EM_KEY_3; /* EMC */
3300 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
3301 case 0x9a: return EL_AMOEBA_WET; /* EMC */
3302 case 0xaf: return EL_DYNAMITE; /* EMC */
3303 case 0xbd: return EL_SAND; /* EMC */
3306 Error(ERR_WARN, "invalid level element %d", element);
3311 static int map_em_element_field(int element)
3313 if (element >= 0xc8 && element <= 0xe1)
3314 return EL_CHAR_A + (element - 0xc8);
3315 else if (element >= 0xe2 && element <= 0xeb)
3316 return EL_CHAR_0 + (element - 0xe2);
3320 case 0x00: return EL_ROCK;
3321 case 0x01: return EL_ROCK; /* EMC */
3322 case 0x02: return EL_DIAMOND;
3323 case 0x03: return EL_DIAMOND;
3324 case 0x04: return EL_ROBOT;
3325 case 0x05: return EL_ROBOT; /* EMC */
3326 case 0x06: return EL_EMPTY_SPACE; /* EMC */
3327 case 0x07: return EL_EMPTY_SPACE; /* EMC */
3328 case 0x08: return EL_SPACESHIP_UP;
3329 case 0x09: return EL_SPACESHIP_RIGHT;
3330 case 0x0a: return EL_SPACESHIP_DOWN;
3331 case 0x0b: return EL_SPACESHIP_LEFT;
3332 case 0x0c: return EL_SPACESHIP_UP;
3333 case 0x0d: return EL_SPACESHIP_RIGHT;
3334 case 0x0e: return EL_SPACESHIP_DOWN;
3335 case 0x0f: return EL_SPACESHIP_LEFT;
3337 case 0x10: return EL_BOMB;
3338 case 0x11: return EL_BOMB; /* EMC */
3339 case 0x12: return EL_EMERALD;
3340 case 0x13: return EL_EMERALD;
3341 case 0x14: return EL_BUG_UP;
3342 case 0x15: return EL_BUG_RIGHT;
3343 case 0x16: return EL_BUG_DOWN;
3344 case 0x17: return EL_BUG_LEFT;
3345 case 0x18: return EL_BUG_UP;
3346 case 0x19: return EL_BUG_RIGHT;
3347 case 0x1a: return EL_BUG_DOWN;
3348 case 0x1b: return EL_BUG_LEFT;
3349 case 0x1c: return EL_AMOEBA_DROP;
3350 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
3351 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
3352 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
3354 case 0x20: return EL_ROCK;
3355 case 0x21: return EL_BOMB; /* EMC */
3356 case 0x22: return EL_DIAMOND; /* EMC */
3357 case 0x23: return EL_EMERALD; /* EMC */
3358 case 0x24: return EL_MAGIC_WALL;
3359 case 0x25: return EL_NUT;
3360 case 0x26: return EL_NUT; /* EMC */
3361 case 0x27: return EL_NUT; /* EMC */
3363 /* looks like magic wheel, but is _always_ activated */
3364 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
3366 case 0x29: return EL_YAMYAM; /* up */
3367 case 0x2a: return EL_YAMYAM; /* down */
3368 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
3369 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
3370 case 0x2d: return EL_QUICKSAND_FULL;
3371 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
3372 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
3374 case 0x30: return EL_EMPTY_SPACE; /* EMC */
3375 case 0x31: return EL_SAND; /* EMC */
3376 case 0x32: return EL_SAND; /* EMC */
3377 case 0x33: return EL_SAND; /* EMC */
3378 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
3379 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
3380 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
3381 case 0x37: return EL_SAND; /* EMC */
3382 case 0x38: return EL_ROCK; /* EMC */
3383 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
3384 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
3385 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
3386 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
3387 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
3388 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
3389 case 0x3f: return EL_ACID_POOL_BOTTOM;
3391 case 0x40: return EL_EXIT_OPEN; /* 1 */
3392 case 0x41: return EL_EXIT_OPEN; /* 2 */
3393 case 0x42: return EL_EXIT_OPEN; /* 3 */
3394 case 0x43: return EL_BALLOON; /* EMC */
3395 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
3396 case 0x45: return EL_SPRING; /* EMC */
3397 case 0x46: return EL_SPRING; /* falling */ /* EMC */
3398 case 0x47: return EL_SPRING; /* left */ /* EMC */
3399 case 0x48: return EL_SPRING; /* right */ /* EMC */
3400 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
3401 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
3402 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
3403 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
3404 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
3405 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
3406 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
3408 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
3409 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
3410 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
3411 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
3412 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
3413 case 0x55: return EL_EMPTY_SPACE; /* EMC */
3414 case 0x56: return EL_EMPTY_SPACE; /* EMC */
3415 case 0x57: return EL_EMPTY_SPACE; /* EMC */
3416 case 0x58: return EL_EMPTY_SPACE; /* EMC */
3417 case 0x59: return EL_EMPTY_SPACE; /* EMC */
3418 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
3419 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
3420 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
3421 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
3422 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
3423 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
3425 case 0x60: return EL_EMPTY_SPACE; /* EMC */
3426 case 0x61: return EL_EMPTY_SPACE; /* EMC */
3427 case 0x62: return EL_EMPTY_SPACE; /* EMC */
3428 case 0x63: return EL_SPRING; /* left */ /* EMC */
3429 case 0x64: return EL_SPRING; /* right */ /* EMC */
3430 case 0x65: return EL_ACID; /* 1 */ /* EMC */
3431 case 0x66: return EL_ACID; /* 2 */ /* EMC */
3432 case 0x67: return EL_ACID; /* 3 */ /* EMC */
3433 case 0x68: return EL_ACID; /* 4 */ /* EMC */
3434 case 0x69: return EL_ACID; /* 5 */ /* EMC */
3435 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
3436 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
3437 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
3438 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
3439 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
3440 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
3442 case 0x70: return EL_EMPTY_SPACE; /* EMC */
3443 case 0x71: return EL_EMPTY_SPACE; /* EMC */
3444 case 0x72: return EL_NUT; /* left */ /* EMC */
3445 case 0x73: return EL_SAND; /* EMC (? "nut") */
3446 case 0x74: return EL_STEELWALL;
3447 case 0x75: return EL_EMPTY_SPACE; /* EMC */
3448 case 0x76: return EL_EMPTY_SPACE; /* EMC */
3449 case 0x77: return EL_BOMB; /* left */ /* EMC */
3450 case 0x78: return EL_BOMB; /* right */ /* EMC */
3451 case 0x79: return EL_ROCK; /* left */ /* EMC */
3452 case 0x7a: return EL_ROCK; /* right */ /* EMC */
3453 case 0x7b: return EL_ACID; /* (? EMC "blank") */
3454 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
3455 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
3456 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
3457 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
3459 case 0x80: return EL_EMPTY;
3460 case 0x81: return EL_WALL_SLIPPERY;
3461 case 0x82: return EL_SAND;
3462 case 0x83: return EL_STEELWALL;
3463 case 0x84: return EL_WALL;
3464 case 0x85: return EL_EM_KEY_1;
3465 case 0x86: return EL_EM_KEY_2;
3466 case 0x87: return EL_EM_KEY_4;
3467 case 0x88: return EL_EM_KEY_3;
3468 case 0x89: return EL_EM_GATE_1;
3469 case 0x8a: return EL_EM_GATE_2;
3470 case 0x8b: return EL_EM_GATE_4;
3471 case 0x8c: return EL_EM_GATE_3;
3472 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
3473 case 0x8e: return EL_EM_GATE_1_GRAY;
3474 case 0x8f: return EL_EM_GATE_2_GRAY;
3476 case 0x90: return EL_EM_GATE_4_GRAY;
3477 case 0x91: return EL_EM_GATE_3_GRAY;
3478 case 0x92: return EL_MAGIC_WALL;
3479 case 0x93: return EL_ROBOT_WHEEL;
3480 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
3481 case 0x95: return EL_ACID_POOL_TOPLEFT;
3482 case 0x96: return EL_ACID_POOL_TOPRIGHT;
3483 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
3484 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
3485 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
3486 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
3487 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
3488 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
3489 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
3490 case 0x9e: return EL_EXIT_CLOSED;
3491 case 0x9f: return EL_CHAR_LESS; /* arrow left */
3493 /* looks like normal sand, but behaves like wall */
3494 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
3495 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
3496 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
3497 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
3498 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
3499 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
3500 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
3501 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
3502 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
3503 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
3504 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
3505 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
3506 case 0xac: return EL_CHAR_COMMA; /* EMC */
3507 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
3508 case 0xae: return EL_CHAR_MINUS; /* EMC */
3509 case 0xaf: return EL_DYNAMITE;
3511 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
3512 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
3513 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
3514 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
3515 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
3516 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
3517 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
3518 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
3519 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
3520 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
3521 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
3522 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
3523 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
3524 case 0xbd: return EL_SAND; /* EMC ("dirt") */
3525 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
3526 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
3528 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
3529 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
3530 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
3531 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
3532 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
3533 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
3534 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
3535 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
3537 /* characters: see above */
3539 case 0xec: return EL_CHAR_PERIOD;
3540 case 0xed: return EL_CHAR_EXCLAM;
3541 case 0xee: return EL_CHAR_COLON;
3542 case 0xef: return EL_CHAR_QUESTION;
3544 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
3545 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
3546 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
3547 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
3548 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
3549 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
3550 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
3551 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
3553 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
3554 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
3555 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
3556 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
3557 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
3558 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
3560 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
3561 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
3564 /* should never happen (all 8-bit value cases should be handled) */
3565 Error(ERR_WARN, "invalid level element %d", element);
3570 #define EM_LEVEL_SIZE 2106
3571 #define EM_LEVEL_XSIZE 64
3572 #define EM_LEVEL_YSIZE 32
3574 static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
3575 struct LevelFileInfo *level_file_info)
3577 char *filename = level_file_info->filename;
3579 unsigned char leveldata[EM_LEVEL_SIZE];
3580 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
3581 int nr = level_file_info->nr;
3584 if (!(file = fopen(filename, MODE_READ)))
3586 level->no_valid_file = TRUE;
3588 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3593 for (i = 0; i < EM_LEVEL_SIZE; i++)
3594 leveldata[i] = fgetc(file);
3598 /* check if level data is crypted by testing against known starting bytes
3599 of the few existing crypted level files (from Emerald Mine 1 + 2) */
3601 if ((leveldata[0] == 0xf1 ||
3602 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
3604 unsigned char code0 = 0x65;
3605 unsigned char code1 = 0x11;
3607 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
3608 leveldata[0] = 0xf1;
3610 /* decode crypted level data */
3612 for (i = 0; i < EM_LEVEL_SIZE; i++)
3614 leveldata[i] ^= code0;
3615 leveldata[i] -= code1;
3617 code0 = (code0 + 7) & 0xff;
3621 level->fieldx = EM_LEVEL_XSIZE;
3622 level->fieldy = EM_LEVEL_YSIZE;
3624 level->time = header[46] * 10;
3625 level->gems_needed = header[47];
3627 /* The original Emerald Mine levels have their level number stored
3628 at the second byte of the level file...
3629 Do not trust this information at other level files, e.g. EMC,
3630 but correct it anyway (normally the first row is completely
3631 steel wall, so the correction does not hurt anyway). */
3633 if (leveldata[1] == nr)
3634 leveldata[1] = leveldata[2]; /* correct level number field */
3636 sprintf(level->name, "Level %d", nr); /* set level name */
3638 level->score[SC_EMERALD] = header[36];
3639 level->score[SC_DIAMOND] = header[37];
3640 level->score[SC_ROBOT] = header[38];
3641 level->score[SC_SPACESHIP] = header[39];
3642 level->score[SC_BUG] = header[40];
3643 level->score[SC_YAMYAM] = header[41];
3644 level->score[SC_NUT] = header[42];
3645 level->score[SC_DYNAMITE] = header[43];
3646 level->score[SC_TIME_BONUS] = header[44];
3648 level->num_yamyam_contents = 4;
3650 for (i = 0; i < level->num_yamyam_contents; i++)
3651 for (y = 0; y < 3; y++)
3652 for (x = 0; x < 3; x++)
3653 level->yamyam_content[i].e[x][y] =
3654 map_em_element_yam(header[i * 9 + y * 3 + x]);
3656 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
3657 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
3658 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
3659 level->amoeba_content = EL_DIAMOND;
3661 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3663 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
3665 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
3666 new_element = EL_AMOEBA_WET;
3668 level->field[x][y] = new_element;
3671 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
3672 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
3673 level->field[x][y] = EL_PLAYER_1;
3675 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
3676 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
3677 level->field[x][y] = EL_PLAYER_2;
3682 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3684 static int ball_xy[8][2] =
3695 struct LevelInfo_EM *level_em = level->native_em_level;
3696 struct LEVEL *lev = level_em->lev;
3697 struct PLAYER **ply = level_em->ply;
3700 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3701 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3703 lev->time_seconds = level->time;
3704 lev->required_initial = level->gems_needed;
3706 lev->emerald_score = level->score[SC_EMERALD];
3707 lev->diamond_score = level->score[SC_DIAMOND];
3708 lev->alien_score = level->score[SC_ROBOT];
3709 lev->tank_score = level->score[SC_SPACESHIP];
3710 lev->bug_score = level->score[SC_BUG];
3711 lev->eater_score = level->score[SC_YAMYAM];
3712 lev->nut_score = level->score[SC_NUT];
3713 lev->dynamite_score = level->score[SC_DYNAMITE];
3714 lev->key_score = level->score[SC_KEY];
3715 lev->exit_score = level->score[SC_TIME_BONUS];
3717 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3718 for (y = 0; y < 3; y++)
3719 for (x = 0; x < 3; x++)
3720 lev->eater_array[i][y * 3 + x] =
3721 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3723 lev->amoeba_time = level->amoeba_speed;
3724 lev->wonderwall_time_initial = level->time_magic_wall;
3725 lev->wheel_time = level->time_wheel;
3727 lev->android_move_time = level->android_move_time;
3728 lev->android_clone_time = level->android_clone_time;
3729 lev->ball_random = level->ball_random;
3730 lev->ball_state_initial = level->ball_state_initial;
3731 lev->ball_time = level->ball_time;
3732 lev->num_ball_arrays = level->num_ball_contents;
3734 lev->lenses_score = level->lenses_score;
3735 lev->magnify_score = level->magnify_score;
3736 lev->slurp_score = level->slurp_score;
3738 lev->lenses_time = level->lenses_time;
3739 lev->magnify_time = level->magnify_time;
3741 lev->wind_direction_initial =
3742 map_direction_RND_to_EM(level->wind_direction_initial);
3743 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3744 lev->wind_time : 0);
3746 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3747 for (j = 0; j < 8; j++)
3748 lev->ball_array[i][j] =
3749 map_element_RND_to_EM(level->
3750 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3752 map_android_clone_elements_RND_to_EM(level);
3754 /* first fill the complete playfield with the default border element */
3755 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3756 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3757 level_em->cave[x][y] = ZBORDER;
3759 if (BorderElement == EL_STEELWALL)
3761 for (y = 0; y < lev->height + 2; y++)
3762 for (x = 0; x < lev->width + 2; x++)
3763 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3766 /* then copy the real level contents from level file into the playfield */
3767 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3769 int new_element = map_element_RND_to_EM(level->field[x][y]);
3770 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3771 int xx = x + 1 + offset;
3772 int yy = y + 1 + offset;
3774 if (level->field[x][y] == EL_AMOEBA_DEAD)
3775 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3777 level_em->cave[xx][yy] = new_element;
3780 for (i = 0; i < MAX_PLAYERS; i++)
3782 ply[i]->x_initial = 0;
3783 ply[i]->y_initial = 0;
3786 /* initialize player positions and delete players from the playfield */
3787 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3789 if (ELEM_IS_PLAYER(level->field[x][y]))
3791 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3792 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3793 int xx = x + 1 + offset;
3794 int yy = y + 1 + offset;
3796 ply[player_nr]->x_initial = xx;
3797 ply[player_nr]->y_initial = yy;
3799 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3803 if (BorderElement == EL_STEELWALL)
3810 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3812 static int ball_xy[8][2] =
3823 struct LevelInfo_EM *level_em = level->native_em_level;
3824 struct LEVEL *lev = level_em->lev;
3825 struct PLAYER **ply = level_em->ply;
3828 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3829 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3831 level->time = lev->time_seconds;
3832 level->gems_needed = lev->required_initial;
3834 sprintf(level->name, "Level %d", level->file_info.nr);
3836 level->score[SC_EMERALD] = lev->emerald_score;
3837 level->score[SC_DIAMOND] = lev->diamond_score;
3838 level->score[SC_ROBOT] = lev->alien_score;
3839 level->score[SC_SPACESHIP] = lev->tank_score;
3840 level->score[SC_BUG] = lev->bug_score;
3841 level->score[SC_YAMYAM] = lev->eater_score;
3842 level->score[SC_NUT] = lev->nut_score;
3843 level->score[SC_DYNAMITE] = lev->dynamite_score;
3844 level->score[SC_KEY] = lev->key_score;
3845 level->score[SC_TIME_BONUS] = lev->exit_score;
3847 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3849 for (i = 0; i < level->num_yamyam_contents; i++)
3850 for (y = 0; y < 3; y++)
3851 for (x = 0; x < 3; x++)
3852 level->yamyam_content[i].e[x][y] =
3853 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3855 level->amoeba_speed = lev->amoeba_time;
3856 level->time_magic_wall = lev->wonderwall_time_initial;
3857 level->time_wheel = lev->wheel_time;
3859 level->android_move_time = lev->android_move_time;
3860 level->android_clone_time = lev->android_clone_time;
3861 level->ball_random = lev->ball_random;
3862 level->ball_state_initial = lev->ball_state_initial;
3863 level->ball_time = lev->ball_time;
3864 level->num_ball_contents = lev->num_ball_arrays;
3866 level->lenses_score = lev->lenses_score;
3867 level->magnify_score = lev->magnify_score;
3868 level->slurp_score = lev->slurp_score;
3870 level->lenses_time = lev->lenses_time;
3871 level->magnify_time = lev->magnify_time;
3873 level->wind_direction_initial =
3874 map_direction_EM_to_RND(lev->wind_direction_initial);
3876 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3877 for (j = 0; j < 8; j++)
3878 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3879 map_element_EM_to_RND(lev->ball_array[i][j]);
3881 map_android_clone_elements_EM_to_RND(level);
3883 /* convert the playfield (some elements need special treatment) */
3884 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3886 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3888 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3889 new_element = EL_AMOEBA_DEAD;
3891 level->field[x][y] = new_element;
3894 for (i = 0; i < MAX_PLAYERS; i++)
3896 /* in case of all players set to the same field, use the first player */
3897 int nr = MAX_PLAYERS - i - 1;
3898 int jx = ply[nr]->x_initial - 1;
3899 int jy = ply[nr]->y_initial - 1;
3901 if (jx != -1 && jy != -1)
3902 level->field[jx][jy] = EL_PLAYER_1 + nr;
3907 /* ------------------------------------------------------------------------- */
3908 /* functions for loading SP level */
3909 /* ------------------------------------------------------------------------- */
3913 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
3914 #define SP_LEVEL_SIZE 1536
3915 #define SP_LEVEL_XSIZE 60
3916 #define SP_LEVEL_YSIZE 24
3917 #define SP_LEVEL_NAME_LEN 23
3919 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
3922 int initial_player_gravity;
3923 int num_special_ports;
3926 /* for details of the Supaplex level format, see Herman Perk's Supaplex
3927 documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
3929 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
3930 for (y = 0; y < SP_LEVEL_YSIZE; y++)
3932 for (x = 0; x < SP_LEVEL_XSIZE; x++)
3934 int element_old = fgetc(file);
3937 if (element_old <= 0x27)
3938 element_new = getMappedElement(EL_SP_START + element_old);
3939 else if (element_old == 0x28)
3940 element_new = EL_INVISIBLE_WALL;
3943 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
3944 Error(ERR_WARN, "invalid level element %d", element_old);
3946 element_new = EL_UNKNOWN;
3949 level->field[x][y] = element_new;
3953 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
3955 /* initial gravity: 1 == "on", anything else (0) == "off" */
3956 initial_player_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
3958 for (i = 0; i < MAX_PLAYERS; i++)
3959 level->initial_player_gravity[i] = initial_player_gravity;
3961 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
3963 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
3964 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3965 level->name[i] = fgetc(file);
3966 level->name[SP_LEVEL_NAME_LEN] = '\0';
3968 /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
3969 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
3971 /* number of infotrons needed; 0 means that Supaplex will count the total
3972 amount of infotrons in the level and use the low byte of that number
3973 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
3974 level->gems_needed = fgetc(file);
3976 /* number of special ("gravity") port entries below (maximum 10 allowed) */
3977 num_special_ports = fgetc(file);
3979 /* database of properties of up to 10 special ports (6 bytes per port) */
3980 for (i = 0; i < 10; i++)
3982 int port_location, port_x, port_y, port_element;
3985 /* high and low byte of the location of a special port; if (x, y) are the
3986 coordinates of a port in the field and (0, 0) is the top-left corner,
3987 the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
3988 of what may be expected: Supaplex works with a game field in memory
3989 which is 2 bytes per tile) */
3990 port_location = getFile16BitBE(file);
3992 /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
3993 gravity = fgetc(file);
3995 /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
3996 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
3998 /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
3999 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
4001 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
4003 if (i >= num_special_ports)
4006 port_x = (port_location / 2) % SP_LEVEL_XSIZE;
4007 port_y = (port_location / 2) / SP_LEVEL_XSIZE;
4009 if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
4010 port_y < 0 || port_y >= SP_LEVEL_YSIZE)
4012 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
4018 port_element = level->field[port_x][port_y];
4020 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4021 port_element > EL_SP_GRAVITY_PORT_UP)
4023 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
4028 /* change previous (wrong) gravity inverting special port to either
4029 gravity enabling special port or gravity disabling special port */
4030 level->field[port_x][port_y] +=
4031 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4032 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4035 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
4037 /* change special gravity ports without database entries to normal ports */
4038 for (y = 0; y < SP_LEVEL_YSIZE; y++)
4039 for (x = 0; x < SP_LEVEL_XSIZE; x++)
4040 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4041 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4042 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4044 /* auto-determine number of infotrons if it was stored as "0" -- see above */
4045 if (level->gems_needed == 0)
4047 for (y = 0; y < SP_LEVEL_YSIZE; y++)
4048 for (x = 0; x < SP_LEVEL_XSIZE; x++)
4049 if (level->field[x][y] == EL_SP_INFOTRON)
4050 level->gems_needed++;
4052 level->gems_needed &= 0xff; /* only use low byte -- see above */
4055 level->fieldx = SP_LEVEL_XSIZE;
4056 level->fieldy = SP_LEVEL_YSIZE;
4058 level->time = 0; /* no time limit */
4059 level->amoeba_speed = 0;
4060 level->time_magic_wall = 0;
4061 level->time_wheel = 0;
4062 level->amoeba_content = EL_EMPTY;
4065 /* original Supaplex does not use score values -- use default values */
4067 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4068 level->score[i] = 0;
4071 /* there are no yamyams in supaplex levels */
4072 for (i = 0; i < level->num_yamyam_contents; i++)
4073 for (y = 0; y < 3; y++)
4074 for (x = 0; x < 3; x++)
4075 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4078 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
4079 struct LevelFileInfo *level_file_info)
4081 char *filename = level_file_info->filename;
4083 int nr = level_file_info->nr - leveldir_current->first_level;
4085 char name_first, name_last;
4086 struct LevelInfo multipart_level;
4087 int multipart_xpos, multipart_ypos;
4088 boolean is_multipart_level;
4089 boolean is_first_part;
4090 boolean reading_multipart_level = FALSE;
4091 boolean use_empty_level = FALSE;
4093 if (!(file = fopen(filename, MODE_READ)))
4095 level->no_valid_file = TRUE;
4097 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4102 /* position file stream to the requested level inside the level package */
4103 if (level_file_info->packed &&
4104 fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
4106 level->no_valid_file = TRUE;
4108 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
4113 /* there exist Supaplex level package files with multi-part levels which
4114 can be detected as follows: instead of leading and trailing dashes ('-')
4115 to pad the level name, they have leading and trailing numbers which are
4116 the x and y coordinations of the current part of the multi-part level;
4117 if there are '?' characters instead of numbers on the left or right side
4118 of the level name, the multi-part level consists of only horizontal or
4121 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
4123 LoadLevelFromFileStream_SP(file, level, l);
4125 /* check if this level is a part of a bigger multi-part level */
4127 name_first = level->name[0];
4128 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
4130 is_multipart_level =
4131 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
4132 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
4135 ((name_first == '?' || name_first == '1') &&
4136 (name_last == '?' || name_last == '1'));
4138 /* correct leading multipart level meta information in level name */
4139 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
4140 level->name[i] = '-';
4142 /* correct trailing multipart level meta information in level name */
4143 for (i = SP_LEVEL_NAME_LEN - 1; i >= 0 && level->name[i] == name_last; i--)
4144 level->name[i] = '-';
4146 /* ---------- check for normal single level ---------- */
4148 if (!reading_multipart_level && !is_multipart_level)
4150 /* the current level is simply a normal single-part level, and we are
4151 not reading a multi-part level yet, so return the level as it is */
4156 /* ---------- check for empty level (unused multi-part) ---------- */
4158 if (!reading_multipart_level && is_multipart_level && !is_first_part)
4160 /* this is a part of a multi-part level, but not the first part
4161 (and we are not already reading parts of a multi-part level);
4162 in this case, use an empty level instead of the single part */
4164 use_empty_level = TRUE;
4169 /* ---------- check for finished multi-part level ---------- */
4171 if (reading_multipart_level &&
4172 (!is_multipart_level ||
4173 !strEqual(level->name, multipart_level.name)))
4175 /* we are already reading parts of a multi-part level, but this level is
4176 either not a multi-part level, or a part of a different multi-part
4177 level; in both cases, the multi-part level seems to be complete */
4182 /* ---------- here we have one part of a multi-part level ---------- */
4184 reading_multipart_level = TRUE;
4186 if (is_first_part) /* start with first part of new multi-part level */
4188 /* copy level info structure from first part */
4189 multipart_level = *level;
4191 /* clear playfield of new multi-part level */
4192 for (y = 0; y < MAX_LEV_FIELDY; y++)
4193 for (x = 0; x < MAX_LEV_FIELDX; x++)
4194 multipart_level.field[x][y] = EL_EMPTY;
4197 if (name_first == '?')
4199 if (name_last == '?')
4202 multipart_xpos = (int)(name_first - '0');
4203 multipart_ypos = (int)(name_last - '0');
4206 printf("----------> part (%d/%d) of multi-part level '%s'\n",
4207 multipart_xpos, multipart_ypos, multipart_level.name);
4210 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
4211 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
4213 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
4218 multipart_level.fieldx = MAX(multipart_level.fieldx,
4219 multipart_xpos * SP_LEVEL_XSIZE);
4220 multipart_level.fieldy = MAX(multipart_level.fieldy,
4221 multipart_ypos * SP_LEVEL_YSIZE);
4223 /* copy level part at the right position of multi-part level */
4224 for (y = 0; y < SP_LEVEL_YSIZE; y++)
4226 for (x = 0; x < SP_LEVEL_XSIZE; x++)
4228 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
4229 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
4231 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
4238 if (use_empty_level)
4240 setLevelInfoToDefaults(level);
4242 level->fieldx = SP_LEVEL_XSIZE;
4243 level->fieldy = SP_LEVEL_YSIZE;
4245 for (y = 0; y < SP_LEVEL_YSIZE; y++)
4246 for (x = 0; x < SP_LEVEL_XSIZE; x++)
4247 level->field[x][y] = EL_EMPTY;
4249 strcpy(level->name, "-------- EMPTY --------");
4251 Error(ERR_WARN, "single part of multi-part level -- using empty level");
4254 if (reading_multipart_level)
4255 *level = multipart_level;
4260 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4262 struct LevelInfo_SP *level_sp = level->native_sp_level;
4263 LevelInfoType *header = &level_sp->header;
4266 level_sp->width = level->fieldx;
4267 level_sp->height = level->fieldy;
4269 for (x = 0; x < level->fieldx; x++)
4271 for (y = 0; y < level->fieldy; y++)
4273 int element_old = level->field[x][y];
4276 if (element_old >= EL_SP_START &&
4277 element_old <= EL_SP_END)
4278 element_new = element_old - EL_SP_START;
4279 else if (element_old == EL_EMPTY_SPACE)
4281 else if (element_old == EL_INVISIBLE_WALL)
4284 element_new = 0x20; /* map unknown elements to yellow "hardware" */
4286 level_sp->playfield[x][y] = element_new;
4290 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4292 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4293 header->LevelTitle[i] = level->name[i];
4294 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
4296 header->InfotronsNeeded = level->gems_needed;
4298 header->SpecialPortCount = 0;
4300 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4302 boolean gravity_port_found = FALSE;
4303 boolean gravity_port_valid = FALSE;
4304 int gravity_port_flag;
4305 int gravity_port_base_element;
4306 int element = level->field[x][y];
4308 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4309 element <= EL_SP_GRAVITY_ON_PORT_UP)
4311 gravity_port_found = TRUE;
4312 gravity_port_valid = TRUE;
4313 gravity_port_flag = 1;
4314 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4316 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4317 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4319 gravity_port_found = TRUE;
4320 gravity_port_valid = TRUE;
4321 gravity_port_flag = 0;
4322 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4324 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4325 element <= EL_SP_GRAVITY_PORT_UP)
4327 /* change R'n'D style gravity inverting special port to normal port
4328 (there are no gravity inverting ports in native Supaplex engine) */
4330 gravity_port_found = TRUE;
4331 gravity_port_valid = FALSE;
4332 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4335 if (gravity_port_found)
4337 if (gravity_port_valid &&
4338 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4340 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4342 port->PortLocation = (y * level->fieldx + x) * 2;
4343 port->Gravity = gravity_port_flag;
4345 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4347 header->SpecialPortCount++;
4351 /* change special gravity port to normal port */
4353 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4356 level_sp->playfield[x][y] = element - EL_SP_START;
4361 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4363 struct LevelInfo_SP *level_sp = level->native_sp_level;
4364 LevelInfoType *header = &level_sp->header;
4367 level->fieldx = level_sp->width;
4368 level->fieldy = level_sp->height;
4370 for (x = 0; x < level->fieldx; x++)
4372 for (y = 0; y < level->fieldy; y++)
4374 int element_old = level_sp->playfield[x][y];
4377 if (element_old <= 0x27)
4378 element_new = getMappedElement(EL_SP_START + element_old);
4379 else if (element_old == 0x28)
4380 element_new = EL_INVISIBLE_WALL;
4383 Error(ERR_WARN, "invalid element %d at position %d, %d",
4386 element_new = EL_UNKNOWN;
4389 level->field[x][y] = element_new;
4393 for (i = 0; i < MAX_PLAYERS; i++)
4394 level->initial_player_gravity[i] =
4395 (header->InitialGravity == 1 ? TRUE : FALSE);
4397 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4398 level->name[i] = header->LevelTitle[i];
4399 level->name[SP_LEVEL_NAME_LEN] = '\0';
4401 level->gems_needed = header->InfotronsNeeded;
4403 for (i = 0; i < header->SpecialPortCount; i++)
4405 SpecialPortType *port = &header->SpecialPort[i];
4406 int port_location = port->PortLocation;
4407 int gravity = port->Gravity;
4408 int port_x, port_y, port_element;
4410 port_x = (port_location / 2) % level->fieldx;
4411 port_y = (port_location / 2) / level->fieldx;
4413 if (port_x < 0 || port_x >= level->fieldx ||
4414 port_y < 0 || port_y >= level->fieldy)
4416 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
4422 port_element = level->field[port_x][port_y];
4424 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4425 port_element > EL_SP_GRAVITY_PORT_UP)
4427 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
4432 /* change previous (wrong) gravity inverting special port to either
4433 gravity enabling special port or gravity disabling special port */
4434 level->field[port_x][port_y] +=
4435 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4436 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4439 /* change special gravity ports without database entries to normal ports */
4440 for (x = 0; x < level->fieldx; x++)
4441 for (y = 0; y < level->fieldy; y++)
4442 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4443 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4444 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4446 level->time = 0; /* no time limit */
4447 level->amoeba_speed = 0;
4448 level->time_magic_wall = 0;
4449 level->time_wheel = 0;
4450 level->amoeba_content = EL_EMPTY;
4453 /* original Supaplex does not use score values -- use default values */
4455 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4456 level->score[i] = 0;
4459 /* there are no yamyams in supaplex levels */
4460 for (i = 0; i < level->num_yamyam_contents; i++)
4461 for (x = 0; x < 3; x++)
4462 for (y = 0; y < 3; y++)
4463 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4466 static void setTapeInfoToDefaults();
4468 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4470 struct LevelInfo_SP *level_sp = level->native_sp_level;
4471 struct DemoInfo_SP *demo = &level_sp->demo;
4474 /* always start with reliable default values */
4475 setTapeInfoToDefaults();
4477 tape.level_nr = demo->level_nr; /* (currently not used) */
4478 tape.length = demo->length - 1; /* without "end of demo" byte */
4479 tape.random_seed = level_sp->header.DemoRandomSeed;
4481 // tape.date = <SET FROM FILE DATE OF *.SP FILE>
4483 for (i = 0; i < demo->length - 1; i++)
4485 int demo_action = demo->data[i] & 0x0f;
4486 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4488 tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
4489 tape.pos[i].delay = demo_repeat + 1;
4492 tape.length_seconds = GetTapeLength();
4496 /* ------------------------------------------------------------------------- */
4497 /* functions for loading DC level */
4498 /* ------------------------------------------------------------------------- */
4500 #define DC_LEVEL_HEADER_SIZE 344
4502 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
4504 static int last_data_encoded;
4508 int diff_hi, diff_lo;
4509 int data_hi, data_lo;
4510 unsigned short data_decoded;
4514 last_data_encoded = 0;
4521 diff = data_encoded - last_data_encoded;
4522 diff_hi = diff & ~0xff;
4523 diff_lo = diff & 0xff;
4527 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4528 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4529 data_hi = data_hi & 0xff00;
4531 data_decoded = data_hi | data_lo;
4533 last_data_encoded = data_encoded;
4535 offset1 = (offset1 + 1) % 31;
4536 offset2 = offset2 & 0xff;
4538 return data_decoded;
4541 int getMappedElement_DC(int element)
4549 /* 0x0117 - 0x036e: (?) */
4552 /* 0x042d - 0x0684: (?) */
4568 element = EL_CRYSTAL;
4571 case 0x0e77: /* quicksand (boulder) */
4572 element = EL_QUICKSAND_FAST_FULL;
4575 case 0x0e99: /* slow quicksand (boulder) */
4576 element = EL_QUICKSAND_FULL;
4580 element = EL_EM_EXIT_OPEN;
4584 element = EL_EM_EXIT_CLOSED;
4588 element = EL_EM_STEEL_EXIT_OPEN;
4592 element = EL_EM_STEEL_EXIT_CLOSED;
4595 case 0x0f4f: /* dynamite (lit 1) */
4596 element = EL_EM_DYNAMITE_ACTIVE;
4599 case 0x0f57: /* dynamite (lit 2) */
4600 element = EL_EM_DYNAMITE_ACTIVE;
4603 case 0x0f5f: /* dynamite (lit 3) */
4604 element = EL_EM_DYNAMITE_ACTIVE;
4607 case 0x0f67: /* dynamite (lit 4) */
4608 element = EL_EM_DYNAMITE_ACTIVE;
4615 element = EL_AMOEBA_WET;
4619 element = EL_AMOEBA_DROP;
4623 element = EL_DC_MAGIC_WALL;
4627 element = EL_SPACESHIP_UP;
4631 element = EL_SPACESHIP_DOWN;
4635 element = EL_SPACESHIP_LEFT;
4639 element = EL_SPACESHIP_RIGHT;
4643 element = EL_BUG_UP;
4647 element = EL_BUG_DOWN;
4651 element = EL_BUG_LEFT;
4655 element = EL_BUG_RIGHT;
4659 element = EL_MOLE_UP;
4663 element = EL_MOLE_DOWN;
4667 element = EL_MOLE_LEFT;
4671 element = EL_MOLE_RIGHT;
4679 element = EL_YAMYAM;
4683 element = EL_SWITCHGATE_OPEN;
4687 element = EL_SWITCHGATE_CLOSED;
4691 element = EL_DC_SWITCHGATE_SWITCH_UP;
4695 element = EL_TIMEGATE_CLOSED;
4698 case 0x144c: /* conveyor belt switch (green) */
4699 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4702 case 0x144f: /* conveyor belt switch (red) */
4703 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4706 case 0x1452: /* conveyor belt switch (blue) */
4707 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4711 element = EL_CONVEYOR_BELT_3_MIDDLE;
4715 element = EL_CONVEYOR_BELT_3_LEFT;
4719 element = EL_CONVEYOR_BELT_3_RIGHT;
4723 element = EL_CONVEYOR_BELT_1_MIDDLE;
4727 element = EL_CONVEYOR_BELT_1_LEFT;
4731 element = EL_CONVEYOR_BELT_1_RIGHT;
4735 element = EL_CONVEYOR_BELT_4_MIDDLE;
4739 element = EL_CONVEYOR_BELT_4_LEFT;
4743 element = EL_CONVEYOR_BELT_4_RIGHT;
4747 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4751 element = EL_EXPANDABLE_WALL_VERTICAL;
4755 element = EL_EXPANDABLE_WALL_ANY;
4758 case 0x14ce: /* growing steel wall (left/right) */
4759 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4762 case 0x14df: /* growing steel wall (up/down) */
4763 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4766 case 0x14e8: /* growing steel wall (up/down/left/right) */
4767 element = EL_EXPANDABLE_STEELWALL_ANY;
4771 element = EL_SHIELD_DEADLY;
4775 element = EL_EXTRA_TIME;
4783 element = EL_EMPTY_SPACE;
4786 case 0x1578: /* quicksand (empty) */
4787 element = EL_QUICKSAND_FAST_EMPTY;
4790 case 0x1579: /* slow quicksand (empty) */
4791 element = EL_QUICKSAND_EMPTY;
4794 /* 0x157c - 0x158b: */
4797 /* 0x1590 - 0x159f: */
4798 /* EL_DC_LANDMINE */
4801 element = EL_EM_DYNAMITE;
4804 case 0x15a1: /* key (red) */
4805 element = EL_EM_KEY_1;
4808 case 0x15a2: /* key (yellow) */
4809 element = EL_EM_KEY_2;
4812 case 0x15a3: /* key (blue) */
4813 element = EL_EM_KEY_4;
4816 case 0x15a4: /* key (green) */
4817 element = EL_EM_KEY_3;
4820 case 0x15a5: /* key (white) */
4821 element = EL_DC_KEY_WHITE;
4825 element = EL_WALL_SLIPPERY;
4832 case 0x15a8: /* wall (not round) */
4836 case 0x15a9: /* (blue) */
4837 element = EL_CHAR_A;
4840 case 0x15aa: /* (blue) */
4841 element = EL_CHAR_B;
4844 case 0x15ab: /* (blue) */
4845 element = EL_CHAR_C;
4848 case 0x15ac: /* (blue) */
4849 element = EL_CHAR_D;
4852 case 0x15ad: /* (blue) */
4853 element = EL_CHAR_E;
4856 case 0x15ae: /* (blue) */
4857 element = EL_CHAR_F;
4860 case 0x15af: /* (blue) */
4861 element = EL_CHAR_G;
4864 case 0x15b0: /* (blue) */
4865 element = EL_CHAR_H;
4868 case 0x15b1: /* (blue) */
4869 element = EL_CHAR_I;
4872 case 0x15b2: /* (blue) */
4873 element = EL_CHAR_J;
4876 case 0x15b3: /* (blue) */
4877 element = EL_CHAR_K;
4880 case 0x15b4: /* (blue) */
4881 element = EL_CHAR_L;
4884 case 0x15b5: /* (blue) */
4885 element = EL_CHAR_M;
4888 case 0x15b6: /* (blue) */
4889 element = EL_CHAR_N;
4892 case 0x15b7: /* (blue) */
4893 element = EL_CHAR_O;
4896 case 0x15b8: /* (blue) */
4897 element = EL_CHAR_P;
4900 case 0x15b9: /* (blue) */
4901 element = EL_CHAR_Q;
4904 case 0x15ba: /* (blue) */
4905 element = EL_CHAR_R;
4908 case 0x15bb: /* (blue) */
4909 element = EL_CHAR_S;
4912 case 0x15bc: /* (blue) */
4913 element = EL_CHAR_T;
4916 case 0x15bd: /* (blue) */
4917 element = EL_CHAR_U;
4920 case 0x15be: /* (blue) */
4921 element = EL_CHAR_V;
4924 case 0x15bf: /* (blue) */
4925 element = EL_CHAR_W;
4928 case 0x15c0: /* (blue) */
4929 element = EL_CHAR_X;
4932 case 0x15c1: /* (blue) */
4933 element = EL_CHAR_Y;
4936 case 0x15c2: /* (blue) */
4937 element = EL_CHAR_Z;
4940 case 0x15c3: /* (blue) */
4941 element = EL_CHAR_AUMLAUT;
4944 case 0x15c4: /* (blue) */
4945 element = EL_CHAR_OUMLAUT;
4948 case 0x15c5: /* (blue) */
4949 element = EL_CHAR_UUMLAUT;
4952 case 0x15c6: /* (blue) */
4953 element = EL_CHAR_0;
4956 case 0x15c7: /* (blue) */
4957 element = EL_CHAR_1;
4960 case 0x15c8: /* (blue) */
4961 element = EL_CHAR_2;
4964 case 0x15c9: /* (blue) */
4965 element = EL_CHAR_3;
4968 case 0x15ca: /* (blue) */
4969 element = EL_CHAR_4;
4972 case 0x15cb: /* (blue) */
4973 element = EL_CHAR_5;
4976 case 0x15cc: /* (blue) */
4977 element = EL_CHAR_6;
4980 case 0x15cd: /* (blue) */
4981 element = EL_CHAR_7;
4984 case 0x15ce: /* (blue) */
4985 element = EL_CHAR_8;
4988 case 0x15cf: /* (blue) */
4989 element = EL_CHAR_9;
4992 case 0x15d0: /* (blue) */
4993 element = EL_CHAR_PERIOD;
4996 case 0x15d1: /* (blue) */
4997 element = EL_CHAR_EXCLAM;
5000 case 0x15d2: /* (blue) */
5001 element = EL_CHAR_COLON;
5004 case 0x15d3: /* (blue) */
5005 element = EL_CHAR_LESS;
5008 case 0x15d4: /* (blue) */
5009 element = EL_CHAR_GREATER;
5012 case 0x15d5: /* (blue) */
5013 element = EL_CHAR_QUESTION;
5016 case 0x15d6: /* (blue) */
5017 element = EL_CHAR_COPYRIGHT;
5020 case 0x15d7: /* (blue) */
5021 element = EL_CHAR_UP;
5024 case 0x15d8: /* (blue) */
5025 element = EL_CHAR_DOWN;
5028 case 0x15d9: /* (blue) */
5029 element = EL_CHAR_BUTTON;
5032 case 0x15da: /* (blue) */
5033 element = EL_CHAR_PLUS;
5036 case 0x15db: /* (blue) */
5037 element = EL_CHAR_MINUS;
5040 case 0x15dc: /* (blue) */
5041 element = EL_CHAR_APOSTROPHE;
5044 case 0x15dd: /* (blue) */
5045 element = EL_CHAR_PARENLEFT;
5048 case 0x15de: /* (blue) */
5049 element = EL_CHAR_PARENRIGHT;
5052 case 0x15df: /* (green) */
5053 element = EL_CHAR_A;
5056 case 0x15e0: /* (green) */
5057 element = EL_CHAR_B;
5060 case 0x15e1: /* (green) */
5061 element = EL_CHAR_C;
5064 case 0x15e2: /* (green) */
5065 element = EL_CHAR_D;
5068 case 0x15e3: /* (green) */
5069 element = EL_CHAR_E;
5072 case 0x15e4: /* (green) */
5073 element = EL_CHAR_F;
5076 case 0x15e5: /* (green) */
5077 element = EL_CHAR_G;
5080 case 0x15e6: /* (green) */
5081 element = EL_CHAR_H;
5084 case 0x15e7: /* (green) */
5085 element = EL_CHAR_I;
5088 case 0x15e8: /* (green) */
5089 element = EL_CHAR_J;
5092 case 0x15e9: /* (green) */
5093 element = EL_CHAR_K;
5096 case 0x15ea: /* (green) */
5097 element = EL_CHAR_L;
5100 case 0x15eb: /* (green) */
5101 element = EL_CHAR_M;
5104 case 0x15ec: /* (green) */
5105 element = EL_CHAR_N;
5108 case 0x15ed: /* (green) */
5109 element = EL_CHAR_O;
5112 case 0x15ee: /* (green) */
5113 element = EL_CHAR_P;
5116 case 0x15ef: /* (green) */
5117 element = EL_CHAR_Q;
5120 case 0x15f0: /* (green) */
5121 element = EL_CHAR_R;
5124 case 0x15f1: /* (green) */
5125 element = EL_CHAR_S;
5128 case 0x15f2: /* (green) */
5129 element = EL_CHAR_T;
5132 case 0x15f3: /* (green) */
5133 element = EL_CHAR_U;
5136 case 0x15f4: /* (green) */
5137 element = EL_CHAR_V;
5140 case 0x15f5: /* (green) */
5141 element = EL_CHAR_W;
5144 case 0x15f6: /* (green) */
5145 element = EL_CHAR_X;
5148 case 0x15f7: /* (green) */
5149 element = EL_CHAR_Y;
5152 case 0x15f8: /* (green) */
5153 element = EL_CHAR_Z;
5156 case 0x15f9: /* (green) */
5157 element = EL_CHAR_AUMLAUT;
5160 case 0x15fa: /* (green) */
5161 element = EL_CHAR_OUMLAUT;
5164 case 0x15fb: /* (green) */
5165 element = EL_CHAR_UUMLAUT;
5168 case 0x15fc: /* (green) */
5169 element = EL_CHAR_0;
5172 case 0x15fd: /* (green) */
5173 element = EL_CHAR_1;
5176 case 0x15fe: /* (green) */
5177 element = EL_CHAR_2;
5180 case 0x15ff: /* (green) */
5181 element = EL_CHAR_3;
5184 case 0x1600: /* (green) */
5185 element = EL_CHAR_4;
5188 case 0x1601: /* (green) */
5189 element = EL_CHAR_5;
5192 case 0x1602: /* (green) */
5193 element = EL_CHAR_6;
5196 case 0x1603: /* (green) */
5197 element = EL_CHAR_7;
5200 case 0x1604: /* (green) */
5201 element = EL_CHAR_8;
5204 case 0x1605: /* (green) */
5205 element = EL_CHAR_9;
5208 case 0x1606: /* (green) */
5209 element = EL_CHAR_PERIOD;
5212 case 0x1607: /* (green) */
5213 element = EL_CHAR_EXCLAM;
5216 case 0x1608: /* (green) */
5217 element = EL_CHAR_COLON;
5220 case 0x1609: /* (green) */
5221 element = EL_CHAR_LESS;
5224 case 0x160a: /* (green) */
5225 element = EL_CHAR_GREATER;
5228 case 0x160b: /* (green) */
5229 element = EL_CHAR_QUESTION;
5232 case 0x160c: /* (green) */
5233 element = EL_CHAR_COPYRIGHT;
5236 case 0x160d: /* (green) */
5237 element = EL_CHAR_UP;
5240 case 0x160e: /* (green) */
5241 element = EL_CHAR_DOWN;
5244 case 0x160f: /* (green) */
5245 element = EL_CHAR_BUTTON;
5248 case 0x1610: /* (green) */
5249 element = EL_CHAR_PLUS;
5252 case 0x1611: /* (green) */
5253 element = EL_CHAR_MINUS;
5256 case 0x1612: /* (green) */
5257 element = EL_CHAR_APOSTROPHE;
5260 case 0x1613: /* (green) */
5261 element = EL_CHAR_PARENLEFT;
5264 case 0x1614: /* (green) */
5265 element = EL_CHAR_PARENRIGHT;
5268 case 0x1615: /* (blue steel) */
5269 element = EL_STEEL_CHAR_A;
5272 case 0x1616: /* (blue steel) */
5273 element = EL_STEEL_CHAR_B;
5276 case 0x1617: /* (blue steel) */
5277 element = EL_STEEL_CHAR_C;
5280 case 0x1618: /* (blue steel) */
5281 element = EL_STEEL_CHAR_D;
5284 case 0x1619: /* (blue steel) */
5285 element = EL_STEEL_CHAR_E;
5288 case 0x161a: /* (blue steel) */
5289 element = EL_STEEL_CHAR_F;
5292 case 0x161b: /* (blue steel) */
5293 element = EL_STEEL_CHAR_G;
5296 case 0x161c: /* (blue steel) */
5297 element = EL_STEEL_CHAR_H;
5300 case 0x161d: /* (blue steel) */
5301 element = EL_STEEL_CHAR_I;
5304 case 0x161e: /* (blue steel) */
5305 element = EL_STEEL_CHAR_J;
5308 case 0x161f: /* (blue steel) */
5309 element = EL_STEEL_CHAR_K;
5312 case 0x1620: /* (blue steel) */
5313 element = EL_STEEL_CHAR_L;
5316 case 0x1621: /* (blue steel) */
5317 element = EL_STEEL_CHAR_M;
5320 case 0x1622: /* (blue steel) */
5321 element = EL_STEEL_CHAR_N;
5324 case 0x1623: /* (blue steel) */
5325 element = EL_STEEL_CHAR_O;
5328 case 0x1624: /* (blue steel) */
5329 element = EL_STEEL_CHAR_P;
5332 case 0x1625: /* (blue steel) */
5333 element = EL_STEEL_CHAR_Q;
5336 case 0x1626: /* (blue steel) */
5337 element = EL_STEEL_CHAR_R;
5340 case 0x1627: /* (blue steel) */
5341 element = EL_STEEL_CHAR_S;
5344 case 0x1628: /* (blue steel) */
5345 element = EL_STEEL_CHAR_T;
5348 case 0x1629: /* (blue steel) */
5349 element = EL_STEEL_CHAR_U;
5352 case 0x162a: /* (blue steel) */
5353 element = EL_STEEL_CHAR_V;
5356 case 0x162b: /* (blue steel) */
5357 element = EL_STEEL_CHAR_W;
5360 case 0x162c: /* (blue steel) */
5361 element = EL_STEEL_CHAR_X;
5364 case 0x162d: /* (blue steel) */
5365 element = EL_STEEL_CHAR_Y;
5368 case 0x162e: /* (blue steel) */
5369 element = EL_STEEL_CHAR_Z;
5372 case 0x162f: /* (blue steel) */
5373 element = EL_STEEL_CHAR_AUMLAUT;
5376 case 0x1630: /* (blue steel) */
5377 element = EL_STEEL_CHAR_OUMLAUT;
5380 case 0x1631: /* (blue steel) */
5381 element = EL_STEEL_CHAR_UUMLAUT;
5384 case 0x1632: /* (blue steel) */
5385 element = EL_STEEL_CHAR_0;
5388 case 0x1633: /* (blue steel) */
5389 element = EL_STEEL_CHAR_1;
5392 case 0x1634: /* (blue steel) */
5393 element = EL_STEEL_CHAR_2;
5396 case 0x1635: /* (blue steel) */
5397 element = EL_STEEL_CHAR_3;
5400 case 0x1636: /* (blue steel) */
5401 element = EL_STEEL_CHAR_4;
5404 case 0x1637: /* (blue steel) */
5405 element = EL_STEEL_CHAR_5;
5408 case 0x1638: /* (blue steel) */
5409 element = EL_STEEL_CHAR_6;
5412 case 0x1639: /* (blue steel) */
5413 element = EL_STEEL_CHAR_7;
5416 case 0x163a: /* (blue steel) */
5417 element = EL_STEEL_CHAR_8;
5420 case 0x163b: /* (blue steel) */
5421 element = EL_STEEL_CHAR_9;
5424 case 0x163c: /* (blue steel) */
5425 element = EL_STEEL_CHAR_PERIOD;
5428 case 0x163d: /* (blue steel) */
5429 element = EL_STEEL_CHAR_EXCLAM;
5432 case 0x163e: /* (blue steel) */
5433 element = EL_STEEL_CHAR_COLON;
5436 case 0x163f: /* (blue steel) */
5437 element = EL_STEEL_CHAR_LESS;
5440 case 0x1640: /* (blue steel) */
5441 element = EL_STEEL_CHAR_GREATER;
5444 case 0x1641: /* (blue steel) */
5445 element = EL_STEEL_CHAR_QUESTION;
5448 case 0x1642: /* (blue steel) */
5449 element = EL_STEEL_CHAR_COPYRIGHT;
5452 case 0x1643: /* (blue steel) */
5453 element = EL_STEEL_CHAR_UP;
5456 case 0x1644: /* (blue steel) */
5457 element = EL_STEEL_CHAR_DOWN;
5460 case 0x1645: /* (blue steel) */
5461 element = EL_STEEL_CHAR_BUTTON;
5464 case 0x1646: /* (blue steel) */
5465 element = EL_STEEL_CHAR_PLUS;
5468 case 0x1647: /* (blue steel) */
5469 element = EL_STEEL_CHAR_MINUS;
5472 case 0x1648: /* (blue steel) */
5473 element = EL_STEEL_CHAR_APOSTROPHE;
5476 case 0x1649: /* (blue steel) */
5477 element = EL_STEEL_CHAR_PARENLEFT;
5480 case 0x164a: /* (blue steel) */
5481 element = EL_STEEL_CHAR_PARENRIGHT;
5484 case 0x164b: /* (green steel) */
5485 element = EL_STEEL_CHAR_A;
5488 case 0x164c: /* (green steel) */
5489 element = EL_STEEL_CHAR_B;
5492 case 0x164d: /* (green steel) */
5493 element = EL_STEEL_CHAR_C;
5496 case 0x164e: /* (green steel) */
5497 element = EL_STEEL_CHAR_D;
5500 case 0x164f: /* (green steel) */
5501 element = EL_STEEL_CHAR_E;
5504 case 0x1650: /* (green steel) */
5505 element = EL_STEEL_CHAR_F;
5508 case 0x1651: /* (green steel) */
5509 element = EL_STEEL_CHAR_G;
5512 case 0x1652: /* (green steel) */
5513 element = EL_STEEL_CHAR_H;
5516 case 0x1653: /* (green steel) */
5517 element = EL_STEEL_CHAR_I;
5520 case 0x1654: /* (green steel) */
5521 element = EL_STEEL_CHAR_J;
5524 case 0x1655: /* (green steel) */
5525 element = EL_STEEL_CHAR_K;
5528 case 0x1656: /* (green steel) */
5529 element = EL_STEEL_CHAR_L;
5532 case 0x1657: /* (green steel) */
5533 element = EL_STEEL_CHAR_M;
5536 case 0x1658: /* (green steel) */
5537 element = EL_STEEL_CHAR_N;
5540 case 0x1659: /* (green steel) */
5541 element = EL_STEEL_CHAR_O;
5544 case 0x165a: /* (green steel) */
5545 element = EL_STEEL_CHAR_P;
5548 case 0x165b: /* (green steel) */
5549 element = EL_STEEL_CHAR_Q;
5552 case 0x165c: /* (green steel) */
5553 element = EL_STEEL_CHAR_R;
5556 case 0x165d: /* (green steel) */
5557 element = EL_STEEL_CHAR_S;
5560 case 0x165e: /* (green steel) */
5561 element = EL_STEEL_CHAR_T;
5564 case 0x165f: /* (green steel) */
5565 element = EL_STEEL_CHAR_U;
5568 case 0x1660: /* (green steel) */
5569 element = EL_STEEL_CHAR_V;
5572 case 0x1661: /* (green steel) */
5573 element = EL_STEEL_CHAR_W;
5576 case 0x1662: /* (green steel) */
5577 element = EL_STEEL_CHAR_X;
5580 case 0x1663: /* (green steel) */
5581 element = EL_STEEL_CHAR_Y;
5584 case 0x1664: /* (green steel) */
5585 element = EL_STEEL_CHAR_Z;
5588 case 0x1665: /* (green steel) */
5589 element = EL_STEEL_CHAR_AUMLAUT;
5592 case 0x1666: /* (green steel) */
5593 element = EL_STEEL_CHAR_OUMLAUT;
5596 case 0x1667: /* (green steel) */
5597 element = EL_STEEL_CHAR_UUMLAUT;
5600 case 0x1668: /* (green steel) */
5601 element = EL_STEEL_CHAR_0;
5604 case 0x1669: /* (green steel) */
5605 element = EL_STEEL_CHAR_1;
5608 case 0x166a: /* (green steel) */
5609 element = EL_STEEL_CHAR_2;
5612 case 0x166b: /* (green steel) */
5613 element = EL_STEEL_CHAR_3;
5616 case 0x166c: /* (green steel) */
5617 element = EL_STEEL_CHAR_4;
5620 case 0x166d: /* (green steel) */
5621 element = EL_STEEL_CHAR_5;
5624 case 0x166e: /* (green steel) */
5625 element = EL_STEEL_CHAR_6;
5628 case 0x166f: /* (green steel) */
5629 element = EL_STEEL_CHAR_7;
5632 case 0x1670: /* (green steel) */
5633 element = EL_STEEL_CHAR_8;
5636 case 0x1671: /* (green steel) */
5637 element = EL_STEEL_CHAR_9;
5640 case 0x1672: /* (green steel) */
5641 element = EL_STEEL_CHAR_PERIOD;
5644 case 0x1673: /* (green steel) */
5645 element = EL_STEEL_CHAR_EXCLAM;
5648 case 0x1674: /* (green steel) */
5649 element = EL_STEEL_CHAR_COLON;
5652 case 0x1675: /* (green steel) */
5653 element = EL_STEEL_CHAR_LESS;
5656 case 0x1676: /* (green steel) */
5657 element = EL_STEEL_CHAR_GREATER;
5660 case 0x1677: /* (green steel) */
5661 element = EL_STEEL_CHAR_QUESTION;
5664 case 0x1678: /* (green steel) */
5665 element = EL_STEEL_CHAR_COPYRIGHT;
5668 case 0x1679: /* (green steel) */
5669 element = EL_STEEL_CHAR_UP;
5672 case 0x167a: /* (green steel) */
5673 element = EL_STEEL_CHAR_DOWN;
5676 case 0x167b: /* (green steel) */
5677 element = EL_STEEL_CHAR_BUTTON;
5680 case 0x167c: /* (green steel) */
5681 element = EL_STEEL_CHAR_PLUS;
5684 case 0x167d: /* (green steel) */
5685 element = EL_STEEL_CHAR_MINUS;
5688 case 0x167e: /* (green steel) */
5689 element = EL_STEEL_CHAR_APOSTROPHE;
5692 case 0x167f: /* (green steel) */
5693 element = EL_STEEL_CHAR_PARENLEFT;
5696 case 0x1680: /* (green steel) */
5697 element = EL_STEEL_CHAR_PARENRIGHT;
5700 case 0x1681: /* gate (red) */
5701 element = EL_EM_GATE_1;
5704 case 0x1682: /* secret gate (red) */
5705 element = EL_GATE_1_GRAY;
5708 case 0x1683: /* gate (yellow) */
5709 element = EL_EM_GATE_2;
5712 case 0x1684: /* secret gate (yellow) */
5713 element = EL_GATE_2_GRAY;
5716 case 0x1685: /* gate (blue) */
5717 element = EL_EM_GATE_4;
5720 case 0x1686: /* secret gate (blue) */
5721 element = EL_GATE_4_GRAY;
5724 case 0x1687: /* gate (green) */
5725 element = EL_EM_GATE_3;
5728 case 0x1688: /* secret gate (green) */
5729 element = EL_GATE_3_GRAY;
5732 case 0x1689: /* gate (white) */
5733 element = EL_DC_GATE_WHITE;
5736 case 0x168a: /* secret gate (white) */
5737 element = EL_DC_GATE_WHITE_GRAY;
5740 case 0x168b: /* secret gate (no key) */
5741 element = EL_DC_GATE_FAKE_GRAY;
5745 element = EL_ROBOT_WHEEL;
5749 element = EL_DC_TIMEGATE_SWITCH;
5753 element = EL_ACID_POOL_BOTTOM;
5757 element = EL_ACID_POOL_TOPLEFT;
5761 element = EL_ACID_POOL_TOPRIGHT;
5765 element = EL_ACID_POOL_BOTTOMLEFT;
5769 element = EL_ACID_POOL_BOTTOMRIGHT;
5773 element = EL_STEELWALL;
5777 element = EL_STEELWALL_SLIPPERY;
5780 case 0x1695: /* steel wall (not round) */
5781 element = EL_STEELWALL;
5784 case 0x1696: /* steel wall (left) */
5785 element = EL_DC_STEELWALL_1_LEFT;
5788 case 0x1697: /* steel wall (bottom) */
5789 element = EL_DC_STEELWALL_1_BOTTOM;
5792 case 0x1698: /* steel wall (right) */
5793 element = EL_DC_STEELWALL_1_RIGHT;
5796 case 0x1699: /* steel wall (top) */
5797 element = EL_DC_STEELWALL_1_TOP;
5800 case 0x169a: /* steel wall (left/bottom) */
5801 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5804 case 0x169b: /* steel wall (right/bottom) */
5805 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5808 case 0x169c: /* steel wall (right/top) */
5809 element = EL_DC_STEELWALL_1_TOPRIGHT;
5812 case 0x169d: /* steel wall (left/top) */
5813 element = EL_DC_STEELWALL_1_TOPLEFT;
5816 case 0x169e: /* steel wall (right/bottom small) */
5817 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5820 case 0x169f: /* steel wall (left/bottom small) */
5821 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5824 case 0x16a0: /* steel wall (right/top small) */
5825 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5828 case 0x16a1: /* steel wall (left/top small) */
5829 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5832 case 0x16a2: /* steel wall (left/right) */
5833 element = EL_DC_STEELWALL_1_VERTICAL;
5836 case 0x16a3: /* steel wall (top/bottom) */
5837 element = EL_DC_STEELWALL_1_HORIZONTAL;
5840 case 0x16a4: /* steel wall 2 (left end) */
5841 element = EL_DC_STEELWALL_2_LEFT;
5844 case 0x16a5: /* steel wall 2 (right end) */
5845 element = EL_DC_STEELWALL_2_RIGHT;
5848 case 0x16a6: /* steel wall 2 (top end) */
5849 element = EL_DC_STEELWALL_2_TOP;
5852 case 0x16a7: /* steel wall 2 (bottom end) */
5853 element = EL_DC_STEELWALL_2_BOTTOM;
5856 case 0x16a8: /* steel wall 2 (left/right) */
5857 element = EL_DC_STEELWALL_2_HORIZONTAL;
5860 case 0x16a9: /* steel wall 2 (up/down) */
5861 element = EL_DC_STEELWALL_2_VERTICAL;
5864 case 0x16aa: /* steel wall 2 (mid) */
5865 element = EL_DC_STEELWALL_2_MIDDLE;
5869 element = EL_SIGN_EXCLAMATION;
5873 element = EL_SIGN_RADIOACTIVITY;
5877 element = EL_SIGN_STOP;
5881 element = EL_SIGN_WHEELCHAIR;
5885 element = EL_SIGN_PARKING;
5889 element = EL_SIGN_NO_ENTRY;
5893 element = EL_SIGN_HEART;
5897 element = EL_SIGN_GIVE_WAY;
5901 element = EL_SIGN_ENTRY_FORBIDDEN;
5905 element = EL_SIGN_EMERGENCY_EXIT;
5909 element = EL_SIGN_YIN_YANG;
5913 element = EL_WALL_EMERALD;
5917 element = EL_WALL_DIAMOND;
5921 element = EL_WALL_PEARL;
5925 element = EL_WALL_CRYSTAL;
5929 element = EL_INVISIBLE_WALL;
5933 element = EL_INVISIBLE_STEELWALL;
5936 /* 0x16bc - 0x16cb: */
5937 /* EL_INVISIBLE_SAND */
5940 element = EL_LIGHT_SWITCH;
5944 element = EL_ENVELOPE_1;
5948 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5949 element = EL_DIAMOND;
5950 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5951 element = EL_EMERALD;
5952 else if (element >= 0x157c && element <= 0x158b)
5954 else if (element >= 0x1590 && element <= 0x159f)
5955 element = EL_DC_LANDMINE;
5956 else if (element >= 0x16bc && element <= 0x16cb)
5957 element = EL_INVISIBLE_SAND;
5960 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5961 element = EL_UNKNOWN;
5966 return getMappedElement(element);
5971 static void LoadLevelFromFileStream_DC(FILE *file, struct LevelInfo *level,
5974 byte header[DC_LEVEL_HEADER_SIZE];
5976 int envelope_header_pos = 62;
5977 int envelope_content_pos = 94;
5978 int level_name_pos = 251;
5979 int level_author_pos = 292;
5980 int envelope_header_len;
5981 int envelope_content_len;
5983 int level_author_len;
5985 int num_yamyam_contents;
5988 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5990 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5992 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5994 header[i * 2 + 0] = header_word >> 8;
5995 header[i * 2 + 1] = header_word & 0xff;
5998 /* read some values from level header to check level decoding integrity */
5999 fieldx = header[6] | (header[7] << 8);
6000 fieldy = header[8] | (header[9] << 8);
6001 num_yamyam_contents = header[60] | (header[61] << 8);
6003 /* do some simple sanity checks to ensure that level was correctly decoded */
6004 if (fieldx < 1 || fieldx > 256 ||
6005 fieldy < 1 || fieldy > 256 ||
6006 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6008 level->no_valid_file = TRUE;
6010 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
6015 /* maximum envelope header size is 31 bytes */
6016 envelope_header_len = header[envelope_header_pos];
6017 /* maximum envelope content size is 110 (156?) bytes */
6018 envelope_content_len = header[envelope_content_pos];
6020 /* maximum level title size is 40 bytes */
6021 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6022 /* maximum level author size is 30 (51?) bytes */
6023 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6027 for (i = 0; i < envelope_header_len; i++)
6028 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6029 level->envelope[0].text[envelope_size++] =
6030 header[envelope_header_pos + 1 + i];
6032 if (envelope_header_len > 0 && envelope_content_len > 0)
6034 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6035 level->envelope[0].text[envelope_size++] = '\n';
6036 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6037 level->envelope[0].text[envelope_size++] = '\n';
6040 for (i = 0; i < envelope_content_len; i++)
6041 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6042 level->envelope[0].text[envelope_size++] =
6043 header[envelope_content_pos + 1 + i];
6045 level->envelope[0].text[envelope_size] = '\0';
6047 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6048 level->envelope[0].ysize = 10;
6049 level->envelope[0].autowrap = TRUE;
6050 level->envelope[0].centered = TRUE;
6052 for (i = 0; i < level_name_len; i++)
6053 level->name[i] = header[level_name_pos + 1 + i];
6054 level->name[level_name_len] = '\0';
6056 for (i = 0; i < level_author_len; i++)
6057 level->author[i] = header[level_author_pos + 1 + i];
6058 level->author[level_author_len] = '\0';
6060 num_yamyam_contents = header[60] | (header[61] << 8);
6061 level->num_yamyam_contents =
6062 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6064 for (i = 0; i < num_yamyam_contents; i++)
6066 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6068 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6070 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6072 int element_dc = word;
6075 if (i < MAX_ELEMENT_CONTENTS)
6076 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6080 fieldx = header[6] | (header[7] << 8);
6081 fieldy = header[8] | (header[9] << 8);
6082 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6083 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6085 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6087 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6089 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6091 int element_dc = word;
6094 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6095 level->field[x][y] = getMappedElement_DC(element_dc);
6098 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6099 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6100 level->field[x][y] = EL_PLAYER_1;
6102 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6103 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6104 level->field[x][y] = EL_PLAYER_2;
6106 level->gems_needed = header[18] | (header[19] << 8);
6108 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6109 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6110 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6111 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6112 level->score[SC_NUT] = header[28] | (header[29] << 8);
6113 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6114 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6115 level->score[SC_BUG] = header[34] | (header[35] << 8);
6116 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6117 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6118 level->score[SC_KEY] = header[40] | (header[41] << 8);
6119 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6121 level->time = header[44] | (header[45] << 8);
6123 level->amoeba_speed = header[46] | (header[47] << 8);
6124 level->time_light = header[48] | (header[49] << 8);
6125 level->time_timegate = header[50] | (header[51] << 8);
6126 level->time_wheel = header[52] | (header[53] << 8);
6127 level->time_magic_wall = header[54] | (header[55] << 8);
6128 level->extra_time = header[56] | (header[57] << 8);
6129 level->shield_normal_time = header[58] | (header[59] << 8);
6131 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6132 can slip down from flat walls, like normal walls and steel walls */
6133 level->em_slippery_gems = TRUE;
6136 /* Diamond Caves II levels are always surrounded by indestructible wall, but
6137 not necessarily in a rectangular way -- fill with invisible steel wall */
6139 /* !!! not always true !!! keep level and set BorderElement instead !!! */
6141 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6144 if ((x == 0 || x == level->fieldx - 1 ||
6145 y == 0 || y == level->fieldy - 1) &&
6146 level->field[x][y] == EL_EMPTY)
6147 level->field[x][y] = EL_INVISIBLE_STEELWALL;
6149 if ((x == 0 || x == level->fieldx - 1 ||
6150 y == 0 || y == level->fieldy - 1) &&
6151 level->field[x][y] == EL_EMPTY)
6152 FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
6153 level->field, level->fieldx, level->fieldy);
6159 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6160 struct LevelFileInfo *level_file_info)
6162 char *filename = level_file_info->filename;
6164 int num_magic_bytes = 8;
6165 char magic_bytes[num_magic_bytes + 1];
6166 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6168 if (!(file = fopen(filename, MODE_READ)))
6170 level->no_valid_file = TRUE;
6172 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6177 // fseek(file, 0x0000, SEEK_SET);
6179 if (level_file_info->packed)
6181 /* read "magic bytes" from start of file */
6182 fgets(magic_bytes, num_magic_bytes + 1, file);
6184 /* check "magic bytes" for correct file format */
6185 if (!strPrefix(magic_bytes, "DC2"))
6187 level->no_valid_file = TRUE;
6189 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
6195 if (strPrefix(magic_bytes, "DC2Win95") ||
6196 strPrefix(magic_bytes, "DC2Win98"))
6198 int position_first_level = 0x00fa;
6199 int extra_bytes = 4;
6202 /* advance file stream to first level inside the level package */
6203 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6205 /* each block of level data is followed by block of non-level data */
6206 num_levels_to_skip *= 2;
6208 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
6209 while (num_levels_to_skip >= 0)
6211 /* advance file stream to next level inside the level package */
6212 if (fseek(file, skip_bytes, SEEK_CUR) != 0)
6214 level->no_valid_file = TRUE;
6216 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
6222 /* skip apparently unused extra bytes following each level */
6223 ReadUnusedBytesFromFile(file, extra_bytes);
6225 /* read size of next level in level package */
6226 skip_bytes = getFile32BitLE(file);
6228 num_levels_to_skip--;
6233 level->no_valid_file = TRUE;
6235 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
6242 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
6249 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6250 struct LevelFileInfo *level_file_info)
6252 char *filename = level_file_info->filename;
6255 int nr = level_file_info->nr - leveldir_current->first_level;
6257 byte header[DC_LEVEL_HEADER_SIZE];
6259 int envelope_header_pos = 62;
6260 int envelope_content_pos = 94;
6261 int level_name_pos = 251;
6262 int level_author_pos = 292;
6263 int envelope_header_len;
6264 int envelope_content_len;
6266 int level_author_len;
6268 int num_yamyam_contents;
6271 if (!(file = fopen(filename, MODE_READ)))
6273 level->no_valid_file = TRUE;
6275 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6281 /* position file stream to the requested level inside the level package */
6282 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
6284 level->no_valid_file = TRUE;
6286 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
6292 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
6294 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6296 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6298 header[i * 2 + 0] = header_word >> 8;
6299 header[i * 2 + 1] = header_word & 0xff;
6302 /* read some values from level header to check level decoding integrity */
6303 fieldx = header[6] | (header[7] << 8);
6304 fieldy = header[8] | (header[9] << 8);
6305 num_yamyam_contents = header[60] | (header[61] << 8);
6307 /* do some simple sanity checks to ensure that level was correctly decoded */
6308 if (fieldx < 1 || fieldx > 256 ||
6309 fieldy < 1 || fieldy > 256 ||
6310 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6312 level->no_valid_file = TRUE;
6314 Error(ERR_WARN, "cannot read level from file '%s' -- using empty level",
6320 /* maximum envelope header size is 31 bytes */
6321 envelope_header_len = header[envelope_header_pos];
6322 /* maximum envelope content size is 110 (156?) bytes */
6323 envelope_content_len = header[envelope_content_pos];
6325 /* maximum level title size is 40 bytes */
6326 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6327 /* maximum level author size is 30 (51?) bytes */
6328 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6332 for (i = 0; i < envelope_header_len; i++)
6333 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6334 level->envelope[0].text[envelope_size++] =
6335 header[envelope_header_pos + 1 + i];
6337 if (envelope_header_len > 0 && envelope_content_len > 0)
6339 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6340 level->envelope[0].text[envelope_size++] = '\n';
6341 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6342 level->envelope[0].text[envelope_size++] = '\n';
6345 for (i = 0; i < envelope_content_len; i++)
6346 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6347 level->envelope[0].text[envelope_size++] =
6348 header[envelope_content_pos + 1 + i];
6350 level->envelope[0].text[envelope_size] = '\0';
6352 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6353 level->envelope[0].ysize = 10;
6354 level->envelope[0].autowrap = TRUE;
6355 level->envelope[0].centered = TRUE;
6357 for (i = 0; i < level_name_len; i++)
6358 level->name[i] = header[level_name_pos + 1 + i];
6359 level->name[level_name_len] = '\0';
6361 for (i = 0; i < level_author_len; i++)
6362 level->author[i] = header[level_author_pos + 1 + i];
6363 level->author[level_author_len] = '\0';
6365 num_yamyam_contents = header[60] | (header[61] << 8);
6366 level->num_yamyam_contents =
6367 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6369 for (i = 0; i < num_yamyam_contents; i++)
6371 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6373 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6375 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6377 int element_dc = word;
6380 if (i < MAX_ELEMENT_CONTENTS)
6381 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6385 fieldx = header[6] | (header[7] << 8);
6386 fieldy = header[8] | (header[9] << 8);
6387 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6388 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6390 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6392 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6394 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6396 int element_dc = word;
6399 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6400 level->field[x][y] = getMappedElement_DC(element_dc);
6403 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6404 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6405 level->field[x][y] = EL_PLAYER_1;
6407 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6408 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6409 level->field[x][y] = EL_PLAYER_2;
6411 level->gems_needed = header[18] | (header[19] << 8);
6413 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6414 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6415 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6416 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6417 level->score[SC_NUT] = header[28] | (header[29] << 8);
6418 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6419 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6420 level->score[SC_BUG] = header[34] | (header[35] << 8);
6421 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6422 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6423 level->score[SC_KEY] = header[40] | (header[41] << 8);
6424 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6426 level->time = header[44] | (header[45] << 8);
6428 level->amoeba_speed = header[46] | (header[47] << 8);
6429 level->time_light = header[48] | (header[49] << 8);
6430 level->time_timegate = header[50] | (header[51] << 8);
6431 level->time_wheel = header[52] | (header[53] << 8);
6432 level->time_magic_wall = header[54] | (header[55] << 8);
6433 level->extra_time = header[56] | (header[57] << 8);
6434 level->shield_normal_time = header[58] | (header[59] << 8);
6438 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6439 can slip down from flat walls, like normal walls and steel walls */
6440 level->em_slippery_gems = TRUE;
6443 /* Diamond Caves II levels are always surrounded by indestructible wall, but
6444 not necessarily in a rectangular way -- fill with invisible steel wall */
6446 /* !!! not always true !!! keep level and set BorderElement instead !!! */
6448 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6451 if ((x == 0 || x == level->fieldx - 1 ||
6452 y == 0 || y == level->fieldy - 1) &&
6453 level->field[x][y] == EL_EMPTY)
6454 level->field[x][y] = EL_INVISIBLE_STEELWALL;
6456 if ((x == 0 || x == level->fieldx - 1 ||
6457 y == 0 || y == level->fieldy - 1) &&
6458 level->field[x][y] == EL_EMPTY)
6459 FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
6460 level->field, level->fieldx, level->fieldy);
6469 /* ------------------------------------------------------------------------- */
6470 /* functions for loading SB level */
6471 /* ------------------------------------------------------------------------- */
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;
6522 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6523 int file_level_nr = 0;
6528 printf("::: looking for level number %d [%d]\n",
6529 level_file_info->nr, num_levels_to_skip);
6532 last_comment[0] = '\0';
6533 level_name[0] = '\0';
6535 if (!(file = fopen(filename, MODE_READ)))
6537 level->no_valid_file = TRUE;
6539 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6546 /* level successfully read, but next level may follow here */
6547 if (!got_valid_playfield_line && reading_playfield)
6550 printf("::: read complete playfield\n");
6553 /* read playfield from single level file -- skip remaining file */
6554 if (!level_file_info->packed)
6557 if (file_level_nr >= num_levels_to_skip)
6562 last_comment[0] = '\0';
6563 level_name[0] = '\0';
6565 reading_playfield = FALSE;
6568 got_valid_playfield_line = FALSE;
6570 /* read next line of input file */
6571 if (!fgets(line, MAX_LINE_LEN, file))
6574 /* check if line was completely read and is terminated by line break */
6575 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6578 /* cut trailing line break (this can be newline and/or carriage return) */
6579 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6580 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6583 /* copy raw input line for later use (mainly debugging output) */
6584 strcpy(line_raw, line);
6586 if (read_continued_line)
6588 /* append new line to existing line, if there is enough space */
6589 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6590 strcat(previous_line, line_ptr);
6592 strcpy(line, previous_line); /* copy storage buffer to line */
6594 read_continued_line = FALSE;
6597 /* if the last character is '\', continue at next line */
6598 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6600 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
6601 strcpy(previous_line, line); /* copy line to storage buffer */
6603 read_continued_line = TRUE;
6608 /* skip empty lines */
6609 if (line[0] == '\0')
6612 /* extract comment text from comment line */
6615 for (line_ptr = line; *line_ptr; line_ptr++)
6616 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6619 strcpy(last_comment, line_ptr);
6622 printf("::: found comment '%s' in line %d\n", last_comment, line_nr);
6628 /* extract level title text from line containing level title */
6629 if (line[0] == '\'')
6631 strcpy(level_name, &line[1]);
6633 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6634 level_name[strlen(level_name) - 1] = '\0';
6637 printf("::: found level name '%s' in line %d\n", level_name, line_nr);
6643 /* skip lines containing only spaces (or empty lines) */
6644 for (line_ptr = line; *line_ptr; line_ptr++)
6645 if (*line_ptr != ' ')
6647 if (*line_ptr == '\0')
6650 /* at this point, we have found a line containing part of a playfield */
6653 printf("::: found playfield row in line %d\n", line_nr);
6656 got_valid_playfield_line = TRUE;
6658 if (!reading_playfield)
6660 reading_playfield = TRUE;
6661 invalid_playfield_char = FALSE;
6663 for (x = 0; x < MAX_LEV_FIELDX; x++)
6664 for (y = 0; y < MAX_LEV_FIELDY; y++)
6665 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6670 /* start with topmost tile row */
6674 /* skip playfield line if larger row than allowed */
6675 if (y >= MAX_LEV_FIELDY)
6678 /* start with leftmost tile column */
6681 /* read playfield elements from line */
6682 for (line_ptr = line; *line_ptr; line_ptr++)
6684 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6686 /* stop parsing playfield line if larger column than allowed */
6687 if (x >= MAX_LEV_FIELDX)
6690 if (mapped_sb_element == EL_UNDEFINED)
6692 invalid_playfield_char = TRUE;
6697 level->field[x][y] = mapped_sb_element;
6699 /* continue with next tile column */
6702 level->fieldx = MAX(x, level->fieldx);
6705 if (invalid_playfield_char)
6707 /* if first playfield line, treat invalid lines as comment lines */
6709 reading_playfield = FALSE;
6714 /* continue with next tile row */
6722 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6723 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6725 if (!reading_playfield)
6727 level->no_valid_file = TRUE;
6729 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6734 if (*level_name != '\0')
6736 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6737 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6740 printf(":1: level name: '%s'\n", level->name);
6743 else if (*last_comment != '\0')
6745 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6746 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6749 printf(":2: level name: '%s'\n", level->name);
6754 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6757 /* set all empty fields beyond the border walls to invisible steel wall */
6758 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6760 if ((x == 0 || x == level->fieldx - 1 ||
6761 y == 0 || y == level->fieldy - 1) &&
6762 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6763 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6764 level->field, level->fieldx, level->fieldy);
6767 /* set special level settings for Sokoban levels */
6770 level->use_step_counter = TRUE;
6772 if (load_xsb_to_ces)
6775 /* !!! special global settings can now be set in level template !!! */
6777 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()