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_BOOLEAN, CONF_VALUE_8_BIT(2),
188 &li.use_step_counter, FALSE
193 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
194 &li.wind_direction_initial, MV_NONE
199 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
200 &li.em_slippery_gems, FALSE
205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
206 &li.use_custom_template, FALSE
211 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
212 &li.can_move_into_acid_bits, ~0 /* default: everything can */
217 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
218 &li.dont_collide_with_bits, ~0 /* default: always deadly */
223 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
224 &li.score[SC_TIME_BONUS], 1
234 static struct LevelFileConfigInfo chunk_config_ELEM[] =
236 /* (these values are the same for each player) */
239 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
240 &li.block_last_field, FALSE /* default case for EM levels */
244 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
245 &li.sp_block_last_field, TRUE /* default case for SP levels */
249 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
250 &li.instant_relocation, FALSE
254 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
255 &li.can_pass_to_walkable, FALSE
259 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
260 &li.block_snap_field, TRUE
264 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
265 &li.continuous_snapping, TRUE
268 /* (these values are different for each player) */
271 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
272 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
276 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
277 &li.initial_player_gravity[0], FALSE
281 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
282 &li.use_start_element[0], FALSE
286 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
287 &li.start_element[0], EL_PLAYER_1
291 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
292 &li.use_artwork_element[0], FALSE
296 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
297 &li.artwork_element[0], EL_PLAYER_1
301 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
302 &li.use_explosion_element[0], FALSE
306 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
307 &li.explosion_element[0], EL_PLAYER_1
312 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
313 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
317 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
318 &li.initial_player_gravity[1], FALSE
322 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
323 &li.use_start_element[1], FALSE
327 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
328 &li.start_element[1], EL_PLAYER_2
332 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
333 &li.use_artwork_element[1], FALSE
337 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
338 &li.artwork_element[1], EL_PLAYER_2
342 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
343 &li.use_explosion_element[1], FALSE
347 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
348 &li.explosion_element[1], EL_PLAYER_2
353 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
354 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
358 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
359 &li.initial_player_gravity[2], FALSE
363 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
364 &li.use_start_element[2], FALSE
368 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
369 &li.start_element[2], EL_PLAYER_3
373 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
374 &li.use_artwork_element[2], FALSE
378 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
379 &li.artwork_element[2], EL_PLAYER_3
383 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
384 &li.use_explosion_element[2], FALSE
388 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
389 &li.explosion_element[2], EL_PLAYER_3
394 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
395 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
399 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
400 &li.initial_player_gravity[3], FALSE
404 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
405 &li.use_start_element[3], FALSE
409 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
410 &li.start_element[3], EL_PLAYER_4
414 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
415 &li.use_artwork_element[3], FALSE
419 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
420 &li.artwork_element[3], EL_PLAYER_4
424 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
425 &li.use_explosion_element[3], FALSE
429 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
430 &li.explosion_element[3], EL_PLAYER_4
435 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
436 &li.score[SC_EMERALD], 10
441 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
442 &li.score[SC_DIAMOND], 10
447 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
448 &li.score[SC_BUG], 10
453 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
454 &li.score[SC_SPACESHIP], 10
459 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
460 &li.score[SC_PACMAN], 10
465 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
466 &li.score[SC_NUT], 10
471 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
472 &li.score[SC_DYNAMITE], 10
477 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
478 &li.score[SC_KEY], 10
483 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
484 &li.score[SC_PEARL], 10
489 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
490 &li.score[SC_CRYSTAL], 10
495 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
496 &li.amoeba_content, EL_DIAMOND
500 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
505 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
506 &li.grow_into_diggable, TRUE
511 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
512 &li.yamyam_content, EL_ROCK, NULL,
513 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
517 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
518 &li.score[SC_YAMYAM], 10
523 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
524 &li.score[SC_ROBOT], 10
528 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
534 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
540 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
541 &li.time_magic_wall, 10
546 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
547 &li.game_of_life[0], 2
551 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
552 &li.game_of_life[1], 3
556 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
557 &li.game_of_life[2], 3
561 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
562 &li.game_of_life[3], 3
567 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
572 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
577 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
582 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
587 EL_TIMEGATE_SWITCH, -1,
588 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
589 &li.time_timegate, 10
593 EL_LIGHT_SWITCH_ACTIVE, -1,
594 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
599 EL_SHIELD_NORMAL, -1,
600 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
601 &li.shield_normal_time, 10
604 EL_SHIELD_NORMAL, -1,
605 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
606 &li.score[SC_SHIELD], 10
610 EL_SHIELD_DEADLY, -1,
611 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
612 &li.shield_deadly_time, 10
615 EL_SHIELD_DEADLY, -1,
616 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
617 &li.score[SC_SHIELD], 10
622 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
627 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
628 &li.extra_time_score, 10
632 EL_TIME_ORB_FULL, -1,
633 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
634 &li.time_orb_time, 10
637 EL_TIME_ORB_FULL, -1,
638 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
639 &li.use_time_orb_bug, FALSE
644 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
645 &li.use_spring_bug, FALSE
650 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
651 &li.android_move_time, 10
655 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
656 &li.android_clone_time, 10
660 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
661 &li.android_clone_element[0], EL_EMPTY, NULL,
662 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
667 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
672 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
677 EL_EMC_MAGNIFIER, -1,
678 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
679 &li.magnify_score, 10
682 EL_EMC_MAGNIFIER, -1,
683 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
688 EL_EMC_MAGIC_BALL, -1,
689 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
693 EL_EMC_MAGIC_BALL, -1,
694 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
695 &li.ball_random, FALSE
698 EL_EMC_MAGIC_BALL, -1,
699 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
700 &li.ball_state_initial, FALSE
703 EL_EMC_MAGIC_BALL, -1,
704 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
705 &li.ball_content, EL_EMPTY, NULL,
706 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
709 /* ---------- unused values ----------------------------------------------- */
712 EL_UNKNOWN, SAVE_CONF_NEVER,
713 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
714 &li.score[SC_UNKNOWN_14], 10
717 EL_UNKNOWN, SAVE_CONF_NEVER,
718 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
719 &li.score[SC_UNKNOWN_15], 10
729 static struct LevelFileConfigInfo chunk_config_NOTE[] =
733 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
734 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
738 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
739 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
744 TYPE_STRING, CONF_VALUE_BYTES(1),
745 &xx_envelope.text, -1, NULL,
746 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
747 &xx_default_string_empty[0]
757 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
761 TYPE_STRING, CONF_VALUE_BYTES(1),
762 &xx_ei.description[0], -1,
763 &yy_ei.description[0],
764 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
765 &xx_default_description[0]
770 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
771 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
772 &yy_ei.properties[EP_BITFIELD_BASE_NR]
778 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
779 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
780 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
786 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
787 &xx_ei.use_gfx_element, FALSE,
788 &yy_ei.use_gfx_element
792 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
793 &xx_ei.gfx_element, EL_EMPTY_SPACE,
799 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
800 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
801 &yy_ei.access_direction
806 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
807 &xx_ei.collect_score_initial, 10,
808 &yy_ei.collect_score_initial
812 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
813 &xx_ei.collect_count_initial, 1,
814 &yy_ei.collect_count_initial
819 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
820 &xx_ei.ce_value_fixed_initial, 0,
821 &yy_ei.ce_value_fixed_initial
825 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
826 &xx_ei.ce_value_random_initial, 0,
827 &yy_ei.ce_value_random_initial
831 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
832 &xx_ei.use_last_ce_value, FALSE,
833 &yy_ei.use_last_ce_value
838 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
839 &xx_ei.push_delay_fixed, 8,
840 &yy_ei.push_delay_fixed
844 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
845 &xx_ei.push_delay_random, 8,
846 &yy_ei.push_delay_random
850 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
851 &xx_ei.drop_delay_fixed, 0,
852 &yy_ei.drop_delay_fixed
856 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
857 &xx_ei.drop_delay_random, 0,
858 &yy_ei.drop_delay_random
862 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
863 &xx_ei.move_delay_fixed, 0,
864 &yy_ei.move_delay_fixed
868 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
869 &xx_ei.move_delay_random, 0,
870 &yy_ei.move_delay_random
875 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
876 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
881 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
882 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
883 &yy_ei.move_direction_initial
887 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
888 &xx_ei.move_stepsize, TILEX / 8,
894 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
895 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
896 &yy_ei.move_enter_element
900 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
901 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
902 &yy_ei.move_leave_element
906 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
907 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
908 &yy_ei.move_leave_type
913 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
914 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
920 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
921 &xx_ei.explosion_type, EXPLODES_3X3,
922 &yy_ei.explosion_type
926 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
927 &xx_ei.explosion_delay, 16,
928 &yy_ei.explosion_delay
932 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
933 &xx_ei.ignition_delay, 8,
934 &yy_ei.ignition_delay
939 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
940 &xx_ei.content, EL_EMPTY_SPACE,
942 &xx_num_contents, 1, 1
945 /* ---------- "num_change_pages" must be the last entry ------------------- */
948 -1, SAVE_CONF_ALWAYS,
949 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
950 &xx_ei.num_change_pages, 1,
951 &yy_ei.num_change_pages
962 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
964 /* ---------- "current_change_page" must be the first entry --------------- */
967 -1, SAVE_CONF_ALWAYS,
968 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
969 &xx_current_change_page, -1
972 /* ---------- (the remaining entries can be in any order) ----------------- */
976 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
977 &xx_change.can_change, FALSE
982 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
987 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
993 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
994 &xx_change.trigger_player, CH_PLAYER_ANY
998 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
999 &xx_change.trigger_side, CH_SIDE_ANY
1003 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1004 &xx_change.trigger_page, CH_PAGE_ANY
1009 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1010 &xx_change.target_element, EL_EMPTY_SPACE
1015 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1016 &xx_change.delay_fixed, 0
1020 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1021 &xx_change.delay_random, 0
1025 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1026 &xx_change.delay_frames, FRAMES_PER_SECOND
1031 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1032 &xx_change.trigger_element, EL_EMPTY_SPACE
1037 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1038 &xx_change.explode, FALSE
1042 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1043 &xx_change.use_target_content, FALSE
1047 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1048 &xx_change.only_if_complete, FALSE
1052 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1053 &xx_change.use_random_replace, FALSE
1057 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1058 &xx_change.random_percentage, 100
1062 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1063 &xx_change.replace_when, CP_WHEN_EMPTY
1068 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1069 &xx_change.has_action, FALSE
1073 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1074 &xx_change.action_type, CA_NO_ACTION
1078 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1079 &xx_change.action_mode, CA_MODE_UNDEFINED
1083 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1084 &xx_change.action_arg, CA_ARG_UNDEFINED
1089 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1090 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1091 &xx_num_contents, 1, 1
1101 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1105 TYPE_STRING, CONF_VALUE_BYTES(1),
1106 &xx_ei.description[0], -1, NULL,
1107 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1108 &xx_default_description[0]
1113 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1114 &xx_ei.use_gfx_element, FALSE
1118 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1119 &xx_ei.gfx_element, EL_EMPTY_SPACE
1124 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1125 &xx_group.choice_mode, ANIM_RANDOM
1130 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1131 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1132 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1142 static struct LevelFileConfigInfo chunk_config_CONF[] = /* (OBSOLETE) */
1146 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1147 &li.block_snap_field, TRUE
1151 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1152 &li.continuous_snapping, TRUE
1156 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1157 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1161 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1162 &li.use_start_element[0], FALSE
1166 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1167 &li.start_element[0], EL_PLAYER_1
1171 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1172 &li.use_artwork_element[0], FALSE
1176 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1177 &li.artwork_element[0], EL_PLAYER_1
1181 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1182 &li.use_explosion_element[0], FALSE
1186 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1187 &li.explosion_element[0], EL_PLAYER_1
1202 filetype_id_list[] =
1204 { LEVEL_FILE_TYPE_RND, "RND" },
1205 { LEVEL_FILE_TYPE_BD, "BD" },
1206 { LEVEL_FILE_TYPE_EM, "EM" },
1207 { LEVEL_FILE_TYPE_SP, "SP" },
1208 { LEVEL_FILE_TYPE_DX, "DX" },
1209 { LEVEL_FILE_TYPE_SB, "SB" },
1210 { LEVEL_FILE_TYPE_DC, "DC" },
1215 /* ========================================================================= */
1216 /* level file functions */
1217 /* ========================================================================= */
1219 static struct DateInfo getCurrentDate()
1221 time_t epoch_seconds = time(NULL);
1222 struct tm *now = localtime(&epoch_seconds);
1223 struct DateInfo date;
1225 date.year = now->tm_year + 1900;
1226 date.month = now->tm_mon + 1;
1227 date.day = now->tm_mday;
1232 static void resetEventFlags(struct ElementChangeInfo *change)
1236 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1237 change->has_event[i] = FALSE;
1240 static void resetEventBits()
1244 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1245 xx_event_bits[i] = 0;
1248 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1252 /* important: only change event flag if corresponding event bit is set
1253 (this is because all xx_event_bits[] values are loaded separately,
1254 and all xx_event_bits[] values are set back to zero before loading
1255 another value xx_event_bits[x] (each value representing 32 flags)) */
1257 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1258 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1259 change->has_event[i] = TRUE;
1262 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1266 /* in contrast to the above function setEventFlagsFromEventBits(), it
1267 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1268 depending on the corresponding change->has_event[i] values here, as
1269 all xx_event_bits[] values are reset in resetEventBits() before */
1271 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1272 if (change->has_event[i])
1273 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1276 static char *getDefaultElementDescription(struct ElementInfo *ei)
1278 static char description[MAX_ELEMENT_NAME_LEN + 1];
1279 char *default_description = (ei->custom_description != NULL ?
1280 ei->custom_description :
1281 ei->editor_description);
1284 /* always start with reliable default values */
1285 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1286 description[i] = '\0';
1288 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1289 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1291 return &description[0];
1294 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1296 char *default_description = getDefaultElementDescription(ei);
1299 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1300 ei->description[i] = default_description[i];
1303 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1307 for (i = 0; conf[i].data_type != -1; i++)
1309 int default_value = conf[i].default_value;
1310 int data_type = conf[i].data_type;
1311 int conf_type = conf[i].conf_type;
1312 int byte_mask = conf_type & CONF_MASK_BYTES;
1314 if (byte_mask == CONF_MASK_MULTI_BYTES)
1316 int default_num_entities = conf[i].default_num_entities;
1317 int max_num_entities = conf[i].max_num_entities;
1319 *(int *)(conf[i].num_entities) = default_num_entities;
1321 if (data_type == TYPE_STRING)
1323 char *default_string = conf[i].default_string;
1324 char *string = (char *)(conf[i].value);
1326 strncpy(string, default_string, max_num_entities);
1328 else if (data_type == TYPE_ELEMENT_LIST)
1330 int *element_array = (int *)(conf[i].value);
1333 for (j = 0; j < max_num_entities; j++)
1334 element_array[j] = default_value;
1336 else if (data_type == TYPE_CONTENT_LIST)
1338 struct Content *content = (struct Content *)(conf[i].value);
1341 for (c = 0; c < max_num_entities; c++)
1342 for (y = 0; y < 3; y++)
1343 for (x = 0; x < 3; x++)
1344 content[c].e[x][y] = default_value;
1347 else /* constant size configuration data (1, 2 or 4 bytes) */
1349 if (data_type == TYPE_BOOLEAN)
1350 *(boolean *)(conf[i].value) = default_value;
1352 *(int *) (conf[i].value) = default_value;
1357 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1361 for (i = 0; conf[i].data_type != -1; i++)
1363 int data_type = conf[i].data_type;
1364 int conf_type = conf[i].conf_type;
1365 int byte_mask = conf_type & CONF_MASK_BYTES;
1367 if (byte_mask == CONF_MASK_MULTI_BYTES)
1369 int max_num_entities = conf[i].max_num_entities;
1371 if (data_type == TYPE_STRING)
1373 char *string = (char *)(conf[i].value);
1374 char *string_copy = (char *)(conf[i].value_copy);
1376 strncpy(string_copy, string, max_num_entities);
1378 else if (data_type == TYPE_ELEMENT_LIST)
1380 int *element_array = (int *)(conf[i].value);
1381 int *element_array_copy = (int *)(conf[i].value_copy);
1384 for (j = 0; j < max_num_entities; j++)
1385 element_array_copy[j] = element_array[j];
1387 else if (data_type == TYPE_CONTENT_LIST)
1389 struct Content *content = (struct Content *)(conf[i].value);
1390 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1393 for (c = 0; c < max_num_entities; c++)
1394 for (y = 0; y < 3; y++)
1395 for (x = 0; x < 3; x++)
1396 content_copy[c].e[x][y] = content[c].e[x][y];
1399 else /* constant size configuration data (1, 2 or 4 bytes) */
1401 if (data_type == TYPE_BOOLEAN)
1402 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1404 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1409 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1413 xx_ei = *ei_from; /* copy element data into temporary buffer */
1414 yy_ei = *ei_to; /* copy element data into temporary buffer */
1416 copyConfigFromConfigList(chunk_config_CUSX_base);
1421 /* ---------- reinitialize and copy change pages ---------- */
1423 ei_to->num_change_pages = ei_from->num_change_pages;
1424 ei_to->current_change_page = ei_from->current_change_page;
1426 setElementChangePages(ei_to, ei_to->num_change_pages);
1428 for (i = 0; i < ei_to->num_change_pages; i++)
1429 ei_to->change_page[i] = ei_from->change_page[i];
1431 /* ---------- copy group element info ---------- */
1432 if (ei_from->group != NULL && ei_to->group != NULL) /* group or internal */
1433 *ei_to->group = *ei_from->group;
1435 /* mark this custom element as modified */
1436 ei_to->modified_settings = TRUE;
1439 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1441 int change_page_size = sizeof(struct ElementChangeInfo);
1443 ei->num_change_pages = MAX(1, change_pages);
1446 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1448 if (ei->current_change_page >= ei->num_change_pages)
1449 ei->current_change_page = ei->num_change_pages - 1;
1451 ei->change = &ei->change_page[ei->current_change_page];
1454 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1456 xx_change = *change; /* copy change data into temporary buffer */
1459 /* (not needed; set by setConfigToDefaultsFromConfigList()) */
1460 xx_num_contents = 1;
1463 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1465 *change = xx_change;
1467 resetEventFlags(change);
1469 change->direct_action = 0;
1470 change->other_action = 0;
1472 change->pre_change_function = NULL;
1473 change->change_function = NULL;
1474 change->post_change_function = NULL;
1477 static void setLevelInfoToDefaults(struct LevelInfo *level)
1479 static boolean clipboard_elements_initialized = FALSE;
1482 InitElementPropertiesStatic();
1484 li = *level; /* copy level data into temporary buffer */
1486 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1487 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1489 *level = li; /* copy temporary buffer back to level data */
1491 setLevelInfoToDefaults_EM();
1493 level->native_em_level = &native_em_level;
1495 level->file_version = FILE_VERSION_ACTUAL;
1496 level->game_version = GAME_VERSION_ACTUAL;
1498 level->creation_date = getCurrentDate();
1500 level->encoding_16bit_field = TRUE;
1501 level->encoding_16bit_yamyam = TRUE;
1502 level->encoding_16bit_amoeba = TRUE;
1504 for (x = 0; x < MAX_LEV_FIELDX; x++)
1505 for (y = 0; y < MAX_LEV_FIELDY; y++)
1506 level->field[x][y] = EL_SAND;
1508 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1509 level->name[i] = '\0';
1510 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1511 level->author[i] = '\0';
1513 strcpy(level->name, NAMELESS_LEVEL_NAME);
1514 strcpy(level->author, ANONYMOUS_NAME);
1516 level->field[0][0] = EL_PLAYER_1;
1517 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1519 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1522 struct ElementInfo *ei = &element_info[element];
1524 /* never initialize clipboard elements after the very first time */
1525 /* (to be able to use clipboard elements between several levels) */
1526 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1529 if (IS_ENVELOPE(element))
1531 int envelope_nr = element - EL_ENVELOPE_1;
1533 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1535 level->envelope[envelope_nr] = xx_envelope;
1538 if (IS_CUSTOM_ELEMENT(element) ||
1539 IS_GROUP_ELEMENT(element) ||
1540 IS_INTERNAL_ELEMENT(element))
1542 xx_ei = *ei; /* copy element data into temporary buffer */
1544 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1549 setElementChangePages(ei, 1);
1550 setElementChangeInfoToDefaults(ei->change);
1552 if (IS_CUSTOM_ELEMENT(element) ||
1553 IS_GROUP_ELEMENT(element) ||
1554 IS_INTERNAL_ELEMENT(element))
1556 setElementDescriptionToDefault(ei);
1558 ei->modified_settings = FALSE;
1561 if (IS_CUSTOM_ELEMENT(element) ||
1562 IS_INTERNAL_ELEMENT(element))
1564 /* internal values used in level editor */
1566 ei->access_type = 0;
1567 ei->access_layer = 0;
1568 ei->access_protected = 0;
1569 ei->walk_to_action = 0;
1570 ei->smash_targets = 0;
1573 ei->can_explode_by_fire = FALSE;
1574 ei->can_explode_smashed = FALSE;
1575 ei->can_explode_impact = FALSE;
1577 ei->current_change_page = 0;
1580 if (IS_GROUP_ELEMENT(element) ||
1581 IS_INTERNAL_ELEMENT(element))
1583 struct ElementGroupInfo *group;
1585 /* initialize memory for list of elements in group */
1586 if (ei->group == NULL)
1587 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1591 xx_group = *group; /* copy group data into temporary buffer */
1593 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1599 clipboard_elements_initialized = TRUE;
1601 BorderElement = EL_STEELWALL;
1603 level->no_valid_file = FALSE;
1605 level->changed = FALSE;
1607 if (leveldir_current == NULL) /* only when dumping level */
1610 /* try to determine better author name than 'anonymous' */
1611 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1613 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1614 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1618 switch (LEVELCLASS(leveldir_current))
1620 case LEVELCLASS_TUTORIAL:
1621 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1624 case LEVELCLASS_CONTRIB:
1625 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1626 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1629 case LEVELCLASS_PRIVATE:
1630 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1631 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1635 /* keep default value */
1641 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1643 level_file_info->nr = 0;
1644 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1645 level_file_info->packed = FALSE;
1646 level_file_info->basename = NULL;
1647 level_file_info->filename = NULL;
1650 static void ActivateLevelTemplate()
1652 /* Currently there is no special action needed to activate the template
1653 data, because 'element_info' property settings overwrite the original
1654 level data, while all other variables do not change. */
1657 static char *getLevelFilenameFromBasename(char *basename)
1659 static char *filename = NULL;
1661 checked_free(filename);
1663 filename = getPath2(getCurrentLevelDir(), basename);
1668 static int getFileTypeFromBasename(char *basename)
1670 static char *filename = NULL;
1671 struct stat file_status;
1673 /* ---------- try to determine file type from filename ---------- */
1675 /* check for typical filename of a Supaplex level package file */
1676 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
1677 strncmp(basename, "LEVELS.D", 8) == 0))
1678 return LEVEL_FILE_TYPE_SP;
1680 /* ---------- try to determine file type from filesize ---------- */
1682 checked_free(filename);
1683 filename = getPath2(getCurrentLevelDir(), basename);
1685 if (stat(filename, &file_status) == 0)
1687 /* check for typical filesize of a Supaplex level package file */
1688 if (file_status.st_size == 170496)
1689 return LEVEL_FILE_TYPE_SP;
1692 return LEVEL_FILE_TYPE_UNKNOWN;
1695 static char *getSingleLevelBasename(int nr)
1697 static char basename[MAX_FILENAME_LEN];
1700 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
1702 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
1707 static char *getPackedLevelBasename(int type)
1709 static char basename[MAX_FILENAME_LEN];
1710 char *directory = getCurrentLevelDir();
1712 struct dirent *dir_entry;
1714 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
1716 if ((dir = opendir(directory)) == NULL)
1718 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
1723 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1725 char *entry_basename = dir_entry->d_name;
1726 int entry_type = getFileTypeFromBasename(entry_basename);
1728 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
1730 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
1733 strcpy(basename, entry_basename);
1745 static char *getSingleLevelFilename(int nr)
1747 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
1751 static char *getPackedLevelFilename(int type)
1753 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
1757 char *getDefaultLevelFilename(int nr)
1759 return getSingleLevelFilename(nr);
1763 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
1767 lfi->packed = FALSE;
1768 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
1769 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1773 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
1774 int type, char *format, ...)
1776 static char basename[MAX_FILENAME_LEN];
1779 va_start(ap, format);
1780 vsprintf(basename, format, ap);
1784 lfi->packed = FALSE;
1785 lfi->basename = basename;
1786 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1789 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
1794 lfi->basename = getPackedLevelBasename(lfi->type);
1795 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1798 static int getFiletypeFromID(char *filetype_id)
1800 char *filetype_id_lower;
1801 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
1804 if (filetype_id == NULL)
1805 return LEVEL_FILE_TYPE_UNKNOWN;
1807 filetype_id_lower = getStringToLower(filetype_id);
1809 for (i = 0; filetype_id_list[i].id != NULL; i++)
1811 char *id_lower = getStringToLower(filetype_id_list[i].id);
1813 if (strEqual(filetype_id_lower, id_lower))
1814 filetype = filetype_id_list[i].filetype;
1818 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
1822 free(filetype_id_lower);
1827 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
1831 /* special case: level number is negative => check for level template file */
1834 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
1835 "template.%s", LEVELFILE_EXTENSION);
1837 /* no fallback if template file not existing */
1841 /* special case: check for file name/pattern specified in "levelinfo.conf" */
1842 if (leveldir_current->level_filename != NULL)
1844 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
1846 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
1847 leveldir_current->level_filename, nr);
1848 if (fileExists(lfi->filename))
1852 /* check for native Rocks'n'Diamonds level file */
1853 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
1854 "%03d.%s", nr, LEVELFILE_EXTENSION);
1855 if (fileExists(lfi->filename))
1858 /* check for Emerald Mine level file (V1) */
1859 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
1860 'a' + (nr / 10) % 26, '0' + nr % 10);
1861 if (fileExists(lfi->filename))
1863 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
1864 'A' + (nr / 10) % 26, '0' + nr % 10);
1865 if (fileExists(lfi->filename))
1868 /* check for Emerald Mine level file (V2 to V5) */
1869 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
1870 if (fileExists(lfi->filename))
1873 /* check for Emerald Mine level file (V6 / single mode) */
1874 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
1875 if (fileExists(lfi->filename))
1877 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
1878 if (fileExists(lfi->filename))
1881 /* check for Emerald Mine level file (V6 / teamwork mode) */
1882 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
1883 if (fileExists(lfi->filename))
1885 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
1886 if (fileExists(lfi->filename))
1889 /* check for various packed level file formats */
1890 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
1891 if (fileExists(lfi->filename))
1894 /* no known level file found -- use default values (and fail later) */
1895 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
1896 "%03d.%s", nr, LEVELFILE_EXTENSION);
1899 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
1901 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
1902 lfi->type = getFileTypeFromBasename(lfi->basename);
1905 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
1907 /* always start with reliable default values */
1908 setFileInfoToDefaults(level_file_info);
1910 level_file_info->nr = nr; /* set requested level number */
1912 determineLevelFileInfo_Filename(level_file_info);
1913 determineLevelFileInfo_Filetype(level_file_info);
1916 /* ------------------------------------------------------------------------- */
1917 /* functions for loading R'n'D level */
1918 /* ------------------------------------------------------------------------- */
1920 int getMappedElement(int element)
1922 /* remap some (historic, now obsolete) elements */
1926 case EL_PLAYER_OBSOLETE:
1927 element = EL_PLAYER_1;
1930 case EL_KEY_OBSOLETE:
1933 case EL_EM_KEY_1_FILE_OBSOLETE:
1934 element = EL_EM_KEY_1;
1937 case EL_EM_KEY_2_FILE_OBSOLETE:
1938 element = EL_EM_KEY_2;
1941 case EL_EM_KEY_3_FILE_OBSOLETE:
1942 element = EL_EM_KEY_3;
1945 case EL_EM_KEY_4_FILE_OBSOLETE:
1946 element = EL_EM_KEY_4;
1949 case EL_ENVELOPE_OBSOLETE:
1950 element = EL_ENVELOPE_1;
1958 if (element >= NUM_FILE_ELEMENTS)
1960 Error(ERR_WARN, "invalid level element %d", element);
1962 element = EL_UNKNOWN;
1970 int getMappedElementByVersion(int element, int game_version)
1972 /* remap some elements due to certain game version */
1974 if (game_version <= VERSION_IDENT(2,2,0,0))
1976 /* map game font elements */
1977 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1978 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1979 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1980 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1983 if (game_version < VERSION_IDENT(3,0,0,0))
1985 /* map Supaplex gravity tube elements */
1986 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1987 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1988 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1989 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1996 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
1998 level->file_version = getFileVersion(file);
1999 level->game_version = getFileVersion(file);
2004 static int LoadLevel_DATE(FILE *file, int chunk_size, struct LevelInfo *level)
2006 level->creation_date.year = getFile16BitBE(file);
2007 level->creation_date.month = getFile8Bit(file);
2008 level->creation_date.day = getFile8Bit(file);
2013 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
2015 int initial_player_stepsize;
2016 int initial_player_gravity;
2019 level->fieldx = getFile8Bit(file);
2020 level->fieldy = getFile8Bit(file);
2022 level->time = getFile16BitBE(file);
2023 level->gems_needed = getFile16BitBE(file);
2025 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2026 level->name[i] = getFile8Bit(file);
2027 level->name[MAX_LEVEL_NAME_LEN] = 0;
2029 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2030 level->score[i] = getFile8Bit(file);
2032 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2033 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2034 for (y = 0; y < 3; y++)
2035 for (x = 0; x < 3; x++)
2036 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2038 level->amoeba_speed = getFile8Bit(file);
2039 level->time_magic_wall = getFile8Bit(file);
2040 level->time_wheel = getFile8Bit(file);
2041 level->amoeba_content = getMappedElement(getFile8Bit(file));
2043 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2046 for (i = 0; i < MAX_PLAYERS; i++)
2047 level->initial_player_stepsize[i] = initial_player_stepsize;
2049 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2051 for (i = 0; i < MAX_PLAYERS; i++)
2052 level->initial_player_gravity[i] = initial_player_gravity;
2054 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2055 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2057 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2059 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2060 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2061 level->can_move_into_acid_bits = getFile32BitBE(file);
2062 level->dont_collide_with_bits = getFile8Bit(file);
2064 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2065 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2067 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2068 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2069 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2071 level->game_engine_type = getFile8Bit(file);
2073 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2078 static int LoadLevel_NAME(FILE *file, int chunk_size, struct LevelInfo *level)
2082 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2083 level->name[i] = getFile8Bit(file);
2084 level->name[MAX_LEVEL_NAME_LEN] = 0;
2089 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
2093 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2094 level->author[i] = getFile8Bit(file);
2095 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2100 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
2103 int chunk_size_expected = level->fieldx * level->fieldy;
2105 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2106 stored with 16-bit encoding (and should be twice as big then).
2107 Even worse, playfield data was stored 16-bit when only yamyam content
2108 contained 16-bit elements and vice versa. */
2110 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2111 chunk_size_expected *= 2;
2113 if (chunk_size_expected != chunk_size)
2115 ReadUnusedBytesFromFile(file, chunk_size);
2116 return chunk_size_expected;
2119 for (y = 0; y < level->fieldy; y++)
2120 for (x = 0; x < level->fieldx; x++)
2121 level->field[x][y] =
2122 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2127 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
2130 int header_size = 4;
2131 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2132 int chunk_size_expected = header_size + content_size;
2134 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2135 stored with 16-bit encoding (and should be twice as big then).
2136 Even worse, playfield data was stored 16-bit when only yamyam content
2137 contained 16-bit elements and vice versa. */
2139 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2140 chunk_size_expected += content_size;
2142 if (chunk_size_expected != chunk_size)
2144 ReadUnusedBytesFromFile(file, chunk_size);
2145 return chunk_size_expected;
2149 level->num_yamyam_contents = getFile8Bit(file);
2153 /* correct invalid number of content fields -- should never happen */
2154 if (level->num_yamyam_contents < 1 ||
2155 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2156 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2158 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2159 for (y = 0; y < 3; y++)
2160 for (x = 0; x < 3; x++)
2161 level->yamyam_content[i].e[x][y] =
2162 getMappedElement(level->encoding_16bit_field ?
2163 getFile16BitBE(file) : getFile8Bit(file));
2167 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
2171 int num_contents, content_xsize, content_ysize;
2172 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2174 element = getMappedElement(getFile16BitBE(file));
2175 num_contents = getFile8Bit(file);
2176 content_xsize = getFile8Bit(file);
2177 content_ysize = getFile8Bit(file);
2179 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2181 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2182 for (y = 0; y < 3; y++)
2183 for (x = 0; x < 3; x++)
2184 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2186 /* correct invalid number of content fields -- should never happen */
2187 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2188 num_contents = STD_ELEMENT_CONTENTS;
2190 if (element == EL_YAMYAM)
2192 level->num_yamyam_contents = num_contents;
2194 for (i = 0; i < num_contents; i++)
2195 for (y = 0; y < 3; y++)
2196 for (x = 0; x < 3; x++)
2197 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2199 else if (element == EL_BD_AMOEBA)
2201 level->amoeba_content = content_array[0][0][0];
2205 Error(ERR_WARN, "cannot load content for element '%d'", element);
2211 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
2217 int chunk_size_expected;
2219 element = getMappedElement(getFile16BitBE(file));
2220 if (!IS_ENVELOPE(element))
2221 element = EL_ENVELOPE_1;
2223 envelope_nr = element - EL_ENVELOPE_1;
2225 envelope_len = getFile16BitBE(file);
2227 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2228 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2230 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2232 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2233 if (chunk_size_expected != chunk_size)
2235 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2236 return chunk_size_expected;
2239 for (i = 0; i < envelope_len; i++)
2240 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2245 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
2247 int num_changed_custom_elements = getFile16BitBE(file);
2248 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2251 if (chunk_size_expected != chunk_size)
2253 ReadUnusedBytesFromFile(file, chunk_size - 2);
2254 return chunk_size_expected;
2257 for (i = 0; i < num_changed_custom_elements; i++)
2259 int element = getMappedElement(getFile16BitBE(file));
2260 int properties = getFile32BitBE(file);
2262 if (IS_CUSTOM_ELEMENT(element))
2263 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2265 Error(ERR_WARN, "invalid custom element number %d", element);
2267 /* older game versions that wrote level files with CUS1 chunks used
2268 different default push delay values (not yet stored in level file) */
2269 element_info[element].push_delay_fixed = 2;
2270 element_info[element].push_delay_random = 8;
2276 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
2278 int num_changed_custom_elements = getFile16BitBE(file);
2279 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2282 if (chunk_size_expected != chunk_size)
2284 ReadUnusedBytesFromFile(file, chunk_size - 2);
2285 return chunk_size_expected;
2288 for (i = 0; i < num_changed_custom_elements; i++)
2290 int element = getMappedElement(getFile16BitBE(file));
2291 int custom_target_element = getMappedElement(getFile16BitBE(file));
2293 if (IS_CUSTOM_ELEMENT(element))
2294 element_info[element].change->target_element = custom_target_element;
2296 Error(ERR_WARN, "invalid custom element number %d", element);
2302 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
2304 int num_changed_custom_elements = getFile16BitBE(file);
2305 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2308 if (chunk_size_expected != chunk_size)
2310 ReadUnusedBytesFromFile(file, chunk_size - 2);
2311 return chunk_size_expected;
2314 for (i = 0; i < num_changed_custom_elements; i++)
2316 int element = getMappedElement(getFile16BitBE(file));
2317 struct ElementInfo *ei = &element_info[element];
2318 unsigned int event_bits;
2320 if (!IS_CUSTOM_ELEMENT(element))
2322 Error(ERR_WARN, "invalid custom element number %d", element);
2324 element = EL_INTERNAL_DUMMY;
2327 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2328 ei->description[j] = getFile8Bit(file);
2329 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2331 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2333 /* some free bytes for future properties and padding */
2334 ReadUnusedBytesFromFile(file, 7);
2336 ei->use_gfx_element = getFile8Bit(file);
2337 ei->gfx_element = getMappedElement(getFile16BitBE(file));
2339 ei->collect_score_initial = getFile8Bit(file);
2340 ei->collect_count_initial = getFile8Bit(file);
2342 ei->push_delay_fixed = getFile16BitBE(file);
2343 ei->push_delay_random = getFile16BitBE(file);
2344 ei->move_delay_fixed = getFile16BitBE(file);
2345 ei->move_delay_random = getFile16BitBE(file);
2347 ei->move_pattern = getFile16BitBE(file);
2348 ei->move_direction_initial = getFile8Bit(file);
2349 ei->move_stepsize = getFile8Bit(file);
2351 for (y = 0; y < 3; y++)
2352 for (x = 0; x < 3; x++)
2353 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2355 event_bits = getFile32BitBE(file);
2356 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2357 if (event_bits & (1 << j))
2358 ei->change->has_event[j] = TRUE;
2360 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2362 ei->change->delay_fixed = getFile16BitBE(file);
2363 ei->change->delay_random = getFile16BitBE(file);
2364 ei->change->delay_frames = getFile16BitBE(file);
2366 ei->change->trigger_element = getMappedElement(getFile16BitBE(file));
2368 ei->change->explode = getFile8Bit(file);
2369 ei->change->use_target_content = getFile8Bit(file);
2370 ei->change->only_if_complete = getFile8Bit(file);
2371 ei->change->use_random_replace = getFile8Bit(file);
2373 ei->change->random_percentage = getFile8Bit(file);
2374 ei->change->replace_when = getFile8Bit(file);
2376 for (y = 0; y < 3; y++)
2377 for (x = 0; x < 3; x++)
2378 ei->change->target_content.e[x][y] =
2379 getMappedElement(getFile16BitBE(file));
2381 ei->slippery_type = getFile8Bit(file);
2383 /* some free bytes for future properties and padding */
2384 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2386 /* mark that this custom element has been modified */
2387 ei->modified_settings = TRUE;
2393 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
2395 struct ElementInfo *ei;
2396 int chunk_size_expected;
2400 /* ---------- custom element base property values (96 bytes) ------------- */
2402 element = getMappedElement(getFile16BitBE(file));
2404 if (!IS_CUSTOM_ELEMENT(element))
2406 Error(ERR_WARN, "invalid custom element number %d", element);
2408 ReadUnusedBytesFromFile(file, chunk_size - 2);
2412 ei = &element_info[element];
2414 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2415 ei->description[i] = getFile8Bit(file);
2416 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2418 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2420 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2422 ei->num_change_pages = getFile8Bit(file);
2424 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2425 if (chunk_size_expected != chunk_size)
2427 ReadUnusedBytesFromFile(file, chunk_size - 43);
2428 return chunk_size_expected;
2431 ei->ce_value_fixed_initial = getFile16BitBE(file);
2432 ei->ce_value_random_initial = getFile16BitBE(file);
2433 ei->use_last_ce_value = getFile8Bit(file);
2435 ei->use_gfx_element = getFile8Bit(file);
2436 ei->gfx_element = getMappedElement(getFile16BitBE(file));
2438 ei->collect_score_initial = getFile8Bit(file);
2439 ei->collect_count_initial = getFile8Bit(file);
2441 ei->drop_delay_fixed = getFile8Bit(file);
2442 ei->push_delay_fixed = getFile8Bit(file);
2443 ei->drop_delay_random = getFile8Bit(file);
2444 ei->push_delay_random = getFile8Bit(file);
2445 ei->move_delay_fixed = getFile16BitBE(file);
2446 ei->move_delay_random = getFile16BitBE(file);
2448 /* bits 0 - 15 of "move_pattern" ... */
2449 ei->move_pattern = getFile16BitBE(file);
2450 ei->move_direction_initial = getFile8Bit(file);
2451 ei->move_stepsize = getFile8Bit(file);
2453 ei->slippery_type = getFile8Bit(file);
2455 for (y = 0; y < 3; y++)
2456 for (x = 0; x < 3; x++)
2457 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2459 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2460 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2461 ei->move_leave_type = getFile8Bit(file);
2463 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2464 ei->move_pattern |= (getFile16BitBE(file) << 16);
2466 ei->access_direction = getFile8Bit(file);
2468 ei->explosion_delay = getFile8Bit(file);
2469 ei->ignition_delay = getFile8Bit(file);
2470 ei->explosion_type = getFile8Bit(file);
2472 /* some free bytes for future custom property values and padding */
2473 ReadUnusedBytesFromFile(file, 1);
2475 /* ---------- change page property values (48 bytes) --------------------- */
2477 setElementChangePages(ei, ei->num_change_pages);
2479 for (i = 0; i < ei->num_change_pages; i++)
2481 struct ElementChangeInfo *change = &ei->change_page[i];
2482 unsigned int event_bits;
2484 /* always start with reliable default values */
2485 setElementChangeInfoToDefaults(change);
2487 /* bits 0 - 31 of "has_event[]" ... */
2488 event_bits = getFile32BitBE(file);
2489 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2490 if (event_bits & (1 << j))
2491 change->has_event[j] = TRUE;
2493 change->target_element = getMappedElement(getFile16BitBE(file));
2495 change->delay_fixed = getFile16BitBE(file);
2496 change->delay_random = getFile16BitBE(file);
2497 change->delay_frames = getFile16BitBE(file);
2499 change->trigger_element = getMappedElement(getFile16BitBE(file));
2501 change->explode = getFile8Bit(file);
2502 change->use_target_content = getFile8Bit(file);
2503 change->only_if_complete = getFile8Bit(file);
2504 change->use_random_replace = getFile8Bit(file);
2506 change->random_percentage = getFile8Bit(file);
2507 change->replace_when = getFile8Bit(file);
2509 for (y = 0; y < 3; y++)
2510 for (x = 0; x < 3; x++)
2511 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2513 change->can_change = getFile8Bit(file);
2515 change->trigger_side = getFile8Bit(file);
2517 change->trigger_player = getFile8Bit(file);
2518 change->trigger_page = getFile8Bit(file);
2520 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2521 CH_PAGE_ANY : (1 << change->trigger_page));
2523 change->has_action = getFile8Bit(file);
2524 change->action_type = getFile8Bit(file);
2525 change->action_mode = getFile8Bit(file);
2526 change->action_arg = getFile16BitBE(file);
2528 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2529 event_bits = getFile8Bit(file);
2530 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2531 if (event_bits & (1 << (j - 32)))
2532 change->has_event[j] = TRUE;
2535 /* mark this custom element as modified */
2536 ei->modified_settings = TRUE;
2541 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
2543 struct ElementInfo *ei;
2544 struct ElementGroupInfo *group;
2548 element = getMappedElement(getFile16BitBE(file));
2550 if (!IS_GROUP_ELEMENT(element))
2552 Error(ERR_WARN, "invalid group element number %d", element);
2554 ReadUnusedBytesFromFile(file, chunk_size - 2);
2558 ei = &element_info[element];
2560 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2561 ei->description[i] = getFile8Bit(file);
2562 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2564 group = element_info[element].group;
2566 group->num_elements = getFile8Bit(file);
2568 ei->use_gfx_element = getFile8Bit(file);
2569 ei->gfx_element = getMappedElement(getFile16BitBE(file));
2571 group->choice_mode = getFile8Bit(file);
2573 /* some free bytes for future values and padding */
2574 ReadUnusedBytesFromFile(file, 3);
2576 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2577 group->element[i] = getMappedElement(getFile16BitBE(file));
2579 /* mark this group element as modified */
2580 element_info[element].modified_settings = TRUE;
2585 static int LoadLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *conf,
2586 int element, int real_element)
2588 int micro_chunk_size = 0;
2589 int conf_type = getFile8Bit(file);
2590 int byte_mask = conf_type & CONF_MASK_BYTES;
2591 boolean element_found = FALSE;
2594 micro_chunk_size += 1;
2596 if (byte_mask == CONF_MASK_MULTI_BYTES)
2598 int num_bytes = getFile16BitBE(file);
2599 byte *buffer = checked_malloc(num_bytes);
2601 ReadBytesFromFile(file, buffer, num_bytes);
2603 for (i = 0; conf[i].data_type != -1; i++)
2605 if (conf[i].element == element &&
2606 conf[i].conf_type == conf_type)
2608 int data_type = conf[i].data_type;
2609 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2610 int max_num_entities = conf[i].max_num_entities;
2612 if (num_entities > max_num_entities)
2615 "truncating number of entities for element %d from %d to %d",
2616 element, num_entities, max_num_entities);
2618 num_entities = max_num_entities;
2621 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
2622 data_type == TYPE_CONTENT_LIST))
2624 /* for element and content lists, zero entities are not allowed */
2625 Error(ERR_WARN, "found empty list of entities for element %d",
2628 /* do not set "num_entities" here to prevent reading behind buffer */
2630 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
2634 *(int *)(conf[i].num_entities) = num_entities;
2637 element_found = TRUE;
2639 if (data_type == TYPE_STRING)
2641 char *string = (char *)(conf[i].value);
2644 for (j = 0; j < max_num_entities; j++)
2645 string[j] = (j < num_entities ? buffer[j] : '\0');
2647 else if (data_type == TYPE_ELEMENT_LIST)
2649 int *element_array = (int *)(conf[i].value);
2652 for (j = 0; j < num_entities; j++)
2654 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
2656 else if (data_type == TYPE_CONTENT_LIST)
2658 struct Content *content= (struct Content *)(conf[i].value);
2661 for (c = 0; c < num_entities; c++)
2662 for (y = 0; y < 3; y++)
2663 for (x = 0; x < 3; x++)
2664 content[c].e[x][y] =
2665 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
2668 element_found = FALSE;
2674 checked_free(buffer);
2676 micro_chunk_size += 2 + num_bytes;
2678 else /* constant size configuration data (1, 2 or 4 bytes) */
2680 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
2681 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
2682 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
2684 for (i = 0; conf[i].data_type != -1; i++)
2686 if (conf[i].element == element &&
2687 conf[i].conf_type == conf_type)
2689 int data_type = conf[i].data_type;
2691 if (data_type == TYPE_ELEMENT)
2692 value = getMappedElement(value);
2694 if (data_type == TYPE_BOOLEAN)
2695 *(boolean *)(conf[i].value) = value;
2697 *(int *) (conf[i].value) = value;
2699 element_found = TRUE;
2705 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
2710 char *error_conf_chunk_bytes =
2711 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
2712 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
2713 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
2714 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
2715 int error_element = real_element;
2717 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
2718 error_conf_chunk_bytes, error_conf_chunk_token,
2719 error_element, EL_NAME(error_element));
2722 return micro_chunk_size;
2725 static int LoadLevel_INFO(FILE *file, int chunk_size, struct LevelInfo *level)
2727 int real_chunk_size = 0;
2729 li = *level; /* copy level data into temporary buffer */
2733 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
2735 if (real_chunk_size >= chunk_size)
2739 *level = li; /* copy temporary buffer back to level data */
2741 return real_chunk_size;
2744 static int LoadLevel_CONF(FILE *file, int chunk_size, struct LevelInfo *level)
2746 int real_chunk_size = 0;
2748 li = *level; /* copy level data into temporary buffer */
2752 int element = getMappedElement(getFile16BitBE(file));
2754 real_chunk_size += 2;
2755 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
2757 if (real_chunk_size >= chunk_size)
2761 *level = li; /* copy temporary buffer back to level data */
2763 return real_chunk_size;
2766 static int LoadLevel_ELEM(FILE *file, int chunk_size, struct LevelInfo *level)
2768 int real_chunk_size = 0;
2770 li = *level; /* copy level data into temporary buffer */
2774 int element = getMappedElement(getFile16BitBE(file));
2776 real_chunk_size += 2;
2777 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
2779 if (real_chunk_size >= chunk_size)
2783 *level = li; /* copy temporary buffer back to level data */
2785 return real_chunk_size;
2788 static int LoadLevel_NOTE(FILE *file, int chunk_size, struct LevelInfo *level)
2790 int element = getMappedElement(getFile16BitBE(file));
2791 int envelope_nr = element - EL_ENVELOPE_1;
2792 int real_chunk_size = 2;
2796 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
2799 if (real_chunk_size >= chunk_size)
2803 level->envelope[envelope_nr] = xx_envelope;
2805 return real_chunk_size;
2808 static int LoadLevel_CUSX(FILE *file, int chunk_size, struct LevelInfo *level)
2810 int element = getMappedElement(getFile16BitBE(file));
2811 int real_chunk_size = 2;
2812 struct ElementInfo *ei = &element_info[element];
2815 xx_ei = *ei; /* copy element data into temporary buffer */
2817 xx_ei.num_change_pages = -1;
2821 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
2823 if (xx_ei.num_change_pages != -1)
2826 if (real_chunk_size >= chunk_size)
2832 if (ei->num_change_pages == -1)
2834 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
2837 ei->num_change_pages = 1;
2839 setElementChangePages(ei, 1);
2840 setElementChangeInfoToDefaults(ei->change);
2842 return real_chunk_size;
2845 /* initialize number of change pages stored for this custom element */
2846 setElementChangePages(ei, ei->num_change_pages);
2847 for (i = 0; i < ei->num_change_pages; i++)
2848 setElementChangeInfoToDefaults(&ei->change_page[i]);
2850 /* start with reading properties for the first change page */
2851 xx_current_change_page = 0;
2855 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
2857 xx_change = *change; /* copy change data into temporary buffer */
2859 resetEventBits(); /* reset bits; change page might have changed */
2861 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
2864 *change = xx_change;
2866 setEventFlagsFromEventBits(change);
2868 if (real_chunk_size >= chunk_size)
2872 return real_chunk_size;
2875 static int LoadLevel_GRPX(FILE *file, int chunk_size, struct LevelInfo *level)
2877 int element = getMappedElement(getFile16BitBE(file));
2878 int real_chunk_size = 2;
2879 struct ElementInfo *ei = &element_info[element];
2880 struct ElementGroupInfo *group = ei->group;
2882 xx_ei = *ei; /* copy element data into temporary buffer */
2883 xx_group = *group; /* copy group data into temporary buffer */
2887 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
2890 if (real_chunk_size >= chunk_size)
2897 return real_chunk_size;
2900 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
2901 struct LevelFileInfo *level_file_info)
2903 char *filename = level_file_info->filename;
2904 char cookie[MAX_LINE_LEN];
2905 char chunk_name[CHUNK_ID_LEN + 1];
2909 if (!(file = fopen(filename, MODE_READ)))
2911 level->no_valid_file = TRUE;
2913 if (level != &level_template)
2914 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
2919 getFileChunkBE(file, chunk_name, NULL);
2920 if (strEqual(chunk_name, "RND1"))
2922 getFile32BitBE(file); /* not used */
2924 getFileChunkBE(file, chunk_name, NULL);
2925 if (!strEqual(chunk_name, "CAVE"))
2927 level->no_valid_file = TRUE;
2929 Error(ERR_WARN, "unknown format of level file '%s'", filename);
2934 else /* check for pre-2.0 file format with cookie string */
2936 strcpy(cookie, chunk_name);
2937 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
2938 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2939 cookie[strlen(cookie) - 1] = '\0';
2941 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
2943 level->no_valid_file = TRUE;
2945 Error(ERR_WARN, "unknown format of level file '%s'", filename);
2950 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
2952 level->no_valid_file = TRUE;
2954 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
2959 /* pre-2.0 level files have no game version, so use file version here */
2960 level->game_version = level->file_version;
2963 if (level->file_version < FILE_VERSION_1_2)
2965 /* level files from versions before 1.2.0 without chunk structure */
2966 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
2967 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
2975 int (*loader)(FILE *, int, struct LevelInfo *);
2979 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
2980 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
2981 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
2982 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
2983 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
2984 { "INFO", -1, LoadLevel_INFO },
2985 { "BODY", -1, LoadLevel_BODY },
2986 { "CONT", -1, LoadLevel_CONT },
2987 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
2988 { "CNT3", -1, LoadLevel_CNT3 },
2989 { "CUS1", -1, LoadLevel_CUS1 },
2990 { "CUS2", -1, LoadLevel_CUS2 },
2991 { "CUS3", -1, LoadLevel_CUS3 },
2992 { "CUS4", -1, LoadLevel_CUS4 },
2993 { "GRP1", -1, LoadLevel_GRP1 },
2994 { "CONF", -1, LoadLevel_CONF },
2995 { "ELEM", -1, LoadLevel_ELEM },
2996 { "NOTE", -1, LoadLevel_NOTE },
2997 { "CUSX", -1, LoadLevel_CUSX },
2998 { "GRPX", -1, LoadLevel_GRPX },
3003 while (getFileChunkBE(file, chunk_name, &chunk_size))
3007 while (chunk_info[i].name != NULL &&
3008 !strEqual(chunk_name, chunk_info[i].name))
3011 if (chunk_info[i].name == NULL)
3013 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3014 chunk_name, filename);
3015 ReadUnusedBytesFromFile(file, chunk_size);
3017 else if (chunk_info[i].size != -1 &&
3018 chunk_info[i].size != chunk_size)
3020 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3021 chunk_size, chunk_name, filename);
3022 ReadUnusedBytesFromFile(file, chunk_size);
3026 /* call function to load this level chunk */
3027 int chunk_size_expected =
3028 (chunk_info[i].loader)(file, chunk_size, level);
3030 /* the size of some chunks cannot be checked before reading other
3031 chunks first (like "HEAD" and "BODY") that contain some header
3032 information, so check them here */
3033 if (chunk_size_expected != chunk_size)
3035 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3036 chunk_size, chunk_name, filename);
3045 /* ------------------------------------------------------------------------- */
3046 /* functions for loading EM level */
3047 /* ------------------------------------------------------------------------- */
3051 static int map_em_element_yam(int element)
3055 case 0x00: return EL_EMPTY;
3056 case 0x01: return EL_EMERALD;
3057 case 0x02: return EL_DIAMOND;
3058 case 0x03: return EL_ROCK;
3059 case 0x04: return EL_ROBOT;
3060 case 0x05: return EL_SPACESHIP_UP;
3061 case 0x06: return EL_BOMB;
3062 case 0x07: return EL_BUG_UP;
3063 case 0x08: return EL_AMOEBA_DROP;
3064 case 0x09: return EL_NUT;
3065 case 0x0a: return EL_YAMYAM;
3066 case 0x0b: return EL_QUICKSAND_FULL;
3067 case 0x0c: return EL_SAND;
3068 case 0x0d: return EL_WALL_SLIPPERY;
3069 case 0x0e: return EL_STEELWALL;
3070 case 0x0f: return EL_WALL;
3071 case 0x10: return EL_EM_KEY_1;
3072 case 0x11: return EL_EM_KEY_2;
3073 case 0x12: return EL_EM_KEY_4;
3074 case 0x13: return EL_EM_KEY_3;
3075 case 0x14: return EL_MAGIC_WALL;
3076 case 0x15: return EL_ROBOT_WHEEL;
3077 case 0x16: return EL_DYNAMITE;
3079 case 0x17: return EL_EM_KEY_1; /* EMC */
3080 case 0x18: return EL_BUG_UP; /* EMC */
3081 case 0x1a: return EL_DIAMOND; /* EMC */
3082 case 0x1b: return EL_EMERALD; /* EMC */
3083 case 0x25: return EL_NUT; /* EMC */
3084 case 0x80: return EL_EMPTY; /* EMC */
3085 case 0x85: return EL_EM_KEY_1; /* EMC */
3086 case 0x86: return EL_EM_KEY_2; /* EMC */
3087 case 0x87: return EL_EM_KEY_4; /* EMC */
3088 case 0x88: return EL_EM_KEY_3; /* EMC */
3089 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
3090 case 0x9a: return EL_AMOEBA_WET; /* EMC */
3091 case 0xaf: return EL_DYNAMITE; /* EMC */
3092 case 0xbd: return EL_SAND; /* EMC */
3095 Error(ERR_WARN, "invalid level element %d", element);
3100 static int map_em_element_field(int element)
3102 if (element >= 0xc8 && element <= 0xe1)
3103 return EL_CHAR_A + (element - 0xc8);
3104 else if (element >= 0xe2 && element <= 0xeb)
3105 return EL_CHAR_0 + (element - 0xe2);
3109 case 0x00: return EL_ROCK;
3110 case 0x01: return EL_ROCK; /* EMC */
3111 case 0x02: return EL_DIAMOND;
3112 case 0x03: return EL_DIAMOND;
3113 case 0x04: return EL_ROBOT;
3114 case 0x05: return EL_ROBOT; /* EMC */
3115 case 0x06: return EL_EMPTY_SPACE; /* EMC */
3116 case 0x07: return EL_EMPTY_SPACE; /* EMC */
3117 case 0x08: return EL_SPACESHIP_UP;
3118 case 0x09: return EL_SPACESHIP_RIGHT;
3119 case 0x0a: return EL_SPACESHIP_DOWN;
3120 case 0x0b: return EL_SPACESHIP_LEFT;
3121 case 0x0c: return EL_SPACESHIP_UP;
3122 case 0x0d: return EL_SPACESHIP_RIGHT;
3123 case 0x0e: return EL_SPACESHIP_DOWN;
3124 case 0x0f: return EL_SPACESHIP_LEFT;
3126 case 0x10: return EL_BOMB;
3127 case 0x11: return EL_BOMB; /* EMC */
3128 case 0x12: return EL_EMERALD;
3129 case 0x13: return EL_EMERALD;
3130 case 0x14: return EL_BUG_UP;
3131 case 0x15: return EL_BUG_RIGHT;
3132 case 0x16: return EL_BUG_DOWN;
3133 case 0x17: return EL_BUG_LEFT;
3134 case 0x18: return EL_BUG_UP;
3135 case 0x19: return EL_BUG_RIGHT;
3136 case 0x1a: return EL_BUG_DOWN;
3137 case 0x1b: return EL_BUG_LEFT;
3138 case 0x1c: return EL_AMOEBA_DROP;
3139 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
3140 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
3141 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
3143 case 0x20: return EL_ROCK;
3144 case 0x21: return EL_BOMB; /* EMC */
3145 case 0x22: return EL_DIAMOND; /* EMC */
3146 case 0x23: return EL_EMERALD; /* EMC */
3147 case 0x24: return EL_MAGIC_WALL;
3148 case 0x25: return EL_NUT;
3149 case 0x26: return EL_NUT; /* EMC */
3150 case 0x27: return EL_NUT; /* EMC */
3152 /* looks like magic wheel, but is _always_ activated */
3153 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
3155 case 0x29: return EL_YAMYAM; /* up */
3156 case 0x2a: return EL_YAMYAM; /* down */
3157 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
3158 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
3159 case 0x2d: return EL_QUICKSAND_FULL;
3160 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
3161 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
3163 case 0x30: return EL_EMPTY_SPACE; /* EMC */
3164 case 0x31: return EL_SAND; /* EMC */
3165 case 0x32: return EL_SAND; /* EMC */
3166 case 0x33: return EL_SAND; /* EMC */
3167 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
3168 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
3169 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
3170 case 0x37: return EL_SAND; /* EMC */
3171 case 0x38: return EL_ROCK; /* EMC */
3172 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
3173 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
3174 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
3175 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
3176 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
3177 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
3178 case 0x3f: return EL_ACID_POOL_BOTTOM;
3180 case 0x40: return EL_EXIT_OPEN; /* 1 */
3181 case 0x41: return EL_EXIT_OPEN; /* 2 */
3182 case 0x42: return EL_EXIT_OPEN; /* 3 */
3183 case 0x43: return EL_BALLOON; /* EMC */
3184 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
3185 case 0x45: return EL_SPRING; /* EMC */
3186 case 0x46: return EL_SPRING; /* falling */ /* EMC */
3187 case 0x47: return EL_SPRING; /* left */ /* EMC */
3188 case 0x48: return EL_SPRING; /* right */ /* EMC */
3189 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
3190 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
3191 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
3192 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
3193 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
3194 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
3195 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
3197 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
3198 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
3199 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
3200 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
3201 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
3202 case 0x55: return EL_EMPTY_SPACE; /* EMC */
3203 case 0x56: return EL_EMPTY_SPACE; /* EMC */
3204 case 0x57: return EL_EMPTY_SPACE; /* EMC */
3205 case 0x58: return EL_EMPTY_SPACE; /* EMC */
3206 case 0x59: return EL_EMPTY_SPACE; /* EMC */
3207 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
3208 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
3209 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
3210 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
3211 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
3212 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
3214 case 0x60: return EL_EMPTY_SPACE; /* EMC */
3215 case 0x61: return EL_EMPTY_SPACE; /* EMC */
3216 case 0x62: return EL_EMPTY_SPACE; /* EMC */
3217 case 0x63: return EL_SPRING; /* left */ /* EMC */
3218 case 0x64: return EL_SPRING; /* right */ /* EMC */
3219 case 0x65: return EL_ACID; /* 1 */ /* EMC */
3220 case 0x66: return EL_ACID; /* 2 */ /* EMC */
3221 case 0x67: return EL_ACID; /* 3 */ /* EMC */
3222 case 0x68: return EL_ACID; /* 4 */ /* EMC */
3223 case 0x69: return EL_ACID; /* 5 */ /* EMC */
3224 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
3225 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
3226 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
3227 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
3228 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
3229 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
3231 case 0x70: return EL_EMPTY_SPACE; /* EMC */
3232 case 0x71: return EL_EMPTY_SPACE; /* EMC */
3233 case 0x72: return EL_NUT; /* left */ /* EMC */
3234 case 0x73: return EL_SAND; /* EMC (? "nut") */
3235 case 0x74: return EL_STEELWALL;
3236 case 0x75: return EL_EMPTY_SPACE; /* EMC */
3237 case 0x76: return EL_EMPTY_SPACE; /* EMC */
3238 case 0x77: return EL_BOMB; /* left */ /* EMC */
3239 case 0x78: return EL_BOMB; /* right */ /* EMC */
3240 case 0x79: return EL_ROCK; /* left */ /* EMC */
3241 case 0x7a: return EL_ROCK; /* right */ /* EMC */
3242 case 0x7b: return EL_ACID; /* (? EMC "blank") */
3243 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
3244 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
3245 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
3246 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
3248 case 0x80: return EL_EMPTY;
3249 case 0x81: return EL_WALL_SLIPPERY;
3250 case 0x82: return EL_SAND;
3251 case 0x83: return EL_STEELWALL;
3252 case 0x84: return EL_WALL;
3253 case 0x85: return EL_EM_KEY_1;
3254 case 0x86: return EL_EM_KEY_2;
3255 case 0x87: return EL_EM_KEY_4;
3256 case 0x88: return EL_EM_KEY_3;
3257 case 0x89: return EL_EM_GATE_1;
3258 case 0x8a: return EL_EM_GATE_2;
3259 case 0x8b: return EL_EM_GATE_4;
3260 case 0x8c: return EL_EM_GATE_3;
3261 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
3262 case 0x8e: return EL_EM_GATE_1_GRAY;
3263 case 0x8f: return EL_EM_GATE_2_GRAY;
3265 case 0x90: return EL_EM_GATE_4_GRAY;
3266 case 0x91: return EL_EM_GATE_3_GRAY;
3267 case 0x92: return EL_MAGIC_WALL;
3268 case 0x93: return EL_ROBOT_WHEEL;
3269 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
3270 case 0x95: return EL_ACID_POOL_TOPLEFT;
3271 case 0x96: return EL_ACID_POOL_TOPRIGHT;
3272 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
3273 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
3274 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
3275 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
3276 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
3277 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
3278 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
3279 case 0x9e: return EL_EXIT_CLOSED;
3280 case 0x9f: return EL_CHAR_LESS; /* arrow left */
3282 /* looks like normal sand, but behaves like wall */
3283 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
3284 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
3285 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
3286 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
3287 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
3288 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
3289 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
3290 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
3291 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
3292 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
3293 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
3294 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
3295 case 0xac: return EL_CHAR_COMMA; /* EMC */
3296 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
3297 case 0xae: return EL_CHAR_MINUS; /* EMC */
3298 case 0xaf: return EL_DYNAMITE;
3300 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
3301 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
3302 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
3303 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
3304 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
3305 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
3306 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
3307 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
3308 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
3309 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
3310 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
3311 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
3312 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
3313 case 0xbd: return EL_SAND; /* EMC ("dirt") */
3314 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
3315 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
3317 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
3318 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
3319 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
3320 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
3321 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
3322 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
3323 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
3324 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
3326 /* characters: see above */
3328 case 0xec: return EL_CHAR_PERIOD;
3329 case 0xed: return EL_CHAR_EXCLAM;
3330 case 0xee: return EL_CHAR_COLON;
3331 case 0xef: return EL_CHAR_QUESTION;
3333 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
3334 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
3335 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
3336 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
3337 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
3338 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
3339 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
3340 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
3342 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
3343 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
3344 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
3345 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
3346 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
3347 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
3349 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
3350 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
3353 /* should never happen (all 8-bit value cases should be handled) */
3354 Error(ERR_WARN, "invalid level element %d", element);
3359 #define EM_LEVEL_SIZE 2106
3360 #define EM_LEVEL_XSIZE 64
3361 #define EM_LEVEL_YSIZE 32
3363 static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
3364 struct LevelFileInfo *level_file_info)
3366 char *filename = level_file_info->filename;
3368 unsigned char leveldata[EM_LEVEL_SIZE];
3369 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
3370 int nr = level_file_info->nr;
3373 if (!(file = fopen(filename, MODE_READ)))
3375 level->no_valid_file = TRUE;
3377 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3382 for (i = 0; i < EM_LEVEL_SIZE; i++)
3383 leveldata[i] = fgetc(file);
3387 /* check if level data is crypted by testing against known starting bytes
3388 of the few existing crypted level files (from Emerald Mine 1 + 2) */
3390 if ((leveldata[0] == 0xf1 ||
3391 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
3393 unsigned char code0 = 0x65;
3394 unsigned char code1 = 0x11;
3396 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
3397 leveldata[0] = 0xf1;
3399 /* decode crypted level data */
3401 for (i = 0; i < EM_LEVEL_SIZE; i++)
3403 leveldata[i] ^= code0;
3404 leveldata[i] -= code1;
3406 code0 = (code0 + 7) & 0xff;
3410 level->fieldx = EM_LEVEL_XSIZE;
3411 level->fieldy = EM_LEVEL_YSIZE;
3413 level->time = header[46] * 10;
3414 level->gems_needed = header[47];
3416 /* The original Emerald Mine levels have their level number stored
3417 at the second byte of the level file...
3418 Do not trust this information at other level files, e.g. EMC,
3419 but correct it anyway (normally the first row is completely
3420 steel wall, so the correction does not hurt anyway). */
3422 if (leveldata[1] == nr)
3423 leveldata[1] = leveldata[2]; /* correct level number field */
3425 sprintf(level->name, "Level %d", nr); /* set level name */
3427 level->score[SC_EMERALD] = header[36];
3428 level->score[SC_DIAMOND] = header[37];
3429 level->score[SC_ROBOT] = header[38];
3430 level->score[SC_SPACESHIP] = header[39];
3431 level->score[SC_BUG] = header[40];
3432 level->score[SC_YAMYAM] = header[41];
3433 level->score[SC_NUT] = header[42];
3434 level->score[SC_DYNAMITE] = header[43];
3435 level->score[SC_TIME_BONUS] = header[44];
3437 level->num_yamyam_contents = 4;
3439 for (i = 0; i < level->num_yamyam_contents; i++)
3440 for (y = 0; y < 3; y++)
3441 for (x = 0; x < 3; x++)
3442 level->yamyam_content[i].e[x][y] =
3443 map_em_element_yam(header[i * 9 + y * 3 + x]);
3445 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
3446 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
3447 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
3448 level->amoeba_content = EL_DIAMOND;
3450 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3452 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
3454 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
3455 new_element = EL_AMOEBA_WET;
3457 level->field[x][y] = new_element;
3460 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
3461 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
3462 level->field[x][y] = EL_PLAYER_1;
3464 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
3465 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
3466 level->field[x][y] = EL_PLAYER_2;
3471 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3473 static int ball_xy[8][2] =
3484 struct LevelInfo_EM *level_em = level->native_em_level;
3485 struct LEVEL *lev = level_em->lev;
3486 struct PLAYER **ply = level_em->ply;
3489 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3490 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3492 lev->time_seconds = level->time;
3493 lev->required_initial = level->gems_needed;
3495 lev->emerald_score = level->score[SC_EMERALD];
3496 lev->diamond_score = level->score[SC_DIAMOND];
3497 lev->alien_score = level->score[SC_ROBOT];
3498 lev->tank_score = level->score[SC_SPACESHIP];
3499 lev->bug_score = level->score[SC_BUG];
3500 lev->eater_score = level->score[SC_YAMYAM];
3501 lev->nut_score = level->score[SC_NUT];
3502 lev->dynamite_score = level->score[SC_DYNAMITE];
3503 lev->key_score = level->score[SC_KEY];
3504 lev->exit_score = level->score[SC_TIME_BONUS];
3506 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3507 for (y = 0; y < 3; y++)
3508 for (x = 0; x < 3; x++)
3509 lev->eater_array[i][y * 3 + x] =
3510 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3512 lev->amoeba_time = level->amoeba_speed;
3513 lev->wonderwall_time_initial = level->time_magic_wall;
3514 lev->wheel_time = level->time_wheel;
3516 lev->android_move_time = level->android_move_time;
3517 lev->android_clone_time = level->android_clone_time;
3518 lev->ball_random = level->ball_random;
3519 lev->ball_state_initial = level->ball_state_initial;
3520 lev->ball_time = level->ball_time;
3521 lev->num_ball_arrays = level->num_ball_contents;
3523 lev->lenses_score = level->lenses_score;
3524 lev->magnify_score = level->magnify_score;
3525 lev->slurp_score = level->slurp_score;
3527 lev->lenses_time = level->lenses_time;
3528 lev->magnify_time = level->magnify_time;
3530 lev->wind_direction_initial =
3531 map_direction_RND_to_EM(level->wind_direction_initial);
3532 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3533 lev->wind_time : 0);
3535 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3536 for (j = 0; j < 8; j++)
3537 lev->ball_array[i][j] =
3538 map_element_RND_to_EM(level->
3539 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3541 map_android_clone_elements_RND_to_EM(level);
3543 /* first fill the complete playfield with the default border element */
3544 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3545 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3546 level_em->cave[x][y] = ZBORDER;
3548 if (BorderElement == EL_STEELWALL)
3550 for (y = 0; y < lev->height + 2; y++)
3551 for (x = 0; x < lev->width + 2; x++)
3552 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3555 /* then copy the real level contents from level file into the playfield */
3556 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3558 int new_element = map_element_RND_to_EM(level->field[x][y]);
3559 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3560 int xx = x + 1 + offset;
3561 int yy = y + 1 + offset;
3563 if (level->field[x][y] == EL_AMOEBA_DEAD)
3564 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3566 level_em->cave[xx][yy] = new_element;
3569 for (i = 0; i < MAX_PLAYERS; i++)
3571 ply[i]->x_initial = 0;
3572 ply[i]->y_initial = 0;
3575 /* initialize player positions and delete players from the playfield */
3576 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3578 if (ELEM_IS_PLAYER(level->field[x][y]))
3580 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3581 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3582 int xx = x + 1 + offset;
3583 int yy = y + 1 + offset;
3585 ply[player_nr]->x_initial = xx;
3586 ply[player_nr]->y_initial = yy;
3588 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3592 if (BorderElement == EL_STEELWALL)
3599 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3601 static int ball_xy[8][2] =
3612 struct LevelInfo_EM *level_em = level->native_em_level;
3613 struct LEVEL *lev = level_em->lev;
3614 struct PLAYER **ply = level_em->ply;
3617 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3618 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3620 level->time = lev->time_seconds;
3621 level->gems_needed = lev->required_initial;
3623 sprintf(level->name, "Level %d", level->file_info.nr);
3625 level->score[SC_EMERALD] = lev->emerald_score;
3626 level->score[SC_DIAMOND] = lev->diamond_score;
3627 level->score[SC_ROBOT] = lev->alien_score;
3628 level->score[SC_SPACESHIP] = lev->tank_score;
3629 level->score[SC_BUG] = lev->bug_score;
3630 level->score[SC_YAMYAM] = lev->eater_score;
3631 level->score[SC_NUT] = lev->nut_score;
3632 level->score[SC_DYNAMITE] = lev->dynamite_score;
3633 level->score[SC_KEY] = lev->key_score;
3634 level->score[SC_TIME_BONUS] = lev->exit_score;
3636 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3638 for (i = 0; i < level->num_yamyam_contents; i++)
3639 for (y = 0; y < 3; y++)
3640 for (x = 0; x < 3; x++)
3641 level->yamyam_content[i].e[x][y] =
3642 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3644 level->amoeba_speed = lev->amoeba_time;
3645 level->time_magic_wall = lev->wonderwall_time_initial;
3646 level->time_wheel = lev->wheel_time;
3648 level->android_move_time = lev->android_move_time;
3649 level->android_clone_time = lev->android_clone_time;
3650 level->ball_random = lev->ball_random;
3651 level->ball_state_initial = lev->ball_state_initial;
3652 level->ball_time = lev->ball_time;
3653 level->num_ball_contents = lev->num_ball_arrays;
3655 level->lenses_score = lev->lenses_score;
3656 level->magnify_score = lev->magnify_score;
3657 level->slurp_score = lev->slurp_score;
3659 level->lenses_time = lev->lenses_time;
3660 level->magnify_time = lev->magnify_time;
3662 level->wind_direction_initial =
3663 map_direction_EM_to_RND(lev->wind_direction_initial);
3665 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3666 for (j = 0; j < 8; j++)
3667 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3668 map_element_EM_to_RND(lev->ball_array[i][j]);
3670 map_android_clone_elements_EM_to_RND(level);
3672 /* convert the playfield (some elements need special treatment) */
3673 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3675 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3677 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3678 new_element = EL_AMOEBA_DEAD;
3680 level->field[x][y] = new_element;
3683 for (i = 0; i < MAX_PLAYERS; i++)
3685 /* in case of all players set to the same field, use the first player */
3686 int nr = MAX_PLAYERS - i - 1;
3687 int jx = ply[nr]->x_initial - 1;
3688 int jy = ply[nr]->y_initial - 1;
3690 if (jx != -1 && jy != -1)
3691 level->field[jx][jy] = EL_PLAYER_1 + nr;
3695 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
3696 struct LevelFileInfo *level_file_info)
3698 if (!LoadNativeLevel_EM(level_file_info->filename))
3699 level->no_valid_file = TRUE;
3702 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
3704 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
3705 CopyNativeLevel_RND_to_EM(level);
3708 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
3710 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
3711 CopyNativeLevel_EM_to_RND(level);
3715 /* ------------------------------------------------------------------------- */
3716 /* functions for loading SP level */
3717 /* ------------------------------------------------------------------------- */
3719 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
3720 #define SP_LEVEL_SIZE 1536
3721 #define SP_LEVEL_XSIZE 60
3722 #define SP_LEVEL_YSIZE 24
3723 #define SP_LEVEL_NAME_LEN 23
3725 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
3728 int initial_player_gravity;
3729 int num_special_ports;
3732 /* for details of the Supaplex level format, see Herman Perk's Supaplex
3733 documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
3735 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
3736 for (y = 0; y < SP_LEVEL_YSIZE; y++)
3738 for (x = 0; x < SP_LEVEL_XSIZE; x++)
3740 int element_old = fgetc(file);
3743 if (element_old <= 0x27)
3744 element_new = getMappedElement(EL_SP_START + element_old);
3745 else if (element_old == 0x28)
3746 element_new = EL_INVISIBLE_WALL;
3749 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
3750 Error(ERR_WARN, "invalid level element %d", element_old);
3752 element_new = EL_UNKNOWN;
3755 level->field[x][y] = element_new;
3759 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
3761 /* initial gravity: 1 == "on", anything else (0) == "off" */
3762 initial_player_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
3764 for (i = 0; i < MAX_PLAYERS; i++)
3765 level->initial_player_gravity[i] = initial_player_gravity;
3767 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
3769 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
3770 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3771 level->name[i] = fgetc(file);
3772 level->name[SP_LEVEL_NAME_LEN] = '\0';
3774 /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
3775 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
3777 /* number of infotrons needed; 0 means that Supaplex will count the total
3778 amount of infotrons in the level and use the low byte of that number
3779 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
3780 level->gems_needed = fgetc(file);
3782 /* number of special ("gravity") port entries below (maximum 10 allowed) */
3783 num_special_ports = fgetc(file);
3785 /* database of properties of up to 10 special ports (6 bytes per port) */
3786 for (i = 0; i < 10; i++)
3788 int port_location, port_x, port_y, port_element;
3791 /* high and low byte of the location of a special port; if (x, y) are the
3792 coordinates of a port in the field and (0, 0) is the top-left corner,
3793 the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
3794 of what may be expected: Supaplex works with a game field in memory
3795 which is 2 bytes per tile) */
3796 port_location = getFile16BitBE(file);
3798 /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
3799 gravity = fgetc(file);
3801 /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
3802 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
3804 /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
3805 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
3807 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
3809 if (i >= num_special_ports)
3812 port_x = (port_location / 2) % SP_LEVEL_XSIZE;
3813 port_y = (port_location / 2) / SP_LEVEL_XSIZE;
3815 if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
3816 port_y < 0 || port_y >= SP_LEVEL_YSIZE)
3818 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3824 port_element = level->field[port_x][port_y];
3826 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3827 port_element > EL_SP_GRAVITY_PORT_UP)
3829 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3834 /* change previous (wrong) gravity inverting special port to either
3835 gravity enabling special port or gravity disabling special port */
3836 level->field[port_x][port_y] +=
3837 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3838 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3841 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
3843 /* change special gravity ports without database entries to normal ports */
3844 for (y = 0; y < SP_LEVEL_YSIZE; y++)
3845 for (x = 0; x < SP_LEVEL_XSIZE; x++)
3846 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3847 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3848 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3850 /* auto-determine number of infotrons if it was stored as "0" -- see above */
3851 if (level->gems_needed == 0)
3853 for (y = 0; y < SP_LEVEL_YSIZE; y++)
3854 for (x = 0; x < SP_LEVEL_XSIZE; x++)
3855 if (level->field[x][y] == EL_SP_INFOTRON)
3856 level->gems_needed++;
3858 level->gems_needed &= 0xff; /* only use low byte -- see above */
3861 level->fieldx = SP_LEVEL_XSIZE;
3862 level->fieldy = SP_LEVEL_YSIZE;
3864 level->time = 0; /* no time limit */
3865 level->amoeba_speed = 0;
3866 level->time_magic_wall = 0;
3867 level->time_wheel = 0;
3868 level->amoeba_content = EL_EMPTY;
3871 /* original Supaplex does not use score values -- use default values */
3873 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3874 level->score[i] = 0;
3877 /* there are no yamyams in supaplex levels */
3878 for (i = 0; i < level->num_yamyam_contents; i++)
3879 for (y = 0; y < 3; y++)
3880 for (x = 0; x < 3; x++)
3881 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3884 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
3885 struct LevelFileInfo *level_file_info)
3887 char *filename = level_file_info->filename;
3889 int nr = level_file_info->nr - leveldir_current->first_level;
3891 char name_first, name_last;
3892 struct LevelInfo multipart_level;
3893 int multipart_xpos, multipart_ypos;
3894 boolean is_multipart_level;
3895 boolean is_first_part;
3896 boolean reading_multipart_level = FALSE;
3897 boolean use_empty_level = FALSE;
3899 if (!(file = fopen(filename, MODE_READ)))
3901 level->no_valid_file = TRUE;
3903 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3908 /* position file stream to the requested level inside the level package */
3909 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
3911 level->no_valid_file = TRUE;
3913 Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
3918 /* there exist Supaplex level package files with multi-part levels which
3919 can be detected as follows: instead of leading and trailing dashes ('-')
3920 to pad the level name, they have leading and trailing numbers which are
3921 the x and y coordinations of the current part of the multi-part level;
3922 if there are '?' characters instead of numbers on the left or right side
3923 of the level name, the multi-part level consists of only horizontal or
3926 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
3928 LoadLevelFromFileStream_SP(file, level, l);
3930 /* check if this level is a part of a bigger multi-part level */
3932 name_first = level->name[0];
3933 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
3935 is_multipart_level =
3936 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
3937 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
3940 ((name_first == '?' || name_first == '1') &&
3941 (name_last == '?' || name_last == '1'));
3943 /* correct leading multipart level meta information in level name */
3944 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
3945 level->name[i] = '-';
3947 /* correct trailing multipart level meta information in level name */
3948 for (i = SP_LEVEL_NAME_LEN - 1; i >= 0 && level->name[i] == name_last; i--)
3949 level->name[i] = '-';
3951 /* ---------- check for normal single level ---------- */
3953 if (!reading_multipart_level && !is_multipart_level)
3955 /* the current level is simply a normal single-part level, and we are
3956 not reading a multi-part level yet, so return the level as it is */
3961 /* ---------- check for empty level (unused multi-part) ---------- */
3963 if (!reading_multipart_level && is_multipart_level && !is_first_part)
3965 /* this is a part of a multi-part level, but not the first part
3966 (and we are not already reading parts of a multi-part level);
3967 in this case, use an empty level instead of the single part */
3969 use_empty_level = TRUE;
3974 /* ---------- check for finished multi-part level ---------- */
3976 if (reading_multipart_level &&
3977 (!is_multipart_level ||
3978 !strEqual(level->name, multipart_level.name)))
3980 /* we are already reading parts of a multi-part level, but this level is
3981 either not a multi-part level, or a part of a different multi-part
3982 level; in both cases, the multi-part level seems to be complete */
3987 /* ---------- here we have one part of a multi-part level ---------- */
3989 reading_multipart_level = TRUE;
3991 if (is_first_part) /* start with first part of new multi-part level */
3993 /* copy level info structure from first part */
3994 multipart_level = *level;
3996 /* clear playfield of new multi-part level */
3997 for (y = 0; y < MAX_LEV_FIELDY; y++)
3998 for (x = 0; x < MAX_LEV_FIELDX; x++)
3999 multipart_level.field[x][y] = EL_EMPTY;
4002 if (name_first == '?')
4004 if (name_last == '?')
4007 multipart_xpos = (int)(name_first - '0');
4008 multipart_ypos = (int)(name_last - '0');
4011 printf("----------> part (%d/%d) of multi-part level '%s'\n",
4012 multipart_xpos, multipart_ypos, multipart_level.name);
4015 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
4016 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
4018 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
4023 multipart_level.fieldx = MAX(multipart_level.fieldx,
4024 multipart_xpos * SP_LEVEL_XSIZE);
4025 multipart_level.fieldy = MAX(multipart_level.fieldy,
4026 multipart_ypos * SP_LEVEL_YSIZE);
4028 /* copy level part at the right position of multi-part level */
4029 for (y = 0; y < SP_LEVEL_YSIZE; y++)
4031 for (x = 0; x < SP_LEVEL_XSIZE; x++)
4033 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
4034 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
4036 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
4043 if (use_empty_level)
4045 setLevelInfoToDefaults(level);
4047 level->fieldx = SP_LEVEL_XSIZE;
4048 level->fieldy = SP_LEVEL_YSIZE;
4050 for (y = 0; y < SP_LEVEL_YSIZE; y++)
4051 for (x = 0; x < SP_LEVEL_XSIZE; x++)
4052 level->field[x][y] = EL_EMPTY;
4054 strcpy(level->name, "-------- EMPTY --------");
4056 Error(ERR_WARN, "single part of multi-part level -- using empty level");
4059 if (reading_multipart_level)
4060 *level = multipart_level;
4064 #define DC_LEVEL_HEADER_SIZE 344
4066 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
4068 static int last_data_encoded;
4072 int diff_hi, diff_lo;
4073 int data_hi, data_lo;
4074 unsigned short data_decoded;
4078 last_data_encoded = 0;
4085 diff = data_encoded - last_data_encoded;
4086 diff_hi = diff & ~0xff;
4087 diff_lo = diff & 0xff;
4091 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4092 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4093 data_hi = data_hi & 0xff00;
4095 data_decoded = data_hi | data_lo;
4097 last_data_encoded = data_encoded;
4099 offset1 = (offset1 + 1) % 31;
4100 offset2 = offset2 & 0xff;
4102 return data_decoded;
4105 int getMappedElement_DC(int element)
4113 /* 0x0117 - 0x036e: (?) */
4116 /* 0x042d - 0x0684: (?) */
4132 element = EL_CRYSTAL;
4135 case 0x0e77: /* quicksand (boulder) */
4136 element = EL_QUICKSAND_FULL;
4139 case 0x0e99: /* slow quicksand (boulder) */
4140 element = EL_QUICKSAND_FULL;
4144 element = EL_EXIT_OPEN;
4148 element = EL_EXIT_CLOSED;
4151 case 0x0eeb: /* steel exit (open) */
4152 element = EL_EXIT_OPEN;
4155 case 0x0efc: /* steel exit (closed) */
4156 element = EL_EXIT_CLOSED;
4159 case 0x0f4f: /* dynamite (lit 1) */
4160 element = EL_DYNAMITE_ACTIVE;
4163 case 0x0f57: /* dynamite (lit 2) */
4164 element = EL_DYNAMITE_ACTIVE;
4167 case 0x0f5f: /* dynamite (lit 3) */
4168 element = EL_DYNAMITE_ACTIVE;
4171 case 0x0f67: /* dynamite (lit 4) */
4172 element = EL_DYNAMITE_ACTIVE;
4179 element = EL_AMOEBA_WET;
4183 element = EL_AMOEBA_DROP;
4187 element = EL_MAGIC_WALL;
4191 element = EL_SPACESHIP_UP;
4195 element = EL_SPACESHIP_DOWN;
4199 element = EL_SPACESHIP_LEFT;
4203 element = EL_SPACESHIP_RIGHT;
4207 element = EL_BUG_UP;
4211 element = EL_BUG_DOWN;
4215 element = EL_BUG_LEFT;
4219 element = EL_BUG_RIGHT;
4223 element = EL_MOLE_UP;
4227 element = EL_MOLE_DOWN;
4231 element = EL_MOLE_LEFT;
4235 element = EL_MOLE_RIGHT;
4243 element = EL_YAMYAM;
4247 element = EL_SWITCHGATE_OPEN;
4251 element = EL_SWITCHGATE_CLOSED;
4255 element = EL_SWITCHGATE_SWITCH_UP;
4259 element = EL_TIMEGATE_CLOSED;
4262 case 0x144c: /* conveyor belt switch (green) */
4263 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4266 case 0x144f: /* conveyor belt switch (red) */
4267 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4270 case 0x1452: /* conveyor belt switch (blue) */
4271 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4275 element = EL_CONVEYOR_BELT_3_MIDDLE;
4279 element = EL_CONVEYOR_BELT_3_LEFT;
4283 element = EL_CONVEYOR_BELT_3_RIGHT;
4287 element = EL_CONVEYOR_BELT_1_MIDDLE;
4291 element = EL_CONVEYOR_BELT_1_LEFT;
4295 element = EL_CONVEYOR_BELT_1_RIGHT;
4299 element = EL_CONVEYOR_BELT_4_MIDDLE;
4303 element = EL_CONVEYOR_BELT_4_LEFT;
4307 element = EL_CONVEYOR_BELT_4_RIGHT;
4311 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4315 element = EL_EXPANDABLE_WALL_VERTICAL;
4319 element = EL_EXPANDABLE_WALL_ANY;
4322 case 0x14ce: /* growing steel wall (left/right) */
4323 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4326 case 0x14df: /* growing steel wall (up/down) */
4327 element = EL_EXPANDABLE_WALL_VERTICAL;
4330 case 0x14e8: /* growing steel wall (up/down/left/right) */
4331 element = EL_EXPANDABLE_WALL_ANY;
4335 element = EL_SHIELD_NORMAL;
4339 element = EL_EXTRA_TIME;
4347 element = EL_EMPTY_SPACE;
4350 case 0x1578: /* quicksand (empty) */
4351 element = EL_QUICKSAND_EMPTY;
4354 case 0x1579: /* slow quicksand (empty) */
4355 element = EL_QUICKSAND_EMPTY;
4358 /* 0x157c - 0x158b: */
4361 /* 0x1590 - 0x159f: */
4365 element = EL_DYNAMITE;
4368 case 0x15a1: /* key (red) */
4369 element = EL_EM_KEY_1;
4372 case 0x15a2: /* key (yellow) */
4373 element = EL_EM_KEY_2;
4376 case 0x15a3: /* key (blue) */
4377 element = EL_EM_KEY_4;
4380 case 0x15a4: /* key (green) */
4381 element = EL_EM_KEY_3;
4384 case 0x15a5: /* key (white) */
4385 element = EL_KEY_WHITE;
4389 element = EL_WALL_SLIPPERY;
4396 case 0x15a8: /* wall (not round) */
4400 case 0x15a9: /* (blue) */
4401 element = EL_CHAR_A;
4404 case 0x15aa: /* (blue) */
4405 element = EL_CHAR_B;
4408 case 0x15ab: /* (blue) */
4409 element = EL_CHAR_C;
4412 case 0x15ac: /* (blue) */
4413 element = EL_CHAR_D;
4416 case 0x15ad: /* (blue) */
4417 element = EL_CHAR_E;
4420 case 0x15ae: /* (blue) */
4421 element = EL_CHAR_F;
4424 case 0x15af: /* (blue) */
4425 element = EL_CHAR_G;
4428 case 0x15b0: /* (blue) */
4429 element = EL_CHAR_H;
4432 case 0x15b1: /* (blue) */
4433 element = EL_CHAR_I;
4436 case 0x15b2: /* (blue) */
4437 element = EL_CHAR_J;
4440 case 0x15b3: /* (blue) */
4441 element = EL_CHAR_K;
4444 case 0x15b4: /* (blue) */
4445 element = EL_CHAR_L;
4448 case 0x15b5: /* (blue) */
4449 element = EL_CHAR_M;
4452 case 0x15b6: /* (blue) */
4453 element = EL_CHAR_N;
4456 case 0x15b7: /* (blue) */
4457 element = EL_CHAR_O;
4460 case 0x15b8: /* (blue) */
4461 element = EL_CHAR_P;
4464 case 0x15b9: /* (blue) */
4465 element = EL_CHAR_Q;
4468 case 0x15ba: /* (blue) */
4469 element = EL_CHAR_R;
4472 case 0x15bb: /* (blue) */
4473 element = EL_CHAR_S;
4476 case 0x15bc: /* (blue) */
4477 element = EL_CHAR_T;
4480 case 0x15bd: /* (blue) */
4481 element = EL_CHAR_U;
4484 case 0x15be: /* (blue) */
4485 element = EL_CHAR_V;
4488 case 0x15bf: /* (blue) */
4489 element = EL_CHAR_W;
4492 case 0x15c0: /* (blue) */
4493 element = EL_CHAR_X;
4496 case 0x15c1: /* (blue) */
4497 element = EL_CHAR_Y;
4500 case 0x15c2: /* (blue) */
4501 element = EL_CHAR_Z;
4504 case 0x15c3: /* (blue) */
4505 element = EL_CHAR_AUMLAUT;
4508 case 0x15c4: /* (blue) */
4509 element = EL_CHAR_OUMLAUT;
4512 case 0x15c5: /* (blue) */
4513 element = EL_CHAR_UUMLAUT;
4516 case 0x15c6: /* (blue) */
4517 element = EL_CHAR_0;
4520 case 0x15c7: /* (blue) */
4521 element = EL_CHAR_1;
4524 case 0x15c8: /* (blue) */
4525 element = EL_CHAR_2;
4528 case 0x15c9: /* (blue) */
4529 element = EL_CHAR_3;
4532 case 0x15ca: /* (blue) */
4533 element = EL_CHAR_4;
4536 case 0x15cb: /* (blue) */
4537 element = EL_CHAR_5;
4540 case 0x15cc: /* (blue) */
4541 element = EL_CHAR_6;
4544 case 0x15cd: /* (blue) */
4545 element = EL_CHAR_7;
4548 case 0x15ce: /* (blue) */
4549 element = EL_CHAR_8;
4552 case 0x15cf: /* (blue) */
4553 element = EL_CHAR_9;
4556 case 0x15d0: /* (blue) */
4557 element = EL_CHAR_PERIOD;
4560 case 0x15d1: /* (blue) */
4561 element = EL_CHAR_EXCLAM;
4564 case 0x15d2: /* (blue) */
4565 element = EL_CHAR_COLON;
4568 case 0x15d3: /* (blue) */
4569 element = EL_CHAR_LESS;
4572 case 0x15d4: /* (blue) */
4573 element = EL_CHAR_GREATER;
4576 case 0x15d5: /* (blue) */
4577 element = EL_CHAR_QUESTION;
4580 case 0x15d6: /* (blue) */
4581 element = EL_CHAR_COPYRIGHT;
4584 case 0x15d7: /* (blue) */
4585 element = EL_CHAR_UP;
4588 case 0x15d8: /* (blue) */
4589 element = EL_CHAR_DOWN;
4592 case 0x15d9: /* (blue) */
4593 element = EL_CHAR_BUTTON;
4596 case 0x15da: /* (blue) */
4597 element = EL_CHAR_PLUS;
4600 case 0x15db: /* (blue) */
4601 element = EL_CHAR_MINUS;
4604 case 0x15dc: /* (blue) */
4605 element = EL_CHAR_APOSTROPHE;
4608 case 0x15dd: /* (blue) */
4609 element = EL_CHAR_PARENLEFT;
4612 case 0x15de: /* (blue) */
4613 element = EL_CHAR_PARENRIGHT;
4616 case 0x15df: /* (green) */
4617 element = EL_CHAR_A;
4620 case 0x15e0: /* (green) */
4621 element = EL_CHAR_B;
4624 case 0x15e1: /* (green) */
4625 element = EL_CHAR_C;
4628 case 0x15e2: /* (green) */
4629 element = EL_CHAR_D;
4632 case 0x15e3: /* (green) */
4633 element = EL_CHAR_E;
4636 case 0x15e4: /* (green) */
4637 element = EL_CHAR_F;
4640 case 0x15e5: /* (green) */
4641 element = EL_CHAR_G;
4644 case 0x15e6: /* (green) */
4645 element = EL_CHAR_H;
4648 case 0x15e7: /* (green) */
4649 element = EL_CHAR_I;
4652 case 0x15e8: /* (green) */
4653 element = EL_CHAR_J;
4656 case 0x15e9: /* (green) */
4657 element = EL_CHAR_K;
4660 case 0x15ea: /* (green) */
4661 element = EL_CHAR_L;
4664 case 0x15eb: /* (green) */
4665 element = EL_CHAR_M;
4668 case 0x15ec: /* (green) */
4669 element = EL_CHAR_N;
4672 case 0x15ed: /* (green) */
4673 element = EL_CHAR_O;
4676 case 0x15ee: /* (green) */
4677 element = EL_CHAR_P;
4680 case 0x15ef: /* (green) */
4681 element = EL_CHAR_Q;
4684 case 0x15f0: /* (green) */
4685 element = EL_CHAR_R;
4688 case 0x15f1: /* (green) */
4689 element = EL_CHAR_S;
4692 case 0x15f2: /* (green) */
4693 element = EL_CHAR_T;
4696 case 0x15f3: /* (green) */
4697 element = EL_CHAR_U;
4700 case 0x15f4: /* (green) */
4701 element = EL_CHAR_V;
4704 case 0x15f5: /* (green) */
4705 element = EL_CHAR_W;
4708 case 0x15f6: /* (green) */
4709 element = EL_CHAR_X;
4712 case 0x15f7: /* (green) */
4713 element = EL_CHAR_Y;
4716 case 0x15f8: /* (green) */
4717 element = EL_CHAR_Z;
4720 case 0x15f9: /* (green) */
4721 element = EL_CHAR_AUMLAUT;
4724 case 0x15fa: /* (green) */
4725 element = EL_CHAR_OUMLAUT;
4728 case 0x15fb: /* (green) */
4729 element = EL_CHAR_UUMLAUT;
4732 case 0x15fc: /* (green) */
4733 element = EL_CHAR_0;
4736 case 0x15fd: /* (green) */
4737 element = EL_CHAR_1;
4740 case 0x15fe: /* (green) */
4741 element = EL_CHAR_2;
4744 case 0x15ff: /* (green) */
4745 element = EL_CHAR_3;
4748 case 0x1600: /* (green) */
4749 element = EL_CHAR_4;
4752 case 0x1601: /* (green) */
4753 element = EL_CHAR_5;
4756 case 0x1602: /* (green) */
4757 element = EL_CHAR_6;
4760 case 0x1603: /* (green) */
4761 element = EL_CHAR_7;
4764 case 0x1604: /* (green) */
4765 element = EL_CHAR_8;
4768 case 0x1605: /* (green) */
4769 element = EL_CHAR_9;
4772 case 0x1606: /* (green) */
4773 element = EL_CHAR_PERIOD;
4776 case 0x1607: /* (green) */
4777 element = EL_CHAR_EXCLAM;
4780 case 0x1608: /* (green) */
4781 element = EL_CHAR_COLON;
4784 case 0x1609: /* (green) */
4785 element = EL_CHAR_LESS;
4788 case 0x160a: /* (green) */
4789 element = EL_CHAR_GREATER;
4792 case 0x160b: /* (green) */
4793 element = EL_CHAR_QUESTION;
4796 case 0x160c: /* (green) */
4797 element = EL_CHAR_COPYRIGHT;
4800 case 0x160d: /* (green) */
4801 element = EL_CHAR_UP;
4804 case 0x160e: /* (green) */
4805 element = EL_CHAR_DOWN;
4808 case 0x160f: /* (green) */
4809 element = EL_CHAR_BUTTON;
4812 case 0x1610: /* (green) */
4813 element = EL_CHAR_PLUS;
4816 case 0x1611: /* (green) */
4817 element = EL_CHAR_MINUS;
4820 case 0x1612: /* (green) */
4821 element = EL_CHAR_APOSTROPHE;
4824 case 0x1613: /* (green) */
4825 element = EL_CHAR_PARENLEFT;
4828 case 0x1614: /* (green) */
4829 element = EL_CHAR_PARENRIGHT;
4832 case 0x1615: /* (blue steel) */
4833 element = EL_STEELCHAR_A;
4836 case 0x1616: /* (blue steel) */
4837 element = EL_STEELCHAR_B;
4840 case 0x1617: /* (blue steel) */
4841 element = EL_STEELCHAR_C;
4844 case 0x1618: /* (blue steel) */
4845 element = EL_STEELCHAR_D;
4848 case 0x1619: /* (blue steel) */
4849 element = EL_STEELCHAR_E;
4852 case 0x161a: /* (blue steel) */
4853 element = EL_STEELCHAR_F;
4856 case 0x161b: /* (blue steel) */
4857 element = EL_STEELCHAR_G;
4860 case 0x161c: /* (blue steel) */
4861 element = EL_STEELCHAR_H;
4864 case 0x161d: /* (blue steel) */
4865 element = EL_STEELCHAR_I;
4868 case 0x161e: /* (blue steel) */
4869 element = EL_STEELCHAR_J;
4872 case 0x161f: /* (blue steel) */
4873 element = EL_STEELCHAR_K;
4876 case 0x1620: /* (blue steel) */
4877 element = EL_STEELCHAR_L;
4880 case 0x1621: /* (blue steel) */
4881 element = EL_STEELCHAR_M;
4884 case 0x1622: /* (blue steel) */
4885 element = EL_STEELCHAR_N;
4888 case 0x1623: /* (blue steel) */
4889 element = EL_STEELCHAR_O;
4892 case 0x1624: /* (blue steel) */
4893 element = EL_STEELCHAR_P;
4896 case 0x1625: /* (blue steel) */
4897 element = EL_STEELCHAR_Q;
4900 case 0x1626: /* (blue steel) */
4901 element = EL_STEELCHAR_R;
4904 case 0x1627: /* (blue steel) */
4905 element = EL_STEELCHAR_S;
4908 case 0x1628: /* (blue steel) */
4909 element = EL_STEELCHAR_T;
4912 case 0x1629: /* (blue steel) */
4913 element = EL_STEELCHAR_U;
4916 case 0x162a: /* (blue steel) */
4917 element = EL_STEELCHAR_V;
4920 case 0x162b: /* (blue steel) */
4921 element = EL_STEELCHAR_W;
4924 case 0x162c: /* (blue steel) */
4925 element = EL_STEELCHAR_X;
4928 case 0x162d: /* (blue steel) */
4929 element = EL_STEELCHAR_Y;
4932 case 0x162e: /* (blue steel) */
4933 element = EL_STEELCHAR_Z;
4936 case 0x162f: /* (blue steel) */
4937 element = EL_STEELCHAR_AUMLAUT;
4940 case 0x1630: /* (blue steel) */
4941 element = EL_STEELCHAR_OUMLAUT;
4944 case 0x1631: /* (blue steel) */
4945 element = EL_STEELCHAR_UUMLAUT;
4948 case 0x1632: /* (blue steel) */
4949 element = EL_STEELCHAR_0;
4952 case 0x1633: /* (blue steel) */
4953 element = EL_STEELCHAR_1;
4956 case 0x1634: /* (blue steel) */
4957 element = EL_STEELCHAR_2;
4960 case 0x1635: /* (blue steel) */
4961 element = EL_STEELCHAR_3;
4964 case 0x1636: /* (blue steel) */
4965 element = EL_STEELCHAR_4;
4968 case 0x1637: /* (blue steel) */
4969 element = EL_STEELCHAR_5;
4972 case 0x1638: /* (blue steel) */
4973 element = EL_STEELCHAR_6;
4976 case 0x1639: /* (blue steel) */
4977 element = EL_STEELCHAR_7;
4980 case 0x163a: /* (blue steel) */
4981 element = EL_STEELCHAR_8;
4984 case 0x163b: /* (blue steel) */
4985 element = EL_STEELCHAR_9;
4988 case 0x163c: /* (blue steel) */
4989 element = EL_STEELCHAR_PERIOD;
4992 case 0x163d: /* (blue steel) */
4993 element = EL_STEELCHAR_EXCLAM;
4996 case 0x163e: /* (blue steel) */
4997 element = EL_STEELCHAR_COLON;
5000 case 0x163f: /* (blue steel) */
5001 element = EL_STEELCHAR_LESS;
5004 case 0x1640: /* (blue steel) */
5005 element = EL_STEELCHAR_GREATER;
5008 case 0x1641: /* (blue steel) */
5009 element = EL_STEELCHAR_QUESTION;
5012 case 0x1642: /* (blue steel) */
5013 element = EL_STEELCHAR_COPYRIGHT;
5016 case 0x1643: /* (blue steel) */
5017 element = EL_STEELCHAR_UP;
5020 case 0x1644: /* (blue steel) */
5021 element = EL_STEELCHAR_DOWN;
5024 case 0x1645: /* (blue steel) */
5025 element = EL_STEELCHAR_BUTTON;
5028 case 0x1646: /* (blue steel) */
5029 element = EL_STEELCHAR_PLUS;
5032 case 0x1647: /* (blue steel) */
5033 element = EL_STEELCHAR_MINUS;
5036 case 0x1648: /* (blue steel) */
5037 element = EL_STEELCHAR_APOSTROPHE;
5040 case 0x1649: /* (blue steel) */
5041 element = EL_STEELCHAR_PARENLEFT;
5044 case 0x164a: /* (blue steel) */
5045 element = EL_STEELCHAR_PARENRIGHT;
5048 case 0x164b: /* (green steel) */
5049 element = EL_STEELCHAR_A;
5052 case 0x164c: /* (green steel) */
5053 element = EL_STEELCHAR_B;
5056 case 0x164d: /* (green steel) */
5057 element = EL_STEELCHAR_C;
5060 case 0x164e: /* (green steel) */
5061 element = EL_STEELCHAR_D;
5064 case 0x164f: /* (green steel) */
5065 element = EL_STEELCHAR_E;
5068 case 0x1650: /* (green steel) */
5069 element = EL_STEELCHAR_F;
5072 case 0x1651: /* (green steel) */
5073 element = EL_STEELCHAR_G;
5076 case 0x1652: /* (green steel) */
5077 element = EL_STEELCHAR_H;
5080 case 0x1653: /* (green steel) */
5081 element = EL_STEELCHAR_I;
5084 case 0x1654: /* (green steel) */
5085 element = EL_STEELCHAR_J;
5088 case 0x1655: /* (green steel) */
5089 element = EL_STEELCHAR_K;
5092 case 0x1656: /* (green steel) */
5093 element = EL_STEELCHAR_L;
5096 case 0x1657: /* (green steel) */
5097 element = EL_STEELCHAR_M;
5100 case 0x1658: /* (green steel) */
5101 element = EL_STEELCHAR_N;
5104 case 0x1659: /* (green steel) */
5105 element = EL_STEELCHAR_O;
5108 case 0x165a: /* (green steel) */
5109 element = EL_STEELCHAR_P;
5112 case 0x165b: /* (green steel) */
5113 element = EL_STEELCHAR_Q;
5116 case 0x165c: /* (green steel) */
5117 element = EL_STEELCHAR_R;
5120 case 0x165d: /* (green steel) */
5121 element = EL_STEELCHAR_S;
5124 case 0x165e: /* (green steel) */
5125 element = EL_STEELCHAR_T;
5128 case 0x165f: /* (green steel) */
5129 element = EL_STEELCHAR_U;
5132 case 0x1660: /* (green steel) */
5133 element = EL_STEELCHAR_V;
5136 case 0x1661: /* (green steel) */
5137 element = EL_STEELCHAR_W;
5140 case 0x1662: /* (green steel) */
5141 element = EL_STEELCHAR_X;
5144 case 0x1663: /* (green steel) */
5145 element = EL_STEELCHAR_Y;
5148 case 0x1664: /* (green steel) */
5149 element = EL_STEELCHAR_Z;
5152 case 0x1665: /* (green steel) */
5153 element = EL_STEELCHAR_AUMLAUT;
5156 case 0x1666: /* (green steel) */
5157 element = EL_STEELCHAR_OUMLAUT;
5160 case 0x1667: /* (green steel) */
5161 element = EL_STEELCHAR_UUMLAUT;
5164 case 0x1668: /* (green steel) */
5165 element = EL_STEELCHAR_0;
5168 case 0x1669: /* (green steel) */
5169 element = EL_STEELCHAR_1;
5172 case 0x166a: /* (green steel) */
5173 element = EL_STEELCHAR_2;
5176 case 0x166b: /* (green steel) */
5177 element = EL_STEELCHAR_3;
5180 case 0x166c: /* (green steel) */
5181 element = EL_STEELCHAR_4;
5184 case 0x166d: /* (green steel) */
5185 element = EL_STEELCHAR_5;
5188 case 0x166e: /* (green steel) */
5189 element = EL_STEELCHAR_6;
5192 case 0x166f: /* (green steel) */
5193 element = EL_STEELCHAR_7;
5196 case 0x1670: /* (green steel) */
5197 element = EL_STEELCHAR_8;
5200 case 0x1671: /* (green steel) */
5201 element = EL_STEELCHAR_9;
5204 case 0x1672: /* (green steel) */
5205 element = EL_STEELCHAR_PERIOD;
5208 case 0x1673: /* (green steel) */
5209 element = EL_STEELCHAR_EXCLAM;
5212 case 0x1674: /* (green steel) */
5213 element = EL_STEELCHAR_COLON;
5216 case 0x1675: /* (green steel) */
5217 element = EL_STEELCHAR_LESS;
5220 case 0x1676: /* (green steel) */
5221 element = EL_STEELCHAR_GREATER;
5224 case 0x1677: /* (green steel) */
5225 element = EL_STEELCHAR_QUESTION;
5228 case 0x1678: /* (green steel) */
5229 element = EL_STEELCHAR_COPYRIGHT;
5232 case 0x1679: /* (green steel) */
5233 element = EL_STEELCHAR_UP;
5236 case 0x167a: /* (green steel) */
5237 element = EL_STEELCHAR_DOWN;
5240 case 0x167b: /* (green steel) */
5241 element = EL_STEELCHAR_BUTTON;
5244 case 0x167c: /* (green steel) */
5245 element = EL_STEELCHAR_PLUS;
5248 case 0x167d: /* (green steel) */
5249 element = EL_STEELCHAR_MINUS;
5252 case 0x167e: /* (green steel) */
5253 element = EL_STEELCHAR_APOSTROPHE;
5256 case 0x167f: /* (green steel) */
5257 element = EL_STEELCHAR_PARENLEFT;
5260 case 0x1680: /* (green steel) */
5261 element = EL_STEELCHAR_PARENRIGHT;
5264 case 0x1681: /* gate (red) */
5265 element = EL_EM_GATE_1;
5268 case 0x1682: /* secret gate (red) */
5269 element = EL_GATE_1_GRAY;
5272 case 0x1683: /* gate (yellow) */
5273 element = EL_EM_GATE_2;
5276 case 0x1684: /* secret gate (yellow) */
5277 element = EL_GATE_2_GRAY;
5280 case 0x1685: /* gate (blue) */
5281 element = EL_EM_GATE_4;
5284 case 0x1686: /* secret gate (blue) */
5285 element = EL_GATE_4_GRAY;
5288 case 0x1687: /* gate (green) */
5289 element = EL_EM_GATE_3;
5292 case 0x1688: /* secret gate (green) */
5293 element = EL_GATE_3_GRAY;
5296 case 0x1689: /* gate (white) */
5297 element = EL_DOOR_WHITE;
5300 case 0x168a: /* secret gate (white) */
5301 element = EL_DOOR_WHITE_GRAY;
5304 case 0x168b: /* secret gate (no key) */
5305 element = EL_UNKNOWN;
5309 element = EL_ROBOT_WHEEL;
5313 element = EL_TIMEGATE_SWITCH;
5317 element = EL_ACID_POOL_BOTTOM;
5321 element = EL_ACID_POOL_TOPLEFT;
5325 element = EL_ACID_POOL_TOPRIGHT;
5329 element = EL_ACID_POOL_BOTTOMLEFT;
5333 element = EL_ACID_POOL_BOTTOMRIGHT;
5337 element = EL_STEELWALL;
5341 element = EL_STEELWALL_SLIPPERY;
5344 case 0x1695: /* steel wall (not round) */
5345 element = EL_STEELWALL;
5348 case 0x1696: /* steel wall (left) */
5349 element = EL_STEELWALL;
5352 case 0x1697: /* steel wall (bottom) */
5353 element = EL_STEELWALL;
5356 case 0x1698: /* steel wall (right) */
5357 element = EL_STEELWALL;
5360 case 0x1699: /* steel wall (top) */
5361 element = EL_STEELWALL;
5364 case 0x169a: /* steel wall (left/bottom) */
5365 element = EL_STEELWALL;
5368 case 0x169b: /* steel wall (right/bottom) */
5369 element = EL_STEELWALL;
5372 case 0x169c: /* steel wall (right/top) */
5373 element = EL_STEELWALL;
5376 case 0x169d: /* steel wall (left/top) */
5377 element = EL_STEELWALL;
5380 case 0x169e: /* steel wall (right/bottom small) */
5381 element = EL_STEELWALL;
5384 case 0x169f: /* steel wall (left/bottom small) */
5385 element = EL_STEELWALL;
5388 case 0x16a0: /* steel wall (right/top small) */
5389 element = EL_STEELWALL;
5392 case 0x16a1: /* steel wall (left/top small) */
5393 element = EL_STEELWALL;
5396 case 0x16a2: /* steel wall (left/right) */
5397 element = EL_STEELWALL;
5400 case 0x16a3: /* steel wall (top/bottom) */
5401 element = EL_STEELWALL;
5404 case 0x16a4: /* steel wall 2 (left end) */
5405 element = EL_STEELWALL;
5408 case 0x16a5: /* steel wall 2 (right end) */
5409 element = EL_STEELWALL;
5412 case 0x16a6: /* steel wall 2 (top end) */
5413 element = EL_STEELWALL;
5416 case 0x16a7: /* steel wall 2 (bottom end) */
5417 element = EL_STEELWALL;
5420 case 0x16a8: /* steel wall 2 (left/right) */
5421 element = EL_STEELWALL;
5424 case 0x16a9: /* steel wall 2 (up/down) */
5425 element = EL_STEELWALL;
5428 case 0x16aa: /* steel wall 2 (mid) */
5429 element = EL_STEELWALL;
5433 element = EL_SIGN_EXCLAMATION;
5437 element = EL_SIGN_RADIOACTIVITY;
5441 element = EL_SIGN_STOP;
5445 element = EL_SIGN_WHEELCHAIR;
5449 element = EL_SIGN_PARKING;
5453 element = EL_SIGN_ONEWAY;
5457 element = EL_SIGN_HEART;
5461 element = EL_SIGN_TRIANGLE;
5465 element = EL_SIGN_ROUND;
5469 element = EL_SIGN_EXIT;
5473 element = EL_SIGN_YINYANG;
5477 element = EL_WALL_EMERALD;
5481 element = EL_WALL_DIAMOND;
5485 element = EL_WALL_PEARL;
5489 element = EL_WALL_CRYSTAL;
5493 element = EL_INVISIBLE_WALL;
5497 element = EL_INVISIBLE_STEELWALL;
5500 /* 0x16bc - 0x16cb: */
5501 /* EL_INVISIBLE_SAND */
5504 element = EL_LIGHT_SWITCH;
5508 element = EL_ENVELOPE_1;
5512 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5513 element = EL_DIAMOND;
5514 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5515 element = EL_EMERALD;
5516 else if (element >= 0x157c && element <= 0x158b)
5518 else if (element >= 0x1590 && element <= 0x159f)
5519 element = EL_LANDMINE;
5520 else if (element >= 0x16bc && element <= 0x16cb)
5521 element = EL_INVISIBLE_SAND;
5524 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5525 element = EL_UNKNOWN;
5530 return getMappedElement(element);
5533 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5534 struct LevelFileInfo *level_file_info)
5536 char *filename = level_file_info->filename;
5539 int nr = level_file_info->nr - leveldir_current->first_level;
5541 byte header[DC_LEVEL_HEADER_SIZE];
5543 int envelope_header_pos = 62;
5544 int envelope_content_pos = 94;
5545 int level_name_pos = 251;
5546 int level_author_pos = 292;
5547 int envelope_header_len;
5548 int envelope_content_len;
5550 int level_author_len;
5552 int num_yamyam_contents;
5555 if (!(file = fopen(filename, MODE_READ)))
5557 level->no_valid_file = TRUE;
5559 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5565 /* position file stream to the requested level inside the level package */
5566 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
5568 level->no_valid_file = TRUE;
5570 Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
5576 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5578 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5580 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5582 header[i * 2 + 0] = header_word >> 8;
5583 header[i * 2 + 1] = header_word & 0xff;
5586 /* maximum envelope header size is 31 bytes */
5587 envelope_header_len = header[envelope_header_pos];
5588 /* maximum envelope content size is 110 (156?) bytes */
5589 envelope_content_len = header[envelope_content_pos];
5591 /* maximum level title size is 40 bytes */
5592 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5593 /* maximum level author size is 30 (51?) bytes */
5594 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5598 for (i = 0; i < envelope_header_len; i++)
5599 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5600 level->envelope[0].text[envelope_size++] =
5601 header[envelope_header_pos + 1 + i];
5603 if (envelope_header_len > 0 && envelope_content_len > 0)
5605 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5606 level->envelope[0].text[envelope_size++] = '\n';
5607 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5608 level->envelope[0].text[envelope_size++] = '\n';
5611 for (i = 0; i < envelope_content_len; i++)
5612 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5613 level->envelope[0].text[envelope_size++] =
5614 header[envelope_content_pos + 1 + i];
5616 level->envelope[0].text[envelope_size] = '\0';
5618 for (i = 0; i < level_name_len; i++)
5619 level->name[i] = header[level_name_pos + 1 + i];
5620 level->name[level_name_len] = '\0';
5622 for (i = 0; i < level_author_len; i++)
5623 level->author[i] = header[level_author_pos + 1 + i];
5624 level->author[level_author_len] = '\0';
5626 num_yamyam_contents = header[60] | (header[61] << 8);
5627 level->num_yamyam_contents =
5628 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5630 for (i = 0; i < num_yamyam_contents; i++)
5632 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5634 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5636 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5638 int element_dc = word;
5641 if (i < MAX_ELEMENT_CONTENTS)
5642 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5646 fieldx = header[6] | (header[7] << 8);
5647 fieldy = header[8] | (header[9] << 8);
5648 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5649 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5651 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5653 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5655 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5657 int element_dc = word;
5660 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5661 level->field[x][y] = getMappedElement_DC(element_dc);
5664 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5665 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5666 level->field[x][y] = EL_PLAYER_1;
5668 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5669 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5670 level->field[x][y] = EL_PLAYER_2;
5672 level->gems_needed = header[18] | (header[19] << 8);
5674 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5675 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5676 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5677 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5678 level->score[SC_NUT] = header[28] | (header[29] << 8);
5679 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5680 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5681 level->score[SC_BUG] = header[34] | (header[35] << 8);
5682 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5683 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5684 level->score[SC_KEY] = header[40] | (header[41] << 8);
5685 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5687 level->time = header[44] | (header[45] << 8);
5689 level->amoeba_speed = header[46] | (header[47] << 8);
5690 level->time_light = header[48] | (header[49] << 8);
5691 level->time_timegate = header[50] | (header[51] << 8);
5692 level->time_wheel = header[52] | (header[53] << 8);
5693 level->time_magic_wall = header[54] | (header[55] << 8);
5694 level->extra_time = header[56] | (header[57] << 8);
5695 level->shield_normal_time = header[58] | (header[59] << 8);
5701 /* ------------------------------------------------------------------------- */
5702 /* functions for loading generic level */
5703 /* ------------------------------------------------------------------------- */
5705 void LoadLevelFromFileInfo(struct LevelInfo *level,
5706 struct LevelFileInfo *level_file_info)
5708 /* always start with reliable default values */
5709 setLevelInfoToDefaults(level);
5711 switch (level_file_info->type)
5713 case LEVEL_FILE_TYPE_RND:
5714 LoadLevelFromFileInfo_RND(level, level_file_info);
5717 case LEVEL_FILE_TYPE_EM:
5718 LoadLevelFromFileInfo_EM(level, level_file_info);
5719 level->game_engine_type = GAME_ENGINE_TYPE_EM;
5722 case LEVEL_FILE_TYPE_SP:
5723 LoadLevelFromFileInfo_SP(level, level_file_info);
5726 case LEVEL_FILE_TYPE_DC:
5727 LoadLevelFromFileInfo_DC(level, level_file_info);
5731 LoadLevelFromFileInfo_RND(level, level_file_info);
5735 /* if level file is invalid, restore level structure to default values */
5736 if (level->no_valid_file)
5737 setLevelInfoToDefaults(level);
5739 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
5740 level->game_engine_type = GAME_ENGINE_TYPE_RND;
5742 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
5743 CopyNativeLevel_Native_to_RND(level);
5746 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
5748 static struct LevelFileInfo level_file_info;
5750 /* always start with reliable default values */
5751 setFileInfoToDefaults(&level_file_info);
5753 level_file_info.nr = 0; /* unknown level number */
5754 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
5755 level_file_info.filename = filename;
5757 LoadLevelFromFileInfo(level, &level_file_info);
5760 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
5764 if (leveldir_current == NULL) /* only when dumping level */
5767 /* all engine modifications also valid for levels which use latest engine */
5768 if (level->game_version < VERSION_IDENT(3,2,0,5))
5770 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
5771 level->score[SC_TIME_BONUS] /= 10;
5775 leveldir_current->latest_engine = TRUE; /* !!! TEST ONLY !!! */
5778 if (leveldir_current->latest_engine)
5780 /* ---------- use latest game engine ----------------------------------- */
5782 /* For all levels which are forced to use the latest game engine version
5783 (normally all but user contributed, private and undefined levels), set
5784 the game engine version to the actual version; this allows for actual
5785 corrections in the game engine to take effect for existing, converted
5786 levels (from "classic" or other existing games) to make the emulation
5787 of the corresponding game more accurate, while (hopefully) not breaking
5788 existing levels created from other players. */
5790 level->game_version = GAME_VERSION_ACTUAL;
5792 /* Set special EM style gems behaviour: EM style gems slip down from
5793 normal, steel and growing wall. As this is a more fundamental change,
5794 it seems better to set the default behaviour to "off" (as it is more
5795 natural) and make it configurable in the level editor (as a property
5796 of gem style elements). Already existing converted levels (neither
5797 private nor contributed levels) are changed to the new behaviour. */
5799 if (level->file_version < FILE_VERSION_2_0)
5800 level->em_slippery_gems = TRUE;
5805 /* ---------- use game engine the level was created with ----------------- */
5807 /* For all levels which are not forced to use the latest game engine
5808 version (normally user contributed, private and undefined levels),
5809 use the version of the game engine the levels were created for.
5811 Since 2.0.1, the game engine version is now directly stored
5812 in the level file (chunk "VERS"), so there is no need anymore
5813 to set the game version from the file version (except for old,
5814 pre-2.0 levels, where the game version is still taken from the
5815 file format version used to store the level -- see above). */
5817 /* player was faster than enemies in 1.0.0 and before */
5818 if (level->file_version == FILE_VERSION_1_0)
5819 for (i = 0; i < MAX_PLAYERS; i++)
5820 level->initial_player_stepsize[i] = STEPSIZE_FAST;
5822 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
5823 if (level->game_version == VERSION_IDENT(2,0,1,0))
5824 level->em_slippery_gems = TRUE;
5826 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
5827 if (level->game_version < VERSION_IDENT(2,2,0,0))
5828 level->use_spring_bug = TRUE;
5830 if (level->game_version < VERSION_IDENT(3,2,0,5))
5832 /* time orb caused limited time in endless time levels before 3.2.0-5 */
5833 level->use_time_orb_bug = TRUE;
5835 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
5836 level->block_snap_field = FALSE;
5838 /* extra time score was same value as time left score before 3.2.0-5 */
5839 level->extra_time_score = level->score[SC_TIME_BONUS];
5842 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
5843 level->score[SC_TIME_BONUS] /= 10;
5847 if (level->game_version < VERSION_IDENT(3,2,0,7))
5849 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
5850 level->continuous_snapping = FALSE;
5853 /* only few elements were able to actively move into acid before 3.1.0 */
5854 /* trigger settings did not exist before 3.1.0; set to default "any" */
5855 if (level->game_version < VERSION_IDENT(3,1,0,0))
5857 /* correct "can move into acid" settings (all zero in old levels) */
5859 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
5860 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
5862 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
5863 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
5864 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
5865 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
5867 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5868 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
5870 /* correct trigger settings (stored as zero == "none" in old levels) */
5872 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5874 int element = EL_CUSTOM_START + i;
5875 struct ElementInfo *ei = &element_info[element];
5877 for (j = 0; j < ei->num_change_pages; j++)
5879 struct ElementChangeInfo *change = &ei->change_page[j];
5881 change->trigger_player = CH_PLAYER_ANY;
5882 change->trigger_page = CH_PAGE_ANY;
5887 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
5889 int element = EL_CUSTOM_START + 255;
5890 struct ElementInfo *ei = &element_info[element];
5891 struct ElementChangeInfo *change = &ei->change_page[0];
5893 /* This is needed to fix a problem that was caused by a bugfix in function
5894 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
5895 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
5896 not replace walkable elements, but instead just placed the player on it,
5897 without placing the Sokoban field under the player). Unfortunately, this
5898 breaks "Snake Bite" style levels when the snake is halfway through a door
5899 that just closes (the snake head is still alive and can be moved in this
5900 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
5901 player (without Sokoban element) which then gets killed as designed). */
5903 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
5904 strncmp(ei->description, "pause b4 death", 14) == 0) &&
5905 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
5906 change->target_element = EL_PLAYER_1;
5910 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
5914 /* map custom element change events that have changed in newer versions
5915 (these following values were accidentally changed in version 3.0.1)
5916 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
5917 if (level->game_version <= VERSION_IDENT(3,0,0,0))
5919 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5921 int element = EL_CUSTOM_START + i;
5923 /* order of checking and copying events to be mapped is important */
5924 /* (do not change the start and end value -- they are constant) */
5925 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
5927 if (HAS_CHANGE_EVENT(element, j - 2))
5929 SET_CHANGE_EVENT(element, j - 2, FALSE);
5930 SET_CHANGE_EVENT(element, j, TRUE);
5934 /* order of checking and copying events to be mapped is important */
5935 /* (do not change the start and end value -- they are constant) */
5936 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
5938 if (HAS_CHANGE_EVENT(element, j - 1))
5940 SET_CHANGE_EVENT(element, j - 1, FALSE);
5941 SET_CHANGE_EVENT(element, j, TRUE);
5947 /* initialize "can_change" field for old levels with only one change page */
5948 if (level->game_version <= VERSION_IDENT(3,0,2,0))
5950 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5952 int element = EL_CUSTOM_START + i;
5954 if (CAN_CHANGE(element))
5955 element_info[element].change->can_change = TRUE;
5959 /* correct custom element values (for old levels without these options) */
5960 if (level->game_version < VERSION_IDENT(3,1,1,0))
5962 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5964 int element = EL_CUSTOM_START + i;
5965 struct ElementInfo *ei = &element_info[element];
5967 if (ei->access_direction == MV_NO_DIRECTION)
5968 ei->access_direction = MV_ALL_DIRECTIONS;
5972 /* correct custom element values (fix invalid values for all versions) */
5975 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5977 int element = EL_CUSTOM_START + i;
5978 struct ElementInfo *ei = &element_info[element];
5980 for (j = 0; j < ei->num_change_pages; j++)
5982 struct ElementChangeInfo *change = &ei->change_page[j];
5984 if (change->trigger_player == CH_PLAYER_NONE)
5985 change->trigger_player = CH_PLAYER_ANY;
5987 if (change->trigger_side == CH_SIDE_NONE)
5988 change->trigger_side = CH_SIDE_ANY;
5993 /* initialize "can_explode" field for old levels which did not store this */
5994 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
5995 if (level->game_version <= VERSION_IDENT(3,1,0,0))
5997 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5999 int element = EL_CUSTOM_START + i;
6001 if (EXPLODES_1X1_OLD(element))
6002 element_info[element].explosion_type = EXPLODES_1X1;
6004 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6005 EXPLODES_SMASHED(element) ||
6006 EXPLODES_IMPACT(element)));
6010 /* correct previously hard-coded move delay values for maze runner style */
6011 if (level->game_version < VERSION_IDENT(3,1,1,0))
6013 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6015 int element = EL_CUSTOM_START + i;
6017 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6019 /* previously hard-coded and therefore ignored */
6020 element_info[element].move_delay_fixed = 9;
6021 element_info[element].move_delay_random = 0;
6026 /* map elements that have changed in newer versions */
6027 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6028 level->game_version);
6029 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6030 for (x = 0; x < 3; x++)
6031 for (y = 0; y < 3; y++)
6032 level->yamyam_content[i].e[x][y] =
6033 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6034 level->game_version);
6036 /* initialize element properties for level editor etc. */
6037 InitElementPropertiesEngine(level->game_version);
6038 InitElementPropertiesAfterLoading(level->game_version);
6041 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6045 /* map elements that have changed in newer versions */
6046 for (y = 0; y < level->fieldy; y++)
6047 for (x = 0; x < level->fieldx; x++)
6048 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6049 level->game_version);
6051 /* copy elements to runtime playfield array */
6052 for (x = 0; x < MAX_LEV_FIELDX; x++)
6053 for (y = 0; y < MAX_LEV_FIELDY; y++)
6054 Feld[x][y] = level->field[x][y];
6056 /* initialize level size variables for faster access */
6057 lev_fieldx = level->fieldx;
6058 lev_fieldy = level->fieldy;
6060 /* determine border element for this level */
6064 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6066 struct LevelFileInfo *level_file_info = &level->file_info;
6068 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6069 CopyNativeLevel_RND_to_Native(level);
6072 void LoadLevelTemplate(int nr)
6076 setLevelFileInfo(&level_template.file_info, nr);
6077 filename = level_template.file_info.filename;
6079 LoadLevelFromFileInfo(&level_template, &level_template.file_info);
6081 LoadLevel_InitVersion(&level_template, filename);
6082 LoadLevel_InitElements(&level_template, filename);
6084 ActivateLevelTemplate();
6087 void LoadLevel(int nr)
6091 setLevelFileInfo(&level.file_info, nr);
6092 filename = level.file_info.filename;
6094 LoadLevelFromFileInfo(&level, &level.file_info);
6096 if (level.use_custom_template)
6097 LoadLevelTemplate(-1);
6099 LoadLevel_InitVersion(&level, filename);
6100 LoadLevel_InitElements(&level, filename);
6101 LoadLevel_InitPlayfield(&level, filename);
6103 LoadLevel_InitNativeEngines(&level, filename);
6106 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6110 chunk_size += putFileVersion(file, level->file_version);
6111 chunk_size += putFileVersion(file, level->game_version);
6116 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6120 chunk_size += putFile16BitBE(file, level->creation_date.year);
6121 chunk_size += putFile8Bit(file, level->creation_date.month);
6122 chunk_size += putFile8Bit(file, level->creation_date.day);
6128 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6132 putFile8Bit(file, level->fieldx);
6133 putFile8Bit(file, level->fieldy);
6135 putFile16BitBE(file, level->time);
6136 putFile16BitBE(file, level->gems_needed);
6138 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6139 putFile8Bit(file, level->name[i]);
6141 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6142 putFile8Bit(file, level->score[i]);
6144 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6145 for (y = 0; y < 3; y++)
6146 for (x = 0; x < 3; x++)
6147 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6148 level->yamyam_content[i].e[x][y]));
6149 putFile8Bit(file, level->amoeba_speed);
6150 putFile8Bit(file, level->time_magic_wall);
6151 putFile8Bit(file, level->time_wheel);
6152 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6153 level->amoeba_content));
6154 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6155 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6156 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6157 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6159 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6161 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6162 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6163 putFile32BitBE(file, level->can_move_into_acid_bits);
6164 putFile8Bit(file, level->dont_collide_with_bits);
6166 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6167 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6169 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6170 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6171 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6173 putFile8Bit(file, level->game_engine_type);
6175 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6179 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6184 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6185 chunk_size += putFile8Bit(file, level->name[i]);
6190 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6195 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6196 chunk_size += putFile8Bit(file, level->author[i]);
6202 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6207 for (y = 0; y < level->fieldy; y++)
6208 for (x = 0; x < level->fieldx; x++)
6209 if (level->encoding_16bit_field)
6210 chunk_size += putFile16BitBE(file, level->field[x][y]);
6212 chunk_size += putFile8Bit(file, level->field[x][y]);
6218 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6223 for (y = 0; y < level->fieldy; y++)
6224 for (x = 0; x < level->fieldx; x++)
6225 chunk_size += putFile16BitBE(file, level->field[x][y]);
6231 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6235 putFile8Bit(file, EL_YAMYAM);
6236 putFile8Bit(file, level->num_yamyam_contents);
6237 putFile8Bit(file, 0);
6238 putFile8Bit(file, 0);
6240 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6241 for (y = 0; y < 3; y++)
6242 for (x = 0; x < 3; x++)
6243 if (level->encoding_16bit_field)
6244 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6246 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6251 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6254 int num_contents, content_xsize, content_ysize;
6255 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6257 if (element == EL_YAMYAM)
6259 num_contents = level->num_yamyam_contents;
6263 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6264 for (y = 0; y < 3; y++)
6265 for (x = 0; x < 3; x++)
6266 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6268 else if (element == EL_BD_AMOEBA)
6274 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6275 for (y = 0; y < 3; y++)
6276 for (x = 0; x < 3; x++)
6277 content_array[i][x][y] = EL_EMPTY;
6278 content_array[0][0][0] = level->amoeba_content;
6282 /* chunk header already written -- write empty chunk data */
6283 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6285 Error(ERR_WARN, "cannot save content for element '%d'", element);
6289 putFile16BitBE(file, element);
6290 putFile8Bit(file, num_contents);
6291 putFile8Bit(file, content_xsize);
6292 putFile8Bit(file, content_ysize);
6294 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6296 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6297 for (y = 0; y < 3; y++)
6298 for (x = 0; x < 3; x++)
6299 putFile16BitBE(file, content_array[i][x][y]);
6304 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6306 int envelope_nr = element - EL_ENVELOPE_1;
6307 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6311 chunk_size += putFile16BitBE(file, element);
6312 chunk_size += putFile16BitBE(file, envelope_len);
6313 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6314 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6316 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6317 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6319 for (i = 0; i < envelope_len; i++)
6320 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6327 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6328 int num_changed_custom_elements)
6332 putFile16BitBE(file, num_changed_custom_elements);
6334 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6336 int element = EL_CUSTOM_START + i;
6338 struct ElementInfo *ei = &element_info[element];
6340 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6342 if (check < num_changed_custom_elements)
6344 putFile16BitBE(file, element);
6345 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6352 if (check != num_changed_custom_elements) /* should not happen */
6353 Error(ERR_WARN, "inconsistent number of custom element properties");
6358 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6359 int num_changed_custom_elements)
6363 putFile16BitBE(file, num_changed_custom_elements);
6365 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6367 int element = EL_CUSTOM_START + i;
6369 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6371 if (check < num_changed_custom_elements)
6373 putFile16BitBE(file, element);
6374 putFile16BitBE(file, element_info[element].change->target_element);
6381 if (check != num_changed_custom_elements) /* should not happen */
6382 Error(ERR_WARN, "inconsistent number of custom target elements");
6387 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6388 int num_changed_custom_elements)
6390 int i, j, x, y, check = 0;
6392 putFile16BitBE(file, num_changed_custom_elements);
6394 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6396 int element = EL_CUSTOM_START + i;
6397 struct ElementInfo *ei = &element_info[element];
6399 if (ei->modified_settings)
6401 if (check < num_changed_custom_elements)
6403 putFile16BitBE(file, element);
6405 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6406 putFile8Bit(file, ei->description[j]);
6408 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6410 /* some free bytes for future properties and padding */
6411 WriteUnusedBytesToFile(file, 7);
6413 putFile8Bit(file, ei->use_gfx_element);
6414 putFile16BitBE(file, ei->gfx_element);
6416 putFile8Bit(file, ei->collect_score_initial);
6417 putFile8Bit(file, ei->collect_count_initial);
6419 putFile16BitBE(file, ei->push_delay_fixed);
6420 putFile16BitBE(file, ei->push_delay_random);
6421 putFile16BitBE(file, ei->move_delay_fixed);
6422 putFile16BitBE(file, ei->move_delay_random);
6424 putFile16BitBE(file, ei->move_pattern);
6425 putFile8Bit(file, ei->move_direction_initial);
6426 putFile8Bit(file, ei->move_stepsize);
6428 for (y = 0; y < 3; y++)
6429 for (x = 0; x < 3; x++)
6430 putFile16BitBE(file, ei->content.e[x][y]);
6432 putFile32BitBE(file, ei->change->events);
6434 putFile16BitBE(file, ei->change->target_element);
6436 putFile16BitBE(file, ei->change->delay_fixed);
6437 putFile16BitBE(file, ei->change->delay_random);
6438 putFile16BitBE(file, ei->change->delay_frames);
6440 putFile16BitBE(file, ei->change->trigger_element);
6442 putFile8Bit(file, ei->change->explode);
6443 putFile8Bit(file, ei->change->use_target_content);
6444 putFile8Bit(file, ei->change->only_if_complete);
6445 putFile8Bit(file, ei->change->use_random_replace);
6447 putFile8Bit(file, ei->change->random_percentage);
6448 putFile8Bit(file, ei->change->replace_when);
6450 for (y = 0; y < 3; y++)
6451 for (x = 0; x < 3; x++)
6452 putFile16BitBE(file, ei->change->content.e[x][y]);
6454 putFile8Bit(file, ei->slippery_type);
6456 /* some free bytes for future properties and padding */
6457 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6464 if (check != num_changed_custom_elements) /* should not happen */
6465 Error(ERR_WARN, "inconsistent number of custom element properties");
6470 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6472 struct ElementInfo *ei = &element_info[element];
6475 /* ---------- custom element base property values (96 bytes) ------------- */
6477 putFile16BitBE(file, element);
6479 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6480 putFile8Bit(file, ei->description[i]);
6482 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6484 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
6486 putFile8Bit(file, ei->num_change_pages);
6488 putFile16BitBE(file, ei->ce_value_fixed_initial);
6489 putFile16BitBE(file, ei->ce_value_random_initial);
6490 putFile8Bit(file, ei->use_last_ce_value);
6492 putFile8Bit(file, ei->use_gfx_element);
6493 putFile16BitBE(file, ei->gfx_element);
6495 putFile8Bit(file, ei->collect_score_initial);
6496 putFile8Bit(file, ei->collect_count_initial);
6498 putFile8Bit(file, ei->drop_delay_fixed);
6499 putFile8Bit(file, ei->push_delay_fixed);
6500 putFile8Bit(file, ei->drop_delay_random);
6501 putFile8Bit(file, ei->push_delay_random);
6502 putFile16BitBE(file, ei->move_delay_fixed);
6503 putFile16BitBE(file, ei->move_delay_random);
6505 /* bits 0 - 15 of "move_pattern" ... */
6506 putFile16BitBE(file, ei->move_pattern & 0xffff);
6507 putFile8Bit(file, ei->move_direction_initial);
6508 putFile8Bit(file, ei->move_stepsize);
6510 putFile8Bit(file, ei->slippery_type);
6512 for (y = 0; y < 3; y++)
6513 for (x = 0; x < 3; x++)
6514 putFile16BitBE(file, ei->content.e[x][y]);
6516 putFile16BitBE(file, ei->move_enter_element);
6517 putFile16BitBE(file, ei->move_leave_element);
6518 putFile8Bit(file, ei->move_leave_type);
6520 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
6521 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
6523 putFile8Bit(file, ei->access_direction);
6525 putFile8Bit(file, ei->explosion_delay);
6526 putFile8Bit(file, ei->ignition_delay);
6527 putFile8Bit(file, ei->explosion_type);
6529 /* some free bytes for future custom property values and padding */
6530 WriteUnusedBytesToFile(file, 1);
6532 /* ---------- change page property values (48 bytes) --------------------- */
6534 for (i = 0; i < ei->num_change_pages; i++)
6536 struct ElementChangeInfo *change = &ei->change_page[i];
6537 unsigned int event_bits;
6539 /* bits 0 - 31 of "has_event[]" ... */
6541 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
6542 if (change->has_event[j])
6543 event_bits |= (1 << j);
6544 putFile32BitBE(file, event_bits);
6546 putFile16BitBE(file, change->target_element);
6548 putFile16BitBE(file, change->delay_fixed);
6549 putFile16BitBE(file, change->delay_random);
6550 putFile16BitBE(file, change->delay_frames);
6552 putFile16BitBE(file, change->trigger_element);
6554 putFile8Bit(file, change->explode);
6555 putFile8Bit(file, change->use_target_content);
6556 putFile8Bit(file, change->only_if_complete);
6557 putFile8Bit(file, change->use_random_replace);
6559 putFile8Bit(file, change->random_percentage);
6560 putFile8Bit(file, change->replace_when);
6562 for (y = 0; y < 3; y++)
6563 for (x = 0; x < 3; x++)
6564 putFile16BitBE(file, change->target_content.e[x][y]);
6566 putFile8Bit(file, change->can_change);
6568 putFile8Bit(file, change->trigger_side);
6570 putFile8Bit(file, change->trigger_player);
6571 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
6572 log_2(change->trigger_page)));
6574 putFile8Bit(file, change->has_action);
6575 putFile8Bit(file, change->action_type);
6576 putFile8Bit(file, change->action_mode);
6577 putFile16BitBE(file, change->action_arg);
6579 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
6581 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
6582 if (change->has_event[j])
6583 event_bits |= (1 << (j - 32));
6584 putFile8Bit(file, event_bits);
6590 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
6592 struct ElementInfo *ei = &element_info[element];
6593 struct ElementGroupInfo *group = ei->group;
6596 putFile16BitBE(file, element);
6598 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6599 putFile8Bit(file, ei->description[i]);
6601 putFile8Bit(file, group->num_elements);
6603 putFile8Bit(file, ei->use_gfx_element);
6604 putFile16BitBE(file, ei->gfx_element);
6606 putFile8Bit(file, group->choice_mode);
6608 /* some free bytes for future values and padding */
6609 WriteUnusedBytesToFile(file, 3);
6611 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
6612 putFile16BitBE(file, group->element[i]);
6616 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
6617 boolean write_element)
6619 int save_type = entry->save_type;
6620 int data_type = entry->data_type;
6621 int conf_type = entry->conf_type;
6622 int byte_mask = conf_type & CONF_MASK_BYTES;
6623 int element = entry->element;
6624 int default_value = entry->default_value;
6626 boolean modified = FALSE;
6628 if (byte_mask != CONF_MASK_MULTI_BYTES)
6630 void *value_ptr = entry->value;
6631 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
6634 /* check if any settings have been modified before saving them */
6635 if (value != default_value)
6638 /* do not save if explicitly told or if unmodified default settings */
6639 if ((save_type == SAVE_CONF_NEVER) ||
6640 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6644 num_bytes += putFile16BitBE(file, element);
6646 num_bytes += putFile8Bit(file, conf_type);
6647 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
6648 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
6649 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
6652 else if (data_type == TYPE_STRING)
6654 char *default_string = entry->default_string;
6655 char *string = (char *)(entry->value);
6656 int string_length = strlen(string);
6659 /* check if any settings have been modified before saving them */
6660 if (!strEqual(string, default_string))
6663 /* do not save if explicitly told or if unmodified default settings */
6664 if ((save_type == SAVE_CONF_NEVER) ||
6665 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6669 num_bytes += putFile16BitBE(file, element);
6671 num_bytes += putFile8Bit(file, conf_type);
6672 num_bytes += putFile16BitBE(file, string_length);
6674 for (i = 0; i < string_length; i++)
6675 num_bytes += putFile8Bit(file, string[i]);
6677 else if (data_type == TYPE_ELEMENT_LIST)
6679 int *element_array = (int *)(entry->value);
6680 int num_elements = *(int *)(entry->num_entities);
6683 /* check if any settings have been modified before saving them */
6684 for (i = 0; i < num_elements; i++)
6685 if (element_array[i] != default_value)
6688 /* do not save if explicitly told or if unmodified default settings */
6689 if ((save_type == SAVE_CONF_NEVER) ||
6690 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6694 num_bytes += putFile16BitBE(file, element);
6696 num_bytes += putFile8Bit(file, conf_type);
6697 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
6699 for (i = 0; i < num_elements; i++)
6700 num_bytes += putFile16BitBE(file, element_array[i]);
6702 else if (data_type == TYPE_CONTENT_LIST)
6704 struct Content *content = (struct Content *)(entry->value);
6705 int num_contents = *(int *)(entry->num_entities);
6708 /* check if any settings have been modified before saving them */
6709 for (i = 0; i < num_contents; i++)
6710 for (y = 0; y < 3; y++)
6711 for (x = 0; x < 3; x++)
6712 if (content[i].e[x][y] != default_value)
6715 /* do not save if explicitly told or if unmodified default settings */
6716 if ((save_type == SAVE_CONF_NEVER) ||
6717 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6721 num_bytes += putFile16BitBE(file, element);
6723 num_bytes += putFile8Bit(file, conf_type);
6724 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
6726 for (i = 0; i < num_contents; i++)
6727 for (y = 0; y < 3; y++)
6728 for (x = 0; x < 3; x++)
6729 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
6735 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
6740 li = *level; /* copy level data into temporary buffer */
6742 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
6743 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
6748 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
6753 li = *level; /* copy level data into temporary buffer */
6755 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
6756 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
6761 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
6763 int envelope_nr = element - EL_ENVELOPE_1;
6767 chunk_size += putFile16BitBE(file, element);
6769 /* copy envelope data into temporary buffer */
6770 xx_envelope = level->envelope[envelope_nr];
6772 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
6773 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
6778 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
6780 struct ElementInfo *ei = &element_info[element];
6784 chunk_size += putFile16BitBE(file, element);
6786 xx_ei = *ei; /* copy element data into temporary buffer */
6788 /* set default description string for this specific element */
6789 strcpy(xx_default_description, getDefaultElementDescription(ei));
6792 /* set (fixed) number of content areas (may be wrong by broken level file) */
6793 /* (this is now directly corrected for broken level files after loading) */
6794 xx_num_contents = 1;
6797 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
6798 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
6800 for (i = 0; i < ei->num_change_pages; i++)
6802 struct ElementChangeInfo *change = &ei->change_page[i];
6804 xx_current_change_page = i;
6806 xx_change = *change; /* copy change data into temporary buffer */
6809 setEventBitsFromEventFlags(change);
6811 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
6812 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
6819 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
6821 struct ElementInfo *ei = &element_info[element];
6822 struct ElementGroupInfo *group = ei->group;
6826 chunk_size += putFile16BitBE(file, element);
6828 xx_ei = *ei; /* copy element data into temporary buffer */
6829 xx_group = *group; /* copy group data into temporary buffer */
6831 /* set default description string for this specific element */
6832 strcpy(xx_default_description, getDefaultElementDescription(ei));
6834 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
6835 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
6840 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
6846 if (!(file = fopen(filename, MODE_WRITE)))
6848 Error(ERR_WARN, "cannot save level file '%s'", filename);
6852 level->file_version = FILE_VERSION_ACTUAL;
6853 level->game_version = GAME_VERSION_ACTUAL;
6855 level->creation_date = getCurrentDate();
6857 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
6858 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
6860 chunk_size = SaveLevel_VERS(NULL, level);
6861 putFileChunkBE(file, "VERS", chunk_size);
6862 SaveLevel_VERS(file, level);
6864 chunk_size = SaveLevel_DATE(NULL, level);
6865 putFileChunkBE(file, "DATE", chunk_size);
6866 SaveLevel_DATE(file, level);
6868 chunk_size = SaveLevel_NAME(NULL, level);
6869 putFileChunkBE(file, "NAME", chunk_size);
6870 SaveLevel_NAME(file, level);
6872 chunk_size = SaveLevel_AUTH(NULL, level);
6873 putFileChunkBE(file, "AUTH", chunk_size);
6874 SaveLevel_AUTH(file, level);
6876 chunk_size = SaveLevel_INFO(NULL, level);
6877 putFileChunkBE(file, "INFO", chunk_size);
6878 SaveLevel_INFO(file, level);
6880 chunk_size = SaveLevel_BODY(NULL, level);
6881 putFileChunkBE(file, "BODY", chunk_size);
6882 SaveLevel_BODY(file, level);
6884 chunk_size = SaveLevel_ELEM(NULL, level);
6885 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
6887 putFileChunkBE(file, "ELEM", chunk_size);
6888 SaveLevel_ELEM(file, level);
6891 for (i = 0; i < NUM_ENVELOPES; i++)
6893 int element = EL_ENVELOPE_1 + i;
6895 chunk_size = SaveLevel_NOTE(NULL, level, element);
6896 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
6898 putFileChunkBE(file, "NOTE", chunk_size);
6899 SaveLevel_NOTE(file, level, element);
6903 /* if not using template level, check for non-default custom/group elements */
6904 if (!level->use_custom_template)
6906 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6908 int element = EL_CUSTOM_START + i;
6910 chunk_size = SaveLevel_CUSX(NULL, level, element);
6911 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
6913 putFileChunkBE(file, "CUSX", chunk_size);
6914 SaveLevel_CUSX(file, level, element);
6918 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
6920 int element = EL_GROUP_START + i;
6922 chunk_size = SaveLevel_GRPX(NULL, level, element);
6923 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
6925 putFileChunkBE(file, "GRPX", chunk_size);
6926 SaveLevel_GRPX(file, level, element);
6933 SetFilePermissions(filename, PERMS_PRIVATE);
6936 void SaveLevel(int nr)
6938 char *filename = getDefaultLevelFilename(nr);
6940 SaveLevelFromFilename(&level, filename);
6943 void SaveLevelTemplate()
6945 char *filename = getDefaultLevelFilename(-1);
6947 SaveLevelFromFilename(&level, filename);
6950 boolean SaveLevelChecked(int nr)
6952 char *filename = getDefaultLevelFilename(nr);
6953 boolean new_level = !fileExists(filename);
6954 boolean level_saved = FALSE;
6956 if (new_level || Request("Save this level and kill the old ?", REQ_ASK))
6961 Request("Level saved !", REQ_CONFIRM);
6969 void DumpLevel(struct LevelInfo *level)
6971 if (level->no_valid_file)
6973 Error(ERR_WARN, "cannot dump -- no valid level file found");
6978 printf_line("-", 79);
6979 printf("Level xxx (file version %08d, game version %08d)\n",
6980 level->file_version, level->game_version);
6981 printf_line("-", 79);
6983 printf("Level author: '%s'\n", level->author);
6984 printf("Level title: '%s'\n", level->name);
6986 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
6988 printf("Level time: %d seconds\n", level->time);
6989 printf("Gems needed: %d\n", level->gems_needed);
6991 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
6992 printf("Time for wheel: %d seconds\n", level->time_wheel);
6993 printf("Time for light: %d seconds\n", level->time_light);
6994 printf("Time for timegate: %d seconds\n", level->time_timegate);
6996 printf("Amoeba speed: %d\n", level->amoeba_speed);
6999 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7000 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7001 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7002 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7003 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7005 printf_line("-", 79);
7009 /* ========================================================================= */
7010 /* tape file functions */
7011 /* ========================================================================= */
7013 static void setTapeInfoToDefaults()
7017 /* always start with reliable default values (empty tape) */
7020 /* default values (also for pre-1.2 tapes) with only the first player */
7021 tape.player_participates[0] = TRUE;
7022 for (i = 1; i < MAX_PLAYERS; i++)
7023 tape.player_participates[i] = FALSE;
7025 /* at least one (default: the first) player participates in every tape */
7026 tape.num_participating_players = 1;
7028 tape.level_nr = level_nr;
7030 tape.changed = FALSE;
7032 tape.recording = FALSE;
7033 tape.playing = FALSE;
7034 tape.pausing = FALSE;
7036 tape.no_valid_file = FALSE;
7039 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
7041 tape->file_version = getFileVersion(file);
7042 tape->game_version = getFileVersion(file);
7047 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
7051 tape->random_seed = getFile32BitBE(file);
7052 tape->date = getFile32BitBE(file);
7053 tape->length = getFile32BitBE(file);
7055 /* read header fields that are new since version 1.2 */
7056 if (tape->file_version >= FILE_VERSION_1_2)
7058 byte store_participating_players = getFile8Bit(file);
7061 /* since version 1.2, tapes store which players participate in the tape */
7062 tape->num_participating_players = 0;
7063 for (i = 0; i < MAX_PLAYERS; i++)
7065 tape->player_participates[i] = FALSE;
7067 if (store_participating_players & (1 << i))
7069 tape->player_participates[i] = TRUE;
7070 tape->num_participating_players++;
7074 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7076 engine_version = getFileVersion(file);
7077 if (engine_version > 0)
7078 tape->engine_version = engine_version;
7080 tape->engine_version = tape->game_version;
7086 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
7088 int level_identifier_size;
7091 level_identifier_size = getFile16BitBE(file);
7093 tape->level_identifier =
7094 checked_realloc(tape->level_identifier, level_identifier_size);
7096 for (i = 0; i < level_identifier_size; i++)
7097 tape->level_identifier[i] = getFile8Bit(file);
7099 tape->level_nr = getFile16BitBE(file);
7101 chunk_size = 2 + level_identifier_size + 2;
7106 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
7109 int chunk_size_expected =
7110 (tape->num_participating_players + 1) * tape->length;
7112 if (chunk_size_expected != chunk_size)
7114 ReadUnusedBytesFromFile(file, chunk_size);
7115 return chunk_size_expected;
7118 for (i = 0; i < tape->length; i++)
7120 if (i >= MAX_TAPE_LEN)
7123 for (j = 0; j < MAX_PLAYERS; j++)
7125 tape->pos[i].action[j] = MV_NONE;
7127 if (tape->player_participates[j])
7128 tape->pos[i].action[j] = getFile8Bit(file);
7131 tape->pos[i].delay = getFile8Bit(file);
7133 if (tape->file_version == FILE_VERSION_1_0)
7135 /* eliminate possible diagonal moves in old tapes */
7136 /* this is only for backward compatibility */
7138 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7139 byte action = tape->pos[i].action[0];
7140 int k, num_moves = 0;
7142 for (k = 0; k<4; k++)
7144 if (action & joy_dir[k])
7146 tape->pos[i + num_moves].action[0] = joy_dir[k];
7148 tape->pos[i + num_moves].delay = 0;
7157 tape->length += num_moves;
7160 else if (tape->file_version < FILE_VERSION_2_0)
7162 /* convert pre-2.0 tapes to new tape format */
7164 if (tape->pos[i].delay > 1)
7167 tape->pos[i + 1] = tape->pos[i];
7168 tape->pos[i + 1].delay = 1;
7171 for (j = 0; j < MAX_PLAYERS; j++)
7172 tape->pos[i].action[j] = MV_NONE;
7173 tape->pos[i].delay--;
7184 if (i != tape->length)
7185 chunk_size = (tape->num_participating_players + 1) * i;
7190 void LoadTapeFromFilename(char *filename)
7192 char cookie[MAX_LINE_LEN];
7193 char chunk_name[CHUNK_ID_LEN + 1];
7197 /* always start with reliable default values */
7198 setTapeInfoToDefaults();
7200 if (!(file = fopen(filename, MODE_READ)))
7202 tape.no_valid_file = TRUE;
7207 getFileChunkBE(file, chunk_name, NULL);
7208 if (strEqual(chunk_name, "RND1"))
7210 getFile32BitBE(file); /* not used */
7212 getFileChunkBE(file, chunk_name, NULL);
7213 if (!strEqual(chunk_name, "TAPE"))
7215 tape.no_valid_file = TRUE;
7217 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7222 else /* check for pre-2.0 file format with cookie string */
7224 strcpy(cookie, chunk_name);
7225 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
7226 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7227 cookie[strlen(cookie) - 1] = '\0';
7229 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7231 tape.no_valid_file = TRUE;
7233 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7238 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7240 tape.no_valid_file = TRUE;
7242 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7247 /* pre-2.0 tape files have no game version, so use file version here */
7248 tape.game_version = tape.file_version;
7251 if (tape.file_version < FILE_VERSION_1_2)
7253 /* tape files from versions before 1.2.0 without chunk structure */
7254 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7255 LoadTape_BODY(file, 2 * tape.length, &tape);
7263 int (*loader)(FILE *, int, struct TapeInfo *);
7267 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7268 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7269 { "INFO", -1, LoadTape_INFO },
7270 { "BODY", -1, LoadTape_BODY },
7274 while (getFileChunkBE(file, chunk_name, &chunk_size))
7278 while (chunk_info[i].name != NULL &&
7279 !strEqual(chunk_name, chunk_info[i].name))
7282 if (chunk_info[i].name == NULL)
7284 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7285 chunk_name, filename);
7286 ReadUnusedBytesFromFile(file, chunk_size);
7288 else if (chunk_info[i].size != -1 &&
7289 chunk_info[i].size != chunk_size)
7291 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7292 chunk_size, chunk_name, filename);
7293 ReadUnusedBytesFromFile(file, chunk_size);
7297 /* call function to load this tape chunk */
7298 int chunk_size_expected =
7299 (chunk_info[i].loader)(file, chunk_size, &tape);
7301 /* the size of some chunks cannot be checked before reading other
7302 chunks first (like "HEAD" and "BODY") that contain some header
7303 information, so check them here */
7304 if (chunk_size_expected != chunk_size)
7306 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7307 chunk_size, chunk_name, filename);
7315 tape.length_seconds = GetTapeLength();
7318 printf("::: tape file version: %d\n", tape.file_version);
7319 printf("::: tape game version: %d\n", tape.game_version);
7320 printf("::: tape engine version: %d\n", tape.engine_version);
7324 void LoadTape(int nr)
7326 char *filename = getTapeFilename(nr);
7328 LoadTapeFromFilename(filename);
7331 void LoadSolutionTape(int nr)
7333 char *filename = getSolutionTapeFilename(nr);
7335 LoadTapeFromFilename(filename);
7338 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7340 putFileVersion(file, tape->file_version);
7341 putFileVersion(file, tape->game_version);
7344 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7347 byte store_participating_players = 0;
7349 /* set bits for participating players for compact storage */
7350 for (i = 0; i < MAX_PLAYERS; i++)
7351 if (tape->player_participates[i])
7352 store_participating_players |= (1 << i);
7354 putFile32BitBE(file, tape->random_seed);
7355 putFile32BitBE(file, tape->date);
7356 putFile32BitBE(file, tape->length);
7358 putFile8Bit(file, store_participating_players);
7360 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7361 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7363 putFileVersion(file, tape->engine_version);
7366 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7368 int level_identifier_size = strlen(tape->level_identifier) + 1;
7371 putFile16BitBE(file, level_identifier_size);
7373 for (i = 0; i < level_identifier_size; i++)
7374 putFile8Bit(file, tape->level_identifier[i]);
7376 putFile16BitBE(file, tape->level_nr);
7379 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7383 for (i = 0; i < tape->length; i++)
7385 for (j = 0; j < MAX_PLAYERS; j++)
7386 if (tape->player_participates[j])
7387 putFile8Bit(file, tape->pos[i].action[j]);
7389 putFile8Bit(file, tape->pos[i].delay);
7393 void SaveTape(int nr)
7395 char *filename = getTapeFilename(nr);
7398 boolean new_tape = TRUE;
7400 int num_participating_players = 0;
7401 int info_chunk_size;
7402 int body_chunk_size;
7405 InitTapeDirectory(leveldir_current->subdir);
7408 /* if a tape still exists, ask to overwrite it */
7409 if (fileExists(filename))
7412 if (!Request("Replace old tape ?", REQ_ASK))
7417 if (!(file = fopen(filename, MODE_WRITE)))
7419 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7423 tape.file_version = FILE_VERSION_ACTUAL;
7424 tape.game_version = GAME_VERSION_ACTUAL;
7426 /* count number of participating players */
7427 for (i = 0; i < MAX_PLAYERS; i++)
7428 if (tape.player_participates[i])
7429 num_participating_players++;
7431 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7432 body_chunk_size = (num_participating_players + 1) * tape.length;
7434 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7435 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7437 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7438 SaveTape_VERS(file, &tape);
7440 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7441 SaveTape_HEAD(file, &tape);
7443 putFileChunkBE(file, "INFO", info_chunk_size);
7444 SaveTape_INFO(file, &tape);
7446 putFileChunkBE(file, "BODY", body_chunk_size);
7447 SaveTape_BODY(file, &tape);
7451 SetFilePermissions(filename, PERMS_PRIVATE);
7453 tape.changed = FALSE;
7457 Request("Tape saved !", REQ_CONFIRM);
7461 boolean SaveTapeChecked(int nr)
7463 char *filename = getTapeFilename(nr);
7464 boolean new_tape = !fileExists(filename);
7465 boolean tape_saved = FALSE;
7467 if (new_tape || Request("Replace old tape ?", REQ_ASK))
7472 Request("Tape saved !", REQ_CONFIRM);
7480 void DumpTape(struct TapeInfo *tape)
7482 int tape_frame_counter;
7485 if (tape->no_valid_file)
7487 Error(ERR_WARN, "cannot dump -- no valid tape file found");
7492 printf_line("-", 79);
7493 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
7494 tape->level_nr, tape->file_version, tape->game_version);
7495 printf(" (effective engine version %08d)\n",
7496 tape->engine_version);
7497 printf("Level series identifier: '%s'\n", tape->level_identifier);
7498 printf_line("-", 79);
7500 tape_frame_counter = 0;
7502 for (i = 0; i < tape->length; i++)
7504 if (i >= MAX_TAPE_LEN)
7507 printf("%04d: ", i);
7509 for (j = 0; j < MAX_PLAYERS; j++)
7511 if (tape->player_participates[j])
7513 int action = tape->pos[i].action[j];
7515 printf("%d:%02x ", j, action);
7516 printf("[%c%c%c%c|%c%c] - ",
7517 (action & JOY_LEFT ? '<' : ' '),
7518 (action & JOY_RIGHT ? '>' : ' '),
7519 (action & JOY_UP ? '^' : ' '),
7520 (action & JOY_DOWN ? 'v' : ' '),
7521 (action & JOY_BUTTON_1 ? '1' : ' '),
7522 (action & JOY_BUTTON_2 ? '2' : ' '));
7526 printf("(%03d) ", tape->pos[i].delay);
7527 printf("[%05d]\n", tape_frame_counter);
7529 tape_frame_counter += tape->pos[i].delay;
7532 printf_line("-", 79);
7536 /* ========================================================================= */
7537 /* score file functions */
7538 /* ========================================================================= */
7540 void LoadScore(int nr)
7543 char *filename = getScoreFilename(nr);
7544 char cookie[MAX_LINE_LEN];
7545 char line[MAX_LINE_LEN];
7549 /* always start with reliable default values */
7550 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7552 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
7553 highscore[i].Score = 0;
7556 if (!(file = fopen(filename, MODE_READ)))
7559 /* check file identifier */
7560 fgets(cookie, MAX_LINE_LEN, file);
7561 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7562 cookie[strlen(cookie) - 1] = '\0';
7564 if (!checkCookieString(cookie, SCORE_COOKIE))
7566 Error(ERR_WARN, "unknown format of score file '%s'", filename);
7571 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7573 fscanf(file, "%d", &highscore[i].Score);
7574 fgets(line, MAX_LINE_LEN, file);
7576 if (line[strlen(line) - 1] == '\n')
7577 line[strlen(line) - 1] = '\0';
7579 for (line_ptr = line; *line_ptr; line_ptr++)
7581 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
7583 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
7584 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
7593 void SaveScore(int nr)
7596 char *filename = getScoreFilename(nr);
7599 InitScoreDirectory(leveldir_current->subdir);
7601 if (!(file = fopen(filename, MODE_WRITE)))
7603 Error(ERR_WARN, "cannot save score for level %d", nr);
7607 fprintf(file, "%s\n\n", SCORE_COOKIE);
7609 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7610 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
7614 SetFilePermissions(filename, PERMS_PUBLIC);
7618 /* ========================================================================= */
7619 /* setup file functions */
7620 /* ========================================================================= */
7622 #define TOKEN_STR_PLAYER_PREFIX "player_"
7625 #define SETUP_TOKEN_PLAYER_NAME 0
7626 #define SETUP_TOKEN_SOUND 1
7627 #define SETUP_TOKEN_SOUND_LOOPS 2
7628 #define SETUP_TOKEN_SOUND_MUSIC 3
7629 #define SETUP_TOKEN_SOUND_SIMPLE 4
7630 #define SETUP_TOKEN_TOONS 5
7631 #define SETUP_TOKEN_SCROLL_DELAY 6
7632 #define SETUP_TOKEN_SOFT_SCROLLING 7
7633 #define SETUP_TOKEN_FADE_SCREENS 8
7634 #define SETUP_TOKEN_AUTORECORD 9
7635 #define SETUP_TOKEN_SHOW_TITLESCREEN 10
7636 #define SETUP_TOKEN_QUICK_DOORS 11
7637 #define SETUP_TOKEN_TEAM_MODE 12
7638 #define SETUP_TOKEN_HANDICAP 13
7639 #define SETUP_TOKEN_SKIP_LEVELS 14
7640 #define SETUP_TOKEN_TIME_LIMIT 15
7641 #define SETUP_TOKEN_FULLSCREEN 16
7642 #define SETUP_TOKEN_FULLSCREEN_MODE 17
7643 #define SETUP_TOKEN_ASK_ON_ESCAPE 18
7644 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 19
7645 #define SETUP_TOKEN_QUICK_SWITCH 20
7646 #define SETUP_TOKEN_INPUT_ON_FOCUS 21
7647 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 22
7648 #define SETUP_TOKEN_GRAPHICS_SET 23
7649 #define SETUP_TOKEN_SOUNDS_SET 24
7650 #define SETUP_TOKEN_MUSIC_SET 25
7651 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 26
7652 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 27
7653 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 28
7655 #define NUM_GLOBAL_SETUP_TOKENS 29
7658 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
7659 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
7660 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
7661 #define SETUP_TOKEN_EDITOR_EL_MORE 3
7662 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 4
7663 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 5
7664 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 6
7665 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 7
7666 #define SETUP_TOKEN_EDITOR_EL_CHARS 8
7667 #define SETUP_TOKEN_EDITOR_EL_STEELCHARS 9
7668 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 10
7669 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 11
7670 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 12
7671 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 13
7672 #define SETUP_TOKEN_EDITOR_EL_BY_GAME 14
7673 #define SETUP_TOKEN_EDITOR_EL_BY_TYPE 15
7674 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 16
7676 #define NUM_EDITOR_SETUP_TOKENS 17
7678 /* editor cascade setup */
7679 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
7680 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
7681 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
7682 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
7683 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
7684 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
7685 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
7686 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
7687 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
7688 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
7689 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
7690 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
7691 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
7692 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
7693 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
7695 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
7697 /* shortcut setup */
7698 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
7699 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
7700 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
7701 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
7702 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
7703 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
7704 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
7705 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
7707 #define NUM_SHORTCUT_SETUP_TOKENS 8
7710 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
7711 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
7712 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
7713 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
7714 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
7715 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
7716 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
7717 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
7718 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
7719 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
7720 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
7721 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
7722 #define SETUP_TOKEN_PLAYER_KEY_UP 12
7723 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
7724 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
7725 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
7727 #define NUM_PLAYER_SETUP_TOKENS 16
7730 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
7731 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
7733 #define NUM_SYSTEM_SETUP_TOKENS 2
7736 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
7738 #define NUM_OPTIONS_SETUP_TOKENS 1
7741 static struct SetupInfo si;
7742 static struct SetupEditorInfo sei;
7743 static struct SetupEditorCascadeInfo seci;
7744 static struct SetupShortcutInfo ssi;
7745 static struct SetupInputInfo sii;
7746 static struct SetupSystemInfo syi;
7747 static struct OptionInfo soi;
7749 static struct TokenInfo global_setup_tokens[] =
7751 { TYPE_STRING, &si.player_name, "player_name" },
7752 { TYPE_SWITCH, &si.sound, "sound" },
7753 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
7754 { TYPE_SWITCH, &si.sound_music, "background_music" },
7755 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
7756 { TYPE_SWITCH, &si.toons, "toons" },
7757 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
7758 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
7759 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
7760 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
7761 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
7762 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
7763 { TYPE_SWITCH, &si.team_mode, "team_mode" },
7764 { TYPE_SWITCH, &si.handicap, "handicap" },
7765 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
7766 { TYPE_SWITCH, &si.time_limit, "time_limit" },
7767 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
7768 { TYPE_STRING, &si.fullscreen_mode, "fullscreen_mode" },
7769 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
7770 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
7771 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
7772 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
7773 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
7774 { TYPE_STRING, &si.graphics_set, "graphics_set" },
7775 { TYPE_STRING, &si.sounds_set, "sounds_set" },
7776 { TYPE_STRING, &si.music_set, "music_set" },
7777 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
7778 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
7779 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
7782 static boolean not_used = FALSE;
7783 static struct TokenInfo editor_setup_tokens[] =
7786 { TYPE_SWITCH, ¬_used, "editor.el_boulderdash" },
7787 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine" },
7788 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine_club" },
7789 { TYPE_SWITCH, ¬_used, "editor.el_more" },
7790 { TYPE_SWITCH, ¬_used, "editor.el_sokoban" },
7791 { TYPE_SWITCH, ¬_used, "editor.el_supaplex" },
7792 { TYPE_SWITCH, ¬_used, "editor.el_diamond_caves" },
7793 { TYPE_SWITCH, ¬_used, "editor.el_dx_boulderdash" },
7795 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
7796 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
7797 { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
7798 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
7799 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
7800 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
7801 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
7802 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
7804 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
7805 { TYPE_SWITCH, &sei.el_steelchars, "editor.el_steelchars" },
7806 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
7808 { TYPE_SWITCH, ¬_used, "editor.el_headlines" },
7810 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
7812 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
7813 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
7814 { TYPE_SWITCH, &sei.el_by_game, "editor.el_by_game" },
7815 { TYPE_SWITCH, &sei.el_by_type, "editor.el_by_type" },
7816 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
7819 static struct TokenInfo editor_cascade_setup_tokens[] =
7821 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
7822 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
7823 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
7824 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
7825 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
7826 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
7827 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
7828 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
7829 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
7830 { TYPE_SWITCH, &seci.el_steelchars, "editor.cascade.el_steelchars" },
7831 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
7832 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
7833 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
7834 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
7835 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
7838 static struct TokenInfo shortcut_setup_tokens[] =
7840 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
7841 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
7842 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
7843 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
7844 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
7845 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
7846 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
7847 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
7850 static struct TokenInfo player_setup_tokens[] =
7852 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
7853 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
7854 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
7855 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
7856 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
7857 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
7858 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
7859 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
7860 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
7861 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
7862 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
7863 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
7864 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
7865 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
7866 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
7867 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
7870 static struct TokenInfo system_setup_tokens[] =
7872 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
7873 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
7876 static struct TokenInfo options_setup_tokens[] =
7878 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
7881 static char *get_corrected_login_name(char *login_name)
7883 /* needed because player name must be a fixed length string */
7884 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
7886 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
7887 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
7889 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
7890 if (strchr(login_name_new, ' '))
7891 *strchr(login_name_new, ' ') = '\0';
7893 return login_name_new;
7896 static void setSetupInfoToDefaults(struct SetupInfo *si)
7900 si->player_name = get_corrected_login_name(getLoginName());
7903 si->sound_loops = TRUE;
7904 si->sound_music = TRUE;
7905 si->sound_simple = TRUE;
7907 si->double_buffering = TRUE;
7908 si->direct_draw = !si->double_buffering;
7909 si->scroll_delay = TRUE;
7910 si->soft_scrolling = TRUE;
7911 si->fade_screens = TRUE;
7912 si->autorecord = TRUE;
7913 si->show_titlescreen = TRUE;
7914 si->quick_doors = FALSE;
7915 si->team_mode = FALSE;
7916 si->handicap = TRUE;
7917 si->skip_levels = TRUE;
7918 si->time_limit = TRUE;
7919 si->fullscreen = FALSE;
7920 si->fullscreen_mode = getStringCopy(DEFAULT_FULLSCREEN_MODE);
7921 si->ask_on_escape = TRUE;
7922 si->ask_on_escape_editor = TRUE;
7923 si->quick_switch = FALSE;
7924 si->input_on_focus = FALSE;
7925 si->prefer_aga_graphics = TRUE;
7927 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
7928 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
7929 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
7930 si->override_level_graphics = FALSE;
7931 si->override_level_sounds = FALSE;
7932 si->override_level_music = FALSE;
7934 si->editor.el_boulderdash = TRUE;
7935 si->editor.el_emerald_mine = TRUE;
7936 si->editor.el_emerald_mine_club = TRUE;
7937 si->editor.el_more = TRUE;
7938 si->editor.el_sokoban = TRUE;
7939 si->editor.el_supaplex = TRUE;
7940 si->editor.el_diamond_caves = TRUE;
7941 si->editor.el_dx_boulderdash = TRUE;
7942 si->editor.el_chars = TRUE;
7943 si->editor.el_steelchars = TRUE;
7944 si->editor.el_custom = TRUE;
7946 si->editor.el_headlines = TRUE;
7947 si->editor.el_user_defined = FALSE;
7948 si->editor.el_dynamic = TRUE;
7950 si->editor.show_element_token = FALSE;
7952 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
7953 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
7954 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
7956 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
7957 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
7958 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
7959 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
7960 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
7962 for (i = 0; i < MAX_PLAYERS; i++)
7964 si->input[i].use_joystick = FALSE;
7965 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
7966 si->input[i].joy.xleft = JOYSTICK_XLEFT;
7967 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
7968 si->input[i].joy.xright = JOYSTICK_XRIGHT;
7969 si->input[i].joy.yupper = JOYSTICK_YUPPER;
7970 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
7971 si->input[i].joy.ylower = JOYSTICK_YLOWER;
7972 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
7973 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
7974 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
7975 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
7976 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
7977 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
7978 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
7979 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
7982 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
7983 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
7985 si->options.verbose = FALSE;
7988 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
7990 si->editor_cascade.el_bd = TRUE;
7991 si->editor_cascade.el_em = TRUE;
7992 si->editor_cascade.el_emc = TRUE;
7993 si->editor_cascade.el_rnd = TRUE;
7994 si->editor_cascade.el_sb = TRUE;
7995 si->editor_cascade.el_sp = TRUE;
7996 si->editor_cascade.el_dc = TRUE;
7997 si->editor_cascade.el_dx = TRUE;
7999 si->editor_cascade.el_chars = FALSE;
8000 si->editor_cascade.el_steelchars = FALSE;
8001 si->editor_cascade.el_ce = FALSE;
8002 si->editor_cascade.el_ge = FALSE;
8003 si->editor_cascade.el_ref = FALSE;
8004 si->editor_cascade.el_user = FALSE;
8005 si->editor_cascade.el_dynamic = FALSE;
8008 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8012 if (!setup_file_hash)
8017 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8018 setSetupInfo(global_setup_tokens, i,
8019 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
8024 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8025 setSetupInfo(editor_setup_tokens, i,
8026 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
8029 /* shortcut setup */
8030 ssi = setup.shortcut;
8031 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8032 setSetupInfo(shortcut_setup_tokens, i,
8033 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
8034 setup.shortcut = ssi;
8037 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8041 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8043 sii = setup.input[pnr];
8044 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8046 char full_token[100];
8048 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8049 setSetupInfo(player_setup_tokens, i,
8050 getHashEntry(setup_file_hash, full_token));
8052 setup.input[pnr] = sii;
8057 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8058 setSetupInfo(system_setup_tokens, i,
8059 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
8063 soi = setup.options;
8064 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8065 setSetupInfo(options_setup_tokens, i,
8066 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
8067 setup.options = soi;
8070 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8074 if (!setup_file_hash)
8077 /* editor cascade setup */
8078 seci = setup.editor_cascade;
8079 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8080 setSetupInfo(editor_cascade_setup_tokens, i,
8081 getHashEntry(setup_file_hash,
8082 editor_cascade_setup_tokens[i].text));
8083 setup.editor_cascade = seci;
8088 char *filename = getSetupFilename();
8089 SetupFileHash *setup_file_hash = NULL;
8091 /* always start with reliable default values */
8092 setSetupInfoToDefaults(&setup);
8094 setup_file_hash = loadSetupFileHash(filename);
8096 if (setup_file_hash)
8098 char *player_name_new;
8100 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
8101 decodeSetupFileHash(setup_file_hash);
8103 setup.direct_draw = !setup.double_buffering;
8105 freeSetupFileHash(setup_file_hash);
8107 /* needed to work around problems with fixed length strings */
8108 player_name_new = get_corrected_login_name(setup.player_name);
8109 free(setup.player_name);
8110 setup.player_name = player_name_new;
8113 Error(ERR_WARN, "using default setup values");
8116 void LoadSetup_EditorCascade()
8118 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8119 SetupFileHash *setup_file_hash = NULL;
8121 /* always start with reliable default values */
8122 setSetupInfoToDefaults_EditorCascade(&setup);
8124 setup_file_hash = loadSetupFileHash(filename);
8126 if (setup_file_hash)
8128 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
8129 decodeSetupFileHash_EditorCascade(setup_file_hash);
8131 freeSetupFileHash(setup_file_hash);
8139 char *filename = getSetupFilename();
8143 InitUserDataDirectory();
8145 if (!(file = fopen(filename, MODE_WRITE)))
8147 Error(ERR_WARN, "cannot write setup file '%s'", filename);
8151 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
8152 getCookie("SETUP")));
8153 fprintf(file, "\n");
8157 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8159 /* just to make things nicer :) */
8160 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
8161 i == SETUP_TOKEN_GRAPHICS_SET)
8162 fprintf(file, "\n");
8164 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
8169 fprintf(file, "\n");
8170 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8171 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
8173 /* shortcut setup */
8174 ssi = setup.shortcut;
8175 fprintf(file, "\n");
8176 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8177 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
8180 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8184 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8185 fprintf(file, "\n");
8187 sii = setup.input[pnr];
8188 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8189 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
8194 fprintf(file, "\n");
8195 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8196 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
8199 soi = setup.options;
8200 fprintf(file, "\n");
8201 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8202 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
8206 SetFilePermissions(filename, PERMS_PRIVATE);
8209 void SaveSetup_EditorCascade()
8211 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8215 InitUserDataDirectory();
8217 if (!(file = fopen(filename, MODE_WRITE)))
8219 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
8224 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
8225 getCookie("SETUP")));
8226 fprintf(file, "\n");
8228 seci = setup.editor_cascade;
8229 fprintf(file, "\n");
8230 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8231 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
8235 SetFilePermissions(filename, PERMS_PRIVATE);
8240 void LoadCustomElementDescriptions()
8242 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8243 SetupFileHash *setup_file_hash;
8246 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8248 if (element_info[i].custom_description != NULL)
8250 free(element_info[i].custom_description);
8251 element_info[i].custom_description = NULL;
8255 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8258 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8260 char *token = getStringCat2(element_info[i].token_name, ".name");
8261 char *value = getHashEntry(setup_file_hash, token);
8264 element_info[i].custom_description = getStringCopy(value);
8269 freeSetupFileHash(setup_file_hash);
8272 static void LoadSpecialMenuDesignSettingsFromFilename(char *filename)
8274 SetupFileHash *setup_file_hash;
8278 printf("LoadSpecialMenuDesignSettings from file '%s' ...\n", filename);
8281 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8284 /* special case: initialize with default values that may be overwritten */
8285 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8287 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
8288 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
8289 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
8291 if (value_x != NULL)
8292 menu.draw_xoffset[i] = get_integer_from_string(value_x);
8293 if (value_y != NULL)
8294 menu.draw_yoffset[i] = get_integer_from_string(value_y);
8295 if (list_size != NULL)
8296 menu.list_size[i] = get_integer_from_string(list_size);
8299 /* special case: initialize with default values that may be overwritten */
8300 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
8302 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
8303 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
8305 if (value_x != NULL)
8306 menu.draw_xoffset_info[i] = get_integer_from_string(value_x);
8307 if (value_y != NULL)
8308 menu.draw_yoffset_info[i] = get_integer_from_string(value_y);
8311 /* read (and overwrite with) values that may be specified in config file */
8312 for (i = 0; image_config_vars[i].token != NULL; i++)
8314 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
8317 *image_config_vars[i].value =
8318 get_auto_parameter_value(image_config_vars[i].token, value);
8321 freeSetupFileHash(setup_file_hash);
8324 void LoadSpecialMenuDesignSettings()
8326 char *filename_base = UNDEFINED_FILENAME, *filename_local;
8329 /* always start with reliable default values from default config */
8330 for (i = 0; image_config_vars[i].token != NULL; i++)
8331 for (j = 0; image_config[j].token != NULL; j++)
8332 if (strEqual(image_config_vars[i].token, image_config[j].token))
8333 *image_config_vars[i].value =
8334 get_auto_parameter_value(image_config_vars[i].token,
8335 image_config[j].value);
8337 if (!SETUP_OVERRIDE_ARTWORK(setup, ARTWORK_TYPE_GRAPHICS))
8339 /* first look for special settings configured in level series config */
8340 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
8342 if (fileExists(filename_base))
8343 LoadSpecialMenuDesignSettingsFromFilename(filename_base);
8346 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8348 if (filename_local != NULL && !strEqual(filename_base, filename_local))
8349 LoadSpecialMenuDesignSettingsFromFilename(filename_local);
8352 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
8354 char *filename = getEditorSetupFilename();
8355 SetupFileList *setup_file_list, *list;
8356 SetupFileHash *element_hash;
8357 int num_unknown_tokens = 0;
8360 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
8363 element_hash = newSetupFileHash();
8365 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8366 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
8368 /* determined size may be larger than needed (due to unknown elements) */
8370 for (list = setup_file_list; list != NULL; list = list->next)
8373 /* add space for up to 3 more elements for padding that may be needed */
8376 /* free memory for old list of elements, if needed */
8377 checked_free(*elements);
8379 /* allocate memory for new list of elements */
8380 *elements = checked_malloc(*num_elements * sizeof(int));
8383 for (list = setup_file_list; list != NULL; list = list->next)
8385 char *value = getHashEntry(element_hash, list->token);
8387 if (value == NULL) /* try to find obsolete token mapping */
8389 char *mapped_token = get_mapped_token(list->token);
8391 if (mapped_token != NULL)
8393 value = getHashEntry(element_hash, mapped_token);
8401 (*elements)[(*num_elements)++] = atoi(value);
8405 if (num_unknown_tokens == 0)
8407 Error(ERR_RETURN_LINE, "-");
8408 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
8409 Error(ERR_RETURN, "- config file: '%s'", filename);
8411 num_unknown_tokens++;
8414 Error(ERR_RETURN, "- token: '%s'", list->token);
8418 if (num_unknown_tokens > 0)
8419 Error(ERR_RETURN_LINE, "-");
8421 while (*num_elements % 4) /* pad with empty elements, if needed */
8422 (*elements)[(*num_elements)++] = EL_EMPTY;
8424 freeSetupFileList(setup_file_list);
8425 freeSetupFileHash(element_hash);
8428 for (i = 0; i < *num_elements; i++)
8429 printf("editor: element '%s' [%d]\n",
8430 element_info[(*elements)[i]].token_name, (*elements)[i]);
8434 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
8437 SetupFileHash *setup_file_hash = NULL;
8438 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
8439 char *filename_music, *filename_prefix, *filename_info;
8445 token_to_value_ptr[] =
8447 { "title_header", &tmp_music_file_info.title_header },
8448 { "artist_header", &tmp_music_file_info.artist_header },
8449 { "album_header", &tmp_music_file_info.album_header },
8450 { "year_header", &tmp_music_file_info.year_header },
8452 { "title", &tmp_music_file_info.title },
8453 { "artist", &tmp_music_file_info.artist },
8454 { "album", &tmp_music_file_info.album },
8455 { "year", &tmp_music_file_info.year },
8461 filename_music = (is_sound ? getCustomSoundFilename(basename) :
8462 getCustomMusicFilename(basename));
8464 if (filename_music == NULL)
8467 /* ---------- try to replace file extension ---------- */
8469 filename_prefix = getStringCopy(filename_music);
8470 if (strrchr(filename_prefix, '.') != NULL)
8471 *strrchr(filename_prefix, '.') = '\0';
8472 filename_info = getStringCat2(filename_prefix, ".txt");
8475 printf("trying to load file '%s'...\n", filename_info);
8478 if (fileExists(filename_info))
8479 setup_file_hash = loadSetupFileHash(filename_info);
8481 free(filename_prefix);
8482 free(filename_info);
8484 if (setup_file_hash == NULL)
8486 /* ---------- try to add file extension ---------- */
8488 filename_prefix = getStringCopy(filename_music);
8489 filename_info = getStringCat2(filename_prefix, ".txt");
8492 printf("trying to load file '%s'...\n", filename_info);
8495 if (fileExists(filename_info))
8496 setup_file_hash = loadSetupFileHash(filename_info);
8498 free(filename_prefix);
8499 free(filename_info);
8502 if (setup_file_hash == NULL)
8505 /* ---------- music file info found ---------- */
8507 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
8509 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
8511 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
8513 *token_to_value_ptr[i].value_ptr =
8514 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
8517 tmp_music_file_info.basename = getStringCopy(basename);
8518 tmp_music_file_info.music = music;
8519 tmp_music_file_info.is_sound = is_sound;
8521 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
8522 *new_music_file_info = tmp_music_file_info;
8524 return new_music_file_info;
8527 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
8529 return get_music_file_info_ext(basename, music, FALSE);
8532 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
8534 return get_music_file_info_ext(basename, sound, TRUE);
8537 static boolean music_info_listed_ext(struct MusicFileInfo *list,
8538 char *basename, boolean is_sound)
8540 for (; list != NULL; list = list->next)
8541 if (list->is_sound == is_sound && strEqual(list->basename, basename))
8547 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
8549 return music_info_listed_ext(list, basename, FALSE);
8552 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
8554 return music_info_listed_ext(list, basename, TRUE);
8557 void LoadMusicInfo()
8559 char *music_directory = getCustomMusicDirectory();
8560 int num_music = getMusicListSize();
8561 int num_music_noconf = 0;
8562 int num_sounds = getSoundListSize();
8564 struct dirent *dir_entry;
8565 struct FileInfo *music, *sound;
8566 struct MusicFileInfo *next, **new;
8569 while (music_file_info != NULL)
8571 next = music_file_info->next;
8573 checked_free(music_file_info->basename);
8575 checked_free(music_file_info->title_header);
8576 checked_free(music_file_info->artist_header);
8577 checked_free(music_file_info->album_header);
8578 checked_free(music_file_info->year_header);
8580 checked_free(music_file_info->title);
8581 checked_free(music_file_info->artist);
8582 checked_free(music_file_info->album);
8583 checked_free(music_file_info->year);
8585 free(music_file_info);
8587 music_file_info = next;
8590 new = &music_file_info;
8592 for (i = 0; i < num_music; i++)
8594 music = getMusicListEntry(i);
8596 if (music->filename == NULL)
8599 if (strEqual(music->filename, UNDEFINED_FILENAME))
8602 /* a configured file may be not recognized as music */
8603 if (!FileIsMusic(music->filename))
8607 printf("::: -> '%s' (configured)\n", music->filename);
8610 if (!music_info_listed(music_file_info, music->filename))
8612 *new = get_music_file_info(music->filename, i);
8614 new = &(*new)->next;
8618 if ((dir = opendir(music_directory)) == NULL)
8620 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
8624 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
8626 char *basename = dir_entry->d_name;
8627 boolean music_already_used = FALSE;
8630 /* skip all music files that are configured in music config file */
8631 for (i = 0; i < num_music; i++)
8633 music = getMusicListEntry(i);
8635 if (music->filename == NULL)
8638 if (strEqual(basename, music->filename))
8640 music_already_used = TRUE;
8645 if (music_already_used)
8648 if (!FileIsMusic(basename))
8652 printf("::: -> '%s' (found in directory)\n", basename);
8655 if (!music_info_listed(music_file_info, basename))
8657 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
8659 new = &(*new)->next;
8667 for (i = 0; i < num_sounds; i++)
8669 sound = getSoundListEntry(i);
8671 if (sound->filename == NULL)
8674 if (strEqual(sound->filename, UNDEFINED_FILENAME))
8677 /* a configured file may be not recognized as sound */
8678 if (!FileIsSound(sound->filename))
8682 printf("::: -> '%s' (configured)\n", sound->filename);
8685 if (!sound_info_listed(music_file_info, sound->filename))
8687 *new = get_sound_file_info(sound->filename, i);
8689 new = &(*new)->next;
8694 for (next = music_file_info; next != NULL; next = next->next)
8695 printf("::: title == '%s'\n", next->title);
8699 void add_helpanim_entry(int element, int action, int direction, int delay,
8700 int *num_list_entries)
8702 struct HelpAnimInfo *new_list_entry;
8703 (*num_list_entries)++;
8706 checked_realloc(helpanim_info,
8707 *num_list_entries * sizeof(struct HelpAnimInfo));
8708 new_list_entry = &helpanim_info[*num_list_entries - 1];
8710 new_list_entry->element = element;
8711 new_list_entry->action = action;
8712 new_list_entry->direction = direction;
8713 new_list_entry->delay = delay;
8716 void print_unknown_token(char *filename, char *token, int token_nr)
8720 Error(ERR_RETURN_LINE, "-");
8721 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
8722 Error(ERR_RETURN, "- config file: '%s'", filename);
8725 Error(ERR_RETURN, "- token: '%s'", token);
8728 void print_unknown_token_end(int token_nr)
8731 Error(ERR_RETURN_LINE, "-");
8734 void LoadHelpAnimInfo()
8736 char *filename = getHelpAnimFilename();
8737 SetupFileList *setup_file_list = NULL, *list;
8738 SetupFileHash *element_hash, *action_hash, *direction_hash;
8739 int num_list_entries = 0;
8740 int num_unknown_tokens = 0;
8743 if (fileExists(filename))
8744 setup_file_list = loadSetupFileList(filename);
8746 if (setup_file_list == NULL)
8748 /* use reliable default values from static configuration */
8749 SetupFileList *insert_ptr;
8751 insert_ptr = setup_file_list =
8752 newSetupFileList(helpanim_config[0].token,
8753 helpanim_config[0].value);
8755 for (i = 1; helpanim_config[i].token; i++)
8756 insert_ptr = addListEntry(insert_ptr,
8757 helpanim_config[i].token,
8758 helpanim_config[i].value);
8761 element_hash = newSetupFileHash();
8762 action_hash = newSetupFileHash();
8763 direction_hash = newSetupFileHash();
8765 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
8766 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
8768 for (i = 0; i < NUM_ACTIONS; i++)
8769 setHashEntry(action_hash, element_action_info[i].suffix,
8770 i_to_a(element_action_info[i].value));
8772 /* do not store direction index (bit) here, but direction value! */
8773 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
8774 setHashEntry(direction_hash, element_direction_info[i].suffix,
8775 i_to_a(1 << element_direction_info[i].value));
8777 for (list = setup_file_list; list != NULL; list = list->next)
8779 char *element_token, *action_token, *direction_token;
8780 char *element_value, *action_value, *direction_value;
8781 int delay = atoi(list->value);
8783 if (strEqual(list->token, "end"))
8785 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
8790 /* first try to break element into element/action/direction parts;
8791 if this does not work, also accept combined "element[.act][.dir]"
8792 elements (like "dynamite.active"), which are unique elements */
8794 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
8796 element_value = getHashEntry(element_hash, list->token);
8797 if (element_value != NULL) /* element found */
8798 add_helpanim_entry(atoi(element_value), -1, -1, delay,
8802 /* no further suffixes found -- this is not an element */
8803 print_unknown_token(filename, list->token, num_unknown_tokens++);
8809 /* token has format "<prefix>.<something>" */
8811 action_token = strchr(list->token, '.'); /* suffix may be action ... */
8812 direction_token = action_token; /* ... or direction */
8814 element_token = getStringCopy(list->token);
8815 *strchr(element_token, '.') = '\0';
8817 element_value = getHashEntry(element_hash, element_token);
8819 if (element_value == NULL) /* this is no element */
8821 element_value = getHashEntry(element_hash, list->token);
8822 if (element_value != NULL) /* combined element found */
8823 add_helpanim_entry(atoi(element_value), -1, -1, delay,
8826 print_unknown_token(filename, list->token, num_unknown_tokens++);
8828 free(element_token);
8833 action_value = getHashEntry(action_hash, action_token);
8835 if (action_value != NULL) /* action found */
8837 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
8840 free(element_token);
8845 direction_value = getHashEntry(direction_hash, direction_token);
8847 if (direction_value != NULL) /* direction found */
8849 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
8852 free(element_token);
8857 if (strchr(action_token + 1, '.') == NULL)
8859 /* no further suffixes found -- this is not an action nor direction */
8861 element_value = getHashEntry(element_hash, list->token);
8862 if (element_value != NULL) /* combined element found */
8863 add_helpanim_entry(atoi(element_value), -1, -1, delay,
8866 print_unknown_token(filename, list->token, num_unknown_tokens++);
8868 free(element_token);
8873 /* token has format "<prefix>.<suffix>.<something>" */
8875 direction_token = strchr(action_token + 1, '.');
8877 action_token = getStringCopy(action_token);
8878 *strchr(action_token + 1, '.') = '\0';
8880 action_value = getHashEntry(action_hash, action_token);
8882 if (action_value == NULL) /* this is no action */
8884 element_value = getHashEntry(element_hash, list->token);
8885 if (element_value != NULL) /* combined element found */
8886 add_helpanim_entry(atoi(element_value), -1, -1, delay,
8889 print_unknown_token(filename, list->token, num_unknown_tokens++);
8891 free(element_token);
8897 direction_value = getHashEntry(direction_hash, direction_token);
8899 if (direction_value != NULL) /* direction found */
8901 add_helpanim_entry(atoi(element_value), atoi(action_value),
8902 atoi(direction_value), delay, &num_list_entries);
8904 free(element_token);
8910 /* this is no direction */
8912 element_value = getHashEntry(element_hash, list->token);
8913 if (element_value != NULL) /* combined element found */
8914 add_helpanim_entry(atoi(element_value), -1, -1, delay,
8917 print_unknown_token(filename, list->token, num_unknown_tokens++);
8919 free(element_token);
8923 print_unknown_token_end(num_unknown_tokens);
8925 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
8926 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
8928 freeSetupFileList(setup_file_list);
8929 freeSetupFileHash(element_hash);
8930 freeSetupFileHash(action_hash);
8931 freeSetupFileHash(direction_hash);
8934 for (i = 0; i < num_list_entries; i++)
8935 printf("::: '%s': %d, %d, %d => %d\n",
8936 EL_NAME(helpanim_info[i].element),
8937 helpanim_info[i].element,
8938 helpanim_info[i].action,
8939 helpanim_info[i].direction,
8940 helpanim_info[i].delay);
8944 void LoadHelpTextInfo()
8946 char *filename = getHelpTextFilename();
8949 if (helptext_info != NULL)
8951 freeSetupFileHash(helptext_info);
8952 helptext_info = NULL;
8955 if (fileExists(filename))
8956 helptext_info = loadSetupFileHash(filename);
8958 if (helptext_info == NULL)
8960 /* use reliable default values from static configuration */
8961 helptext_info = newSetupFileHash();
8963 for (i = 0; helptext_config[i].token; i++)
8964 setHashEntry(helptext_info,
8965 helptext_config[i].token,
8966 helptext_config[i].value);
8970 BEGIN_HASH_ITERATION(helptext_info, itr)
8972 printf("::: '%s' => '%s'\n",
8973 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
8975 END_HASH_ITERATION(hash, itr)
8980 /* ------------------------------------------------------------------------- *
8982 * ------------------------------------------------------------------------- */
8984 #define MAX_NUM_CONVERT_LEVELS 1000
8986 void ConvertLevels()
8988 static LevelDirTree *convert_leveldir = NULL;
8989 static int convert_level_nr = -1;
8990 static int num_levels_handled = 0;
8991 static int num_levels_converted = 0;
8992 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
8995 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8996 global.convert_leveldir);
8998 if (convert_leveldir == NULL)
8999 Error(ERR_EXIT, "no such level identifier: '%s'",
9000 global.convert_leveldir);
9002 leveldir_current = convert_leveldir;
9004 if (global.convert_level_nr != -1)
9006 convert_leveldir->first_level = global.convert_level_nr;
9007 convert_leveldir->last_level = global.convert_level_nr;
9010 convert_level_nr = convert_leveldir->first_level;
9012 printf_line("=", 79);
9013 printf("Converting levels\n");
9014 printf_line("-", 79);
9015 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
9016 printf("Level series name: '%s'\n", convert_leveldir->name);
9017 printf("Level series author: '%s'\n", convert_leveldir->author);
9018 printf("Number of levels: %d\n", convert_leveldir->levels);
9019 printf_line("=", 79);
9022 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
9023 levels_failed[i] = FALSE;
9025 while (convert_level_nr <= convert_leveldir->last_level)
9027 char *level_filename;
9030 level_nr = convert_level_nr++;
9032 printf("Level %03d: ", level_nr);
9034 LoadLevel(level_nr);
9035 if (level.no_valid_file)
9037 printf("(no level)\n");
9041 printf("converting level ... ");
9043 level_filename = getDefaultLevelFilename(level_nr);
9044 new_level = !fileExists(level_filename);
9048 SaveLevel(level_nr);
9050 num_levels_converted++;
9052 printf("converted.\n");
9056 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
9057 levels_failed[level_nr] = TRUE;
9059 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
9062 num_levels_handled++;
9066 printf_line("=", 79);
9067 printf("Number of levels handled: %d\n", num_levels_handled);
9068 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
9069 (num_levels_handled ?
9070 num_levels_converted * 100 / num_levels_handled : 0));
9071 printf_line("-", 79);
9072 printf("Summary (for automatic parsing by scripts):\n");
9073 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
9074 convert_leveldir->identifier, num_levels_converted,
9076 (num_levels_handled ?
9077 num_levels_converted * 100 / num_levels_handled : 0));
9079 if (num_levels_handled != num_levels_converted)
9081 printf(", FAILED:");
9082 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
9083 if (levels_failed[i])
9088 printf_line("=", 79);