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_FAST_FULL;
4139 case 0x0e99: /* slow quicksand (boulder) */
4140 element = EL_QUICKSAND_FULL;
4144 element = EL_EM_EXIT_OPEN;
4148 element = EL_EM_EXIT_CLOSED;
4152 element = EL_EM_STEEL_EXIT_OPEN;
4156 element = EL_EM_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_DC_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_DC_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_STEELWALL_HORIZONTAL;
4326 case 0x14df: /* growing steel wall (up/down) */
4327 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4330 case 0x14e8: /* growing steel wall (up/down/left/right) */
4331 element = EL_EXPANDABLE_STEELWALL_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_FAST_EMPTY;
4354 case 0x1579: /* slow quicksand (empty) */
4355 element = EL_QUICKSAND_EMPTY;
4358 /* 0x157c - 0x158b: */
4361 /* 0x1590 - 0x159f: */
4362 /* EL_DC_LANDMINE */
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_DC_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_DC_GATE_WHITE;
5300 case 0x168a: /* secret gate (white) */
5301 element = EL_DC_GATE_WHITE_GRAY;
5304 case 0x168b: /* secret gate (no key) */
5305 element = EL_DC_GATE_FAKE_GRAY;
5309 element = EL_ROBOT_WHEEL;
5313 element = EL_DC_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_DC_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 has the same (strange) behaviour as Emerald Mine that gems
5700 can slip down from flat walls, like normal walls and steel walls */
5701 level->em_slippery_gems = TRUE;
5704 /* Diamond Caves II levels are always surrounded by indestructible wall, but
5705 not necessarily in a rectangular way -- fill with invisible steel wall */
5707 /* !!! not always true !!! keep level and set BorderElement instead !!! */
5709 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5712 if ((x == 0 || x == level->fieldx - 1 ||
5713 y == 0 || y == level->fieldy - 1) &&
5714 level->field[x][y] == EL_EMPTY)
5715 level->field[x][y] = EL_INVISIBLE_STEELWALL;
5717 if ((x == 0 || x == level->fieldx - 1 ||
5718 y == 0 || y == level->fieldy - 1) &&
5719 level->field[x][y] == EL_EMPTY)
5720 FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
5721 level->field, level->fieldx, level->fieldy);
5728 /* ------------------------------------------------------------------------- */
5729 /* functions for loading generic level */
5730 /* ------------------------------------------------------------------------- */
5732 void LoadLevelFromFileInfo(struct LevelInfo *level,
5733 struct LevelFileInfo *level_file_info)
5735 /* always start with reliable default values */
5736 setLevelInfoToDefaults(level);
5738 switch (level_file_info->type)
5740 case LEVEL_FILE_TYPE_RND:
5741 LoadLevelFromFileInfo_RND(level, level_file_info);
5744 case LEVEL_FILE_TYPE_EM:
5745 LoadLevelFromFileInfo_EM(level, level_file_info);
5746 level->game_engine_type = GAME_ENGINE_TYPE_EM;
5749 case LEVEL_FILE_TYPE_SP:
5750 LoadLevelFromFileInfo_SP(level, level_file_info);
5753 case LEVEL_FILE_TYPE_DC:
5754 LoadLevelFromFileInfo_DC(level, level_file_info);
5758 LoadLevelFromFileInfo_RND(level, level_file_info);
5762 /* if level file is invalid, restore level structure to default values */
5763 if (level->no_valid_file)
5764 setLevelInfoToDefaults(level);
5766 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
5767 level->game_engine_type = GAME_ENGINE_TYPE_RND;
5769 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
5770 CopyNativeLevel_Native_to_RND(level);
5773 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
5775 static struct LevelFileInfo level_file_info;
5777 /* always start with reliable default values */
5778 setFileInfoToDefaults(&level_file_info);
5780 level_file_info.nr = 0; /* unknown level number */
5781 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
5782 level_file_info.filename = filename;
5784 LoadLevelFromFileInfo(level, &level_file_info);
5787 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
5791 if (leveldir_current == NULL) /* only when dumping level */
5794 /* all engine modifications also valid for levels which use latest engine */
5795 if (level->game_version < VERSION_IDENT(3,2,0,5))
5797 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
5798 level->score[SC_TIME_BONUS] /= 10;
5802 leveldir_current->latest_engine = TRUE; /* !!! TEST ONLY !!! */
5805 if (leveldir_current->latest_engine)
5807 /* ---------- use latest game engine ----------------------------------- */
5809 /* For all levels which are forced to use the latest game engine version
5810 (normally all but user contributed, private and undefined levels), set
5811 the game engine version to the actual version; this allows for actual
5812 corrections in the game engine to take effect for existing, converted
5813 levels (from "classic" or other existing games) to make the emulation
5814 of the corresponding game more accurate, while (hopefully) not breaking
5815 existing levels created from other players. */
5817 level->game_version = GAME_VERSION_ACTUAL;
5819 /* Set special EM style gems behaviour: EM style gems slip down from
5820 normal, steel and growing wall. As this is a more fundamental change,
5821 it seems better to set the default behaviour to "off" (as it is more
5822 natural) and make it configurable in the level editor (as a property
5823 of gem style elements). Already existing converted levels (neither
5824 private nor contributed levels) are changed to the new behaviour. */
5826 if (level->file_version < FILE_VERSION_2_0)
5827 level->em_slippery_gems = TRUE;
5832 /* ---------- use game engine the level was created with ----------------- */
5834 /* For all levels which are not forced to use the latest game engine
5835 version (normally user contributed, private and undefined levels),
5836 use the version of the game engine the levels were created for.
5838 Since 2.0.1, the game engine version is now directly stored
5839 in the level file (chunk "VERS"), so there is no need anymore
5840 to set the game version from the file version (except for old,
5841 pre-2.0 levels, where the game version is still taken from the
5842 file format version used to store the level -- see above). */
5844 /* player was faster than enemies in 1.0.0 and before */
5845 if (level->file_version == FILE_VERSION_1_0)
5846 for (i = 0; i < MAX_PLAYERS; i++)
5847 level->initial_player_stepsize[i] = STEPSIZE_FAST;
5849 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
5850 if (level->game_version == VERSION_IDENT(2,0,1,0))
5851 level->em_slippery_gems = TRUE;
5853 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
5854 if (level->game_version < VERSION_IDENT(2,2,0,0))
5855 level->use_spring_bug = TRUE;
5857 if (level->game_version < VERSION_IDENT(3,2,0,5))
5859 /* time orb caused limited time in endless time levels before 3.2.0-5 */
5860 level->use_time_orb_bug = TRUE;
5862 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
5863 level->block_snap_field = FALSE;
5865 /* extra time score was same value as time left score before 3.2.0-5 */
5866 level->extra_time_score = level->score[SC_TIME_BONUS];
5869 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
5870 level->score[SC_TIME_BONUS] /= 10;
5874 if (level->game_version < VERSION_IDENT(3,2,0,7))
5876 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
5877 level->continuous_snapping = FALSE;
5880 /* only few elements were able to actively move into acid before 3.1.0 */
5881 /* trigger settings did not exist before 3.1.0; set to default "any" */
5882 if (level->game_version < VERSION_IDENT(3,1,0,0))
5884 /* correct "can move into acid" settings (all zero in old levels) */
5886 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
5887 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
5889 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
5890 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
5891 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
5892 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
5894 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5895 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
5897 /* correct trigger settings (stored as zero == "none" in old levels) */
5899 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5901 int element = EL_CUSTOM_START + i;
5902 struct ElementInfo *ei = &element_info[element];
5904 for (j = 0; j < ei->num_change_pages; j++)
5906 struct ElementChangeInfo *change = &ei->change_page[j];
5908 change->trigger_player = CH_PLAYER_ANY;
5909 change->trigger_page = CH_PAGE_ANY;
5914 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
5916 int element = EL_CUSTOM_START + 255;
5917 struct ElementInfo *ei = &element_info[element];
5918 struct ElementChangeInfo *change = &ei->change_page[0];
5920 /* This is needed to fix a problem that was caused by a bugfix in function
5921 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
5922 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
5923 not replace walkable elements, but instead just placed the player on it,
5924 without placing the Sokoban field under the player). Unfortunately, this
5925 breaks "Snake Bite" style levels when the snake is halfway through a door
5926 that just closes (the snake head is still alive and can be moved in this
5927 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
5928 player (without Sokoban element) which then gets killed as designed). */
5930 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
5931 strncmp(ei->description, "pause b4 death", 14) == 0) &&
5932 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
5933 change->target_element = EL_PLAYER_1;
5937 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
5941 /* map custom element change events that have changed in newer versions
5942 (these following values were accidentally changed in version 3.0.1)
5943 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
5944 if (level->game_version <= VERSION_IDENT(3,0,0,0))
5946 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5948 int element = EL_CUSTOM_START + i;
5950 /* order of checking and copying events to be mapped is important */
5951 /* (do not change the start and end value -- they are constant) */
5952 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
5954 if (HAS_CHANGE_EVENT(element, j - 2))
5956 SET_CHANGE_EVENT(element, j - 2, FALSE);
5957 SET_CHANGE_EVENT(element, j, TRUE);
5961 /* order of checking and copying events to be mapped is important */
5962 /* (do not change the start and end value -- they are constant) */
5963 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
5965 if (HAS_CHANGE_EVENT(element, j - 1))
5967 SET_CHANGE_EVENT(element, j - 1, FALSE);
5968 SET_CHANGE_EVENT(element, j, TRUE);
5974 /* initialize "can_change" field for old levels with only one change page */
5975 if (level->game_version <= VERSION_IDENT(3,0,2,0))
5977 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5979 int element = EL_CUSTOM_START + i;
5981 if (CAN_CHANGE(element))
5982 element_info[element].change->can_change = TRUE;
5986 /* correct custom element values (for old levels without these options) */
5987 if (level->game_version < VERSION_IDENT(3,1,1,0))
5989 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5991 int element = EL_CUSTOM_START + i;
5992 struct ElementInfo *ei = &element_info[element];
5994 if (ei->access_direction == MV_NO_DIRECTION)
5995 ei->access_direction = MV_ALL_DIRECTIONS;
5999 /* correct custom element values (fix invalid values for all versions) */
6002 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6004 int element = EL_CUSTOM_START + i;
6005 struct ElementInfo *ei = &element_info[element];
6007 for (j = 0; j < ei->num_change_pages; j++)
6009 struct ElementChangeInfo *change = &ei->change_page[j];
6011 if (change->trigger_player == CH_PLAYER_NONE)
6012 change->trigger_player = CH_PLAYER_ANY;
6014 if (change->trigger_side == CH_SIDE_NONE)
6015 change->trigger_side = CH_SIDE_ANY;
6020 /* initialize "can_explode" field for old levels which did not store this */
6021 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6022 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6024 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6026 int element = EL_CUSTOM_START + i;
6028 if (EXPLODES_1X1_OLD(element))
6029 element_info[element].explosion_type = EXPLODES_1X1;
6031 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6032 EXPLODES_SMASHED(element) ||
6033 EXPLODES_IMPACT(element)));
6037 /* correct previously hard-coded move delay values for maze runner style */
6038 if (level->game_version < VERSION_IDENT(3,1,1,0))
6040 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6042 int element = EL_CUSTOM_START + i;
6044 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6046 /* previously hard-coded and therefore ignored */
6047 element_info[element].move_delay_fixed = 9;
6048 element_info[element].move_delay_random = 0;
6053 /* map elements that have changed in newer versions */
6054 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6055 level->game_version);
6056 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6057 for (x = 0; x < 3; x++)
6058 for (y = 0; y < 3; y++)
6059 level->yamyam_content[i].e[x][y] =
6060 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6061 level->game_version);
6063 /* initialize element properties for level editor etc. */
6064 InitElementPropertiesEngine(level->game_version);
6065 InitElementPropertiesAfterLoading(level->game_version);
6068 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6072 /* map elements that have changed in newer versions */
6073 for (y = 0; y < level->fieldy; y++)
6074 for (x = 0; x < level->fieldx; x++)
6075 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6076 level->game_version);
6078 /* copy elements to runtime playfield array */
6079 for (x = 0; x < MAX_LEV_FIELDX; x++)
6080 for (y = 0; y < MAX_LEV_FIELDY; y++)
6081 Feld[x][y] = level->field[x][y];
6083 /* initialize level size variables for faster access */
6084 lev_fieldx = level->fieldx;
6085 lev_fieldy = level->fieldy;
6087 /* determine border element for this level */
6088 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6089 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6094 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6096 struct LevelFileInfo *level_file_info = &level->file_info;
6098 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6099 CopyNativeLevel_RND_to_Native(level);
6102 void LoadLevelTemplate(int nr)
6106 setLevelFileInfo(&level_template.file_info, nr);
6107 filename = level_template.file_info.filename;
6109 LoadLevelFromFileInfo(&level_template, &level_template.file_info);
6111 LoadLevel_InitVersion(&level_template, filename);
6112 LoadLevel_InitElements(&level_template, filename);
6114 ActivateLevelTemplate();
6117 void LoadLevel(int nr)
6121 setLevelFileInfo(&level.file_info, nr);
6122 filename = level.file_info.filename;
6124 LoadLevelFromFileInfo(&level, &level.file_info);
6126 if (level.use_custom_template)
6127 LoadLevelTemplate(-1);
6129 LoadLevel_InitVersion(&level, filename);
6130 LoadLevel_InitElements(&level, filename);
6131 LoadLevel_InitPlayfield(&level, filename);
6133 LoadLevel_InitNativeEngines(&level, filename);
6136 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6140 chunk_size += putFileVersion(file, level->file_version);
6141 chunk_size += putFileVersion(file, level->game_version);
6146 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6150 chunk_size += putFile16BitBE(file, level->creation_date.year);
6151 chunk_size += putFile8Bit(file, level->creation_date.month);
6152 chunk_size += putFile8Bit(file, level->creation_date.day);
6158 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6162 putFile8Bit(file, level->fieldx);
6163 putFile8Bit(file, level->fieldy);
6165 putFile16BitBE(file, level->time);
6166 putFile16BitBE(file, level->gems_needed);
6168 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6169 putFile8Bit(file, level->name[i]);
6171 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6172 putFile8Bit(file, level->score[i]);
6174 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6175 for (y = 0; y < 3; y++)
6176 for (x = 0; x < 3; x++)
6177 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6178 level->yamyam_content[i].e[x][y]));
6179 putFile8Bit(file, level->amoeba_speed);
6180 putFile8Bit(file, level->time_magic_wall);
6181 putFile8Bit(file, level->time_wheel);
6182 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6183 level->amoeba_content));
6184 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6185 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6186 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6187 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6189 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6191 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6192 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6193 putFile32BitBE(file, level->can_move_into_acid_bits);
6194 putFile8Bit(file, level->dont_collide_with_bits);
6196 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6197 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6199 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6200 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6201 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6203 putFile8Bit(file, level->game_engine_type);
6205 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6209 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6214 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6215 chunk_size += putFile8Bit(file, level->name[i]);
6220 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6225 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6226 chunk_size += putFile8Bit(file, level->author[i]);
6232 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6237 for (y = 0; y < level->fieldy; y++)
6238 for (x = 0; x < level->fieldx; x++)
6239 if (level->encoding_16bit_field)
6240 chunk_size += putFile16BitBE(file, level->field[x][y]);
6242 chunk_size += putFile8Bit(file, level->field[x][y]);
6248 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6253 for (y = 0; y < level->fieldy; y++)
6254 for (x = 0; x < level->fieldx; x++)
6255 chunk_size += putFile16BitBE(file, level->field[x][y]);
6261 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6265 putFile8Bit(file, EL_YAMYAM);
6266 putFile8Bit(file, level->num_yamyam_contents);
6267 putFile8Bit(file, 0);
6268 putFile8Bit(file, 0);
6270 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6271 for (y = 0; y < 3; y++)
6272 for (x = 0; x < 3; x++)
6273 if (level->encoding_16bit_field)
6274 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6276 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6281 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6284 int num_contents, content_xsize, content_ysize;
6285 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6287 if (element == EL_YAMYAM)
6289 num_contents = level->num_yamyam_contents;
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] = level->yamyam_content[i].e[x][y];
6298 else if (element == EL_BD_AMOEBA)
6304 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6305 for (y = 0; y < 3; y++)
6306 for (x = 0; x < 3; x++)
6307 content_array[i][x][y] = EL_EMPTY;
6308 content_array[0][0][0] = level->amoeba_content;
6312 /* chunk header already written -- write empty chunk data */
6313 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6315 Error(ERR_WARN, "cannot save content for element '%d'", element);
6319 putFile16BitBE(file, element);
6320 putFile8Bit(file, num_contents);
6321 putFile8Bit(file, content_xsize);
6322 putFile8Bit(file, content_ysize);
6324 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6326 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6327 for (y = 0; y < 3; y++)
6328 for (x = 0; x < 3; x++)
6329 putFile16BitBE(file, content_array[i][x][y]);
6334 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6336 int envelope_nr = element - EL_ENVELOPE_1;
6337 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6341 chunk_size += putFile16BitBE(file, element);
6342 chunk_size += putFile16BitBE(file, envelope_len);
6343 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6344 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6346 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6347 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6349 for (i = 0; i < envelope_len; i++)
6350 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6357 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6358 int num_changed_custom_elements)
6362 putFile16BitBE(file, num_changed_custom_elements);
6364 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6366 int element = EL_CUSTOM_START + i;
6368 struct ElementInfo *ei = &element_info[element];
6370 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6372 if (check < num_changed_custom_elements)
6374 putFile16BitBE(file, element);
6375 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6382 if (check != num_changed_custom_elements) /* should not happen */
6383 Error(ERR_WARN, "inconsistent number of custom element properties");
6388 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6389 int num_changed_custom_elements)
6393 putFile16BitBE(file, num_changed_custom_elements);
6395 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6397 int element = EL_CUSTOM_START + i;
6399 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6401 if (check < num_changed_custom_elements)
6403 putFile16BitBE(file, element);
6404 putFile16BitBE(file, element_info[element].change->target_element);
6411 if (check != num_changed_custom_elements) /* should not happen */
6412 Error(ERR_WARN, "inconsistent number of custom target elements");
6417 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6418 int num_changed_custom_elements)
6420 int i, j, x, y, check = 0;
6422 putFile16BitBE(file, num_changed_custom_elements);
6424 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6426 int element = EL_CUSTOM_START + i;
6427 struct ElementInfo *ei = &element_info[element];
6429 if (ei->modified_settings)
6431 if (check < num_changed_custom_elements)
6433 putFile16BitBE(file, element);
6435 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6436 putFile8Bit(file, ei->description[j]);
6438 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6440 /* some free bytes for future properties and padding */
6441 WriteUnusedBytesToFile(file, 7);
6443 putFile8Bit(file, ei->use_gfx_element);
6444 putFile16BitBE(file, ei->gfx_element);
6446 putFile8Bit(file, ei->collect_score_initial);
6447 putFile8Bit(file, ei->collect_count_initial);
6449 putFile16BitBE(file, ei->push_delay_fixed);
6450 putFile16BitBE(file, ei->push_delay_random);
6451 putFile16BitBE(file, ei->move_delay_fixed);
6452 putFile16BitBE(file, ei->move_delay_random);
6454 putFile16BitBE(file, ei->move_pattern);
6455 putFile8Bit(file, ei->move_direction_initial);
6456 putFile8Bit(file, ei->move_stepsize);
6458 for (y = 0; y < 3; y++)
6459 for (x = 0; x < 3; x++)
6460 putFile16BitBE(file, ei->content.e[x][y]);
6462 putFile32BitBE(file, ei->change->events);
6464 putFile16BitBE(file, ei->change->target_element);
6466 putFile16BitBE(file, ei->change->delay_fixed);
6467 putFile16BitBE(file, ei->change->delay_random);
6468 putFile16BitBE(file, ei->change->delay_frames);
6470 putFile16BitBE(file, ei->change->trigger_element);
6472 putFile8Bit(file, ei->change->explode);
6473 putFile8Bit(file, ei->change->use_target_content);
6474 putFile8Bit(file, ei->change->only_if_complete);
6475 putFile8Bit(file, ei->change->use_random_replace);
6477 putFile8Bit(file, ei->change->random_percentage);
6478 putFile8Bit(file, ei->change->replace_when);
6480 for (y = 0; y < 3; y++)
6481 for (x = 0; x < 3; x++)
6482 putFile16BitBE(file, ei->change->content.e[x][y]);
6484 putFile8Bit(file, ei->slippery_type);
6486 /* some free bytes for future properties and padding */
6487 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6494 if (check != num_changed_custom_elements) /* should not happen */
6495 Error(ERR_WARN, "inconsistent number of custom element properties");
6500 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6502 struct ElementInfo *ei = &element_info[element];
6505 /* ---------- custom element base property values (96 bytes) ------------- */
6507 putFile16BitBE(file, element);
6509 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6510 putFile8Bit(file, ei->description[i]);
6512 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6514 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
6516 putFile8Bit(file, ei->num_change_pages);
6518 putFile16BitBE(file, ei->ce_value_fixed_initial);
6519 putFile16BitBE(file, ei->ce_value_random_initial);
6520 putFile8Bit(file, ei->use_last_ce_value);
6522 putFile8Bit(file, ei->use_gfx_element);
6523 putFile16BitBE(file, ei->gfx_element);
6525 putFile8Bit(file, ei->collect_score_initial);
6526 putFile8Bit(file, ei->collect_count_initial);
6528 putFile8Bit(file, ei->drop_delay_fixed);
6529 putFile8Bit(file, ei->push_delay_fixed);
6530 putFile8Bit(file, ei->drop_delay_random);
6531 putFile8Bit(file, ei->push_delay_random);
6532 putFile16BitBE(file, ei->move_delay_fixed);
6533 putFile16BitBE(file, ei->move_delay_random);
6535 /* bits 0 - 15 of "move_pattern" ... */
6536 putFile16BitBE(file, ei->move_pattern & 0xffff);
6537 putFile8Bit(file, ei->move_direction_initial);
6538 putFile8Bit(file, ei->move_stepsize);
6540 putFile8Bit(file, ei->slippery_type);
6542 for (y = 0; y < 3; y++)
6543 for (x = 0; x < 3; x++)
6544 putFile16BitBE(file, ei->content.e[x][y]);
6546 putFile16BitBE(file, ei->move_enter_element);
6547 putFile16BitBE(file, ei->move_leave_element);
6548 putFile8Bit(file, ei->move_leave_type);
6550 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
6551 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
6553 putFile8Bit(file, ei->access_direction);
6555 putFile8Bit(file, ei->explosion_delay);
6556 putFile8Bit(file, ei->ignition_delay);
6557 putFile8Bit(file, ei->explosion_type);
6559 /* some free bytes for future custom property values and padding */
6560 WriteUnusedBytesToFile(file, 1);
6562 /* ---------- change page property values (48 bytes) --------------------- */
6564 for (i = 0; i < ei->num_change_pages; i++)
6566 struct ElementChangeInfo *change = &ei->change_page[i];
6567 unsigned int event_bits;
6569 /* bits 0 - 31 of "has_event[]" ... */
6571 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
6572 if (change->has_event[j])
6573 event_bits |= (1 << j);
6574 putFile32BitBE(file, event_bits);
6576 putFile16BitBE(file, change->target_element);
6578 putFile16BitBE(file, change->delay_fixed);
6579 putFile16BitBE(file, change->delay_random);
6580 putFile16BitBE(file, change->delay_frames);
6582 putFile16BitBE(file, change->trigger_element);
6584 putFile8Bit(file, change->explode);
6585 putFile8Bit(file, change->use_target_content);
6586 putFile8Bit(file, change->only_if_complete);
6587 putFile8Bit(file, change->use_random_replace);
6589 putFile8Bit(file, change->random_percentage);
6590 putFile8Bit(file, change->replace_when);
6592 for (y = 0; y < 3; y++)
6593 for (x = 0; x < 3; x++)
6594 putFile16BitBE(file, change->target_content.e[x][y]);
6596 putFile8Bit(file, change->can_change);
6598 putFile8Bit(file, change->trigger_side);
6600 putFile8Bit(file, change->trigger_player);
6601 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
6602 log_2(change->trigger_page)));
6604 putFile8Bit(file, change->has_action);
6605 putFile8Bit(file, change->action_type);
6606 putFile8Bit(file, change->action_mode);
6607 putFile16BitBE(file, change->action_arg);
6609 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
6611 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
6612 if (change->has_event[j])
6613 event_bits |= (1 << (j - 32));
6614 putFile8Bit(file, event_bits);
6620 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
6622 struct ElementInfo *ei = &element_info[element];
6623 struct ElementGroupInfo *group = ei->group;
6626 putFile16BitBE(file, element);
6628 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6629 putFile8Bit(file, ei->description[i]);
6631 putFile8Bit(file, group->num_elements);
6633 putFile8Bit(file, ei->use_gfx_element);
6634 putFile16BitBE(file, ei->gfx_element);
6636 putFile8Bit(file, group->choice_mode);
6638 /* some free bytes for future values and padding */
6639 WriteUnusedBytesToFile(file, 3);
6641 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
6642 putFile16BitBE(file, group->element[i]);
6646 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
6647 boolean write_element)
6649 int save_type = entry->save_type;
6650 int data_type = entry->data_type;
6651 int conf_type = entry->conf_type;
6652 int byte_mask = conf_type & CONF_MASK_BYTES;
6653 int element = entry->element;
6654 int default_value = entry->default_value;
6656 boolean modified = FALSE;
6658 if (byte_mask != CONF_MASK_MULTI_BYTES)
6660 void *value_ptr = entry->value;
6661 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
6664 /* check if any settings have been modified before saving them */
6665 if (value != default_value)
6668 /* do not save if explicitly told or if unmodified default settings */
6669 if ((save_type == SAVE_CONF_NEVER) ||
6670 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6674 num_bytes += putFile16BitBE(file, element);
6676 num_bytes += putFile8Bit(file, conf_type);
6677 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
6678 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
6679 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
6682 else if (data_type == TYPE_STRING)
6684 char *default_string = entry->default_string;
6685 char *string = (char *)(entry->value);
6686 int string_length = strlen(string);
6689 /* check if any settings have been modified before saving them */
6690 if (!strEqual(string, default_string))
6693 /* do not save if explicitly told or if unmodified default settings */
6694 if ((save_type == SAVE_CONF_NEVER) ||
6695 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6699 num_bytes += putFile16BitBE(file, element);
6701 num_bytes += putFile8Bit(file, conf_type);
6702 num_bytes += putFile16BitBE(file, string_length);
6704 for (i = 0; i < string_length; i++)
6705 num_bytes += putFile8Bit(file, string[i]);
6707 else if (data_type == TYPE_ELEMENT_LIST)
6709 int *element_array = (int *)(entry->value);
6710 int num_elements = *(int *)(entry->num_entities);
6713 /* check if any settings have been modified before saving them */
6714 for (i = 0; i < num_elements; i++)
6715 if (element_array[i] != default_value)
6718 /* do not save if explicitly told or if unmodified default settings */
6719 if ((save_type == SAVE_CONF_NEVER) ||
6720 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6724 num_bytes += putFile16BitBE(file, element);
6726 num_bytes += putFile8Bit(file, conf_type);
6727 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
6729 for (i = 0; i < num_elements; i++)
6730 num_bytes += putFile16BitBE(file, element_array[i]);
6732 else if (data_type == TYPE_CONTENT_LIST)
6734 struct Content *content = (struct Content *)(entry->value);
6735 int num_contents = *(int *)(entry->num_entities);
6738 /* check if any settings have been modified before saving them */
6739 for (i = 0; i < num_contents; i++)
6740 for (y = 0; y < 3; y++)
6741 for (x = 0; x < 3; x++)
6742 if (content[i].e[x][y] != default_value)
6745 /* do not save if explicitly told or if unmodified default settings */
6746 if ((save_type == SAVE_CONF_NEVER) ||
6747 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6751 num_bytes += putFile16BitBE(file, element);
6753 num_bytes += putFile8Bit(file, conf_type);
6754 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
6756 for (i = 0; i < num_contents; i++)
6757 for (y = 0; y < 3; y++)
6758 for (x = 0; x < 3; x++)
6759 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
6765 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
6770 li = *level; /* copy level data into temporary buffer */
6772 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
6773 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
6778 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
6783 li = *level; /* copy level data into temporary buffer */
6785 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
6786 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
6791 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
6793 int envelope_nr = element - EL_ENVELOPE_1;
6797 chunk_size += putFile16BitBE(file, element);
6799 /* copy envelope data into temporary buffer */
6800 xx_envelope = level->envelope[envelope_nr];
6802 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
6803 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
6808 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
6810 struct ElementInfo *ei = &element_info[element];
6814 chunk_size += putFile16BitBE(file, element);
6816 xx_ei = *ei; /* copy element data into temporary buffer */
6818 /* set default description string for this specific element */
6819 strcpy(xx_default_description, getDefaultElementDescription(ei));
6822 /* set (fixed) number of content areas (may be wrong by broken level file) */
6823 /* (this is now directly corrected for broken level files after loading) */
6824 xx_num_contents = 1;
6827 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
6828 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
6830 for (i = 0; i < ei->num_change_pages; i++)
6832 struct ElementChangeInfo *change = &ei->change_page[i];
6834 xx_current_change_page = i;
6836 xx_change = *change; /* copy change data into temporary buffer */
6839 setEventBitsFromEventFlags(change);
6841 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
6842 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
6849 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
6851 struct ElementInfo *ei = &element_info[element];
6852 struct ElementGroupInfo *group = ei->group;
6856 chunk_size += putFile16BitBE(file, element);
6858 xx_ei = *ei; /* copy element data into temporary buffer */
6859 xx_group = *group; /* copy group data into temporary buffer */
6861 /* set default description string for this specific element */
6862 strcpy(xx_default_description, getDefaultElementDescription(ei));
6864 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
6865 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
6870 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
6876 if (!(file = fopen(filename, MODE_WRITE)))
6878 Error(ERR_WARN, "cannot save level file '%s'", filename);
6882 level->file_version = FILE_VERSION_ACTUAL;
6883 level->game_version = GAME_VERSION_ACTUAL;
6885 level->creation_date = getCurrentDate();
6887 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
6888 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
6890 chunk_size = SaveLevel_VERS(NULL, level);
6891 putFileChunkBE(file, "VERS", chunk_size);
6892 SaveLevel_VERS(file, level);
6894 chunk_size = SaveLevel_DATE(NULL, level);
6895 putFileChunkBE(file, "DATE", chunk_size);
6896 SaveLevel_DATE(file, level);
6898 chunk_size = SaveLevel_NAME(NULL, level);
6899 putFileChunkBE(file, "NAME", chunk_size);
6900 SaveLevel_NAME(file, level);
6902 chunk_size = SaveLevel_AUTH(NULL, level);
6903 putFileChunkBE(file, "AUTH", chunk_size);
6904 SaveLevel_AUTH(file, level);
6906 chunk_size = SaveLevel_INFO(NULL, level);
6907 putFileChunkBE(file, "INFO", chunk_size);
6908 SaveLevel_INFO(file, level);
6910 chunk_size = SaveLevel_BODY(NULL, level);
6911 putFileChunkBE(file, "BODY", chunk_size);
6912 SaveLevel_BODY(file, level);
6914 chunk_size = SaveLevel_ELEM(NULL, level);
6915 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
6917 putFileChunkBE(file, "ELEM", chunk_size);
6918 SaveLevel_ELEM(file, level);
6921 for (i = 0; i < NUM_ENVELOPES; i++)
6923 int element = EL_ENVELOPE_1 + i;
6925 chunk_size = SaveLevel_NOTE(NULL, level, element);
6926 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
6928 putFileChunkBE(file, "NOTE", chunk_size);
6929 SaveLevel_NOTE(file, level, element);
6933 /* if not using template level, check for non-default custom/group elements */
6934 if (!level->use_custom_template)
6936 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6938 int element = EL_CUSTOM_START + i;
6940 chunk_size = SaveLevel_CUSX(NULL, level, element);
6941 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
6943 putFileChunkBE(file, "CUSX", chunk_size);
6944 SaveLevel_CUSX(file, level, element);
6948 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
6950 int element = EL_GROUP_START + i;
6952 chunk_size = SaveLevel_GRPX(NULL, level, element);
6953 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
6955 putFileChunkBE(file, "GRPX", chunk_size);
6956 SaveLevel_GRPX(file, level, element);
6963 SetFilePermissions(filename, PERMS_PRIVATE);
6966 void SaveLevel(int nr)
6968 char *filename = getDefaultLevelFilename(nr);
6970 SaveLevelFromFilename(&level, filename);
6973 void SaveLevelTemplate()
6975 char *filename = getDefaultLevelFilename(-1);
6977 SaveLevelFromFilename(&level, filename);
6980 boolean SaveLevelChecked(int nr)
6982 char *filename = getDefaultLevelFilename(nr);
6983 boolean new_level = !fileExists(filename);
6984 boolean level_saved = FALSE;
6986 if (new_level || Request("Save this level and kill the old ?", REQ_ASK))
6991 Request("Level saved !", REQ_CONFIRM);
6999 void DumpLevel(struct LevelInfo *level)
7001 if (level->no_valid_file)
7003 Error(ERR_WARN, "cannot dump -- no valid level file found");
7008 printf_line("-", 79);
7009 printf("Level xxx (file version %08d, game version %08d)\n",
7010 level->file_version, level->game_version);
7011 printf_line("-", 79);
7013 printf("Level author: '%s'\n", level->author);
7014 printf("Level title: '%s'\n", level->name);
7016 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7018 printf("Level time: %d seconds\n", level->time);
7019 printf("Gems needed: %d\n", level->gems_needed);
7021 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
7022 printf("Time for wheel: %d seconds\n", level->time_wheel);
7023 printf("Time for light: %d seconds\n", level->time_light);
7024 printf("Time for timegate: %d seconds\n", level->time_timegate);
7026 printf("Amoeba speed: %d\n", level->amoeba_speed);
7029 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7030 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7031 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7032 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7033 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7035 printf_line("-", 79);
7039 /* ========================================================================= */
7040 /* tape file functions */
7041 /* ========================================================================= */
7043 static void setTapeInfoToDefaults()
7047 /* always start with reliable default values (empty tape) */
7050 /* default values (also for pre-1.2 tapes) with only the first player */
7051 tape.player_participates[0] = TRUE;
7052 for (i = 1; i < MAX_PLAYERS; i++)
7053 tape.player_participates[i] = FALSE;
7055 /* at least one (default: the first) player participates in every tape */
7056 tape.num_participating_players = 1;
7058 tape.level_nr = level_nr;
7060 tape.changed = FALSE;
7062 tape.recording = FALSE;
7063 tape.playing = FALSE;
7064 tape.pausing = FALSE;
7066 tape.no_valid_file = FALSE;
7069 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
7071 tape->file_version = getFileVersion(file);
7072 tape->game_version = getFileVersion(file);
7077 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
7081 tape->random_seed = getFile32BitBE(file);
7082 tape->date = getFile32BitBE(file);
7083 tape->length = getFile32BitBE(file);
7085 /* read header fields that are new since version 1.2 */
7086 if (tape->file_version >= FILE_VERSION_1_2)
7088 byte store_participating_players = getFile8Bit(file);
7091 /* since version 1.2, tapes store which players participate in the tape */
7092 tape->num_participating_players = 0;
7093 for (i = 0; i < MAX_PLAYERS; i++)
7095 tape->player_participates[i] = FALSE;
7097 if (store_participating_players & (1 << i))
7099 tape->player_participates[i] = TRUE;
7100 tape->num_participating_players++;
7104 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7106 engine_version = getFileVersion(file);
7107 if (engine_version > 0)
7108 tape->engine_version = engine_version;
7110 tape->engine_version = tape->game_version;
7116 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
7118 int level_identifier_size;
7121 level_identifier_size = getFile16BitBE(file);
7123 tape->level_identifier =
7124 checked_realloc(tape->level_identifier, level_identifier_size);
7126 for (i = 0; i < level_identifier_size; i++)
7127 tape->level_identifier[i] = getFile8Bit(file);
7129 tape->level_nr = getFile16BitBE(file);
7131 chunk_size = 2 + level_identifier_size + 2;
7136 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
7139 int chunk_size_expected =
7140 (tape->num_participating_players + 1) * tape->length;
7142 if (chunk_size_expected != chunk_size)
7144 ReadUnusedBytesFromFile(file, chunk_size);
7145 return chunk_size_expected;
7148 for (i = 0; i < tape->length; i++)
7150 if (i >= MAX_TAPE_LEN)
7153 for (j = 0; j < MAX_PLAYERS; j++)
7155 tape->pos[i].action[j] = MV_NONE;
7157 if (tape->player_participates[j])
7158 tape->pos[i].action[j] = getFile8Bit(file);
7161 tape->pos[i].delay = getFile8Bit(file);
7163 if (tape->file_version == FILE_VERSION_1_0)
7165 /* eliminate possible diagonal moves in old tapes */
7166 /* this is only for backward compatibility */
7168 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7169 byte action = tape->pos[i].action[0];
7170 int k, num_moves = 0;
7172 for (k = 0; k<4; k++)
7174 if (action & joy_dir[k])
7176 tape->pos[i + num_moves].action[0] = joy_dir[k];
7178 tape->pos[i + num_moves].delay = 0;
7187 tape->length += num_moves;
7190 else if (tape->file_version < FILE_VERSION_2_0)
7192 /* convert pre-2.0 tapes to new tape format */
7194 if (tape->pos[i].delay > 1)
7197 tape->pos[i + 1] = tape->pos[i];
7198 tape->pos[i + 1].delay = 1;
7201 for (j = 0; j < MAX_PLAYERS; j++)
7202 tape->pos[i].action[j] = MV_NONE;
7203 tape->pos[i].delay--;
7214 if (i != tape->length)
7215 chunk_size = (tape->num_participating_players + 1) * i;
7220 void LoadTapeFromFilename(char *filename)
7222 char cookie[MAX_LINE_LEN];
7223 char chunk_name[CHUNK_ID_LEN + 1];
7227 /* always start with reliable default values */
7228 setTapeInfoToDefaults();
7230 if (!(file = fopen(filename, MODE_READ)))
7232 tape.no_valid_file = TRUE;
7237 getFileChunkBE(file, chunk_name, NULL);
7238 if (strEqual(chunk_name, "RND1"))
7240 getFile32BitBE(file); /* not used */
7242 getFileChunkBE(file, chunk_name, NULL);
7243 if (!strEqual(chunk_name, "TAPE"))
7245 tape.no_valid_file = TRUE;
7247 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7252 else /* check for pre-2.0 file format with cookie string */
7254 strcpy(cookie, chunk_name);
7255 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
7256 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7257 cookie[strlen(cookie) - 1] = '\0';
7259 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7261 tape.no_valid_file = TRUE;
7263 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7268 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7270 tape.no_valid_file = TRUE;
7272 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7277 /* pre-2.0 tape files have no game version, so use file version here */
7278 tape.game_version = tape.file_version;
7281 if (tape.file_version < FILE_VERSION_1_2)
7283 /* tape files from versions before 1.2.0 without chunk structure */
7284 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7285 LoadTape_BODY(file, 2 * tape.length, &tape);
7293 int (*loader)(FILE *, int, struct TapeInfo *);
7297 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7298 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7299 { "INFO", -1, LoadTape_INFO },
7300 { "BODY", -1, LoadTape_BODY },
7304 while (getFileChunkBE(file, chunk_name, &chunk_size))
7308 while (chunk_info[i].name != NULL &&
7309 !strEqual(chunk_name, chunk_info[i].name))
7312 if (chunk_info[i].name == NULL)
7314 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7315 chunk_name, filename);
7316 ReadUnusedBytesFromFile(file, chunk_size);
7318 else if (chunk_info[i].size != -1 &&
7319 chunk_info[i].size != chunk_size)
7321 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7322 chunk_size, chunk_name, filename);
7323 ReadUnusedBytesFromFile(file, chunk_size);
7327 /* call function to load this tape chunk */
7328 int chunk_size_expected =
7329 (chunk_info[i].loader)(file, chunk_size, &tape);
7331 /* the size of some chunks cannot be checked before reading other
7332 chunks first (like "HEAD" and "BODY") that contain some header
7333 information, so check them here */
7334 if (chunk_size_expected != chunk_size)
7336 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7337 chunk_size, chunk_name, filename);
7345 tape.length_seconds = GetTapeLength();
7348 printf("::: tape file version: %d\n", tape.file_version);
7349 printf("::: tape game version: %d\n", tape.game_version);
7350 printf("::: tape engine version: %d\n", tape.engine_version);
7354 void LoadTape(int nr)
7356 char *filename = getTapeFilename(nr);
7358 LoadTapeFromFilename(filename);
7361 void LoadSolutionTape(int nr)
7363 char *filename = getSolutionTapeFilename(nr);
7365 LoadTapeFromFilename(filename);
7368 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7370 putFileVersion(file, tape->file_version);
7371 putFileVersion(file, tape->game_version);
7374 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7377 byte store_participating_players = 0;
7379 /* set bits for participating players for compact storage */
7380 for (i = 0; i < MAX_PLAYERS; i++)
7381 if (tape->player_participates[i])
7382 store_participating_players |= (1 << i);
7384 putFile32BitBE(file, tape->random_seed);
7385 putFile32BitBE(file, tape->date);
7386 putFile32BitBE(file, tape->length);
7388 putFile8Bit(file, store_participating_players);
7390 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7391 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7393 putFileVersion(file, tape->engine_version);
7396 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7398 int level_identifier_size = strlen(tape->level_identifier) + 1;
7401 putFile16BitBE(file, level_identifier_size);
7403 for (i = 0; i < level_identifier_size; i++)
7404 putFile8Bit(file, tape->level_identifier[i]);
7406 putFile16BitBE(file, tape->level_nr);
7409 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7413 for (i = 0; i < tape->length; i++)
7415 for (j = 0; j < MAX_PLAYERS; j++)
7416 if (tape->player_participates[j])
7417 putFile8Bit(file, tape->pos[i].action[j]);
7419 putFile8Bit(file, tape->pos[i].delay);
7423 void SaveTape(int nr)
7425 char *filename = getTapeFilename(nr);
7428 boolean new_tape = TRUE;
7430 int num_participating_players = 0;
7431 int info_chunk_size;
7432 int body_chunk_size;
7435 InitTapeDirectory(leveldir_current->subdir);
7438 /* if a tape still exists, ask to overwrite it */
7439 if (fileExists(filename))
7442 if (!Request("Replace old tape ?", REQ_ASK))
7447 if (!(file = fopen(filename, MODE_WRITE)))
7449 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7453 tape.file_version = FILE_VERSION_ACTUAL;
7454 tape.game_version = GAME_VERSION_ACTUAL;
7456 /* count number of participating players */
7457 for (i = 0; i < MAX_PLAYERS; i++)
7458 if (tape.player_participates[i])
7459 num_participating_players++;
7461 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7462 body_chunk_size = (num_participating_players + 1) * tape.length;
7464 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7465 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7467 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7468 SaveTape_VERS(file, &tape);
7470 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7471 SaveTape_HEAD(file, &tape);
7473 putFileChunkBE(file, "INFO", info_chunk_size);
7474 SaveTape_INFO(file, &tape);
7476 putFileChunkBE(file, "BODY", body_chunk_size);
7477 SaveTape_BODY(file, &tape);
7481 SetFilePermissions(filename, PERMS_PRIVATE);
7483 tape.changed = FALSE;
7487 Request("Tape saved !", REQ_CONFIRM);
7491 boolean SaveTapeChecked(int nr)
7493 char *filename = getTapeFilename(nr);
7494 boolean new_tape = !fileExists(filename);
7495 boolean tape_saved = FALSE;
7497 if (new_tape || Request("Replace old tape ?", REQ_ASK))
7502 Request("Tape saved !", REQ_CONFIRM);
7510 void DumpTape(struct TapeInfo *tape)
7512 int tape_frame_counter;
7515 if (tape->no_valid_file)
7517 Error(ERR_WARN, "cannot dump -- no valid tape file found");
7522 printf_line("-", 79);
7523 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
7524 tape->level_nr, tape->file_version, tape->game_version);
7525 printf(" (effective engine version %08d)\n",
7526 tape->engine_version);
7527 printf("Level series identifier: '%s'\n", tape->level_identifier);
7528 printf_line("-", 79);
7530 tape_frame_counter = 0;
7532 for (i = 0; i < tape->length; i++)
7534 if (i >= MAX_TAPE_LEN)
7537 printf("%04d: ", i);
7539 for (j = 0; j < MAX_PLAYERS; j++)
7541 if (tape->player_participates[j])
7543 int action = tape->pos[i].action[j];
7545 printf("%d:%02x ", j, action);
7546 printf("[%c%c%c%c|%c%c] - ",
7547 (action & JOY_LEFT ? '<' : ' '),
7548 (action & JOY_RIGHT ? '>' : ' '),
7549 (action & JOY_UP ? '^' : ' '),
7550 (action & JOY_DOWN ? 'v' : ' '),
7551 (action & JOY_BUTTON_1 ? '1' : ' '),
7552 (action & JOY_BUTTON_2 ? '2' : ' '));
7556 printf("(%03d) ", tape->pos[i].delay);
7557 printf("[%05d]\n", tape_frame_counter);
7559 tape_frame_counter += tape->pos[i].delay;
7562 printf_line("-", 79);
7566 /* ========================================================================= */
7567 /* score file functions */
7568 /* ========================================================================= */
7570 void LoadScore(int nr)
7573 char *filename = getScoreFilename(nr);
7574 char cookie[MAX_LINE_LEN];
7575 char line[MAX_LINE_LEN];
7579 /* always start with reliable default values */
7580 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7582 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
7583 highscore[i].Score = 0;
7586 if (!(file = fopen(filename, MODE_READ)))
7589 /* check file identifier */
7590 fgets(cookie, MAX_LINE_LEN, file);
7591 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7592 cookie[strlen(cookie) - 1] = '\0';
7594 if (!checkCookieString(cookie, SCORE_COOKIE))
7596 Error(ERR_WARN, "unknown format of score file '%s'", filename);
7601 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7603 fscanf(file, "%d", &highscore[i].Score);
7604 fgets(line, MAX_LINE_LEN, file);
7606 if (line[strlen(line) - 1] == '\n')
7607 line[strlen(line) - 1] = '\0';
7609 for (line_ptr = line; *line_ptr; line_ptr++)
7611 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
7613 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
7614 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
7623 void SaveScore(int nr)
7626 char *filename = getScoreFilename(nr);
7629 InitScoreDirectory(leveldir_current->subdir);
7631 if (!(file = fopen(filename, MODE_WRITE)))
7633 Error(ERR_WARN, "cannot save score for level %d", nr);
7637 fprintf(file, "%s\n\n", SCORE_COOKIE);
7639 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7640 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
7644 SetFilePermissions(filename, PERMS_PUBLIC);
7648 /* ========================================================================= */
7649 /* setup file functions */
7650 /* ========================================================================= */
7652 #define TOKEN_STR_PLAYER_PREFIX "player_"
7655 #define SETUP_TOKEN_PLAYER_NAME 0
7656 #define SETUP_TOKEN_SOUND 1
7657 #define SETUP_TOKEN_SOUND_LOOPS 2
7658 #define SETUP_TOKEN_SOUND_MUSIC 3
7659 #define SETUP_TOKEN_SOUND_SIMPLE 4
7660 #define SETUP_TOKEN_TOONS 5
7661 #define SETUP_TOKEN_SCROLL_DELAY 6
7662 #define SETUP_TOKEN_SOFT_SCROLLING 7
7663 #define SETUP_TOKEN_FADE_SCREENS 8
7664 #define SETUP_TOKEN_AUTORECORD 9
7665 #define SETUP_TOKEN_SHOW_TITLESCREEN 10
7666 #define SETUP_TOKEN_QUICK_DOORS 11
7667 #define SETUP_TOKEN_TEAM_MODE 12
7668 #define SETUP_TOKEN_HANDICAP 13
7669 #define SETUP_TOKEN_SKIP_LEVELS 14
7670 #define SETUP_TOKEN_TIME_LIMIT 15
7671 #define SETUP_TOKEN_FULLSCREEN 16
7672 #define SETUP_TOKEN_FULLSCREEN_MODE 17
7673 #define SETUP_TOKEN_ASK_ON_ESCAPE 18
7674 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 19
7675 #define SETUP_TOKEN_QUICK_SWITCH 20
7676 #define SETUP_TOKEN_INPUT_ON_FOCUS 21
7677 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 22
7678 #define SETUP_TOKEN_GRAPHICS_SET 23
7679 #define SETUP_TOKEN_SOUNDS_SET 24
7680 #define SETUP_TOKEN_MUSIC_SET 25
7681 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 26
7682 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 27
7683 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 28
7685 #define NUM_GLOBAL_SETUP_TOKENS 29
7688 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
7689 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
7690 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
7691 #define SETUP_TOKEN_EDITOR_EL_MORE 3
7692 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 4
7693 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 5
7694 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 6
7695 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 7
7696 #define SETUP_TOKEN_EDITOR_EL_CHARS 8
7697 #define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS 9
7698 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 10
7699 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 11
7700 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 12
7701 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 13
7702 #define SETUP_TOKEN_EDITOR_EL_BY_GAME 14
7703 #define SETUP_TOKEN_EDITOR_EL_BY_TYPE 15
7704 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 16
7706 #define NUM_EDITOR_SETUP_TOKENS 17
7708 /* editor cascade setup */
7709 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
7710 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
7711 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
7712 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
7713 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
7714 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
7715 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
7716 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
7717 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
7718 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
7719 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
7720 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
7721 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
7722 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
7723 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
7725 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
7727 /* shortcut setup */
7728 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
7729 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
7730 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
7731 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
7732 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
7733 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
7734 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
7735 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
7737 #define NUM_SHORTCUT_SETUP_TOKENS 8
7740 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
7741 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
7742 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
7743 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
7744 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
7745 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
7746 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
7747 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
7748 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
7749 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
7750 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
7751 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
7752 #define SETUP_TOKEN_PLAYER_KEY_UP 12
7753 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
7754 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
7755 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
7757 #define NUM_PLAYER_SETUP_TOKENS 16
7760 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
7761 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
7763 #define NUM_SYSTEM_SETUP_TOKENS 2
7766 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
7768 #define NUM_OPTIONS_SETUP_TOKENS 1
7771 static struct SetupInfo si;
7772 static struct SetupEditorInfo sei;
7773 static struct SetupEditorCascadeInfo seci;
7774 static struct SetupShortcutInfo ssi;
7775 static struct SetupInputInfo sii;
7776 static struct SetupSystemInfo syi;
7777 static struct OptionInfo soi;
7779 static struct TokenInfo global_setup_tokens[] =
7781 { TYPE_STRING, &si.player_name, "player_name" },
7782 { TYPE_SWITCH, &si.sound, "sound" },
7783 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
7784 { TYPE_SWITCH, &si.sound_music, "background_music" },
7785 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
7786 { TYPE_SWITCH, &si.toons, "toons" },
7787 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
7788 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
7789 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
7790 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
7791 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
7792 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
7793 { TYPE_SWITCH, &si.team_mode, "team_mode" },
7794 { TYPE_SWITCH, &si.handicap, "handicap" },
7795 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
7796 { TYPE_SWITCH, &si.time_limit, "time_limit" },
7797 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
7798 { TYPE_STRING, &si.fullscreen_mode, "fullscreen_mode" },
7799 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
7800 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
7801 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
7802 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
7803 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
7804 { TYPE_STRING, &si.graphics_set, "graphics_set" },
7805 { TYPE_STRING, &si.sounds_set, "sounds_set" },
7806 { TYPE_STRING, &si.music_set, "music_set" },
7807 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
7808 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
7809 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
7812 static boolean not_used = FALSE;
7813 static struct TokenInfo editor_setup_tokens[] =
7816 { TYPE_SWITCH, ¬_used, "editor.el_boulderdash" },
7817 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine" },
7818 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine_club" },
7819 { TYPE_SWITCH, ¬_used, "editor.el_more" },
7820 { TYPE_SWITCH, ¬_used, "editor.el_sokoban" },
7821 { TYPE_SWITCH, ¬_used, "editor.el_supaplex" },
7822 { TYPE_SWITCH, ¬_used, "editor.el_diamond_caves" },
7823 { TYPE_SWITCH, ¬_used, "editor.el_dx_boulderdash" },
7825 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
7826 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
7827 { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
7828 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
7829 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
7830 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
7831 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
7832 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
7834 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
7835 { TYPE_SWITCH, &sei.el_steel_chars, "editor.el_steel_chars" },
7836 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
7838 { TYPE_SWITCH, ¬_used, "editor.el_headlines" },
7840 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
7842 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
7843 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
7844 { TYPE_SWITCH, &sei.el_by_game, "editor.el_by_game" },
7845 { TYPE_SWITCH, &sei.el_by_type, "editor.el_by_type" },
7846 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
7849 static struct TokenInfo editor_cascade_setup_tokens[] =
7851 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
7852 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
7853 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
7854 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
7855 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
7856 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
7857 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
7858 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
7859 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
7860 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
7861 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
7862 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
7863 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
7864 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
7865 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
7868 static struct TokenInfo shortcut_setup_tokens[] =
7870 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
7871 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
7872 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
7873 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
7874 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
7875 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
7876 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
7877 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
7880 static struct TokenInfo player_setup_tokens[] =
7882 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
7883 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
7884 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
7885 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
7886 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
7887 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
7888 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
7889 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
7890 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
7891 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
7892 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
7893 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
7894 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
7895 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
7896 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
7897 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
7900 static struct TokenInfo system_setup_tokens[] =
7902 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
7903 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
7906 static struct TokenInfo options_setup_tokens[] =
7908 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
7911 static char *get_corrected_login_name(char *login_name)
7913 /* needed because player name must be a fixed length string */
7914 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
7916 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
7917 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
7919 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
7920 if (strchr(login_name_new, ' '))
7921 *strchr(login_name_new, ' ') = '\0';
7923 return login_name_new;
7926 static void setSetupInfoToDefaults(struct SetupInfo *si)
7930 si->player_name = get_corrected_login_name(getLoginName());
7933 si->sound_loops = TRUE;
7934 si->sound_music = TRUE;
7935 si->sound_simple = TRUE;
7937 si->double_buffering = TRUE;
7938 si->direct_draw = !si->double_buffering;
7939 si->scroll_delay = TRUE;
7940 si->soft_scrolling = TRUE;
7941 si->fade_screens = TRUE;
7942 si->autorecord = TRUE;
7943 si->show_titlescreen = TRUE;
7944 si->quick_doors = FALSE;
7945 si->team_mode = FALSE;
7946 si->handicap = TRUE;
7947 si->skip_levels = TRUE;
7948 si->time_limit = TRUE;
7949 si->fullscreen = FALSE;
7950 si->fullscreen_mode = getStringCopy(DEFAULT_FULLSCREEN_MODE);
7951 si->ask_on_escape = TRUE;
7952 si->ask_on_escape_editor = TRUE;
7953 si->quick_switch = FALSE;
7954 si->input_on_focus = FALSE;
7955 si->prefer_aga_graphics = TRUE;
7957 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
7958 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
7959 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
7960 si->override_level_graphics = FALSE;
7961 si->override_level_sounds = FALSE;
7962 si->override_level_music = FALSE;
7964 si->editor.el_boulderdash = TRUE;
7965 si->editor.el_emerald_mine = TRUE;
7966 si->editor.el_emerald_mine_club = TRUE;
7967 si->editor.el_more = TRUE;
7968 si->editor.el_sokoban = TRUE;
7969 si->editor.el_supaplex = TRUE;
7970 si->editor.el_diamond_caves = TRUE;
7971 si->editor.el_dx_boulderdash = TRUE;
7972 si->editor.el_chars = TRUE;
7973 si->editor.el_steel_chars = TRUE;
7974 si->editor.el_custom = TRUE;
7976 si->editor.el_headlines = TRUE;
7977 si->editor.el_user_defined = FALSE;
7978 si->editor.el_dynamic = TRUE;
7980 si->editor.show_element_token = FALSE;
7982 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
7983 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
7984 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
7986 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
7987 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
7988 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
7989 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
7990 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
7992 for (i = 0; i < MAX_PLAYERS; i++)
7994 si->input[i].use_joystick = FALSE;
7995 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
7996 si->input[i].joy.xleft = JOYSTICK_XLEFT;
7997 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
7998 si->input[i].joy.xright = JOYSTICK_XRIGHT;
7999 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8000 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8001 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8002 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8003 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8004 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8005 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8006 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8007 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8008 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8009 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8012 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8013 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8015 si->options.verbose = FALSE;
8018 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8020 si->editor_cascade.el_bd = TRUE;
8021 si->editor_cascade.el_em = TRUE;
8022 si->editor_cascade.el_emc = TRUE;
8023 si->editor_cascade.el_rnd = TRUE;
8024 si->editor_cascade.el_sb = TRUE;
8025 si->editor_cascade.el_sp = TRUE;
8026 si->editor_cascade.el_dc = TRUE;
8027 si->editor_cascade.el_dx = TRUE;
8029 si->editor_cascade.el_chars = FALSE;
8030 si->editor_cascade.el_steel_chars = FALSE;
8031 si->editor_cascade.el_ce = FALSE;
8032 si->editor_cascade.el_ge = FALSE;
8033 si->editor_cascade.el_ref = FALSE;
8034 si->editor_cascade.el_user = FALSE;
8035 si->editor_cascade.el_dynamic = FALSE;
8038 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8042 if (!setup_file_hash)
8047 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8048 setSetupInfo(global_setup_tokens, i,
8049 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
8054 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8055 setSetupInfo(editor_setup_tokens, i,
8056 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
8059 /* shortcut setup */
8060 ssi = setup.shortcut;
8061 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8062 setSetupInfo(shortcut_setup_tokens, i,
8063 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
8064 setup.shortcut = ssi;
8067 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8071 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8073 sii = setup.input[pnr];
8074 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8076 char full_token[100];
8078 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8079 setSetupInfo(player_setup_tokens, i,
8080 getHashEntry(setup_file_hash, full_token));
8082 setup.input[pnr] = sii;
8087 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8088 setSetupInfo(system_setup_tokens, i,
8089 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
8093 soi = setup.options;
8094 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8095 setSetupInfo(options_setup_tokens, i,
8096 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
8097 setup.options = soi;
8100 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8104 if (!setup_file_hash)
8107 /* editor cascade setup */
8108 seci = setup.editor_cascade;
8109 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8110 setSetupInfo(editor_cascade_setup_tokens, i,
8111 getHashEntry(setup_file_hash,
8112 editor_cascade_setup_tokens[i].text));
8113 setup.editor_cascade = seci;
8118 char *filename = getSetupFilename();
8119 SetupFileHash *setup_file_hash = NULL;
8121 /* always start with reliable default values */
8122 setSetupInfoToDefaults(&setup);
8124 setup_file_hash = loadSetupFileHash(filename);
8126 if (setup_file_hash)
8128 char *player_name_new;
8130 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
8131 decodeSetupFileHash(setup_file_hash);
8133 setup.direct_draw = !setup.double_buffering;
8135 freeSetupFileHash(setup_file_hash);
8137 /* needed to work around problems with fixed length strings */
8138 player_name_new = get_corrected_login_name(setup.player_name);
8139 free(setup.player_name);
8140 setup.player_name = player_name_new;
8143 Error(ERR_WARN, "using default setup values");
8146 void LoadSetup_EditorCascade()
8148 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8149 SetupFileHash *setup_file_hash = NULL;
8151 /* always start with reliable default values */
8152 setSetupInfoToDefaults_EditorCascade(&setup);
8154 setup_file_hash = loadSetupFileHash(filename);
8156 if (setup_file_hash)
8158 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
8159 decodeSetupFileHash_EditorCascade(setup_file_hash);
8161 freeSetupFileHash(setup_file_hash);
8169 char *filename = getSetupFilename();
8173 InitUserDataDirectory();
8175 if (!(file = fopen(filename, MODE_WRITE)))
8177 Error(ERR_WARN, "cannot write setup file '%s'", filename);
8181 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
8182 getCookie("SETUP")));
8183 fprintf(file, "\n");
8187 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8189 /* just to make things nicer :) */
8190 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
8191 i == SETUP_TOKEN_GRAPHICS_SET)
8192 fprintf(file, "\n");
8194 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
8199 fprintf(file, "\n");
8200 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8201 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
8203 /* shortcut setup */
8204 ssi = setup.shortcut;
8205 fprintf(file, "\n");
8206 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8207 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
8210 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8214 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8215 fprintf(file, "\n");
8217 sii = setup.input[pnr];
8218 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8219 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
8224 fprintf(file, "\n");
8225 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8226 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
8229 soi = setup.options;
8230 fprintf(file, "\n");
8231 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8232 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
8236 SetFilePermissions(filename, PERMS_PRIVATE);
8239 void SaveSetup_EditorCascade()
8241 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8245 InitUserDataDirectory();
8247 if (!(file = fopen(filename, MODE_WRITE)))
8249 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
8254 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
8255 getCookie("SETUP")));
8256 fprintf(file, "\n");
8258 seci = setup.editor_cascade;
8259 fprintf(file, "\n");
8260 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8261 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
8265 SetFilePermissions(filename, PERMS_PRIVATE);
8270 void LoadCustomElementDescriptions()
8272 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8273 SetupFileHash *setup_file_hash;
8276 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8278 if (element_info[i].custom_description != NULL)
8280 free(element_info[i].custom_description);
8281 element_info[i].custom_description = NULL;
8285 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8288 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8290 char *token = getStringCat2(element_info[i].token_name, ".name");
8291 char *value = getHashEntry(setup_file_hash, token);
8294 element_info[i].custom_description = getStringCopy(value);
8299 freeSetupFileHash(setup_file_hash);
8302 static void LoadSpecialMenuDesignSettingsFromFilename(char *filename)
8304 SetupFileHash *setup_file_hash;
8308 printf("LoadSpecialMenuDesignSettings from file '%s' ...\n", filename);
8311 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8314 /* special case: initialize with default values that may be overwritten */
8315 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8317 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
8318 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
8319 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
8321 if (value_x != NULL)
8322 menu.draw_xoffset[i] = get_integer_from_string(value_x);
8323 if (value_y != NULL)
8324 menu.draw_yoffset[i] = get_integer_from_string(value_y);
8325 if (list_size != NULL)
8326 menu.list_size[i] = get_integer_from_string(list_size);
8329 /* special case: initialize with default values that may be overwritten */
8330 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
8332 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
8333 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
8335 if (value_x != NULL)
8336 menu.draw_xoffset_info[i] = get_integer_from_string(value_x);
8337 if (value_y != NULL)
8338 menu.draw_yoffset_info[i] = get_integer_from_string(value_y);
8341 /* read (and overwrite with) values that may be specified in config file */
8342 for (i = 0; image_config_vars[i].token != NULL; i++)
8344 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
8347 *image_config_vars[i].value =
8348 get_auto_parameter_value(image_config_vars[i].token, value);
8351 freeSetupFileHash(setup_file_hash);
8354 void LoadSpecialMenuDesignSettings()
8356 char *filename_base = UNDEFINED_FILENAME, *filename_local;
8359 /* always start with reliable default values from default config */
8360 for (i = 0; image_config_vars[i].token != NULL; i++)
8361 for (j = 0; image_config[j].token != NULL; j++)
8362 if (strEqual(image_config_vars[i].token, image_config[j].token))
8363 *image_config_vars[i].value =
8364 get_auto_parameter_value(image_config_vars[i].token,
8365 image_config[j].value);
8367 if (!SETUP_OVERRIDE_ARTWORK(setup, ARTWORK_TYPE_GRAPHICS))
8369 /* first look for special settings configured in level series config */
8370 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
8372 if (fileExists(filename_base))
8373 LoadSpecialMenuDesignSettingsFromFilename(filename_base);
8376 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8378 if (filename_local != NULL && !strEqual(filename_base, filename_local))
8379 LoadSpecialMenuDesignSettingsFromFilename(filename_local);
8382 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
8384 char *filename = getEditorSetupFilename();
8385 SetupFileList *setup_file_list, *list;
8386 SetupFileHash *element_hash;
8387 int num_unknown_tokens = 0;
8390 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
8393 element_hash = newSetupFileHash();
8395 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8396 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
8398 /* determined size may be larger than needed (due to unknown elements) */
8400 for (list = setup_file_list; list != NULL; list = list->next)
8403 /* add space for up to 3 more elements for padding that may be needed */
8406 /* free memory for old list of elements, if needed */
8407 checked_free(*elements);
8409 /* allocate memory for new list of elements */
8410 *elements = checked_malloc(*num_elements * sizeof(int));
8413 for (list = setup_file_list; list != NULL; list = list->next)
8415 char *value = getHashEntry(element_hash, list->token);
8417 if (value == NULL) /* try to find obsolete token mapping */
8419 char *mapped_token = get_mapped_token(list->token);
8421 if (mapped_token != NULL)
8423 value = getHashEntry(element_hash, mapped_token);
8431 (*elements)[(*num_elements)++] = atoi(value);
8435 if (num_unknown_tokens == 0)
8437 Error(ERR_RETURN_LINE, "-");
8438 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
8439 Error(ERR_RETURN, "- config file: '%s'", filename);
8441 num_unknown_tokens++;
8444 Error(ERR_RETURN, "- token: '%s'", list->token);
8448 if (num_unknown_tokens > 0)
8449 Error(ERR_RETURN_LINE, "-");
8451 while (*num_elements % 4) /* pad with empty elements, if needed */
8452 (*elements)[(*num_elements)++] = EL_EMPTY;
8454 freeSetupFileList(setup_file_list);
8455 freeSetupFileHash(element_hash);
8458 for (i = 0; i < *num_elements; i++)
8459 printf("editor: element '%s' [%d]\n",
8460 element_info[(*elements)[i]].token_name, (*elements)[i]);
8464 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
8467 SetupFileHash *setup_file_hash = NULL;
8468 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
8469 char *filename_music, *filename_prefix, *filename_info;
8475 token_to_value_ptr[] =
8477 { "title_header", &tmp_music_file_info.title_header },
8478 { "artist_header", &tmp_music_file_info.artist_header },
8479 { "album_header", &tmp_music_file_info.album_header },
8480 { "year_header", &tmp_music_file_info.year_header },
8482 { "title", &tmp_music_file_info.title },
8483 { "artist", &tmp_music_file_info.artist },
8484 { "album", &tmp_music_file_info.album },
8485 { "year", &tmp_music_file_info.year },
8491 filename_music = (is_sound ? getCustomSoundFilename(basename) :
8492 getCustomMusicFilename(basename));
8494 if (filename_music == NULL)
8497 /* ---------- try to replace file extension ---------- */
8499 filename_prefix = getStringCopy(filename_music);
8500 if (strrchr(filename_prefix, '.') != NULL)
8501 *strrchr(filename_prefix, '.') = '\0';
8502 filename_info = getStringCat2(filename_prefix, ".txt");
8505 printf("trying to load file '%s'...\n", filename_info);
8508 if (fileExists(filename_info))
8509 setup_file_hash = loadSetupFileHash(filename_info);
8511 free(filename_prefix);
8512 free(filename_info);
8514 if (setup_file_hash == NULL)
8516 /* ---------- try to add file extension ---------- */
8518 filename_prefix = getStringCopy(filename_music);
8519 filename_info = getStringCat2(filename_prefix, ".txt");
8522 printf("trying to load file '%s'...\n", filename_info);
8525 if (fileExists(filename_info))
8526 setup_file_hash = loadSetupFileHash(filename_info);
8528 free(filename_prefix);
8529 free(filename_info);
8532 if (setup_file_hash == NULL)
8535 /* ---------- music file info found ---------- */
8537 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
8539 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
8541 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
8543 *token_to_value_ptr[i].value_ptr =
8544 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
8547 tmp_music_file_info.basename = getStringCopy(basename);
8548 tmp_music_file_info.music = music;
8549 tmp_music_file_info.is_sound = is_sound;
8551 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
8552 *new_music_file_info = tmp_music_file_info;
8554 return new_music_file_info;
8557 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
8559 return get_music_file_info_ext(basename, music, FALSE);
8562 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
8564 return get_music_file_info_ext(basename, sound, TRUE);
8567 static boolean music_info_listed_ext(struct MusicFileInfo *list,
8568 char *basename, boolean is_sound)
8570 for (; list != NULL; list = list->next)
8571 if (list->is_sound == is_sound && strEqual(list->basename, basename))
8577 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
8579 return music_info_listed_ext(list, basename, FALSE);
8582 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
8584 return music_info_listed_ext(list, basename, TRUE);
8587 void LoadMusicInfo()
8589 char *music_directory = getCustomMusicDirectory();
8590 int num_music = getMusicListSize();
8591 int num_music_noconf = 0;
8592 int num_sounds = getSoundListSize();
8594 struct dirent *dir_entry;
8595 struct FileInfo *music, *sound;
8596 struct MusicFileInfo *next, **new;
8599 while (music_file_info != NULL)
8601 next = music_file_info->next;
8603 checked_free(music_file_info->basename);
8605 checked_free(music_file_info->title_header);
8606 checked_free(music_file_info->artist_header);
8607 checked_free(music_file_info->album_header);
8608 checked_free(music_file_info->year_header);
8610 checked_free(music_file_info->title);
8611 checked_free(music_file_info->artist);
8612 checked_free(music_file_info->album);
8613 checked_free(music_file_info->year);
8615 free(music_file_info);
8617 music_file_info = next;
8620 new = &music_file_info;
8622 for (i = 0; i < num_music; i++)
8624 music = getMusicListEntry(i);
8626 if (music->filename == NULL)
8629 if (strEqual(music->filename, UNDEFINED_FILENAME))
8632 /* a configured file may be not recognized as music */
8633 if (!FileIsMusic(music->filename))
8637 printf("::: -> '%s' (configured)\n", music->filename);
8640 if (!music_info_listed(music_file_info, music->filename))
8642 *new = get_music_file_info(music->filename, i);
8644 new = &(*new)->next;
8648 if ((dir = opendir(music_directory)) == NULL)
8650 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
8654 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
8656 char *basename = dir_entry->d_name;
8657 boolean music_already_used = FALSE;
8660 /* skip all music files that are configured in music config file */
8661 for (i = 0; i < num_music; i++)
8663 music = getMusicListEntry(i);
8665 if (music->filename == NULL)
8668 if (strEqual(basename, music->filename))
8670 music_already_used = TRUE;
8675 if (music_already_used)
8678 if (!FileIsMusic(basename))
8682 printf("::: -> '%s' (found in directory)\n", basename);
8685 if (!music_info_listed(music_file_info, basename))
8687 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
8689 new = &(*new)->next;
8697 for (i = 0; i < num_sounds; i++)
8699 sound = getSoundListEntry(i);
8701 if (sound->filename == NULL)
8704 if (strEqual(sound->filename, UNDEFINED_FILENAME))
8707 /* a configured file may be not recognized as sound */
8708 if (!FileIsSound(sound->filename))
8712 printf("::: -> '%s' (configured)\n", sound->filename);
8715 if (!sound_info_listed(music_file_info, sound->filename))
8717 *new = get_sound_file_info(sound->filename, i);
8719 new = &(*new)->next;
8724 for (next = music_file_info; next != NULL; next = next->next)
8725 printf("::: title == '%s'\n", next->title);
8729 void add_helpanim_entry(int element, int action, int direction, int delay,
8730 int *num_list_entries)
8732 struct HelpAnimInfo *new_list_entry;
8733 (*num_list_entries)++;
8736 checked_realloc(helpanim_info,
8737 *num_list_entries * sizeof(struct HelpAnimInfo));
8738 new_list_entry = &helpanim_info[*num_list_entries - 1];
8740 new_list_entry->element = element;
8741 new_list_entry->action = action;
8742 new_list_entry->direction = direction;
8743 new_list_entry->delay = delay;
8746 void print_unknown_token(char *filename, char *token, int token_nr)
8750 Error(ERR_RETURN_LINE, "-");
8751 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
8752 Error(ERR_RETURN, "- config file: '%s'", filename);
8755 Error(ERR_RETURN, "- token: '%s'", token);
8758 void print_unknown_token_end(int token_nr)
8761 Error(ERR_RETURN_LINE, "-");
8764 void LoadHelpAnimInfo()
8766 char *filename = getHelpAnimFilename();
8767 SetupFileList *setup_file_list = NULL, *list;
8768 SetupFileHash *element_hash, *action_hash, *direction_hash;
8769 int num_list_entries = 0;
8770 int num_unknown_tokens = 0;
8773 if (fileExists(filename))
8774 setup_file_list = loadSetupFileList(filename);
8776 if (setup_file_list == NULL)
8778 /* use reliable default values from static configuration */
8779 SetupFileList *insert_ptr;
8781 insert_ptr = setup_file_list =
8782 newSetupFileList(helpanim_config[0].token,
8783 helpanim_config[0].value);
8785 for (i = 1; helpanim_config[i].token; i++)
8786 insert_ptr = addListEntry(insert_ptr,
8787 helpanim_config[i].token,
8788 helpanim_config[i].value);
8791 element_hash = newSetupFileHash();
8792 action_hash = newSetupFileHash();
8793 direction_hash = newSetupFileHash();
8795 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
8796 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
8798 for (i = 0; i < NUM_ACTIONS; i++)
8799 setHashEntry(action_hash, element_action_info[i].suffix,
8800 i_to_a(element_action_info[i].value));
8802 /* do not store direction index (bit) here, but direction value! */
8803 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
8804 setHashEntry(direction_hash, element_direction_info[i].suffix,
8805 i_to_a(1 << element_direction_info[i].value));
8807 for (list = setup_file_list; list != NULL; list = list->next)
8809 char *element_token, *action_token, *direction_token;
8810 char *element_value, *action_value, *direction_value;
8811 int delay = atoi(list->value);
8813 if (strEqual(list->token, "end"))
8815 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
8820 /* first try to break element into element/action/direction parts;
8821 if this does not work, also accept combined "element[.act][.dir]"
8822 elements (like "dynamite.active"), which are unique elements */
8824 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
8826 element_value = getHashEntry(element_hash, list->token);
8827 if (element_value != NULL) /* element found */
8828 add_helpanim_entry(atoi(element_value), -1, -1, delay,
8832 /* no further suffixes found -- this is not an element */
8833 print_unknown_token(filename, list->token, num_unknown_tokens++);
8839 /* token has format "<prefix>.<something>" */
8841 action_token = strchr(list->token, '.'); /* suffix may be action ... */
8842 direction_token = action_token; /* ... or direction */
8844 element_token = getStringCopy(list->token);
8845 *strchr(element_token, '.') = '\0';
8847 element_value = getHashEntry(element_hash, element_token);
8849 if (element_value == NULL) /* this is no element */
8851 element_value = getHashEntry(element_hash, list->token);
8852 if (element_value != NULL) /* combined element found */
8853 add_helpanim_entry(atoi(element_value), -1, -1, delay,
8856 print_unknown_token(filename, list->token, num_unknown_tokens++);
8858 free(element_token);
8863 action_value = getHashEntry(action_hash, action_token);
8865 if (action_value != NULL) /* action found */
8867 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
8870 free(element_token);
8875 direction_value = getHashEntry(direction_hash, direction_token);
8877 if (direction_value != NULL) /* direction found */
8879 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
8882 free(element_token);
8887 if (strchr(action_token + 1, '.') == NULL)
8889 /* no further suffixes found -- this is not an action nor direction */
8891 element_value = getHashEntry(element_hash, list->token);
8892 if (element_value != NULL) /* combined element found */
8893 add_helpanim_entry(atoi(element_value), -1, -1, delay,
8896 print_unknown_token(filename, list->token, num_unknown_tokens++);
8898 free(element_token);
8903 /* token has format "<prefix>.<suffix>.<something>" */
8905 direction_token = strchr(action_token + 1, '.');
8907 action_token = getStringCopy(action_token);
8908 *strchr(action_token + 1, '.') = '\0';
8910 action_value = getHashEntry(action_hash, action_token);
8912 if (action_value == NULL) /* this is no action */
8914 element_value = getHashEntry(element_hash, list->token);
8915 if (element_value != NULL) /* combined element found */
8916 add_helpanim_entry(atoi(element_value), -1, -1, delay,
8919 print_unknown_token(filename, list->token, num_unknown_tokens++);
8921 free(element_token);
8927 direction_value = getHashEntry(direction_hash, direction_token);
8929 if (direction_value != NULL) /* direction found */
8931 add_helpanim_entry(atoi(element_value), atoi(action_value),
8932 atoi(direction_value), delay, &num_list_entries);
8934 free(element_token);
8940 /* this is no direction */
8942 element_value = getHashEntry(element_hash, list->token);
8943 if (element_value != NULL) /* combined element found */
8944 add_helpanim_entry(atoi(element_value), -1, -1, delay,
8947 print_unknown_token(filename, list->token, num_unknown_tokens++);
8949 free(element_token);
8953 print_unknown_token_end(num_unknown_tokens);
8955 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
8956 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
8958 freeSetupFileList(setup_file_list);
8959 freeSetupFileHash(element_hash);
8960 freeSetupFileHash(action_hash);
8961 freeSetupFileHash(direction_hash);
8964 for (i = 0; i < num_list_entries; i++)
8965 printf("::: '%s': %d, %d, %d => %d\n",
8966 EL_NAME(helpanim_info[i].element),
8967 helpanim_info[i].element,
8968 helpanim_info[i].action,
8969 helpanim_info[i].direction,
8970 helpanim_info[i].delay);
8974 void LoadHelpTextInfo()
8976 char *filename = getHelpTextFilename();
8979 if (helptext_info != NULL)
8981 freeSetupFileHash(helptext_info);
8982 helptext_info = NULL;
8985 if (fileExists(filename))
8986 helptext_info = loadSetupFileHash(filename);
8988 if (helptext_info == NULL)
8990 /* use reliable default values from static configuration */
8991 helptext_info = newSetupFileHash();
8993 for (i = 0; helptext_config[i].token; i++)
8994 setHashEntry(helptext_info,
8995 helptext_config[i].token,
8996 helptext_config[i].value);
9000 BEGIN_HASH_ITERATION(helptext_info, itr)
9002 printf("::: '%s' => '%s'\n",
9003 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
9005 END_HASH_ITERATION(hash, itr)
9010 /* ------------------------------------------------------------------------- *
9012 * ------------------------------------------------------------------------- */
9014 #define MAX_NUM_CONVERT_LEVELS 1000
9016 void ConvertLevels()
9018 static LevelDirTree *convert_leveldir = NULL;
9019 static int convert_level_nr = -1;
9020 static int num_levels_handled = 0;
9021 static int num_levels_converted = 0;
9022 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
9025 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9026 global.convert_leveldir);
9028 if (convert_leveldir == NULL)
9029 Error(ERR_EXIT, "no such level identifier: '%s'",
9030 global.convert_leveldir);
9032 leveldir_current = convert_leveldir;
9034 if (global.convert_level_nr != -1)
9036 convert_leveldir->first_level = global.convert_level_nr;
9037 convert_leveldir->last_level = global.convert_level_nr;
9040 convert_level_nr = convert_leveldir->first_level;
9042 printf_line("=", 79);
9043 printf("Converting levels\n");
9044 printf_line("-", 79);
9045 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
9046 printf("Level series name: '%s'\n", convert_leveldir->name);
9047 printf("Level series author: '%s'\n", convert_leveldir->author);
9048 printf("Number of levels: %d\n", convert_leveldir->levels);
9049 printf_line("=", 79);
9052 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
9053 levels_failed[i] = FALSE;
9055 while (convert_level_nr <= convert_leveldir->last_level)
9057 char *level_filename;
9060 level_nr = convert_level_nr++;
9062 printf("Level %03d: ", level_nr);
9064 LoadLevel(level_nr);
9065 if (level.no_valid_file)
9067 printf("(no level)\n");
9071 printf("converting level ... ");
9073 level_filename = getDefaultLevelFilename(level_nr);
9074 new_level = !fileExists(level_filename);
9078 SaveLevel(level_nr);
9080 num_levels_converted++;
9082 printf("converted.\n");
9086 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
9087 levels_failed[level_nr] = TRUE;
9089 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
9092 num_levels_handled++;
9096 printf_line("=", 79);
9097 printf("Number of levels handled: %d\n", num_levels_handled);
9098 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
9099 (num_levels_handled ?
9100 num_levels_converted * 100 / num_levels_handled : 0));
9101 printf_line("-", 79);
9102 printf("Summary (for automatic parsing by scripts):\n");
9103 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
9104 convert_leveldir->identifier, num_levels_converted,
9106 (num_levels_handled ?
9107 num_levels_converted * 100 / num_levels_handled : 0));
9109 if (num_levels_handled != num_levels_converted)
9111 printf(", FAILED:");
9112 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
9113 if (levels_failed[i])
9118 printf_line("=", 79);