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;
4152 element = EL_STEEL_EXIT_OPEN;
4156 element = EL_STEEL_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_STEEL_CHAR_A;
4836 case 0x1616: /* (blue steel) */
4837 element = EL_STEEL_CHAR_B;
4840 case 0x1617: /* (blue steel) */
4841 element = EL_STEEL_CHAR_C;
4844 case 0x1618: /* (blue steel) */
4845 element = EL_STEEL_CHAR_D;
4848 case 0x1619: /* (blue steel) */
4849 element = EL_STEEL_CHAR_E;
4852 case 0x161a: /* (blue steel) */
4853 element = EL_STEEL_CHAR_F;
4856 case 0x161b: /* (blue steel) */
4857 element = EL_STEEL_CHAR_G;
4860 case 0x161c: /* (blue steel) */
4861 element = EL_STEEL_CHAR_H;
4864 case 0x161d: /* (blue steel) */
4865 element = EL_STEEL_CHAR_I;
4868 case 0x161e: /* (blue steel) */
4869 element = EL_STEEL_CHAR_J;
4872 case 0x161f: /* (blue steel) */
4873 element = EL_STEEL_CHAR_K;
4876 case 0x1620: /* (blue steel) */
4877 element = EL_STEEL_CHAR_L;
4880 case 0x1621: /* (blue steel) */
4881 element = EL_STEEL_CHAR_M;
4884 case 0x1622: /* (blue steel) */
4885 element = EL_STEEL_CHAR_N;
4888 case 0x1623: /* (blue steel) */
4889 element = EL_STEEL_CHAR_O;
4892 case 0x1624: /* (blue steel) */
4893 element = EL_STEEL_CHAR_P;
4896 case 0x1625: /* (blue steel) */
4897 element = EL_STEEL_CHAR_Q;
4900 case 0x1626: /* (blue steel) */
4901 element = EL_STEEL_CHAR_R;
4904 case 0x1627: /* (blue steel) */
4905 element = EL_STEEL_CHAR_S;
4908 case 0x1628: /* (blue steel) */
4909 element = EL_STEEL_CHAR_T;
4912 case 0x1629: /* (blue steel) */
4913 element = EL_STEEL_CHAR_U;
4916 case 0x162a: /* (blue steel) */
4917 element = EL_STEEL_CHAR_V;
4920 case 0x162b: /* (blue steel) */
4921 element = EL_STEEL_CHAR_W;
4924 case 0x162c: /* (blue steel) */
4925 element = EL_STEEL_CHAR_X;
4928 case 0x162d: /* (blue steel) */
4929 element = EL_STEEL_CHAR_Y;
4932 case 0x162e: /* (blue steel) */
4933 element = EL_STEEL_CHAR_Z;
4936 case 0x162f: /* (blue steel) */
4937 element = EL_STEEL_CHAR_AUMLAUT;
4940 case 0x1630: /* (blue steel) */
4941 element = EL_STEEL_CHAR_OUMLAUT;
4944 case 0x1631: /* (blue steel) */
4945 element = EL_STEEL_CHAR_UUMLAUT;
4948 case 0x1632: /* (blue steel) */
4949 element = EL_STEEL_CHAR_0;
4952 case 0x1633: /* (blue steel) */
4953 element = EL_STEEL_CHAR_1;
4956 case 0x1634: /* (blue steel) */
4957 element = EL_STEEL_CHAR_2;
4960 case 0x1635: /* (blue steel) */
4961 element = EL_STEEL_CHAR_3;
4964 case 0x1636: /* (blue steel) */
4965 element = EL_STEEL_CHAR_4;
4968 case 0x1637: /* (blue steel) */
4969 element = EL_STEEL_CHAR_5;
4972 case 0x1638: /* (blue steel) */
4973 element = EL_STEEL_CHAR_6;
4976 case 0x1639: /* (blue steel) */
4977 element = EL_STEEL_CHAR_7;
4980 case 0x163a: /* (blue steel) */
4981 element = EL_STEEL_CHAR_8;
4984 case 0x163b: /* (blue steel) */
4985 element = EL_STEEL_CHAR_9;
4988 case 0x163c: /* (blue steel) */
4989 element = EL_STEEL_CHAR_PERIOD;
4992 case 0x163d: /* (blue steel) */
4993 element = EL_STEEL_CHAR_EXCLAM;
4996 case 0x163e: /* (blue steel) */
4997 element = EL_STEEL_CHAR_COLON;
5000 case 0x163f: /* (blue steel) */
5001 element = EL_STEEL_CHAR_LESS;
5004 case 0x1640: /* (blue steel) */
5005 element = EL_STEEL_CHAR_GREATER;
5008 case 0x1641: /* (blue steel) */
5009 element = EL_STEEL_CHAR_QUESTION;
5012 case 0x1642: /* (blue steel) */
5013 element = EL_STEEL_CHAR_COPYRIGHT;
5016 case 0x1643: /* (blue steel) */
5017 element = EL_STEEL_CHAR_UP;
5020 case 0x1644: /* (blue steel) */
5021 element = EL_STEEL_CHAR_DOWN;
5024 case 0x1645: /* (blue steel) */
5025 element = EL_STEEL_CHAR_BUTTON;
5028 case 0x1646: /* (blue steel) */
5029 element = EL_STEEL_CHAR_PLUS;
5032 case 0x1647: /* (blue steel) */
5033 element = EL_STEEL_CHAR_MINUS;
5036 case 0x1648: /* (blue steel) */
5037 element = EL_STEEL_CHAR_APOSTROPHE;
5040 case 0x1649: /* (blue steel) */
5041 element = EL_STEEL_CHAR_PARENLEFT;
5044 case 0x164a: /* (blue steel) */
5045 element = EL_STEEL_CHAR_PARENRIGHT;
5048 case 0x164b: /* (green steel) */
5049 element = EL_STEEL_CHAR_A;
5052 case 0x164c: /* (green steel) */
5053 element = EL_STEEL_CHAR_B;
5056 case 0x164d: /* (green steel) */
5057 element = EL_STEEL_CHAR_C;
5060 case 0x164e: /* (green steel) */
5061 element = EL_STEEL_CHAR_D;
5064 case 0x164f: /* (green steel) */
5065 element = EL_STEEL_CHAR_E;
5068 case 0x1650: /* (green steel) */
5069 element = EL_STEEL_CHAR_F;
5072 case 0x1651: /* (green steel) */
5073 element = EL_STEEL_CHAR_G;
5076 case 0x1652: /* (green steel) */
5077 element = EL_STEEL_CHAR_H;
5080 case 0x1653: /* (green steel) */
5081 element = EL_STEEL_CHAR_I;
5084 case 0x1654: /* (green steel) */
5085 element = EL_STEEL_CHAR_J;
5088 case 0x1655: /* (green steel) */
5089 element = EL_STEEL_CHAR_K;
5092 case 0x1656: /* (green steel) */
5093 element = EL_STEEL_CHAR_L;
5096 case 0x1657: /* (green steel) */
5097 element = EL_STEEL_CHAR_M;
5100 case 0x1658: /* (green steel) */
5101 element = EL_STEEL_CHAR_N;
5104 case 0x1659: /* (green steel) */
5105 element = EL_STEEL_CHAR_O;
5108 case 0x165a: /* (green steel) */
5109 element = EL_STEEL_CHAR_P;
5112 case 0x165b: /* (green steel) */
5113 element = EL_STEEL_CHAR_Q;
5116 case 0x165c: /* (green steel) */
5117 element = EL_STEEL_CHAR_R;
5120 case 0x165d: /* (green steel) */
5121 element = EL_STEEL_CHAR_S;
5124 case 0x165e: /* (green steel) */
5125 element = EL_STEEL_CHAR_T;
5128 case 0x165f: /* (green steel) */
5129 element = EL_STEEL_CHAR_U;
5132 case 0x1660: /* (green steel) */
5133 element = EL_STEEL_CHAR_V;
5136 case 0x1661: /* (green steel) */
5137 element = EL_STEEL_CHAR_W;
5140 case 0x1662: /* (green steel) */
5141 element = EL_STEEL_CHAR_X;
5144 case 0x1663: /* (green steel) */
5145 element = EL_STEEL_CHAR_Y;
5148 case 0x1664: /* (green steel) */
5149 element = EL_STEEL_CHAR_Z;
5152 case 0x1665: /* (green steel) */
5153 element = EL_STEEL_CHAR_AUMLAUT;
5156 case 0x1666: /* (green steel) */
5157 element = EL_STEEL_CHAR_OUMLAUT;
5160 case 0x1667: /* (green steel) */
5161 element = EL_STEEL_CHAR_UUMLAUT;
5164 case 0x1668: /* (green steel) */
5165 element = EL_STEEL_CHAR_0;
5168 case 0x1669: /* (green steel) */
5169 element = EL_STEEL_CHAR_1;
5172 case 0x166a: /* (green steel) */
5173 element = EL_STEEL_CHAR_2;
5176 case 0x166b: /* (green steel) */
5177 element = EL_STEEL_CHAR_3;
5180 case 0x166c: /* (green steel) */
5181 element = EL_STEEL_CHAR_4;
5184 case 0x166d: /* (green steel) */
5185 element = EL_STEEL_CHAR_5;
5188 case 0x166e: /* (green steel) */
5189 element = EL_STEEL_CHAR_6;
5192 case 0x166f: /* (green steel) */
5193 element = EL_STEEL_CHAR_7;
5196 case 0x1670: /* (green steel) */
5197 element = EL_STEEL_CHAR_8;
5200 case 0x1671: /* (green steel) */
5201 element = EL_STEEL_CHAR_9;
5204 case 0x1672: /* (green steel) */
5205 element = EL_STEEL_CHAR_PERIOD;
5208 case 0x1673: /* (green steel) */
5209 element = EL_STEEL_CHAR_EXCLAM;
5212 case 0x1674: /* (green steel) */
5213 element = EL_STEEL_CHAR_COLON;
5216 case 0x1675: /* (green steel) */
5217 element = EL_STEEL_CHAR_LESS;
5220 case 0x1676: /* (green steel) */
5221 element = EL_STEEL_CHAR_GREATER;
5224 case 0x1677: /* (green steel) */
5225 element = EL_STEEL_CHAR_QUESTION;
5228 case 0x1678: /* (green steel) */
5229 element = EL_STEEL_CHAR_COPYRIGHT;
5232 case 0x1679: /* (green steel) */
5233 element = EL_STEEL_CHAR_UP;
5236 case 0x167a: /* (green steel) */
5237 element = EL_STEEL_CHAR_DOWN;
5240 case 0x167b: /* (green steel) */
5241 element = EL_STEEL_CHAR_BUTTON;
5244 case 0x167c: /* (green steel) */
5245 element = EL_STEEL_CHAR_PLUS;
5248 case 0x167d: /* (green steel) */
5249 element = EL_STEEL_CHAR_MINUS;
5252 case 0x167e: /* (green steel) */
5253 element = EL_STEEL_CHAR_APOSTROPHE;
5256 case 0x167f: /* (green steel) */
5257 element = EL_STEEL_CHAR_PARENLEFT;
5260 case 0x1680: /* (green steel) */
5261 element = EL_STEEL_CHAR_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_DC_STEELWALL_1_LEFT;
5352 case 0x1697: /* steel wall (bottom) */
5353 element = EL_DC_STEELWALL_1_BOTTOM;
5356 case 0x1698: /* steel wall (right) */
5357 element = EL_DC_STEELWALL_1_RIGHT;
5360 case 0x1699: /* steel wall (top) */
5361 element = EL_DC_STEELWALL_1_TOP;
5364 case 0x169a: /* steel wall (left/bottom) */
5365 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5368 case 0x169b: /* steel wall (right/bottom) */
5369 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5372 case 0x169c: /* steel wall (right/top) */
5373 element = EL_DC_STEELWALL_1_TOPRIGHT;
5376 case 0x169d: /* steel wall (left/top) */
5377 element = EL_DC_STEELWALL_1_TOPLEFT;
5380 case 0x169e: /* steel wall (right/bottom small) */
5381 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5384 case 0x169f: /* steel wall (left/bottom small) */
5385 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5388 case 0x16a0: /* steel wall (right/top small) */
5389 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5392 case 0x16a1: /* steel wall (left/top small) */
5393 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5396 case 0x16a2: /* steel wall (left/right) */
5397 element = EL_DC_STEELWALL_1_VERTICAL;
5400 case 0x16a3: /* steel wall (top/bottom) */
5401 element = EL_DC_STEELWALL_1_HORIZONTAL;
5404 case 0x16a4: /* steel wall 2 (left end) */
5405 element = EL_DC_STEELWALL_2_LEFT;
5408 case 0x16a5: /* steel wall 2 (right end) */
5409 element = EL_DC_STEELWALL_2_RIGHT;
5412 case 0x16a6: /* steel wall 2 (top end) */
5413 element = EL_DC_STEELWALL_2_TOP;
5416 case 0x16a7: /* steel wall 2 (bottom end) */
5417 element = EL_DC_STEELWALL_2_BOTTOM;
5420 case 0x16a8: /* steel wall 2 (left/right) */
5421 element = EL_DC_STEELWALL_2_HORIZONTAL;
5424 case 0x16a9: /* steel wall 2 (up/down) */
5425 element = EL_DC_STEELWALL_2_VERTICAL;
5428 case 0x16aa: /* steel wall 2 (mid) */
5429 element = EL_DC_STEELWALL_2_MIDDLE;
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_NO_ENTRY;
5457 element = EL_SIGN_HEART;
5461 element = EL_SIGN_GIVE_WAY;
5465 element = EL_SIGN_ENTRY_FORBIDDEN;
5469 element = EL_SIGN_EMERGENCY_EXIT;
5473 element = EL_SIGN_YIN_YANG;
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);
5699 /* Diamond Caves II levels are always surrounded by indestructible wall, but
5700 not necessarily in a rectangular way -- fill with invisible steel wall */
5702 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5705 if ((x == 0 || x == level->fieldx - 1 ||
5706 y == 0 || y == level->fieldy - 1) &&
5707 level->field[x][y] == EL_EMPTY)
5708 level->field[x][y] = EL_INVISIBLE_STEELWALL;
5710 if ((x == 0 || x == level->fieldx - 1 ||
5711 y == 0 || y == level->fieldy - 1) &&
5712 level->field[x][y] == EL_EMPTY)
5713 FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
5714 level->field, level->fieldx, level->fieldy);
5720 /* ------------------------------------------------------------------------- */
5721 /* functions for loading generic level */
5722 /* ------------------------------------------------------------------------- */
5724 void LoadLevelFromFileInfo(struct LevelInfo *level,
5725 struct LevelFileInfo *level_file_info)
5727 /* always start with reliable default values */
5728 setLevelInfoToDefaults(level);
5730 switch (level_file_info->type)
5732 case LEVEL_FILE_TYPE_RND:
5733 LoadLevelFromFileInfo_RND(level, level_file_info);
5736 case LEVEL_FILE_TYPE_EM:
5737 LoadLevelFromFileInfo_EM(level, level_file_info);
5738 level->game_engine_type = GAME_ENGINE_TYPE_EM;
5741 case LEVEL_FILE_TYPE_SP:
5742 LoadLevelFromFileInfo_SP(level, level_file_info);
5745 case LEVEL_FILE_TYPE_DC:
5746 LoadLevelFromFileInfo_DC(level, level_file_info);
5750 LoadLevelFromFileInfo_RND(level, level_file_info);
5754 /* if level file is invalid, restore level structure to default values */
5755 if (level->no_valid_file)
5756 setLevelInfoToDefaults(level);
5758 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
5759 level->game_engine_type = GAME_ENGINE_TYPE_RND;
5761 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
5762 CopyNativeLevel_Native_to_RND(level);
5765 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
5767 static struct LevelFileInfo level_file_info;
5769 /* always start with reliable default values */
5770 setFileInfoToDefaults(&level_file_info);
5772 level_file_info.nr = 0; /* unknown level number */
5773 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
5774 level_file_info.filename = filename;
5776 LoadLevelFromFileInfo(level, &level_file_info);
5779 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
5783 if (leveldir_current == NULL) /* only when dumping level */
5786 /* all engine modifications also valid for levels which use latest engine */
5787 if (level->game_version < VERSION_IDENT(3,2,0,5))
5789 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
5790 level->score[SC_TIME_BONUS] /= 10;
5794 leveldir_current->latest_engine = TRUE; /* !!! TEST ONLY !!! */
5797 if (leveldir_current->latest_engine)
5799 /* ---------- use latest game engine ----------------------------------- */
5801 /* For all levels which are forced to use the latest game engine version
5802 (normally all but user contributed, private and undefined levels), set
5803 the game engine version to the actual version; this allows for actual
5804 corrections in the game engine to take effect for existing, converted
5805 levels (from "classic" or other existing games) to make the emulation
5806 of the corresponding game more accurate, while (hopefully) not breaking
5807 existing levels created from other players. */
5809 level->game_version = GAME_VERSION_ACTUAL;
5811 /* Set special EM style gems behaviour: EM style gems slip down from
5812 normal, steel and growing wall. As this is a more fundamental change,
5813 it seems better to set the default behaviour to "off" (as it is more
5814 natural) and make it configurable in the level editor (as a property
5815 of gem style elements). Already existing converted levels (neither
5816 private nor contributed levels) are changed to the new behaviour. */
5818 if (level->file_version < FILE_VERSION_2_0)
5819 level->em_slippery_gems = TRUE;
5824 /* ---------- use game engine the level was created with ----------------- */
5826 /* For all levels which are not forced to use the latest game engine
5827 version (normally user contributed, private and undefined levels),
5828 use the version of the game engine the levels were created for.
5830 Since 2.0.1, the game engine version is now directly stored
5831 in the level file (chunk "VERS"), so there is no need anymore
5832 to set the game version from the file version (except for old,
5833 pre-2.0 levels, where the game version is still taken from the
5834 file format version used to store the level -- see above). */
5836 /* player was faster than enemies in 1.0.0 and before */
5837 if (level->file_version == FILE_VERSION_1_0)
5838 for (i = 0; i < MAX_PLAYERS; i++)
5839 level->initial_player_stepsize[i] = STEPSIZE_FAST;
5841 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
5842 if (level->game_version == VERSION_IDENT(2,0,1,0))
5843 level->em_slippery_gems = TRUE;
5845 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
5846 if (level->game_version < VERSION_IDENT(2,2,0,0))
5847 level->use_spring_bug = TRUE;
5849 if (level->game_version < VERSION_IDENT(3,2,0,5))
5851 /* time orb caused limited time in endless time levels before 3.2.0-5 */
5852 level->use_time_orb_bug = TRUE;
5854 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
5855 level->block_snap_field = FALSE;
5857 /* extra time score was same value as time left score before 3.2.0-5 */
5858 level->extra_time_score = level->score[SC_TIME_BONUS];
5861 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
5862 level->score[SC_TIME_BONUS] /= 10;
5866 if (level->game_version < VERSION_IDENT(3,2,0,7))
5868 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
5869 level->continuous_snapping = FALSE;
5872 /* only few elements were able to actively move into acid before 3.1.0 */
5873 /* trigger settings did not exist before 3.1.0; set to default "any" */
5874 if (level->game_version < VERSION_IDENT(3,1,0,0))
5876 /* correct "can move into acid" settings (all zero in old levels) */
5878 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
5879 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
5881 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
5882 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
5883 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
5884 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
5886 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5887 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
5889 /* correct trigger settings (stored as zero == "none" in old levels) */
5891 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5893 int element = EL_CUSTOM_START + i;
5894 struct ElementInfo *ei = &element_info[element];
5896 for (j = 0; j < ei->num_change_pages; j++)
5898 struct ElementChangeInfo *change = &ei->change_page[j];
5900 change->trigger_player = CH_PLAYER_ANY;
5901 change->trigger_page = CH_PAGE_ANY;
5906 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
5908 int element = EL_CUSTOM_START + 255;
5909 struct ElementInfo *ei = &element_info[element];
5910 struct ElementChangeInfo *change = &ei->change_page[0];
5912 /* This is needed to fix a problem that was caused by a bugfix in function
5913 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
5914 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
5915 not replace walkable elements, but instead just placed the player on it,
5916 without placing the Sokoban field under the player). Unfortunately, this
5917 breaks "Snake Bite" style levels when the snake is halfway through a door
5918 that just closes (the snake head is still alive and can be moved in this
5919 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
5920 player (without Sokoban element) which then gets killed as designed). */
5922 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
5923 strncmp(ei->description, "pause b4 death", 14) == 0) &&
5924 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
5925 change->target_element = EL_PLAYER_1;
5929 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
5933 /* map custom element change events that have changed in newer versions
5934 (these following values were accidentally changed in version 3.0.1)
5935 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
5936 if (level->game_version <= VERSION_IDENT(3,0,0,0))
5938 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5940 int element = EL_CUSTOM_START + i;
5942 /* order of checking and copying events to be mapped is important */
5943 /* (do not change the start and end value -- they are constant) */
5944 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
5946 if (HAS_CHANGE_EVENT(element, j - 2))
5948 SET_CHANGE_EVENT(element, j - 2, FALSE);
5949 SET_CHANGE_EVENT(element, j, TRUE);
5953 /* order of checking and copying events to be mapped is important */
5954 /* (do not change the start and end value -- they are constant) */
5955 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
5957 if (HAS_CHANGE_EVENT(element, j - 1))
5959 SET_CHANGE_EVENT(element, j - 1, FALSE);
5960 SET_CHANGE_EVENT(element, j, TRUE);
5966 /* initialize "can_change" field for old levels with only one change page */
5967 if (level->game_version <= VERSION_IDENT(3,0,2,0))
5969 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5971 int element = EL_CUSTOM_START + i;
5973 if (CAN_CHANGE(element))
5974 element_info[element].change->can_change = TRUE;
5978 /* correct custom element values (for old levels without these options) */
5979 if (level->game_version < VERSION_IDENT(3,1,1,0))
5981 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5983 int element = EL_CUSTOM_START + i;
5984 struct ElementInfo *ei = &element_info[element];
5986 if (ei->access_direction == MV_NO_DIRECTION)
5987 ei->access_direction = MV_ALL_DIRECTIONS;
5991 /* correct custom element values (fix invalid values for all versions) */
5994 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5996 int element = EL_CUSTOM_START + i;
5997 struct ElementInfo *ei = &element_info[element];
5999 for (j = 0; j < ei->num_change_pages; j++)
6001 struct ElementChangeInfo *change = &ei->change_page[j];
6003 if (change->trigger_player == CH_PLAYER_NONE)
6004 change->trigger_player = CH_PLAYER_ANY;
6006 if (change->trigger_side == CH_SIDE_NONE)
6007 change->trigger_side = CH_SIDE_ANY;
6012 /* initialize "can_explode" field for old levels which did not store this */
6013 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6014 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6016 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6018 int element = EL_CUSTOM_START + i;
6020 if (EXPLODES_1X1_OLD(element))
6021 element_info[element].explosion_type = EXPLODES_1X1;
6023 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6024 EXPLODES_SMASHED(element) ||
6025 EXPLODES_IMPACT(element)));
6029 /* correct previously hard-coded move delay values for maze runner style */
6030 if (level->game_version < VERSION_IDENT(3,1,1,0))
6032 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6034 int element = EL_CUSTOM_START + i;
6036 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6038 /* previously hard-coded and therefore ignored */
6039 element_info[element].move_delay_fixed = 9;
6040 element_info[element].move_delay_random = 0;
6045 /* map elements that have changed in newer versions */
6046 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6047 level->game_version);
6048 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6049 for (x = 0; x < 3; x++)
6050 for (y = 0; y < 3; y++)
6051 level->yamyam_content[i].e[x][y] =
6052 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6053 level->game_version);
6055 /* initialize element properties for level editor etc. */
6056 InitElementPropertiesEngine(level->game_version);
6057 InitElementPropertiesAfterLoading(level->game_version);
6060 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6064 /* map elements that have changed in newer versions */
6065 for (y = 0; y < level->fieldy; y++)
6066 for (x = 0; x < level->fieldx; x++)
6067 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6068 level->game_version);
6070 /* copy elements to runtime playfield array */
6071 for (x = 0; x < MAX_LEV_FIELDX; x++)
6072 for (y = 0; y < MAX_LEV_FIELDY; y++)
6073 Feld[x][y] = level->field[x][y];
6075 /* initialize level size variables for faster access */
6076 lev_fieldx = level->fieldx;
6077 lev_fieldy = level->fieldy;
6079 /* determine border element for this level */
6083 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6085 struct LevelFileInfo *level_file_info = &level->file_info;
6087 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6088 CopyNativeLevel_RND_to_Native(level);
6091 void LoadLevelTemplate(int nr)
6095 setLevelFileInfo(&level_template.file_info, nr);
6096 filename = level_template.file_info.filename;
6098 LoadLevelFromFileInfo(&level_template, &level_template.file_info);
6100 LoadLevel_InitVersion(&level_template, filename);
6101 LoadLevel_InitElements(&level_template, filename);
6103 ActivateLevelTemplate();
6106 void LoadLevel(int nr)
6110 setLevelFileInfo(&level.file_info, nr);
6111 filename = level.file_info.filename;
6113 LoadLevelFromFileInfo(&level, &level.file_info);
6115 if (level.use_custom_template)
6116 LoadLevelTemplate(-1);
6118 LoadLevel_InitVersion(&level, filename);
6119 LoadLevel_InitElements(&level, filename);
6120 LoadLevel_InitPlayfield(&level, filename);
6122 LoadLevel_InitNativeEngines(&level, filename);
6125 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6129 chunk_size += putFileVersion(file, level->file_version);
6130 chunk_size += putFileVersion(file, level->game_version);
6135 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6139 chunk_size += putFile16BitBE(file, level->creation_date.year);
6140 chunk_size += putFile8Bit(file, level->creation_date.month);
6141 chunk_size += putFile8Bit(file, level->creation_date.day);
6147 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6151 putFile8Bit(file, level->fieldx);
6152 putFile8Bit(file, level->fieldy);
6154 putFile16BitBE(file, level->time);
6155 putFile16BitBE(file, level->gems_needed);
6157 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6158 putFile8Bit(file, level->name[i]);
6160 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6161 putFile8Bit(file, level->score[i]);
6163 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6164 for (y = 0; y < 3; y++)
6165 for (x = 0; x < 3; x++)
6166 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6167 level->yamyam_content[i].e[x][y]));
6168 putFile8Bit(file, level->amoeba_speed);
6169 putFile8Bit(file, level->time_magic_wall);
6170 putFile8Bit(file, level->time_wheel);
6171 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6172 level->amoeba_content));
6173 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6174 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6175 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6176 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6178 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6180 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6181 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6182 putFile32BitBE(file, level->can_move_into_acid_bits);
6183 putFile8Bit(file, level->dont_collide_with_bits);
6185 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6186 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6188 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6189 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6190 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6192 putFile8Bit(file, level->game_engine_type);
6194 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6198 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6203 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6204 chunk_size += putFile8Bit(file, level->name[i]);
6209 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6214 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6215 chunk_size += putFile8Bit(file, level->author[i]);
6221 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6226 for (y = 0; y < level->fieldy; y++)
6227 for (x = 0; x < level->fieldx; x++)
6228 if (level->encoding_16bit_field)
6229 chunk_size += putFile16BitBE(file, level->field[x][y]);
6231 chunk_size += putFile8Bit(file, level->field[x][y]);
6237 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6242 for (y = 0; y < level->fieldy; y++)
6243 for (x = 0; x < level->fieldx; x++)
6244 chunk_size += putFile16BitBE(file, level->field[x][y]);
6250 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6254 putFile8Bit(file, EL_YAMYAM);
6255 putFile8Bit(file, level->num_yamyam_contents);
6256 putFile8Bit(file, 0);
6257 putFile8Bit(file, 0);
6259 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6260 for (y = 0; y < 3; y++)
6261 for (x = 0; x < 3; x++)
6262 if (level->encoding_16bit_field)
6263 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6265 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6270 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6273 int num_contents, content_xsize, content_ysize;
6274 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6276 if (element == EL_YAMYAM)
6278 num_contents = level->num_yamyam_contents;
6282 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6283 for (y = 0; y < 3; y++)
6284 for (x = 0; x < 3; x++)
6285 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6287 else if (element == EL_BD_AMOEBA)
6293 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6294 for (y = 0; y < 3; y++)
6295 for (x = 0; x < 3; x++)
6296 content_array[i][x][y] = EL_EMPTY;
6297 content_array[0][0][0] = level->amoeba_content;
6301 /* chunk header already written -- write empty chunk data */
6302 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6304 Error(ERR_WARN, "cannot save content for element '%d'", element);
6308 putFile16BitBE(file, element);
6309 putFile8Bit(file, num_contents);
6310 putFile8Bit(file, content_xsize);
6311 putFile8Bit(file, content_ysize);
6313 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6315 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6316 for (y = 0; y < 3; y++)
6317 for (x = 0; x < 3; x++)
6318 putFile16BitBE(file, content_array[i][x][y]);
6323 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6325 int envelope_nr = element - EL_ENVELOPE_1;
6326 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6330 chunk_size += putFile16BitBE(file, element);
6331 chunk_size += putFile16BitBE(file, envelope_len);
6332 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6333 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6335 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6336 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6338 for (i = 0; i < envelope_len; i++)
6339 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6346 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6347 int num_changed_custom_elements)
6351 putFile16BitBE(file, num_changed_custom_elements);
6353 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6355 int element = EL_CUSTOM_START + i;
6357 struct ElementInfo *ei = &element_info[element];
6359 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6361 if (check < num_changed_custom_elements)
6363 putFile16BitBE(file, element);
6364 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6371 if (check != num_changed_custom_elements) /* should not happen */
6372 Error(ERR_WARN, "inconsistent number of custom element properties");
6377 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6378 int num_changed_custom_elements)
6382 putFile16BitBE(file, num_changed_custom_elements);
6384 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6386 int element = EL_CUSTOM_START + i;
6388 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6390 if (check < num_changed_custom_elements)
6392 putFile16BitBE(file, element);
6393 putFile16BitBE(file, element_info[element].change->target_element);
6400 if (check != num_changed_custom_elements) /* should not happen */
6401 Error(ERR_WARN, "inconsistent number of custom target elements");
6406 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6407 int num_changed_custom_elements)
6409 int i, j, x, y, check = 0;
6411 putFile16BitBE(file, num_changed_custom_elements);
6413 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6415 int element = EL_CUSTOM_START + i;
6416 struct ElementInfo *ei = &element_info[element];
6418 if (ei->modified_settings)
6420 if (check < num_changed_custom_elements)
6422 putFile16BitBE(file, element);
6424 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6425 putFile8Bit(file, ei->description[j]);
6427 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6429 /* some free bytes for future properties and padding */
6430 WriteUnusedBytesToFile(file, 7);
6432 putFile8Bit(file, ei->use_gfx_element);
6433 putFile16BitBE(file, ei->gfx_element);
6435 putFile8Bit(file, ei->collect_score_initial);
6436 putFile8Bit(file, ei->collect_count_initial);
6438 putFile16BitBE(file, ei->push_delay_fixed);
6439 putFile16BitBE(file, ei->push_delay_random);
6440 putFile16BitBE(file, ei->move_delay_fixed);
6441 putFile16BitBE(file, ei->move_delay_random);
6443 putFile16BitBE(file, ei->move_pattern);
6444 putFile8Bit(file, ei->move_direction_initial);
6445 putFile8Bit(file, ei->move_stepsize);
6447 for (y = 0; y < 3; y++)
6448 for (x = 0; x < 3; x++)
6449 putFile16BitBE(file, ei->content.e[x][y]);
6451 putFile32BitBE(file, ei->change->events);
6453 putFile16BitBE(file, ei->change->target_element);
6455 putFile16BitBE(file, ei->change->delay_fixed);
6456 putFile16BitBE(file, ei->change->delay_random);
6457 putFile16BitBE(file, ei->change->delay_frames);
6459 putFile16BitBE(file, ei->change->trigger_element);
6461 putFile8Bit(file, ei->change->explode);
6462 putFile8Bit(file, ei->change->use_target_content);
6463 putFile8Bit(file, ei->change->only_if_complete);
6464 putFile8Bit(file, ei->change->use_random_replace);
6466 putFile8Bit(file, ei->change->random_percentage);
6467 putFile8Bit(file, ei->change->replace_when);
6469 for (y = 0; y < 3; y++)
6470 for (x = 0; x < 3; x++)
6471 putFile16BitBE(file, ei->change->content.e[x][y]);
6473 putFile8Bit(file, ei->slippery_type);
6475 /* some free bytes for future properties and padding */
6476 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6483 if (check != num_changed_custom_elements) /* should not happen */
6484 Error(ERR_WARN, "inconsistent number of custom element properties");
6489 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6491 struct ElementInfo *ei = &element_info[element];
6494 /* ---------- custom element base property values (96 bytes) ------------- */
6496 putFile16BitBE(file, element);
6498 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6499 putFile8Bit(file, ei->description[i]);
6501 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6503 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
6505 putFile8Bit(file, ei->num_change_pages);
6507 putFile16BitBE(file, ei->ce_value_fixed_initial);
6508 putFile16BitBE(file, ei->ce_value_random_initial);
6509 putFile8Bit(file, ei->use_last_ce_value);
6511 putFile8Bit(file, ei->use_gfx_element);
6512 putFile16BitBE(file, ei->gfx_element);
6514 putFile8Bit(file, ei->collect_score_initial);
6515 putFile8Bit(file, ei->collect_count_initial);
6517 putFile8Bit(file, ei->drop_delay_fixed);
6518 putFile8Bit(file, ei->push_delay_fixed);
6519 putFile8Bit(file, ei->drop_delay_random);
6520 putFile8Bit(file, ei->push_delay_random);
6521 putFile16BitBE(file, ei->move_delay_fixed);
6522 putFile16BitBE(file, ei->move_delay_random);
6524 /* bits 0 - 15 of "move_pattern" ... */
6525 putFile16BitBE(file, ei->move_pattern & 0xffff);
6526 putFile8Bit(file, ei->move_direction_initial);
6527 putFile8Bit(file, ei->move_stepsize);
6529 putFile8Bit(file, ei->slippery_type);
6531 for (y = 0; y < 3; y++)
6532 for (x = 0; x < 3; x++)
6533 putFile16BitBE(file, ei->content.e[x][y]);
6535 putFile16BitBE(file, ei->move_enter_element);
6536 putFile16BitBE(file, ei->move_leave_element);
6537 putFile8Bit(file, ei->move_leave_type);
6539 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
6540 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
6542 putFile8Bit(file, ei->access_direction);
6544 putFile8Bit(file, ei->explosion_delay);
6545 putFile8Bit(file, ei->ignition_delay);
6546 putFile8Bit(file, ei->explosion_type);
6548 /* some free bytes for future custom property values and padding */
6549 WriteUnusedBytesToFile(file, 1);
6551 /* ---------- change page property values (48 bytes) --------------------- */
6553 for (i = 0; i < ei->num_change_pages; i++)
6555 struct ElementChangeInfo *change = &ei->change_page[i];
6556 unsigned int event_bits;
6558 /* bits 0 - 31 of "has_event[]" ... */
6560 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
6561 if (change->has_event[j])
6562 event_bits |= (1 << j);
6563 putFile32BitBE(file, event_bits);
6565 putFile16BitBE(file, change->target_element);
6567 putFile16BitBE(file, change->delay_fixed);
6568 putFile16BitBE(file, change->delay_random);
6569 putFile16BitBE(file, change->delay_frames);
6571 putFile16BitBE(file, change->trigger_element);
6573 putFile8Bit(file, change->explode);
6574 putFile8Bit(file, change->use_target_content);
6575 putFile8Bit(file, change->only_if_complete);
6576 putFile8Bit(file, change->use_random_replace);
6578 putFile8Bit(file, change->random_percentage);
6579 putFile8Bit(file, change->replace_when);
6581 for (y = 0; y < 3; y++)
6582 for (x = 0; x < 3; x++)
6583 putFile16BitBE(file, change->target_content.e[x][y]);
6585 putFile8Bit(file, change->can_change);
6587 putFile8Bit(file, change->trigger_side);
6589 putFile8Bit(file, change->trigger_player);
6590 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
6591 log_2(change->trigger_page)));
6593 putFile8Bit(file, change->has_action);
6594 putFile8Bit(file, change->action_type);
6595 putFile8Bit(file, change->action_mode);
6596 putFile16BitBE(file, change->action_arg);
6598 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
6600 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
6601 if (change->has_event[j])
6602 event_bits |= (1 << (j - 32));
6603 putFile8Bit(file, event_bits);
6609 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
6611 struct ElementInfo *ei = &element_info[element];
6612 struct ElementGroupInfo *group = ei->group;
6615 putFile16BitBE(file, element);
6617 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6618 putFile8Bit(file, ei->description[i]);
6620 putFile8Bit(file, group->num_elements);
6622 putFile8Bit(file, ei->use_gfx_element);
6623 putFile16BitBE(file, ei->gfx_element);
6625 putFile8Bit(file, group->choice_mode);
6627 /* some free bytes for future values and padding */
6628 WriteUnusedBytesToFile(file, 3);
6630 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
6631 putFile16BitBE(file, group->element[i]);
6635 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
6636 boolean write_element)
6638 int save_type = entry->save_type;
6639 int data_type = entry->data_type;
6640 int conf_type = entry->conf_type;
6641 int byte_mask = conf_type & CONF_MASK_BYTES;
6642 int element = entry->element;
6643 int default_value = entry->default_value;
6645 boolean modified = FALSE;
6647 if (byte_mask != CONF_MASK_MULTI_BYTES)
6649 void *value_ptr = entry->value;
6650 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
6653 /* check if any settings have been modified before saving them */
6654 if (value != default_value)
6657 /* do not save if explicitly told or if unmodified default settings */
6658 if ((save_type == SAVE_CONF_NEVER) ||
6659 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6663 num_bytes += putFile16BitBE(file, element);
6665 num_bytes += putFile8Bit(file, conf_type);
6666 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
6667 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
6668 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
6671 else if (data_type == TYPE_STRING)
6673 char *default_string = entry->default_string;
6674 char *string = (char *)(entry->value);
6675 int string_length = strlen(string);
6678 /* check if any settings have been modified before saving them */
6679 if (!strEqual(string, default_string))
6682 /* do not save if explicitly told or if unmodified default settings */
6683 if ((save_type == SAVE_CONF_NEVER) ||
6684 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6688 num_bytes += putFile16BitBE(file, element);
6690 num_bytes += putFile8Bit(file, conf_type);
6691 num_bytes += putFile16BitBE(file, string_length);
6693 for (i = 0; i < string_length; i++)
6694 num_bytes += putFile8Bit(file, string[i]);
6696 else if (data_type == TYPE_ELEMENT_LIST)
6698 int *element_array = (int *)(entry->value);
6699 int num_elements = *(int *)(entry->num_entities);
6702 /* check if any settings have been modified before saving them */
6703 for (i = 0; i < num_elements; i++)
6704 if (element_array[i] != default_value)
6707 /* do not save if explicitly told or if unmodified default settings */
6708 if ((save_type == SAVE_CONF_NEVER) ||
6709 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6713 num_bytes += putFile16BitBE(file, element);
6715 num_bytes += putFile8Bit(file, conf_type);
6716 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
6718 for (i = 0; i < num_elements; i++)
6719 num_bytes += putFile16BitBE(file, element_array[i]);
6721 else if (data_type == TYPE_CONTENT_LIST)
6723 struct Content *content = (struct Content *)(entry->value);
6724 int num_contents = *(int *)(entry->num_entities);
6727 /* check if any settings have been modified before saving them */
6728 for (i = 0; i < num_contents; i++)
6729 for (y = 0; y < 3; y++)
6730 for (x = 0; x < 3; x++)
6731 if (content[i].e[x][y] != default_value)
6734 /* do not save if explicitly told or if unmodified default settings */
6735 if ((save_type == SAVE_CONF_NEVER) ||
6736 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6740 num_bytes += putFile16BitBE(file, element);
6742 num_bytes += putFile8Bit(file, conf_type);
6743 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
6745 for (i = 0; i < num_contents; i++)
6746 for (y = 0; y < 3; y++)
6747 for (x = 0; x < 3; x++)
6748 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
6754 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
6759 li = *level; /* copy level data into temporary buffer */
6761 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
6762 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
6767 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
6772 li = *level; /* copy level data into temporary buffer */
6774 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
6775 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
6780 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
6782 int envelope_nr = element - EL_ENVELOPE_1;
6786 chunk_size += putFile16BitBE(file, element);
6788 /* copy envelope data into temporary buffer */
6789 xx_envelope = level->envelope[envelope_nr];
6791 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
6792 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
6797 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
6799 struct ElementInfo *ei = &element_info[element];
6803 chunk_size += putFile16BitBE(file, element);
6805 xx_ei = *ei; /* copy element data into temporary buffer */
6807 /* set default description string for this specific element */
6808 strcpy(xx_default_description, getDefaultElementDescription(ei));
6811 /* set (fixed) number of content areas (may be wrong by broken level file) */
6812 /* (this is now directly corrected for broken level files after loading) */
6813 xx_num_contents = 1;
6816 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
6817 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
6819 for (i = 0; i < ei->num_change_pages; i++)
6821 struct ElementChangeInfo *change = &ei->change_page[i];
6823 xx_current_change_page = i;
6825 xx_change = *change; /* copy change data into temporary buffer */
6828 setEventBitsFromEventFlags(change);
6830 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
6831 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
6838 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
6840 struct ElementInfo *ei = &element_info[element];
6841 struct ElementGroupInfo *group = ei->group;
6845 chunk_size += putFile16BitBE(file, element);
6847 xx_ei = *ei; /* copy element data into temporary buffer */
6848 xx_group = *group; /* copy group data into temporary buffer */
6850 /* set default description string for this specific element */
6851 strcpy(xx_default_description, getDefaultElementDescription(ei));
6853 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
6854 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
6859 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
6865 if (!(file = fopen(filename, MODE_WRITE)))
6867 Error(ERR_WARN, "cannot save level file '%s'", filename);
6871 level->file_version = FILE_VERSION_ACTUAL;
6872 level->game_version = GAME_VERSION_ACTUAL;
6874 level->creation_date = getCurrentDate();
6876 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
6877 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
6879 chunk_size = SaveLevel_VERS(NULL, level);
6880 putFileChunkBE(file, "VERS", chunk_size);
6881 SaveLevel_VERS(file, level);
6883 chunk_size = SaveLevel_DATE(NULL, level);
6884 putFileChunkBE(file, "DATE", chunk_size);
6885 SaveLevel_DATE(file, level);
6887 chunk_size = SaveLevel_NAME(NULL, level);
6888 putFileChunkBE(file, "NAME", chunk_size);
6889 SaveLevel_NAME(file, level);
6891 chunk_size = SaveLevel_AUTH(NULL, level);
6892 putFileChunkBE(file, "AUTH", chunk_size);
6893 SaveLevel_AUTH(file, level);
6895 chunk_size = SaveLevel_INFO(NULL, level);
6896 putFileChunkBE(file, "INFO", chunk_size);
6897 SaveLevel_INFO(file, level);
6899 chunk_size = SaveLevel_BODY(NULL, level);
6900 putFileChunkBE(file, "BODY", chunk_size);
6901 SaveLevel_BODY(file, level);
6903 chunk_size = SaveLevel_ELEM(NULL, level);
6904 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
6906 putFileChunkBE(file, "ELEM", chunk_size);
6907 SaveLevel_ELEM(file, level);
6910 for (i = 0; i < NUM_ENVELOPES; i++)
6912 int element = EL_ENVELOPE_1 + i;
6914 chunk_size = SaveLevel_NOTE(NULL, level, element);
6915 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
6917 putFileChunkBE(file, "NOTE", chunk_size);
6918 SaveLevel_NOTE(file, level, element);
6922 /* if not using template level, check for non-default custom/group elements */
6923 if (!level->use_custom_template)
6925 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6927 int element = EL_CUSTOM_START + i;
6929 chunk_size = SaveLevel_CUSX(NULL, level, element);
6930 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
6932 putFileChunkBE(file, "CUSX", chunk_size);
6933 SaveLevel_CUSX(file, level, element);
6937 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
6939 int element = EL_GROUP_START + i;
6941 chunk_size = SaveLevel_GRPX(NULL, level, element);
6942 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
6944 putFileChunkBE(file, "GRPX", chunk_size);
6945 SaveLevel_GRPX(file, level, element);
6952 SetFilePermissions(filename, PERMS_PRIVATE);
6955 void SaveLevel(int nr)
6957 char *filename = getDefaultLevelFilename(nr);
6959 SaveLevelFromFilename(&level, filename);
6962 void SaveLevelTemplate()
6964 char *filename = getDefaultLevelFilename(-1);
6966 SaveLevelFromFilename(&level, filename);
6969 boolean SaveLevelChecked(int nr)
6971 char *filename = getDefaultLevelFilename(nr);
6972 boolean new_level = !fileExists(filename);
6973 boolean level_saved = FALSE;
6975 if (new_level || Request("Save this level and kill the old ?", REQ_ASK))
6980 Request("Level saved !", REQ_CONFIRM);
6988 void DumpLevel(struct LevelInfo *level)
6990 if (level->no_valid_file)
6992 Error(ERR_WARN, "cannot dump -- no valid level file found");
6997 printf_line("-", 79);
6998 printf("Level xxx (file version %08d, game version %08d)\n",
6999 level->file_version, level->game_version);
7000 printf_line("-", 79);
7002 printf("Level author: '%s'\n", level->author);
7003 printf("Level title: '%s'\n", level->name);
7005 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7007 printf("Level time: %d seconds\n", level->time);
7008 printf("Gems needed: %d\n", level->gems_needed);
7010 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
7011 printf("Time for wheel: %d seconds\n", level->time_wheel);
7012 printf("Time for light: %d seconds\n", level->time_light);
7013 printf("Time for timegate: %d seconds\n", level->time_timegate);
7015 printf("Amoeba speed: %d\n", level->amoeba_speed);
7018 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7019 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7020 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7021 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7022 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7024 printf_line("-", 79);
7028 /* ========================================================================= */
7029 /* tape file functions */
7030 /* ========================================================================= */
7032 static void setTapeInfoToDefaults()
7036 /* always start with reliable default values (empty tape) */
7039 /* default values (also for pre-1.2 tapes) with only the first player */
7040 tape.player_participates[0] = TRUE;
7041 for (i = 1; i < MAX_PLAYERS; i++)
7042 tape.player_participates[i] = FALSE;
7044 /* at least one (default: the first) player participates in every tape */
7045 tape.num_participating_players = 1;
7047 tape.level_nr = level_nr;
7049 tape.changed = FALSE;
7051 tape.recording = FALSE;
7052 tape.playing = FALSE;
7053 tape.pausing = FALSE;
7055 tape.no_valid_file = FALSE;
7058 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
7060 tape->file_version = getFileVersion(file);
7061 tape->game_version = getFileVersion(file);
7066 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
7070 tape->random_seed = getFile32BitBE(file);
7071 tape->date = getFile32BitBE(file);
7072 tape->length = getFile32BitBE(file);
7074 /* read header fields that are new since version 1.2 */
7075 if (tape->file_version >= FILE_VERSION_1_2)
7077 byte store_participating_players = getFile8Bit(file);
7080 /* since version 1.2, tapes store which players participate in the tape */
7081 tape->num_participating_players = 0;
7082 for (i = 0; i < MAX_PLAYERS; i++)
7084 tape->player_participates[i] = FALSE;
7086 if (store_participating_players & (1 << i))
7088 tape->player_participates[i] = TRUE;
7089 tape->num_participating_players++;
7093 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7095 engine_version = getFileVersion(file);
7096 if (engine_version > 0)
7097 tape->engine_version = engine_version;
7099 tape->engine_version = tape->game_version;
7105 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
7107 int level_identifier_size;
7110 level_identifier_size = getFile16BitBE(file);
7112 tape->level_identifier =
7113 checked_realloc(tape->level_identifier, level_identifier_size);
7115 for (i = 0; i < level_identifier_size; i++)
7116 tape->level_identifier[i] = getFile8Bit(file);
7118 tape->level_nr = getFile16BitBE(file);
7120 chunk_size = 2 + level_identifier_size + 2;
7125 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
7128 int chunk_size_expected =
7129 (tape->num_participating_players + 1) * tape->length;
7131 if (chunk_size_expected != chunk_size)
7133 ReadUnusedBytesFromFile(file, chunk_size);
7134 return chunk_size_expected;
7137 for (i = 0; i < tape->length; i++)
7139 if (i >= MAX_TAPE_LEN)
7142 for (j = 0; j < MAX_PLAYERS; j++)
7144 tape->pos[i].action[j] = MV_NONE;
7146 if (tape->player_participates[j])
7147 tape->pos[i].action[j] = getFile8Bit(file);
7150 tape->pos[i].delay = getFile8Bit(file);
7152 if (tape->file_version == FILE_VERSION_1_0)
7154 /* eliminate possible diagonal moves in old tapes */
7155 /* this is only for backward compatibility */
7157 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7158 byte action = tape->pos[i].action[0];
7159 int k, num_moves = 0;
7161 for (k = 0; k<4; k++)
7163 if (action & joy_dir[k])
7165 tape->pos[i + num_moves].action[0] = joy_dir[k];
7167 tape->pos[i + num_moves].delay = 0;
7176 tape->length += num_moves;
7179 else if (tape->file_version < FILE_VERSION_2_0)
7181 /* convert pre-2.0 tapes to new tape format */
7183 if (tape->pos[i].delay > 1)
7186 tape->pos[i + 1] = tape->pos[i];
7187 tape->pos[i + 1].delay = 1;
7190 for (j = 0; j < MAX_PLAYERS; j++)
7191 tape->pos[i].action[j] = MV_NONE;
7192 tape->pos[i].delay--;
7203 if (i != tape->length)
7204 chunk_size = (tape->num_participating_players + 1) * i;
7209 void LoadTapeFromFilename(char *filename)
7211 char cookie[MAX_LINE_LEN];
7212 char chunk_name[CHUNK_ID_LEN + 1];
7216 /* always start with reliable default values */
7217 setTapeInfoToDefaults();
7219 if (!(file = fopen(filename, MODE_READ)))
7221 tape.no_valid_file = TRUE;
7226 getFileChunkBE(file, chunk_name, NULL);
7227 if (strEqual(chunk_name, "RND1"))
7229 getFile32BitBE(file); /* not used */
7231 getFileChunkBE(file, chunk_name, NULL);
7232 if (!strEqual(chunk_name, "TAPE"))
7234 tape.no_valid_file = TRUE;
7236 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7241 else /* check for pre-2.0 file format with cookie string */
7243 strcpy(cookie, chunk_name);
7244 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
7245 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7246 cookie[strlen(cookie) - 1] = '\0';
7248 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7250 tape.no_valid_file = TRUE;
7252 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7257 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7259 tape.no_valid_file = TRUE;
7261 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7266 /* pre-2.0 tape files have no game version, so use file version here */
7267 tape.game_version = tape.file_version;
7270 if (tape.file_version < FILE_VERSION_1_2)
7272 /* tape files from versions before 1.2.0 without chunk structure */
7273 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7274 LoadTape_BODY(file, 2 * tape.length, &tape);
7282 int (*loader)(FILE *, int, struct TapeInfo *);
7286 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7287 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7288 { "INFO", -1, LoadTape_INFO },
7289 { "BODY", -1, LoadTape_BODY },
7293 while (getFileChunkBE(file, chunk_name, &chunk_size))
7297 while (chunk_info[i].name != NULL &&
7298 !strEqual(chunk_name, chunk_info[i].name))
7301 if (chunk_info[i].name == NULL)
7303 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7304 chunk_name, filename);
7305 ReadUnusedBytesFromFile(file, chunk_size);
7307 else if (chunk_info[i].size != -1 &&
7308 chunk_info[i].size != chunk_size)
7310 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7311 chunk_size, chunk_name, filename);
7312 ReadUnusedBytesFromFile(file, chunk_size);
7316 /* call function to load this tape chunk */
7317 int chunk_size_expected =
7318 (chunk_info[i].loader)(file, chunk_size, &tape);
7320 /* the size of some chunks cannot be checked before reading other
7321 chunks first (like "HEAD" and "BODY") that contain some header
7322 information, so check them here */
7323 if (chunk_size_expected != chunk_size)
7325 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7326 chunk_size, chunk_name, filename);
7334 tape.length_seconds = GetTapeLength();
7337 printf("::: tape file version: %d\n", tape.file_version);
7338 printf("::: tape game version: %d\n", tape.game_version);
7339 printf("::: tape engine version: %d\n", tape.engine_version);
7343 void LoadTape(int nr)
7345 char *filename = getTapeFilename(nr);
7347 LoadTapeFromFilename(filename);
7350 void LoadSolutionTape(int nr)
7352 char *filename = getSolutionTapeFilename(nr);
7354 LoadTapeFromFilename(filename);
7357 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7359 putFileVersion(file, tape->file_version);
7360 putFileVersion(file, tape->game_version);
7363 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7366 byte store_participating_players = 0;
7368 /* set bits for participating players for compact storage */
7369 for (i = 0; i < MAX_PLAYERS; i++)
7370 if (tape->player_participates[i])
7371 store_participating_players |= (1 << i);
7373 putFile32BitBE(file, tape->random_seed);
7374 putFile32BitBE(file, tape->date);
7375 putFile32BitBE(file, tape->length);
7377 putFile8Bit(file, store_participating_players);
7379 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7380 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7382 putFileVersion(file, tape->engine_version);
7385 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7387 int level_identifier_size = strlen(tape->level_identifier) + 1;
7390 putFile16BitBE(file, level_identifier_size);
7392 for (i = 0; i < level_identifier_size; i++)
7393 putFile8Bit(file, tape->level_identifier[i]);
7395 putFile16BitBE(file, tape->level_nr);
7398 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7402 for (i = 0; i < tape->length; i++)
7404 for (j = 0; j < MAX_PLAYERS; j++)
7405 if (tape->player_participates[j])
7406 putFile8Bit(file, tape->pos[i].action[j]);
7408 putFile8Bit(file, tape->pos[i].delay);
7412 void SaveTape(int nr)
7414 char *filename = getTapeFilename(nr);
7417 boolean new_tape = TRUE;
7419 int num_participating_players = 0;
7420 int info_chunk_size;
7421 int body_chunk_size;
7424 InitTapeDirectory(leveldir_current->subdir);
7427 /* if a tape still exists, ask to overwrite it */
7428 if (fileExists(filename))
7431 if (!Request("Replace old tape ?", REQ_ASK))
7436 if (!(file = fopen(filename, MODE_WRITE)))
7438 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7442 tape.file_version = FILE_VERSION_ACTUAL;
7443 tape.game_version = GAME_VERSION_ACTUAL;
7445 /* count number of participating players */
7446 for (i = 0; i < MAX_PLAYERS; i++)
7447 if (tape.player_participates[i])
7448 num_participating_players++;
7450 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7451 body_chunk_size = (num_participating_players + 1) * tape.length;
7453 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7454 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7456 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7457 SaveTape_VERS(file, &tape);
7459 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7460 SaveTape_HEAD(file, &tape);
7462 putFileChunkBE(file, "INFO", info_chunk_size);
7463 SaveTape_INFO(file, &tape);
7465 putFileChunkBE(file, "BODY", body_chunk_size);
7466 SaveTape_BODY(file, &tape);
7470 SetFilePermissions(filename, PERMS_PRIVATE);
7472 tape.changed = FALSE;
7476 Request("Tape saved !", REQ_CONFIRM);
7480 boolean SaveTapeChecked(int nr)
7482 char *filename = getTapeFilename(nr);
7483 boolean new_tape = !fileExists(filename);
7484 boolean tape_saved = FALSE;
7486 if (new_tape || Request("Replace old tape ?", REQ_ASK))
7491 Request("Tape saved !", REQ_CONFIRM);
7499 void DumpTape(struct TapeInfo *tape)
7501 int tape_frame_counter;
7504 if (tape->no_valid_file)
7506 Error(ERR_WARN, "cannot dump -- no valid tape file found");
7511 printf_line("-", 79);
7512 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
7513 tape->level_nr, tape->file_version, tape->game_version);
7514 printf(" (effective engine version %08d)\n",
7515 tape->engine_version);
7516 printf("Level series identifier: '%s'\n", tape->level_identifier);
7517 printf_line("-", 79);
7519 tape_frame_counter = 0;
7521 for (i = 0; i < tape->length; i++)
7523 if (i >= MAX_TAPE_LEN)
7526 printf("%04d: ", i);
7528 for (j = 0; j < MAX_PLAYERS; j++)
7530 if (tape->player_participates[j])
7532 int action = tape->pos[i].action[j];
7534 printf("%d:%02x ", j, action);
7535 printf("[%c%c%c%c|%c%c] - ",
7536 (action & JOY_LEFT ? '<' : ' '),
7537 (action & JOY_RIGHT ? '>' : ' '),
7538 (action & JOY_UP ? '^' : ' '),
7539 (action & JOY_DOWN ? 'v' : ' '),
7540 (action & JOY_BUTTON_1 ? '1' : ' '),
7541 (action & JOY_BUTTON_2 ? '2' : ' '));
7545 printf("(%03d) ", tape->pos[i].delay);
7546 printf("[%05d]\n", tape_frame_counter);
7548 tape_frame_counter += tape->pos[i].delay;
7551 printf_line("-", 79);
7555 /* ========================================================================= */
7556 /* score file functions */
7557 /* ========================================================================= */
7559 void LoadScore(int nr)
7562 char *filename = getScoreFilename(nr);
7563 char cookie[MAX_LINE_LEN];
7564 char line[MAX_LINE_LEN];
7568 /* always start with reliable default values */
7569 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7571 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
7572 highscore[i].Score = 0;
7575 if (!(file = fopen(filename, MODE_READ)))
7578 /* check file identifier */
7579 fgets(cookie, MAX_LINE_LEN, file);
7580 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7581 cookie[strlen(cookie) - 1] = '\0';
7583 if (!checkCookieString(cookie, SCORE_COOKIE))
7585 Error(ERR_WARN, "unknown format of score file '%s'", filename);
7590 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7592 fscanf(file, "%d", &highscore[i].Score);
7593 fgets(line, MAX_LINE_LEN, file);
7595 if (line[strlen(line) - 1] == '\n')
7596 line[strlen(line) - 1] = '\0';
7598 for (line_ptr = line; *line_ptr; line_ptr++)
7600 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
7602 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
7603 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
7612 void SaveScore(int nr)
7615 char *filename = getScoreFilename(nr);
7618 InitScoreDirectory(leveldir_current->subdir);
7620 if (!(file = fopen(filename, MODE_WRITE)))
7622 Error(ERR_WARN, "cannot save score for level %d", nr);
7626 fprintf(file, "%s\n\n", SCORE_COOKIE);
7628 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7629 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
7633 SetFilePermissions(filename, PERMS_PUBLIC);
7637 /* ========================================================================= */
7638 /* setup file functions */
7639 /* ========================================================================= */
7641 #define TOKEN_STR_PLAYER_PREFIX "player_"
7644 #define SETUP_TOKEN_PLAYER_NAME 0
7645 #define SETUP_TOKEN_SOUND 1
7646 #define SETUP_TOKEN_SOUND_LOOPS 2
7647 #define SETUP_TOKEN_SOUND_MUSIC 3
7648 #define SETUP_TOKEN_SOUND_SIMPLE 4
7649 #define SETUP_TOKEN_TOONS 5
7650 #define SETUP_TOKEN_SCROLL_DELAY 6
7651 #define SETUP_TOKEN_SOFT_SCROLLING 7
7652 #define SETUP_TOKEN_FADE_SCREENS 8
7653 #define SETUP_TOKEN_AUTORECORD 9
7654 #define SETUP_TOKEN_SHOW_TITLESCREEN 10
7655 #define SETUP_TOKEN_QUICK_DOORS 11
7656 #define SETUP_TOKEN_TEAM_MODE 12
7657 #define SETUP_TOKEN_HANDICAP 13
7658 #define SETUP_TOKEN_SKIP_LEVELS 14
7659 #define SETUP_TOKEN_TIME_LIMIT 15
7660 #define SETUP_TOKEN_FULLSCREEN 16
7661 #define SETUP_TOKEN_FULLSCREEN_MODE 17
7662 #define SETUP_TOKEN_ASK_ON_ESCAPE 18
7663 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 19
7664 #define SETUP_TOKEN_QUICK_SWITCH 20
7665 #define SETUP_TOKEN_INPUT_ON_FOCUS 21
7666 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 22
7667 #define SETUP_TOKEN_GRAPHICS_SET 23
7668 #define SETUP_TOKEN_SOUNDS_SET 24
7669 #define SETUP_TOKEN_MUSIC_SET 25
7670 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 26
7671 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 27
7672 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 28
7674 #define NUM_GLOBAL_SETUP_TOKENS 29
7677 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
7678 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
7679 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
7680 #define SETUP_TOKEN_EDITOR_EL_MORE 3
7681 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 4
7682 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 5
7683 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 6
7684 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 7
7685 #define SETUP_TOKEN_EDITOR_EL_CHARS 8
7686 #define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS 9
7687 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 10
7688 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 11
7689 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 12
7690 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 13
7691 #define SETUP_TOKEN_EDITOR_EL_BY_GAME 14
7692 #define SETUP_TOKEN_EDITOR_EL_BY_TYPE 15
7693 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 16
7695 #define NUM_EDITOR_SETUP_TOKENS 17
7697 /* editor cascade setup */
7698 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
7699 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
7700 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
7701 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
7702 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
7703 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
7704 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
7705 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
7706 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
7707 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
7708 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
7709 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
7710 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
7711 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
7712 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
7714 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
7716 /* shortcut setup */
7717 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
7718 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
7719 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
7720 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
7721 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
7722 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
7723 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
7724 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
7726 #define NUM_SHORTCUT_SETUP_TOKENS 8
7729 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
7730 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
7731 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
7732 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
7733 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
7734 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
7735 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
7736 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
7737 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
7738 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
7739 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
7740 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
7741 #define SETUP_TOKEN_PLAYER_KEY_UP 12
7742 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
7743 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
7744 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
7746 #define NUM_PLAYER_SETUP_TOKENS 16
7749 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
7750 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
7752 #define NUM_SYSTEM_SETUP_TOKENS 2
7755 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
7757 #define NUM_OPTIONS_SETUP_TOKENS 1
7760 static struct SetupInfo si;
7761 static struct SetupEditorInfo sei;
7762 static struct SetupEditorCascadeInfo seci;
7763 static struct SetupShortcutInfo ssi;
7764 static struct SetupInputInfo sii;
7765 static struct SetupSystemInfo syi;
7766 static struct OptionInfo soi;
7768 static struct TokenInfo global_setup_tokens[] =
7770 { TYPE_STRING, &si.player_name, "player_name" },
7771 { TYPE_SWITCH, &si.sound, "sound" },
7772 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
7773 { TYPE_SWITCH, &si.sound_music, "background_music" },
7774 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
7775 { TYPE_SWITCH, &si.toons, "toons" },
7776 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
7777 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
7778 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
7779 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
7780 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
7781 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
7782 { TYPE_SWITCH, &si.team_mode, "team_mode" },
7783 { TYPE_SWITCH, &si.handicap, "handicap" },
7784 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
7785 { TYPE_SWITCH, &si.time_limit, "time_limit" },
7786 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
7787 { TYPE_STRING, &si.fullscreen_mode, "fullscreen_mode" },
7788 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
7789 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
7790 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
7791 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
7792 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
7793 { TYPE_STRING, &si.graphics_set, "graphics_set" },
7794 { TYPE_STRING, &si.sounds_set, "sounds_set" },
7795 { TYPE_STRING, &si.music_set, "music_set" },
7796 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
7797 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
7798 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
7801 static boolean not_used = FALSE;
7802 static struct TokenInfo editor_setup_tokens[] =
7805 { TYPE_SWITCH, ¬_used, "editor.el_boulderdash" },
7806 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine" },
7807 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine_club" },
7808 { TYPE_SWITCH, ¬_used, "editor.el_more" },
7809 { TYPE_SWITCH, ¬_used, "editor.el_sokoban" },
7810 { TYPE_SWITCH, ¬_used, "editor.el_supaplex" },
7811 { TYPE_SWITCH, ¬_used, "editor.el_diamond_caves" },
7812 { TYPE_SWITCH, ¬_used, "editor.el_dx_boulderdash" },
7814 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
7815 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
7816 { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
7817 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
7818 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
7819 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
7820 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
7821 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
7823 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
7824 { TYPE_SWITCH, &sei.el_steel_chars, "editor.el_steel_chars" },
7825 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
7827 { TYPE_SWITCH, ¬_used, "editor.el_headlines" },
7829 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
7831 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
7832 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
7833 { TYPE_SWITCH, &sei.el_by_game, "editor.el_by_game" },
7834 { TYPE_SWITCH, &sei.el_by_type, "editor.el_by_type" },
7835 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
7838 static struct TokenInfo editor_cascade_setup_tokens[] =
7840 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
7841 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
7842 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
7843 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
7844 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
7845 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
7846 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
7847 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
7848 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
7849 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
7850 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
7851 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
7852 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
7853 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
7854 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
7857 static struct TokenInfo shortcut_setup_tokens[] =
7859 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
7860 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
7861 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
7862 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
7863 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
7864 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
7865 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
7866 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
7869 static struct TokenInfo player_setup_tokens[] =
7871 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
7872 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
7873 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
7874 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
7875 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
7876 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
7877 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
7878 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
7879 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
7880 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
7881 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
7882 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
7883 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
7884 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
7885 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
7886 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
7889 static struct TokenInfo system_setup_tokens[] =
7891 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
7892 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
7895 static struct TokenInfo options_setup_tokens[] =
7897 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
7900 static char *get_corrected_login_name(char *login_name)
7902 /* needed because player name must be a fixed length string */
7903 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
7905 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
7906 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
7908 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
7909 if (strchr(login_name_new, ' '))
7910 *strchr(login_name_new, ' ') = '\0';
7912 return login_name_new;
7915 static void setSetupInfoToDefaults(struct SetupInfo *si)
7919 si->player_name = get_corrected_login_name(getLoginName());
7922 si->sound_loops = TRUE;
7923 si->sound_music = TRUE;
7924 si->sound_simple = TRUE;
7926 si->double_buffering = TRUE;
7927 si->direct_draw = !si->double_buffering;
7928 si->scroll_delay = TRUE;
7929 si->soft_scrolling = TRUE;
7930 si->fade_screens = TRUE;
7931 si->autorecord = TRUE;
7932 si->show_titlescreen = TRUE;
7933 si->quick_doors = FALSE;
7934 si->team_mode = FALSE;
7935 si->handicap = TRUE;
7936 si->skip_levels = TRUE;
7937 si->time_limit = TRUE;
7938 si->fullscreen = FALSE;
7939 si->fullscreen_mode = getStringCopy(DEFAULT_FULLSCREEN_MODE);
7940 si->ask_on_escape = TRUE;
7941 si->ask_on_escape_editor = TRUE;
7942 si->quick_switch = FALSE;
7943 si->input_on_focus = FALSE;
7944 si->prefer_aga_graphics = TRUE;
7946 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
7947 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
7948 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
7949 si->override_level_graphics = FALSE;
7950 si->override_level_sounds = FALSE;
7951 si->override_level_music = FALSE;
7953 si->editor.el_boulderdash = TRUE;
7954 si->editor.el_emerald_mine = TRUE;
7955 si->editor.el_emerald_mine_club = TRUE;
7956 si->editor.el_more = TRUE;
7957 si->editor.el_sokoban = TRUE;
7958 si->editor.el_supaplex = TRUE;
7959 si->editor.el_diamond_caves = TRUE;
7960 si->editor.el_dx_boulderdash = TRUE;
7961 si->editor.el_chars = TRUE;
7962 si->editor.el_steel_chars = TRUE;
7963 si->editor.el_custom = TRUE;
7965 si->editor.el_headlines = TRUE;
7966 si->editor.el_user_defined = FALSE;
7967 si->editor.el_dynamic = TRUE;
7969 si->editor.show_element_token = FALSE;
7971 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
7972 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
7973 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
7975 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
7976 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
7977 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
7978 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
7979 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
7981 for (i = 0; i < MAX_PLAYERS; i++)
7983 si->input[i].use_joystick = FALSE;
7984 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
7985 si->input[i].joy.xleft = JOYSTICK_XLEFT;
7986 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
7987 si->input[i].joy.xright = JOYSTICK_XRIGHT;
7988 si->input[i].joy.yupper = JOYSTICK_YUPPER;
7989 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
7990 si->input[i].joy.ylower = JOYSTICK_YLOWER;
7991 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
7992 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
7993 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
7994 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
7995 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
7996 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
7997 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
7998 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8001 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8002 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8004 si->options.verbose = FALSE;
8007 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8009 si->editor_cascade.el_bd = TRUE;
8010 si->editor_cascade.el_em = TRUE;
8011 si->editor_cascade.el_emc = TRUE;
8012 si->editor_cascade.el_rnd = TRUE;
8013 si->editor_cascade.el_sb = TRUE;
8014 si->editor_cascade.el_sp = TRUE;
8015 si->editor_cascade.el_dc = TRUE;
8016 si->editor_cascade.el_dx = TRUE;
8018 si->editor_cascade.el_chars = FALSE;
8019 si->editor_cascade.el_steel_chars = FALSE;
8020 si->editor_cascade.el_ce = FALSE;
8021 si->editor_cascade.el_ge = FALSE;
8022 si->editor_cascade.el_ref = FALSE;
8023 si->editor_cascade.el_user = FALSE;
8024 si->editor_cascade.el_dynamic = FALSE;
8027 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8031 if (!setup_file_hash)
8036 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8037 setSetupInfo(global_setup_tokens, i,
8038 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
8043 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8044 setSetupInfo(editor_setup_tokens, i,
8045 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
8048 /* shortcut setup */
8049 ssi = setup.shortcut;
8050 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8051 setSetupInfo(shortcut_setup_tokens, i,
8052 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
8053 setup.shortcut = ssi;
8056 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8060 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8062 sii = setup.input[pnr];
8063 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8065 char full_token[100];
8067 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8068 setSetupInfo(player_setup_tokens, i,
8069 getHashEntry(setup_file_hash, full_token));
8071 setup.input[pnr] = sii;
8076 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8077 setSetupInfo(system_setup_tokens, i,
8078 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
8082 soi = setup.options;
8083 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8084 setSetupInfo(options_setup_tokens, i,
8085 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
8086 setup.options = soi;
8089 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8093 if (!setup_file_hash)
8096 /* editor cascade setup */
8097 seci = setup.editor_cascade;
8098 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8099 setSetupInfo(editor_cascade_setup_tokens, i,
8100 getHashEntry(setup_file_hash,
8101 editor_cascade_setup_tokens[i].text));
8102 setup.editor_cascade = seci;
8107 char *filename = getSetupFilename();
8108 SetupFileHash *setup_file_hash = NULL;
8110 /* always start with reliable default values */
8111 setSetupInfoToDefaults(&setup);
8113 setup_file_hash = loadSetupFileHash(filename);
8115 if (setup_file_hash)
8117 char *player_name_new;
8119 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
8120 decodeSetupFileHash(setup_file_hash);
8122 setup.direct_draw = !setup.double_buffering;
8124 freeSetupFileHash(setup_file_hash);
8126 /* needed to work around problems with fixed length strings */
8127 player_name_new = get_corrected_login_name(setup.player_name);
8128 free(setup.player_name);
8129 setup.player_name = player_name_new;
8132 Error(ERR_WARN, "using default setup values");
8135 void LoadSetup_EditorCascade()
8137 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8138 SetupFileHash *setup_file_hash = NULL;
8140 /* always start with reliable default values */
8141 setSetupInfoToDefaults_EditorCascade(&setup);
8143 setup_file_hash = loadSetupFileHash(filename);
8145 if (setup_file_hash)
8147 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
8148 decodeSetupFileHash_EditorCascade(setup_file_hash);
8150 freeSetupFileHash(setup_file_hash);
8158 char *filename = getSetupFilename();
8162 InitUserDataDirectory();
8164 if (!(file = fopen(filename, MODE_WRITE)))
8166 Error(ERR_WARN, "cannot write setup file '%s'", filename);
8170 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
8171 getCookie("SETUP")));
8172 fprintf(file, "\n");
8176 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8178 /* just to make things nicer :) */
8179 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
8180 i == SETUP_TOKEN_GRAPHICS_SET)
8181 fprintf(file, "\n");
8183 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
8188 fprintf(file, "\n");
8189 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8190 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
8192 /* shortcut setup */
8193 ssi = setup.shortcut;
8194 fprintf(file, "\n");
8195 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8196 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
8199 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8203 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8204 fprintf(file, "\n");
8206 sii = setup.input[pnr];
8207 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8208 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
8213 fprintf(file, "\n");
8214 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8215 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
8218 soi = setup.options;
8219 fprintf(file, "\n");
8220 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8221 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
8225 SetFilePermissions(filename, PERMS_PRIVATE);
8228 void SaveSetup_EditorCascade()
8230 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8234 InitUserDataDirectory();
8236 if (!(file = fopen(filename, MODE_WRITE)))
8238 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
8243 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
8244 getCookie("SETUP")));
8245 fprintf(file, "\n");
8247 seci = setup.editor_cascade;
8248 fprintf(file, "\n");
8249 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8250 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
8254 SetFilePermissions(filename, PERMS_PRIVATE);
8259 void LoadCustomElementDescriptions()
8261 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8262 SetupFileHash *setup_file_hash;
8265 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8267 if (element_info[i].custom_description != NULL)
8269 free(element_info[i].custom_description);
8270 element_info[i].custom_description = NULL;
8274 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8277 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8279 char *token = getStringCat2(element_info[i].token_name, ".name");
8280 char *value = getHashEntry(setup_file_hash, token);
8283 element_info[i].custom_description = getStringCopy(value);
8288 freeSetupFileHash(setup_file_hash);
8291 static void LoadSpecialMenuDesignSettingsFromFilename(char *filename)
8293 SetupFileHash *setup_file_hash;
8297 printf("LoadSpecialMenuDesignSettings from file '%s' ...\n", filename);
8300 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8303 /* special case: initialize with default values that may be overwritten */
8304 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8306 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
8307 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
8308 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
8310 if (value_x != NULL)
8311 menu.draw_xoffset[i] = get_integer_from_string(value_x);
8312 if (value_y != NULL)
8313 menu.draw_yoffset[i] = get_integer_from_string(value_y);
8314 if (list_size != NULL)
8315 menu.list_size[i] = get_integer_from_string(list_size);
8318 /* special case: initialize with default values that may be overwritten */
8319 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
8321 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
8322 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
8324 if (value_x != NULL)
8325 menu.draw_xoffset_info[i] = get_integer_from_string(value_x);
8326 if (value_y != NULL)
8327 menu.draw_yoffset_info[i] = get_integer_from_string(value_y);
8330 /* read (and overwrite with) values that may be specified in config file */
8331 for (i = 0; image_config_vars[i].token != NULL; i++)
8333 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
8336 *image_config_vars[i].value =
8337 get_auto_parameter_value(image_config_vars[i].token, value);
8340 freeSetupFileHash(setup_file_hash);
8343 void LoadSpecialMenuDesignSettings()
8345 char *filename_base = UNDEFINED_FILENAME, *filename_local;
8348 /* always start with reliable default values from default config */
8349 for (i = 0; image_config_vars[i].token != NULL; i++)
8350 for (j = 0; image_config[j].token != NULL; j++)
8351 if (strEqual(image_config_vars[i].token, image_config[j].token))
8352 *image_config_vars[i].value =
8353 get_auto_parameter_value(image_config_vars[i].token,
8354 image_config[j].value);
8356 if (!SETUP_OVERRIDE_ARTWORK(setup, ARTWORK_TYPE_GRAPHICS))
8358 /* first look for special settings configured in level series config */
8359 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
8361 if (fileExists(filename_base))
8362 LoadSpecialMenuDesignSettingsFromFilename(filename_base);
8365 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8367 if (filename_local != NULL && !strEqual(filename_base, filename_local))
8368 LoadSpecialMenuDesignSettingsFromFilename(filename_local);
8371 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
8373 char *filename = getEditorSetupFilename();
8374 SetupFileList *setup_file_list, *list;
8375 SetupFileHash *element_hash;
8376 int num_unknown_tokens = 0;
8379 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
8382 element_hash = newSetupFileHash();
8384 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8385 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
8387 /* determined size may be larger than needed (due to unknown elements) */
8389 for (list = setup_file_list; list != NULL; list = list->next)
8392 /* add space for up to 3 more elements for padding that may be needed */
8395 /* free memory for old list of elements, if needed */
8396 checked_free(*elements);
8398 /* allocate memory for new list of elements */
8399 *elements = checked_malloc(*num_elements * sizeof(int));
8402 for (list = setup_file_list; list != NULL; list = list->next)
8404 char *value = getHashEntry(element_hash, list->token);
8406 if (value == NULL) /* try to find obsolete token mapping */
8408 char *mapped_token = get_mapped_token(list->token);
8410 if (mapped_token != NULL)
8412 value = getHashEntry(element_hash, mapped_token);
8420 (*elements)[(*num_elements)++] = atoi(value);
8424 if (num_unknown_tokens == 0)
8426 Error(ERR_RETURN_LINE, "-");
8427 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
8428 Error(ERR_RETURN, "- config file: '%s'", filename);
8430 num_unknown_tokens++;
8433 Error(ERR_RETURN, "- token: '%s'", list->token);
8437 if (num_unknown_tokens > 0)
8438 Error(ERR_RETURN_LINE, "-");
8440 while (*num_elements % 4) /* pad with empty elements, if needed */
8441 (*elements)[(*num_elements)++] = EL_EMPTY;
8443 freeSetupFileList(setup_file_list);
8444 freeSetupFileHash(element_hash);
8447 for (i = 0; i < *num_elements; i++)
8448 printf("editor: element '%s' [%d]\n",
8449 element_info[(*elements)[i]].token_name, (*elements)[i]);
8453 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
8456 SetupFileHash *setup_file_hash = NULL;
8457 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
8458 char *filename_music, *filename_prefix, *filename_info;
8464 token_to_value_ptr[] =
8466 { "title_header", &tmp_music_file_info.title_header },
8467 { "artist_header", &tmp_music_file_info.artist_header },
8468 { "album_header", &tmp_music_file_info.album_header },
8469 { "year_header", &tmp_music_file_info.year_header },
8471 { "title", &tmp_music_file_info.title },
8472 { "artist", &tmp_music_file_info.artist },
8473 { "album", &tmp_music_file_info.album },
8474 { "year", &tmp_music_file_info.year },
8480 filename_music = (is_sound ? getCustomSoundFilename(basename) :
8481 getCustomMusicFilename(basename));
8483 if (filename_music == NULL)
8486 /* ---------- try to replace file extension ---------- */
8488 filename_prefix = getStringCopy(filename_music);
8489 if (strrchr(filename_prefix, '.') != NULL)
8490 *strrchr(filename_prefix, '.') = '\0';
8491 filename_info = getStringCat2(filename_prefix, ".txt");
8494 printf("trying to load file '%s'...\n", filename_info);
8497 if (fileExists(filename_info))
8498 setup_file_hash = loadSetupFileHash(filename_info);
8500 free(filename_prefix);
8501 free(filename_info);
8503 if (setup_file_hash == NULL)
8505 /* ---------- try to add file extension ---------- */
8507 filename_prefix = getStringCopy(filename_music);
8508 filename_info = getStringCat2(filename_prefix, ".txt");
8511 printf("trying to load file '%s'...\n", filename_info);
8514 if (fileExists(filename_info))
8515 setup_file_hash = loadSetupFileHash(filename_info);
8517 free(filename_prefix);
8518 free(filename_info);
8521 if (setup_file_hash == NULL)
8524 /* ---------- music file info found ---------- */
8526 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
8528 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
8530 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
8532 *token_to_value_ptr[i].value_ptr =
8533 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
8536 tmp_music_file_info.basename = getStringCopy(basename);
8537 tmp_music_file_info.music = music;
8538 tmp_music_file_info.is_sound = is_sound;
8540 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
8541 *new_music_file_info = tmp_music_file_info;
8543 return new_music_file_info;
8546 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
8548 return get_music_file_info_ext(basename, music, FALSE);
8551 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
8553 return get_music_file_info_ext(basename, sound, TRUE);
8556 static boolean music_info_listed_ext(struct MusicFileInfo *list,
8557 char *basename, boolean is_sound)
8559 for (; list != NULL; list = list->next)
8560 if (list->is_sound == is_sound && strEqual(list->basename, basename))
8566 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
8568 return music_info_listed_ext(list, basename, FALSE);
8571 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
8573 return music_info_listed_ext(list, basename, TRUE);
8576 void LoadMusicInfo()
8578 char *music_directory = getCustomMusicDirectory();
8579 int num_music = getMusicListSize();
8580 int num_music_noconf = 0;
8581 int num_sounds = getSoundListSize();
8583 struct dirent *dir_entry;
8584 struct FileInfo *music, *sound;
8585 struct MusicFileInfo *next, **new;
8588 while (music_file_info != NULL)
8590 next = music_file_info->next;
8592 checked_free(music_file_info->basename);
8594 checked_free(music_file_info->title_header);
8595 checked_free(music_file_info->artist_header);
8596 checked_free(music_file_info->album_header);
8597 checked_free(music_file_info->year_header);
8599 checked_free(music_file_info->title);
8600 checked_free(music_file_info->artist);
8601 checked_free(music_file_info->album);
8602 checked_free(music_file_info->year);
8604 free(music_file_info);
8606 music_file_info = next;
8609 new = &music_file_info;
8611 for (i = 0; i < num_music; i++)
8613 music = getMusicListEntry(i);
8615 if (music->filename == NULL)
8618 if (strEqual(music->filename, UNDEFINED_FILENAME))
8621 /* a configured file may be not recognized as music */
8622 if (!FileIsMusic(music->filename))
8626 printf("::: -> '%s' (configured)\n", music->filename);
8629 if (!music_info_listed(music_file_info, music->filename))
8631 *new = get_music_file_info(music->filename, i);
8633 new = &(*new)->next;
8637 if ((dir = opendir(music_directory)) == NULL)
8639 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
8643 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
8645 char *basename = dir_entry->d_name;
8646 boolean music_already_used = FALSE;
8649 /* skip all music files that are configured in music config file */
8650 for (i = 0; i < num_music; i++)
8652 music = getMusicListEntry(i);
8654 if (music->filename == NULL)
8657 if (strEqual(basename, music->filename))
8659 music_already_used = TRUE;
8664 if (music_already_used)
8667 if (!FileIsMusic(basename))
8671 printf("::: -> '%s' (found in directory)\n", basename);
8674 if (!music_info_listed(music_file_info, basename))
8676 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
8678 new = &(*new)->next;
8686 for (i = 0; i < num_sounds; i++)
8688 sound = getSoundListEntry(i);
8690 if (sound->filename == NULL)
8693 if (strEqual(sound->filename, UNDEFINED_FILENAME))
8696 /* a configured file may be not recognized as sound */
8697 if (!FileIsSound(sound->filename))
8701 printf("::: -> '%s' (configured)\n", sound->filename);
8704 if (!sound_info_listed(music_file_info, sound->filename))
8706 *new = get_sound_file_info(sound->filename, i);
8708 new = &(*new)->next;
8713 for (next = music_file_info; next != NULL; next = next->next)
8714 printf("::: title == '%s'\n", next->title);
8718 void add_helpanim_entry(int element, int action, int direction, int delay,
8719 int *num_list_entries)
8721 struct HelpAnimInfo *new_list_entry;
8722 (*num_list_entries)++;
8725 checked_realloc(helpanim_info,
8726 *num_list_entries * sizeof(struct HelpAnimInfo));
8727 new_list_entry = &helpanim_info[*num_list_entries - 1];
8729 new_list_entry->element = element;
8730 new_list_entry->action = action;
8731 new_list_entry->direction = direction;
8732 new_list_entry->delay = delay;
8735 void print_unknown_token(char *filename, char *token, int token_nr)
8739 Error(ERR_RETURN_LINE, "-");
8740 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
8741 Error(ERR_RETURN, "- config file: '%s'", filename);
8744 Error(ERR_RETURN, "- token: '%s'", token);
8747 void print_unknown_token_end(int token_nr)
8750 Error(ERR_RETURN_LINE, "-");
8753 void LoadHelpAnimInfo()
8755 char *filename = getHelpAnimFilename();
8756 SetupFileList *setup_file_list = NULL, *list;
8757 SetupFileHash *element_hash, *action_hash, *direction_hash;
8758 int num_list_entries = 0;
8759 int num_unknown_tokens = 0;
8762 if (fileExists(filename))
8763 setup_file_list = loadSetupFileList(filename);
8765 if (setup_file_list == NULL)
8767 /* use reliable default values from static configuration */
8768 SetupFileList *insert_ptr;
8770 insert_ptr = setup_file_list =
8771 newSetupFileList(helpanim_config[0].token,
8772 helpanim_config[0].value);
8774 for (i = 1; helpanim_config[i].token; i++)
8775 insert_ptr = addListEntry(insert_ptr,
8776 helpanim_config[i].token,
8777 helpanim_config[i].value);
8780 element_hash = newSetupFileHash();
8781 action_hash = newSetupFileHash();
8782 direction_hash = newSetupFileHash();
8784 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
8785 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
8787 for (i = 0; i < NUM_ACTIONS; i++)
8788 setHashEntry(action_hash, element_action_info[i].suffix,
8789 i_to_a(element_action_info[i].value));
8791 /* do not store direction index (bit) here, but direction value! */
8792 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
8793 setHashEntry(direction_hash, element_direction_info[i].suffix,
8794 i_to_a(1 << element_direction_info[i].value));
8796 for (list = setup_file_list; list != NULL; list = list->next)
8798 char *element_token, *action_token, *direction_token;
8799 char *element_value, *action_value, *direction_value;
8800 int delay = atoi(list->value);
8802 if (strEqual(list->token, "end"))
8804 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
8809 /* first try to break element into element/action/direction parts;
8810 if this does not work, also accept combined "element[.act][.dir]"
8811 elements (like "dynamite.active"), which are unique elements */
8813 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
8815 element_value = getHashEntry(element_hash, list->token);
8816 if (element_value != NULL) /* element found */
8817 add_helpanim_entry(atoi(element_value), -1, -1, delay,
8821 /* no further suffixes found -- this is not an element */
8822 print_unknown_token(filename, list->token, num_unknown_tokens++);
8828 /* token has format "<prefix>.<something>" */
8830 action_token = strchr(list->token, '.'); /* suffix may be action ... */
8831 direction_token = action_token; /* ... or direction */
8833 element_token = getStringCopy(list->token);
8834 *strchr(element_token, '.') = '\0';
8836 element_value = getHashEntry(element_hash, element_token);
8838 if (element_value == NULL) /* this is no element */
8840 element_value = getHashEntry(element_hash, list->token);
8841 if (element_value != NULL) /* combined element found */
8842 add_helpanim_entry(atoi(element_value), -1, -1, delay,
8845 print_unknown_token(filename, list->token, num_unknown_tokens++);
8847 free(element_token);
8852 action_value = getHashEntry(action_hash, action_token);
8854 if (action_value != NULL) /* action found */
8856 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
8859 free(element_token);
8864 direction_value = getHashEntry(direction_hash, direction_token);
8866 if (direction_value != NULL) /* direction found */
8868 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
8871 free(element_token);
8876 if (strchr(action_token + 1, '.') == NULL)
8878 /* no further suffixes found -- this is not an action nor direction */
8880 element_value = getHashEntry(element_hash, list->token);
8881 if (element_value != NULL) /* combined element found */
8882 add_helpanim_entry(atoi(element_value), -1, -1, delay,
8885 print_unknown_token(filename, list->token, num_unknown_tokens++);
8887 free(element_token);
8892 /* token has format "<prefix>.<suffix>.<something>" */
8894 direction_token = strchr(action_token + 1, '.');
8896 action_token = getStringCopy(action_token);
8897 *strchr(action_token + 1, '.') = '\0';
8899 action_value = getHashEntry(action_hash, action_token);
8901 if (action_value == NULL) /* this is no action */
8903 element_value = getHashEntry(element_hash, list->token);
8904 if (element_value != NULL) /* combined element found */
8905 add_helpanim_entry(atoi(element_value), -1, -1, delay,
8908 print_unknown_token(filename, list->token, num_unknown_tokens++);
8910 free(element_token);
8916 direction_value = getHashEntry(direction_hash, direction_token);
8918 if (direction_value != NULL) /* direction found */
8920 add_helpanim_entry(atoi(element_value), atoi(action_value),
8921 atoi(direction_value), delay, &num_list_entries);
8923 free(element_token);
8929 /* this is no direction */
8931 element_value = getHashEntry(element_hash, list->token);
8932 if (element_value != NULL) /* combined element found */
8933 add_helpanim_entry(atoi(element_value), -1, -1, delay,
8936 print_unknown_token(filename, list->token, num_unknown_tokens++);
8938 free(element_token);
8942 print_unknown_token_end(num_unknown_tokens);
8944 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
8945 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
8947 freeSetupFileList(setup_file_list);
8948 freeSetupFileHash(element_hash);
8949 freeSetupFileHash(action_hash);
8950 freeSetupFileHash(direction_hash);
8953 for (i = 0; i < num_list_entries; i++)
8954 printf("::: '%s': %d, %d, %d => %d\n",
8955 EL_NAME(helpanim_info[i].element),
8956 helpanim_info[i].element,
8957 helpanim_info[i].action,
8958 helpanim_info[i].direction,
8959 helpanim_info[i].delay);
8963 void LoadHelpTextInfo()
8965 char *filename = getHelpTextFilename();
8968 if (helptext_info != NULL)
8970 freeSetupFileHash(helptext_info);
8971 helptext_info = NULL;
8974 if (fileExists(filename))
8975 helptext_info = loadSetupFileHash(filename);
8977 if (helptext_info == NULL)
8979 /* use reliable default values from static configuration */
8980 helptext_info = newSetupFileHash();
8982 for (i = 0; helptext_config[i].token; i++)
8983 setHashEntry(helptext_info,
8984 helptext_config[i].token,
8985 helptext_config[i].value);
8989 BEGIN_HASH_ITERATION(helptext_info, itr)
8991 printf("::: '%s' => '%s'\n",
8992 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
8994 END_HASH_ITERATION(hash, itr)
8999 /* ------------------------------------------------------------------------- *
9001 * ------------------------------------------------------------------------- */
9003 #define MAX_NUM_CONVERT_LEVELS 1000
9005 void ConvertLevels()
9007 static LevelDirTree *convert_leveldir = NULL;
9008 static int convert_level_nr = -1;
9009 static int num_levels_handled = 0;
9010 static int num_levels_converted = 0;
9011 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
9014 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9015 global.convert_leveldir);
9017 if (convert_leveldir == NULL)
9018 Error(ERR_EXIT, "no such level identifier: '%s'",
9019 global.convert_leveldir);
9021 leveldir_current = convert_leveldir;
9023 if (global.convert_level_nr != -1)
9025 convert_leveldir->first_level = global.convert_level_nr;
9026 convert_leveldir->last_level = global.convert_level_nr;
9029 convert_level_nr = convert_leveldir->first_level;
9031 printf_line("=", 79);
9032 printf("Converting levels\n");
9033 printf_line("-", 79);
9034 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
9035 printf("Level series name: '%s'\n", convert_leveldir->name);
9036 printf("Level series author: '%s'\n", convert_leveldir->author);
9037 printf("Number of levels: %d\n", convert_leveldir->levels);
9038 printf_line("=", 79);
9041 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
9042 levels_failed[i] = FALSE;
9044 while (convert_level_nr <= convert_leveldir->last_level)
9046 char *level_filename;
9049 level_nr = convert_level_nr++;
9051 printf("Level %03d: ", level_nr);
9053 LoadLevel(level_nr);
9054 if (level.no_valid_file)
9056 printf("(no level)\n");
9060 printf("converting level ... ");
9062 level_filename = getDefaultLevelFilename(level_nr);
9063 new_level = !fileExists(level_filename);
9067 SaveLevel(level_nr);
9069 num_levels_converted++;
9071 printf("converted.\n");
9075 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
9076 levels_failed[level_nr] = TRUE;
9078 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
9081 num_levels_handled++;
9085 printf_line("=", 79);
9086 printf("Number of levels handled: %d\n", num_levels_handled);
9087 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
9088 (num_levels_handled ?
9089 num_levels_converted * 100 / num_levels_handled : 0));
9090 printf_line("-", 79);
9091 printf("Summary (for automatic parsing by scripts):\n");
9092 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
9093 convert_leveldir->identifier, num_levels_converted,
9095 (num_levels_handled ?
9096 num_levels_converted * 100 / num_levels_handled : 0));
9098 if (num_levels_handled != num_levels_converted)
9100 printf(", FAILED:");
9101 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
9102 if (levels_failed[i])
9107 printf_line("=", 79);