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;
4063 /* ------------------------------------------------------------------------- */
4064 /* functions for loading generic level */
4065 /* ------------------------------------------------------------------------- */
4067 void LoadLevelFromFileInfo(struct LevelInfo *level,
4068 struct LevelFileInfo *level_file_info)
4070 /* always start with reliable default values */
4071 setLevelInfoToDefaults(level);
4073 switch (level_file_info->type)
4075 case LEVEL_FILE_TYPE_RND:
4076 LoadLevelFromFileInfo_RND(level, level_file_info);
4079 case LEVEL_FILE_TYPE_EM:
4080 LoadLevelFromFileInfo_EM(level, level_file_info);
4081 level->game_engine_type = GAME_ENGINE_TYPE_EM;
4084 case LEVEL_FILE_TYPE_SP:
4085 LoadLevelFromFileInfo_SP(level, level_file_info);
4089 LoadLevelFromFileInfo_RND(level, level_file_info);
4093 /* if level file is invalid, restore level structure to default values */
4094 if (level->no_valid_file)
4095 setLevelInfoToDefaults(level);
4097 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
4098 level->game_engine_type = GAME_ENGINE_TYPE_RND;
4100 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
4101 CopyNativeLevel_Native_to_RND(level);
4104 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
4106 static struct LevelFileInfo level_file_info;
4108 /* always start with reliable default values */
4109 setFileInfoToDefaults(&level_file_info);
4111 level_file_info.nr = 0; /* unknown level number */
4112 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
4113 level_file_info.filename = filename;
4115 LoadLevelFromFileInfo(level, &level_file_info);
4118 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
4122 if (leveldir_current == NULL) /* only when dumping level */
4125 /* all engine modifications also valid for levels which use latest engine */
4126 if (level->game_version < VERSION_IDENT(3,2,0,5))
4128 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
4129 level->score[SC_TIME_BONUS] /= 10;
4133 leveldir_current->latest_engine = TRUE; /* !!! TEST ONLY !!! */
4136 if (leveldir_current->latest_engine)
4138 /* ---------- use latest game engine ----------------------------------- */
4140 /* For all levels which are forced to use the latest game engine version
4141 (normally all but user contributed, private and undefined levels), set
4142 the game engine version to the actual version; this allows for actual
4143 corrections in the game engine to take effect for existing, converted
4144 levels (from "classic" or other existing games) to make the emulation
4145 of the corresponding game more accurate, while (hopefully) not breaking
4146 existing levels created from other players. */
4148 level->game_version = GAME_VERSION_ACTUAL;
4150 /* Set special EM style gems behaviour: EM style gems slip down from
4151 normal, steel and growing wall. As this is a more fundamental change,
4152 it seems better to set the default behaviour to "off" (as it is more
4153 natural) and make it configurable in the level editor (as a property
4154 of gem style elements). Already existing converted levels (neither
4155 private nor contributed levels) are changed to the new behaviour. */
4157 if (level->file_version < FILE_VERSION_2_0)
4158 level->em_slippery_gems = TRUE;
4163 /* ---------- use game engine the level was created with ----------------- */
4165 /* For all levels which are not forced to use the latest game engine
4166 version (normally user contributed, private and undefined levels),
4167 use the version of the game engine the levels were created for.
4169 Since 2.0.1, the game engine version is now directly stored
4170 in the level file (chunk "VERS"), so there is no need anymore
4171 to set the game version from the file version (except for old,
4172 pre-2.0 levels, where the game version is still taken from the
4173 file format version used to store the level -- see above). */
4175 /* player was faster than enemies in 1.0.0 and before */
4176 if (level->file_version == FILE_VERSION_1_0)
4177 for (i = 0; i < MAX_PLAYERS; i++)
4178 level->initial_player_stepsize[i] = STEPSIZE_FAST;
4180 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
4181 if (level->game_version == VERSION_IDENT(2,0,1,0))
4182 level->em_slippery_gems = TRUE;
4184 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
4185 if (level->game_version < VERSION_IDENT(2,2,0,0))
4186 level->use_spring_bug = TRUE;
4188 if (level->game_version < VERSION_IDENT(3,2,0,5))
4190 /* time orb caused limited time in endless time levels before 3.2.0-5 */
4191 level->use_time_orb_bug = TRUE;
4193 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
4194 level->block_snap_field = FALSE;
4196 /* extra time score was same value as time left score before 3.2.0-5 */
4197 level->extra_time_score = level->score[SC_TIME_BONUS];
4200 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
4201 level->score[SC_TIME_BONUS] /= 10;
4205 if (level->game_version < VERSION_IDENT(3,2,0,7))
4207 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
4208 level->continuous_snapping = FALSE;
4211 /* only few elements were able to actively move into acid before 3.1.0 */
4212 /* trigger settings did not exist before 3.1.0; set to default "any" */
4213 if (level->game_version < VERSION_IDENT(3,1,0,0))
4215 /* correct "can move into acid" settings (all zero in old levels) */
4217 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
4218 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
4220 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
4221 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
4222 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
4223 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
4225 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4226 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
4228 /* correct trigger settings (stored as zero == "none" in old levels) */
4230 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4232 int element = EL_CUSTOM_START + i;
4233 struct ElementInfo *ei = &element_info[element];
4235 for (j = 0; j < ei->num_change_pages; j++)
4237 struct ElementChangeInfo *change = &ei->change_page[j];
4239 change->trigger_player = CH_PLAYER_ANY;
4240 change->trigger_page = CH_PAGE_ANY;
4245 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
4247 int element = EL_CUSTOM_START + 255;
4248 struct ElementInfo *ei = &element_info[element];
4249 struct ElementChangeInfo *change = &ei->change_page[0];
4251 /* This is needed to fix a problem that was caused by a bugfix in function
4252 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
4253 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
4254 not replace walkable elements, but instead just placed the player on it,
4255 without placing the Sokoban field under the player). Unfortunately, this
4256 breaks "Snake Bite" style levels when the snake is halfway through a door
4257 that just closes (the snake head is still alive and can be moved in this
4258 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
4259 player (without Sokoban element) which then gets killed as designed). */
4261 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
4262 strncmp(ei->description, "pause b4 death", 14) == 0) &&
4263 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
4264 change->target_element = EL_PLAYER_1;
4268 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
4272 /* map custom element change events that have changed in newer versions
4273 (these following values were accidentally changed in version 3.0.1)
4274 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
4275 if (level->game_version <= VERSION_IDENT(3,0,0,0))
4277 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4279 int element = EL_CUSTOM_START + i;
4281 /* order of checking and copying events to be mapped is important */
4282 /* (do not change the start and end value -- they are constant) */
4283 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
4285 if (HAS_CHANGE_EVENT(element, j - 2))
4287 SET_CHANGE_EVENT(element, j - 2, FALSE);
4288 SET_CHANGE_EVENT(element, j, TRUE);
4292 /* order of checking and copying events to be mapped is important */
4293 /* (do not change the start and end value -- they are constant) */
4294 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
4296 if (HAS_CHANGE_EVENT(element, j - 1))
4298 SET_CHANGE_EVENT(element, j - 1, FALSE);
4299 SET_CHANGE_EVENT(element, j, TRUE);
4305 /* initialize "can_change" field for old levels with only one change page */
4306 if (level->game_version <= VERSION_IDENT(3,0,2,0))
4308 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4310 int element = EL_CUSTOM_START + i;
4312 if (CAN_CHANGE(element))
4313 element_info[element].change->can_change = TRUE;
4317 /* correct custom element values (for old levels without these options) */
4318 if (level->game_version < VERSION_IDENT(3,1,1,0))
4320 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4322 int element = EL_CUSTOM_START + i;
4323 struct ElementInfo *ei = &element_info[element];
4325 if (ei->access_direction == MV_NO_DIRECTION)
4326 ei->access_direction = MV_ALL_DIRECTIONS;
4330 /* correct custom element values (fix invalid values for all versions) */
4333 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4335 int element = EL_CUSTOM_START + i;
4336 struct ElementInfo *ei = &element_info[element];
4338 for (j = 0; j < ei->num_change_pages; j++)
4340 struct ElementChangeInfo *change = &ei->change_page[j];
4342 if (change->trigger_player == CH_PLAYER_NONE)
4343 change->trigger_player = CH_PLAYER_ANY;
4345 if (change->trigger_side == CH_SIDE_NONE)
4346 change->trigger_side = CH_SIDE_ANY;
4351 /* initialize "can_explode" field for old levels which did not store this */
4352 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
4353 if (level->game_version <= VERSION_IDENT(3,1,0,0))
4355 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4357 int element = EL_CUSTOM_START + i;
4359 if (EXPLODES_1X1_OLD(element))
4360 element_info[element].explosion_type = EXPLODES_1X1;
4362 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
4363 EXPLODES_SMASHED(element) ||
4364 EXPLODES_IMPACT(element)));
4368 /* correct previously hard-coded move delay values for maze runner style */
4369 if (level->game_version < VERSION_IDENT(3,1,1,0))
4371 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4373 int element = EL_CUSTOM_START + i;
4375 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
4377 /* previously hard-coded and therefore ignored */
4378 element_info[element].move_delay_fixed = 9;
4379 element_info[element].move_delay_random = 0;
4384 /* map elements that have changed in newer versions */
4385 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
4386 level->game_version);
4387 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4388 for (x = 0; x < 3; x++)
4389 for (y = 0; y < 3; y++)
4390 level->yamyam_content[i].e[x][y] =
4391 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
4392 level->game_version);
4394 /* initialize element properties for level editor etc. */
4395 InitElementPropertiesEngine(level->game_version);
4396 InitElementPropertiesAfterLoading(level->game_version);
4399 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
4403 /* map elements that have changed in newer versions */
4404 for (y = 0; y < level->fieldy; y++)
4405 for (x = 0; x < level->fieldx; x++)
4406 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
4407 level->game_version);
4409 /* copy elements to runtime playfield array */
4410 for (x = 0; x < MAX_LEV_FIELDX; x++)
4411 for (y = 0; y < MAX_LEV_FIELDY; y++)
4412 Feld[x][y] = level->field[x][y];
4414 /* initialize level size variables for faster access */
4415 lev_fieldx = level->fieldx;
4416 lev_fieldy = level->fieldy;
4418 /* determine border element for this level */
4422 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
4424 struct LevelFileInfo *level_file_info = &level->file_info;
4426 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
4427 CopyNativeLevel_RND_to_Native(level);
4430 void LoadLevelTemplate(int nr)
4434 setLevelFileInfo(&level_template.file_info, nr);
4435 filename = level_template.file_info.filename;
4437 LoadLevelFromFileInfo(&level_template, &level_template.file_info);
4439 LoadLevel_InitVersion(&level_template, filename);
4440 LoadLevel_InitElements(&level_template, filename);
4442 ActivateLevelTemplate();
4445 void LoadLevel(int nr)
4449 setLevelFileInfo(&level.file_info, nr);
4450 filename = level.file_info.filename;
4452 LoadLevelFromFileInfo(&level, &level.file_info);
4454 if (level.use_custom_template)
4455 LoadLevelTemplate(-1);
4457 LoadLevel_InitVersion(&level, filename);
4458 LoadLevel_InitElements(&level, filename);
4459 LoadLevel_InitPlayfield(&level, filename);
4461 LoadLevel_InitNativeEngines(&level, filename);
4464 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
4468 chunk_size += putFileVersion(file, level->file_version);
4469 chunk_size += putFileVersion(file, level->game_version);
4474 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
4478 chunk_size += putFile16BitBE(file, level->creation_date.year);
4479 chunk_size += putFile8Bit(file, level->creation_date.month);
4480 chunk_size += putFile8Bit(file, level->creation_date.day);
4486 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
4490 putFile8Bit(file, level->fieldx);
4491 putFile8Bit(file, level->fieldy);
4493 putFile16BitBE(file, level->time);
4494 putFile16BitBE(file, level->gems_needed);
4496 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
4497 putFile8Bit(file, level->name[i]);
4499 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4500 putFile8Bit(file, level->score[i]);
4502 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
4503 for (y = 0; y < 3; y++)
4504 for (x = 0; x < 3; x++)
4505 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
4506 level->yamyam_content[i].e[x][y]));
4507 putFile8Bit(file, level->amoeba_speed);
4508 putFile8Bit(file, level->time_magic_wall);
4509 putFile8Bit(file, level->time_wheel);
4510 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
4511 level->amoeba_content));
4512 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
4513 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
4514 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
4515 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
4517 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
4519 putFile8Bit(file, (level->block_last_field ? 1 : 0));
4520 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
4521 putFile32BitBE(file, level->can_move_into_acid_bits);
4522 putFile8Bit(file, level->dont_collide_with_bits);
4524 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
4525 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
4527 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
4528 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
4529 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
4531 putFile8Bit(file, level->game_engine_type);
4533 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
4537 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
4542 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
4543 chunk_size += putFile8Bit(file, level->name[i]);
4548 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
4553 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
4554 chunk_size += putFile8Bit(file, level->author[i]);
4560 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
4565 for (y = 0; y < level->fieldy; y++)
4566 for (x = 0; x < level->fieldx; x++)
4567 if (level->encoding_16bit_field)
4568 chunk_size += putFile16BitBE(file, level->field[x][y]);
4570 chunk_size += putFile8Bit(file, level->field[x][y]);
4576 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
4581 for (y = 0; y < level->fieldy; y++)
4582 for (x = 0; x < level->fieldx; x++)
4583 chunk_size += putFile16BitBE(file, level->field[x][y]);
4589 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
4593 putFile8Bit(file, EL_YAMYAM);
4594 putFile8Bit(file, level->num_yamyam_contents);
4595 putFile8Bit(file, 0);
4596 putFile8Bit(file, 0);
4598 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4599 for (y = 0; y < 3; y++)
4600 for (x = 0; x < 3; x++)
4601 if (level->encoding_16bit_field)
4602 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
4604 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
4609 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
4612 int num_contents, content_xsize, content_ysize;
4613 int content_array[MAX_ELEMENT_CONTENTS][3][3];
4615 if (element == EL_YAMYAM)
4617 num_contents = level->num_yamyam_contents;
4621 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4622 for (y = 0; y < 3; y++)
4623 for (x = 0; x < 3; x++)
4624 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
4626 else if (element == EL_BD_AMOEBA)
4632 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4633 for (y = 0; y < 3; y++)
4634 for (x = 0; x < 3; x++)
4635 content_array[i][x][y] = EL_EMPTY;
4636 content_array[0][0][0] = level->amoeba_content;
4640 /* chunk header already written -- write empty chunk data */
4641 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
4643 Error(ERR_WARN, "cannot save content for element '%d'", element);
4647 putFile16BitBE(file, element);
4648 putFile8Bit(file, num_contents);
4649 putFile8Bit(file, content_xsize);
4650 putFile8Bit(file, content_ysize);
4652 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
4654 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4655 for (y = 0; y < 3; y++)
4656 for (x = 0; x < 3; x++)
4657 putFile16BitBE(file, content_array[i][x][y]);
4662 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
4664 int envelope_nr = element - EL_ENVELOPE_1;
4665 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
4669 chunk_size += putFile16BitBE(file, element);
4670 chunk_size += putFile16BitBE(file, envelope_len);
4671 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
4672 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
4674 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
4675 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
4677 for (i = 0; i < envelope_len; i++)
4678 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
4685 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
4686 int num_changed_custom_elements)
4690 putFile16BitBE(file, num_changed_custom_elements);
4692 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4694 int element = EL_CUSTOM_START + i;
4696 struct ElementInfo *ei = &element_info[element];
4698 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
4700 if (check < num_changed_custom_elements)
4702 putFile16BitBE(file, element);
4703 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
4710 if (check != num_changed_custom_elements) /* should not happen */
4711 Error(ERR_WARN, "inconsistent number of custom element properties");
4716 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
4717 int num_changed_custom_elements)
4721 putFile16BitBE(file, num_changed_custom_elements);
4723 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4725 int element = EL_CUSTOM_START + i;
4727 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
4729 if (check < num_changed_custom_elements)
4731 putFile16BitBE(file, element);
4732 putFile16BitBE(file, element_info[element].change->target_element);
4739 if (check != num_changed_custom_elements) /* should not happen */
4740 Error(ERR_WARN, "inconsistent number of custom target elements");
4745 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
4746 int num_changed_custom_elements)
4748 int i, j, x, y, check = 0;
4750 putFile16BitBE(file, num_changed_custom_elements);
4752 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4754 int element = EL_CUSTOM_START + i;
4755 struct ElementInfo *ei = &element_info[element];
4757 if (ei->modified_settings)
4759 if (check < num_changed_custom_elements)
4761 putFile16BitBE(file, element);
4763 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
4764 putFile8Bit(file, ei->description[j]);
4766 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
4768 /* some free bytes for future properties and padding */
4769 WriteUnusedBytesToFile(file, 7);
4771 putFile8Bit(file, ei->use_gfx_element);
4772 putFile16BitBE(file, ei->gfx_element);
4774 putFile8Bit(file, ei->collect_score_initial);
4775 putFile8Bit(file, ei->collect_count_initial);
4777 putFile16BitBE(file, ei->push_delay_fixed);
4778 putFile16BitBE(file, ei->push_delay_random);
4779 putFile16BitBE(file, ei->move_delay_fixed);
4780 putFile16BitBE(file, ei->move_delay_random);
4782 putFile16BitBE(file, ei->move_pattern);
4783 putFile8Bit(file, ei->move_direction_initial);
4784 putFile8Bit(file, ei->move_stepsize);
4786 for (y = 0; y < 3; y++)
4787 for (x = 0; x < 3; x++)
4788 putFile16BitBE(file, ei->content.e[x][y]);
4790 putFile32BitBE(file, ei->change->events);
4792 putFile16BitBE(file, ei->change->target_element);
4794 putFile16BitBE(file, ei->change->delay_fixed);
4795 putFile16BitBE(file, ei->change->delay_random);
4796 putFile16BitBE(file, ei->change->delay_frames);
4798 putFile16BitBE(file, ei->change->trigger_element);
4800 putFile8Bit(file, ei->change->explode);
4801 putFile8Bit(file, ei->change->use_target_content);
4802 putFile8Bit(file, ei->change->only_if_complete);
4803 putFile8Bit(file, ei->change->use_random_replace);
4805 putFile8Bit(file, ei->change->random_percentage);
4806 putFile8Bit(file, ei->change->replace_when);
4808 for (y = 0; y < 3; y++)
4809 for (x = 0; x < 3; x++)
4810 putFile16BitBE(file, ei->change->content.e[x][y]);
4812 putFile8Bit(file, ei->slippery_type);
4814 /* some free bytes for future properties and padding */
4815 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
4822 if (check != num_changed_custom_elements) /* should not happen */
4823 Error(ERR_WARN, "inconsistent number of custom element properties");
4828 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
4830 struct ElementInfo *ei = &element_info[element];
4833 /* ---------- custom element base property values (96 bytes) ------------- */
4835 putFile16BitBE(file, element);
4837 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
4838 putFile8Bit(file, ei->description[i]);
4840 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
4842 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
4844 putFile8Bit(file, ei->num_change_pages);
4846 putFile16BitBE(file, ei->ce_value_fixed_initial);
4847 putFile16BitBE(file, ei->ce_value_random_initial);
4848 putFile8Bit(file, ei->use_last_ce_value);
4850 putFile8Bit(file, ei->use_gfx_element);
4851 putFile16BitBE(file, ei->gfx_element);
4853 putFile8Bit(file, ei->collect_score_initial);
4854 putFile8Bit(file, ei->collect_count_initial);
4856 putFile8Bit(file, ei->drop_delay_fixed);
4857 putFile8Bit(file, ei->push_delay_fixed);
4858 putFile8Bit(file, ei->drop_delay_random);
4859 putFile8Bit(file, ei->push_delay_random);
4860 putFile16BitBE(file, ei->move_delay_fixed);
4861 putFile16BitBE(file, ei->move_delay_random);
4863 /* bits 0 - 15 of "move_pattern" ... */
4864 putFile16BitBE(file, ei->move_pattern & 0xffff);
4865 putFile8Bit(file, ei->move_direction_initial);
4866 putFile8Bit(file, ei->move_stepsize);
4868 putFile8Bit(file, ei->slippery_type);
4870 for (y = 0; y < 3; y++)
4871 for (x = 0; x < 3; x++)
4872 putFile16BitBE(file, ei->content.e[x][y]);
4874 putFile16BitBE(file, ei->move_enter_element);
4875 putFile16BitBE(file, ei->move_leave_element);
4876 putFile8Bit(file, ei->move_leave_type);
4878 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
4879 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
4881 putFile8Bit(file, ei->access_direction);
4883 putFile8Bit(file, ei->explosion_delay);
4884 putFile8Bit(file, ei->ignition_delay);
4885 putFile8Bit(file, ei->explosion_type);
4887 /* some free bytes for future custom property values and padding */
4888 WriteUnusedBytesToFile(file, 1);
4890 /* ---------- change page property values (48 bytes) --------------------- */
4892 for (i = 0; i < ei->num_change_pages; i++)
4894 struct ElementChangeInfo *change = &ei->change_page[i];
4895 unsigned int event_bits;
4897 /* bits 0 - 31 of "has_event[]" ... */
4899 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
4900 if (change->has_event[j])
4901 event_bits |= (1 << j);
4902 putFile32BitBE(file, event_bits);
4904 putFile16BitBE(file, change->target_element);
4906 putFile16BitBE(file, change->delay_fixed);
4907 putFile16BitBE(file, change->delay_random);
4908 putFile16BitBE(file, change->delay_frames);
4910 putFile16BitBE(file, change->trigger_element);
4912 putFile8Bit(file, change->explode);
4913 putFile8Bit(file, change->use_target_content);
4914 putFile8Bit(file, change->only_if_complete);
4915 putFile8Bit(file, change->use_random_replace);
4917 putFile8Bit(file, change->random_percentage);
4918 putFile8Bit(file, change->replace_when);
4920 for (y = 0; y < 3; y++)
4921 for (x = 0; x < 3; x++)
4922 putFile16BitBE(file, change->target_content.e[x][y]);
4924 putFile8Bit(file, change->can_change);
4926 putFile8Bit(file, change->trigger_side);
4928 putFile8Bit(file, change->trigger_player);
4929 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
4930 log_2(change->trigger_page)));
4932 putFile8Bit(file, change->has_action);
4933 putFile8Bit(file, change->action_type);
4934 putFile8Bit(file, change->action_mode);
4935 putFile16BitBE(file, change->action_arg);
4937 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
4939 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
4940 if (change->has_event[j])
4941 event_bits |= (1 << (j - 32));
4942 putFile8Bit(file, event_bits);
4948 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
4950 struct ElementInfo *ei = &element_info[element];
4951 struct ElementGroupInfo *group = ei->group;
4954 putFile16BitBE(file, element);
4956 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
4957 putFile8Bit(file, ei->description[i]);
4959 putFile8Bit(file, group->num_elements);
4961 putFile8Bit(file, ei->use_gfx_element);
4962 putFile16BitBE(file, ei->gfx_element);
4964 putFile8Bit(file, group->choice_mode);
4966 /* some free bytes for future values and padding */
4967 WriteUnusedBytesToFile(file, 3);
4969 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
4970 putFile16BitBE(file, group->element[i]);
4974 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
4975 boolean write_element)
4977 int save_type = entry->save_type;
4978 int data_type = entry->data_type;
4979 int conf_type = entry->conf_type;
4980 int byte_mask = conf_type & CONF_MASK_BYTES;
4981 int element = entry->element;
4982 int default_value = entry->default_value;
4984 boolean modified = FALSE;
4986 if (byte_mask != CONF_MASK_MULTI_BYTES)
4988 void *value_ptr = entry->value;
4989 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
4992 /* check if any settings have been modified before saving them */
4993 if (value != default_value)
4996 /* do not save if explicitly told or if unmodified default settings */
4997 if ((save_type == SAVE_CONF_NEVER) ||
4998 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
5002 num_bytes += putFile16BitBE(file, element);
5004 num_bytes += putFile8Bit(file, conf_type);
5005 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
5006 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
5007 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
5010 else if (data_type == TYPE_STRING)
5012 char *default_string = entry->default_string;
5013 char *string = (char *)(entry->value);
5014 int string_length = strlen(string);
5017 /* check if any settings have been modified before saving them */
5018 if (!strEqual(string, default_string))
5021 /* do not save if explicitly told or if unmodified default settings */
5022 if ((save_type == SAVE_CONF_NEVER) ||
5023 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
5027 num_bytes += putFile16BitBE(file, element);
5029 num_bytes += putFile8Bit(file, conf_type);
5030 num_bytes += putFile16BitBE(file, string_length);
5032 for (i = 0; i < string_length; i++)
5033 num_bytes += putFile8Bit(file, string[i]);
5035 else if (data_type == TYPE_ELEMENT_LIST)
5037 int *element_array = (int *)(entry->value);
5038 int num_elements = *(int *)(entry->num_entities);
5041 /* check if any settings have been modified before saving them */
5042 for (i = 0; i < num_elements; i++)
5043 if (element_array[i] != default_value)
5046 /* do not save if explicitly told or if unmodified default settings */
5047 if ((save_type == SAVE_CONF_NEVER) ||
5048 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
5052 num_bytes += putFile16BitBE(file, element);
5054 num_bytes += putFile8Bit(file, conf_type);
5055 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
5057 for (i = 0; i < num_elements; i++)
5058 num_bytes += putFile16BitBE(file, element_array[i]);
5060 else if (data_type == TYPE_CONTENT_LIST)
5062 struct Content *content = (struct Content *)(entry->value);
5063 int num_contents = *(int *)(entry->num_entities);
5066 /* check if any settings have been modified before saving them */
5067 for (i = 0; i < num_contents; i++)
5068 for (y = 0; y < 3; y++)
5069 for (x = 0; x < 3; x++)
5070 if (content[i].e[x][y] != default_value)
5073 /* do not save if explicitly told or if unmodified default settings */
5074 if ((save_type == SAVE_CONF_NEVER) ||
5075 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
5079 num_bytes += putFile16BitBE(file, element);
5081 num_bytes += putFile8Bit(file, conf_type);
5082 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
5084 for (i = 0; i < num_contents; i++)
5085 for (y = 0; y < 3; y++)
5086 for (x = 0; x < 3; x++)
5087 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
5093 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
5098 li = *level; /* copy level data into temporary buffer */
5100 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
5101 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
5106 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
5111 li = *level; /* copy level data into temporary buffer */
5113 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
5114 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
5119 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
5121 int envelope_nr = element - EL_ENVELOPE_1;
5125 chunk_size += putFile16BitBE(file, element);
5127 /* copy envelope data into temporary buffer */
5128 xx_envelope = level->envelope[envelope_nr];
5130 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
5131 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
5136 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
5138 struct ElementInfo *ei = &element_info[element];
5142 chunk_size += putFile16BitBE(file, element);
5144 xx_ei = *ei; /* copy element data into temporary buffer */
5146 /* set default description string for this specific element */
5147 strcpy(xx_default_description, getDefaultElementDescription(ei));
5150 /* set (fixed) number of content areas (may be wrong by broken level file) */
5151 /* (this is now directly corrected for broken level files after loading) */
5152 xx_num_contents = 1;
5155 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
5156 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
5158 for (i = 0; i < ei->num_change_pages; i++)
5160 struct ElementChangeInfo *change = &ei->change_page[i];
5162 xx_current_change_page = i;
5164 xx_change = *change; /* copy change data into temporary buffer */
5167 setEventBitsFromEventFlags(change);
5169 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
5170 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
5177 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
5179 struct ElementInfo *ei = &element_info[element];
5180 struct ElementGroupInfo *group = ei->group;
5184 chunk_size += putFile16BitBE(file, element);
5186 xx_ei = *ei; /* copy element data into temporary buffer */
5187 xx_group = *group; /* copy group data into temporary buffer */
5189 /* set default description string for this specific element */
5190 strcpy(xx_default_description, getDefaultElementDescription(ei));
5192 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
5193 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
5198 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
5204 if (!(file = fopen(filename, MODE_WRITE)))
5206 Error(ERR_WARN, "cannot save level file '%s'", filename);
5210 level->file_version = FILE_VERSION_ACTUAL;
5211 level->game_version = GAME_VERSION_ACTUAL;
5213 level->creation_date = getCurrentDate();
5215 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
5216 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
5218 chunk_size = SaveLevel_VERS(NULL, level);
5219 putFileChunkBE(file, "VERS", chunk_size);
5220 SaveLevel_VERS(file, level);
5222 chunk_size = SaveLevel_DATE(NULL, level);
5223 putFileChunkBE(file, "DATE", chunk_size);
5224 SaveLevel_DATE(file, level);
5226 chunk_size = SaveLevel_NAME(NULL, level);
5227 putFileChunkBE(file, "NAME", chunk_size);
5228 SaveLevel_NAME(file, level);
5230 chunk_size = SaveLevel_AUTH(NULL, level);
5231 putFileChunkBE(file, "AUTH", chunk_size);
5232 SaveLevel_AUTH(file, level);
5234 chunk_size = SaveLevel_INFO(NULL, level);
5235 putFileChunkBE(file, "INFO", chunk_size);
5236 SaveLevel_INFO(file, level);
5238 chunk_size = SaveLevel_BODY(NULL, level);
5239 putFileChunkBE(file, "BODY", chunk_size);
5240 SaveLevel_BODY(file, level);
5242 chunk_size = SaveLevel_ELEM(NULL, level);
5243 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
5245 putFileChunkBE(file, "ELEM", chunk_size);
5246 SaveLevel_ELEM(file, level);
5249 for (i = 0; i < NUM_ENVELOPES; i++)
5251 int element = EL_ENVELOPE_1 + i;
5253 chunk_size = SaveLevel_NOTE(NULL, level, element);
5254 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
5256 putFileChunkBE(file, "NOTE", chunk_size);
5257 SaveLevel_NOTE(file, level, element);
5261 /* if not using template level, check for non-default custom/group elements */
5262 if (!level->use_custom_template)
5264 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5266 int element = EL_CUSTOM_START + i;
5268 chunk_size = SaveLevel_CUSX(NULL, level, element);
5269 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
5271 putFileChunkBE(file, "CUSX", chunk_size);
5272 SaveLevel_CUSX(file, level, element);
5276 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
5278 int element = EL_GROUP_START + i;
5280 chunk_size = SaveLevel_GRPX(NULL, level, element);
5281 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
5283 putFileChunkBE(file, "GRPX", chunk_size);
5284 SaveLevel_GRPX(file, level, element);
5291 SetFilePermissions(filename, PERMS_PRIVATE);
5294 void SaveLevel(int nr)
5296 char *filename = getDefaultLevelFilename(nr);
5298 SaveLevelFromFilename(&level, filename);
5301 void SaveLevelTemplate()
5303 char *filename = getDefaultLevelFilename(-1);
5305 SaveLevelFromFilename(&level, filename);
5308 boolean SaveLevelChecked(int nr)
5310 char *filename = getDefaultLevelFilename(nr);
5311 boolean new_level = !fileExists(filename);
5312 boolean level_saved = FALSE;
5314 if (new_level || Request("Save this level and kill the old ?", REQ_ASK))
5319 Request("Level saved !", REQ_CONFIRM);
5327 void DumpLevel(struct LevelInfo *level)
5329 if (level->no_valid_file)
5331 Error(ERR_WARN, "cannot dump -- no valid level file found");
5336 printf_line("-", 79);
5337 printf("Level xxx (file version %08d, game version %08d)\n",
5338 level->file_version, level->game_version);
5339 printf_line("-", 79);
5341 printf("Level author: '%s'\n", level->author);
5342 printf("Level title: '%s'\n", level->name);
5344 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
5346 printf("Level time: %d seconds\n", level->time);
5347 printf("Gems needed: %d\n", level->gems_needed);
5349 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
5350 printf("Time for wheel: %d seconds\n", level->time_wheel);
5351 printf("Time for light: %d seconds\n", level->time_light);
5352 printf("Time for timegate: %d seconds\n", level->time_timegate);
5354 printf("Amoeba speed: %d\n", level->amoeba_speed);
5357 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
5358 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
5359 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
5360 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
5361 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
5363 printf_line("-", 79);
5367 /* ========================================================================= */
5368 /* tape file functions */
5369 /* ========================================================================= */
5371 static void setTapeInfoToDefaults()
5375 /* always start with reliable default values (empty tape) */
5378 /* default values (also for pre-1.2 tapes) with only the first player */
5379 tape.player_participates[0] = TRUE;
5380 for (i = 1; i < MAX_PLAYERS; i++)
5381 tape.player_participates[i] = FALSE;
5383 /* at least one (default: the first) player participates in every tape */
5384 tape.num_participating_players = 1;
5386 tape.level_nr = level_nr;
5388 tape.changed = FALSE;
5390 tape.recording = FALSE;
5391 tape.playing = FALSE;
5392 tape.pausing = FALSE;
5394 tape.no_valid_file = FALSE;
5397 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
5399 tape->file_version = getFileVersion(file);
5400 tape->game_version = getFileVersion(file);
5405 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
5409 tape->random_seed = getFile32BitBE(file);
5410 tape->date = getFile32BitBE(file);
5411 tape->length = getFile32BitBE(file);
5413 /* read header fields that are new since version 1.2 */
5414 if (tape->file_version >= FILE_VERSION_1_2)
5416 byte store_participating_players = getFile8Bit(file);
5419 /* since version 1.2, tapes store which players participate in the tape */
5420 tape->num_participating_players = 0;
5421 for (i = 0; i < MAX_PLAYERS; i++)
5423 tape->player_participates[i] = FALSE;
5425 if (store_participating_players & (1 << i))
5427 tape->player_participates[i] = TRUE;
5428 tape->num_participating_players++;
5432 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
5434 engine_version = getFileVersion(file);
5435 if (engine_version > 0)
5436 tape->engine_version = engine_version;
5438 tape->engine_version = tape->game_version;
5444 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
5446 int level_identifier_size;
5449 level_identifier_size = getFile16BitBE(file);
5451 tape->level_identifier =
5452 checked_realloc(tape->level_identifier, level_identifier_size);
5454 for (i = 0; i < level_identifier_size; i++)
5455 tape->level_identifier[i] = getFile8Bit(file);
5457 tape->level_nr = getFile16BitBE(file);
5459 chunk_size = 2 + level_identifier_size + 2;
5464 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
5467 int chunk_size_expected =
5468 (tape->num_participating_players + 1) * tape->length;
5470 if (chunk_size_expected != chunk_size)
5472 ReadUnusedBytesFromFile(file, chunk_size);
5473 return chunk_size_expected;
5476 for (i = 0; i < tape->length; i++)
5478 if (i >= MAX_TAPE_LEN)
5481 for (j = 0; j < MAX_PLAYERS; j++)
5483 tape->pos[i].action[j] = MV_NONE;
5485 if (tape->player_participates[j])
5486 tape->pos[i].action[j] = getFile8Bit(file);
5489 tape->pos[i].delay = getFile8Bit(file);
5491 if (tape->file_version == FILE_VERSION_1_0)
5493 /* eliminate possible diagonal moves in old tapes */
5494 /* this is only for backward compatibility */
5496 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
5497 byte action = tape->pos[i].action[0];
5498 int k, num_moves = 0;
5500 for (k = 0; k<4; k++)
5502 if (action & joy_dir[k])
5504 tape->pos[i + num_moves].action[0] = joy_dir[k];
5506 tape->pos[i + num_moves].delay = 0;
5515 tape->length += num_moves;
5518 else if (tape->file_version < FILE_VERSION_2_0)
5520 /* convert pre-2.0 tapes to new tape format */
5522 if (tape->pos[i].delay > 1)
5525 tape->pos[i + 1] = tape->pos[i];
5526 tape->pos[i + 1].delay = 1;
5529 for (j = 0; j < MAX_PLAYERS; j++)
5530 tape->pos[i].action[j] = MV_NONE;
5531 tape->pos[i].delay--;
5542 if (i != tape->length)
5543 chunk_size = (tape->num_participating_players + 1) * i;
5548 void LoadTapeFromFilename(char *filename)
5550 char cookie[MAX_LINE_LEN];
5551 char chunk_name[CHUNK_ID_LEN + 1];
5555 /* always start with reliable default values */
5556 setTapeInfoToDefaults();
5558 if (!(file = fopen(filename, MODE_READ)))
5560 tape.no_valid_file = TRUE;
5565 getFileChunkBE(file, chunk_name, NULL);
5566 if (strEqual(chunk_name, "RND1"))
5568 getFile32BitBE(file); /* not used */
5570 getFileChunkBE(file, chunk_name, NULL);
5571 if (!strEqual(chunk_name, "TAPE"))
5573 tape.no_valid_file = TRUE;
5575 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
5580 else /* check for pre-2.0 file format with cookie string */
5582 strcpy(cookie, chunk_name);
5583 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
5584 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
5585 cookie[strlen(cookie) - 1] = '\0';
5587 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
5589 tape.no_valid_file = TRUE;
5591 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
5596 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
5598 tape.no_valid_file = TRUE;
5600 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
5605 /* pre-2.0 tape files have no game version, so use file version here */
5606 tape.game_version = tape.file_version;
5609 if (tape.file_version < FILE_VERSION_1_2)
5611 /* tape files from versions before 1.2.0 without chunk structure */
5612 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
5613 LoadTape_BODY(file, 2 * tape.length, &tape);
5621 int (*loader)(FILE *, int, struct TapeInfo *);
5625 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
5626 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
5627 { "INFO", -1, LoadTape_INFO },
5628 { "BODY", -1, LoadTape_BODY },
5632 while (getFileChunkBE(file, chunk_name, &chunk_size))
5636 while (chunk_info[i].name != NULL &&
5637 !strEqual(chunk_name, chunk_info[i].name))
5640 if (chunk_info[i].name == NULL)
5642 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
5643 chunk_name, filename);
5644 ReadUnusedBytesFromFile(file, chunk_size);
5646 else if (chunk_info[i].size != -1 &&
5647 chunk_info[i].size != chunk_size)
5649 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
5650 chunk_size, chunk_name, filename);
5651 ReadUnusedBytesFromFile(file, chunk_size);
5655 /* call function to load this tape chunk */
5656 int chunk_size_expected =
5657 (chunk_info[i].loader)(file, chunk_size, &tape);
5659 /* the size of some chunks cannot be checked before reading other
5660 chunks first (like "HEAD" and "BODY") that contain some header
5661 information, so check them here */
5662 if (chunk_size_expected != chunk_size)
5664 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
5665 chunk_size, chunk_name, filename);
5673 tape.length_seconds = GetTapeLength();
5676 printf("::: tape file version: %d\n", tape.file_version);
5677 printf("::: tape game version: %d\n", tape.game_version);
5678 printf("::: tape engine version: %d\n", tape.engine_version);
5682 void LoadTape(int nr)
5684 char *filename = getTapeFilename(nr);
5686 LoadTapeFromFilename(filename);
5689 void LoadSolutionTape(int nr)
5691 char *filename = getSolutionTapeFilename(nr);
5693 LoadTapeFromFilename(filename);
5696 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
5698 putFileVersion(file, tape->file_version);
5699 putFileVersion(file, tape->game_version);
5702 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
5705 byte store_participating_players = 0;
5707 /* set bits for participating players for compact storage */
5708 for (i = 0; i < MAX_PLAYERS; i++)
5709 if (tape->player_participates[i])
5710 store_participating_players |= (1 << i);
5712 putFile32BitBE(file, tape->random_seed);
5713 putFile32BitBE(file, tape->date);
5714 putFile32BitBE(file, tape->length);
5716 putFile8Bit(file, store_participating_players);
5718 /* unused bytes not at the end here for 4-byte alignment of engine_version */
5719 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
5721 putFileVersion(file, tape->engine_version);
5724 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
5726 int level_identifier_size = strlen(tape->level_identifier) + 1;
5729 putFile16BitBE(file, level_identifier_size);
5731 for (i = 0; i < level_identifier_size; i++)
5732 putFile8Bit(file, tape->level_identifier[i]);
5734 putFile16BitBE(file, tape->level_nr);
5737 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
5741 for (i = 0; i < tape->length; i++)
5743 for (j = 0; j < MAX_PLAYERS; j++)
5744 if (tape->player_participates[j])
5745 putFile8Bit(file, tape->pos[i].action[j]);
5747 putFile8Bit(file, tape->pos[i].delay);
5751 void SaveTape(int nr)
5753 char *filename = getTapeFilename(nr);
5756 boolean new_tape = TRUE;
5758 int num_participating_players = 0;
5759 int info_chunk_size;
5760 int body_chunk_size;
5763 InitTapeDirectory(leveldir_current->subdir);
5766 /* if a tape still exists, ask to overwrite it */
5767 if (fileExists(filename))
5770 if (!Request("Replace old tape ?", REQ_ASK))
5775 if (!(file = fopen(filename, MODE_WRITE)))
5777 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
5781 tape.file_version = FILE_VERSION_ACTUAL;
5782 tape.game_version = GAME_VERSION_ACTUAL;
5784 /* count number of participating players */
5785 for (i = 0; i < MAX_PLAYERS; i++)
5786 if (tape.player_participates[i])
5787 num_participating_players++;
5789 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
5790 body_chunk_size = (num_participating_players + 1) * tape.length;
5792 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
5793 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
5795 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
5796 SaveTape_VERS(file, &tape);
5798 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
5799 SaveTape_HEAD(file, &tape);
5801 putFileChunkBE(file, "INFO", info_chunk_size);
5802 SaveTape_INFO(file, &tape);
5804 putFileChunkBE(file, "BODY", body_chunk_size);
5805 SaveTape_BODY(file, &tape);
5809 SetFilePermissions(filename, PERMS_PRIVATE);
5811 tape.changed = FALSE;
5815 Request("Tape saved !", REQ_CONFIRM);
5819 boolean SaveTapeChecked(int nr)
5821 char *filename = getTapeFilename(nr);
5822 boolean new_tape = !fileExists(filename);
5823 boolean tape_saved = FALSE;
5825 if (new_tape || Request("Replace old tape ?", REQ_ASK))
5830 Request("Tape saved !", REQ_CONFIRM);
5838 void DumpTape(struct TapeInfo *tape)
5840 int tape_frame_counter;
5843 if (tape->no_valid_file)
5845 Error(ERR_WARN, "cannot dump -- no valid tape file found");
5850 printf_line("-", 79);
5851 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
5852 tape->level_nr, tape->file_version, tape->game_version);
5853 printf(" (effective engine version %08d)\n",
5854 tape->engine_version);
5855 printf("Level series identifier: '%s'\n", tape->level_identifier);
5856 printf_line("-", 79);
5858 tape_frame_counter = 0;
5860 for (i = 0; i < tape->length; i++)
5862 if (i >= MAX_TAPE_LEN)
5865 printf("%04d: ", i);
5867 for (j = 0; j < MAX_PLAYERS; j++)
5869 if (tape->player_participates[j])
5871 int action = tape->pos[i].action[j];
5873 printf("%d:%02x ", j, action);
5874 printf("[%c%c%c%c|%c%c] - ",
5875 (action & JOY_LEFT ? '<' : ' '),
5876 (action & JOY_RIGHT ? '>' : ' '),
5877 (action & JOY_UP ? '^' : ' '),
5878 (action & JOY_DOWN ? 'v' : ' '),
5879 (action & JOY_BUTTON_1 ? '1' : ' '),
5880 (action & JOY_BUTTON_2 ? '2' : ' '));
5884 printf("(%03d) ", tape->pos[i].delay);
5885 printf("[%05d]\n", tape_frame_counter);
5887 tape_frame_counter += tape->pos[i].delay;
5890 printf_line("-", 79);
5894 /* ========================================================================= */
5895 /* score file functions */
5896 /* ========================================================================= */
5898 void LoadScore(int nr)
5901 char *filename = getScoreFilename(nr);
5902 char cookie[MAX_LINE_LEN];
5903 char line[MAX_LINE_LEN];
5907 /* always start with reliable default values */
5908 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5910 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
5911 highscore[i].Score = 0;
5914 if (!(file = fopen(filename, MODE_READ)))
5917 /* check file identifier */
5918 fgets(cookie, MAX_LINE_LEN, file);
5919 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
5920 cookie[strlen(cookie) - 1] = '\0';
5922 if (!checkCookieString(cookie, SCORE_COOKIE))
5924 Error(ERR_WARN, "unknown format of score file '%s'", filename);
5929 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5931 fscanf(file, "%d", &highscore[i].Score);
5932 fgets(line, MAX_LINE_LEN, file);
5934 if (line[strlen(line) - 1] == '\n')
5935 line[strlen(line) - 1] = '\0';
5937 for (line_ptr = line; *line_ptr; line_ptr++)
5939 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
5941 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
5942 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
5951 void SaveScore(int nr)
5954 char *filename = getScoreFilename(nr);
5957 InitScoreDirectory(leveldir_current->subdir);
5959 if (!(file = fopen(filename, MODE_WRITE)))
5961 Error(ERR_WARN, "cannot save score for level %d", nr);
5965 fprintf(file, "%s\n\n", SCORE_COOKIE);
5967 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5968 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
5972 SetFilePermissions(filename, PERMS_PUBLIC);
5976 /* ========================================================================= */
5977 /* setup file functions */
5978 /* ========================================================================= */
5980 #define TOKEN_STR_PLAYER_PREFIX "player_"
5983 #define SETUP_TOKEN_PLAYER_NAME 0
5984 #define SETUP_TOKEN_SOUND 1
5985 #define SETUP_TOKEN_SOUND_LOOPS 2
5986 #define SETUP_TOKEN_SOUND_MUSIC 3
5987 #define SETUP_TOKEN_SOUND_SIMPLE 4
5988 #define SETUP_TOKEN_TOONS 5
5989 #define SETUP_TOKEN_SCROLL_DELAY 6
5990 #define SETUP_TOKEN_SOFT_SCROLLING 7
5991 #define SETUP_TOKEN_FADE_SCREENS 8
5992 #define SETUP_TOKEN_AUTORECORD 9
5993 #define SETUP_TOKEN_SHOW_TITLESCREEN 10
5994 #define SETUP_TOKEN_QUICK_DOORS 11
5995 #define SETUP_TOKEN_TEAM_MODE 12
5996 #define SETUP_TOKEN_HANDICAP 13
5997 #define SETUP_TOKEN_SKIP_LEVELS 14
5998 #define SETUP_TOKEN_TIME_LIMIT 15
5999 #define SETUP_TOKEN_FULLSCREEN 16
6000 #define SETUP_TOKEN_FULLSCREEN_MODE 17
6001 #define SETUP_TOKEN_ASK_ON_ESCAPE 18
6002 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 19
6003 #define SETUP_TOKEN_QUICK_SWITCH 20
6004 #define SETUP_TOKEN_INPUT_ON_FOCUS 21
6005 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 22
6006 #define SETUP_TOKEN_GRAPHICS_SET 23
6007 #define SETUP_TOKEN_SOUNDS_SET 24
6008 #define SETUP_TOKEN_MUSIC_SET 25
6009 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 26
6010 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 27
6011 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 28
6013 #define NUM_GLOBAL_SETUP_TOKENS 29
6016 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
6017 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
6018 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
6019 #define SETUP_TOKEN_EDITOR_EL_MORE 3
6020 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 4
6021 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 5
6022 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 6
6023 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 7
6024 #define SETUP_TOKEN_EDITOR_EL_CHARS 8
6025 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 9
6026 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
6027 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
6028 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 12
6029 #define SETUP_TOKEN_EDITOR_EL_BY_GAME 13
6030 #define SETUP_TOKEN_EDITOR_EL_BY_TYPE 14
6031 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 15
6033 #define NUM_EDITOR_SETUP_TOKENS 16
6035 /* editor cascade setup */
6036 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
6037 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
6038 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
6039 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
6040 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
6041 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
6042 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
6043 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
6044 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
6045 #define SETUP_TOKEN_EDITOR_CASCADE_CE 9
6046 #define SETUP_TOKEN_EDITOR_CASCADE_GE 10
6047 #define SETUP_TOKEN_EDITOR_CASCADE_REF 11
6048 #define SETUP_TOKEN_EDITOR_CASCADE_USER 12
6049 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 13
6051 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 14
6053 /* shortcut setup */
6054 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
6055 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
6056 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
6057 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
6058 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
6059 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
6060 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
6061 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
6063 #define NUM_SHORTCUT_SETUP_TOKENS 8
6066 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
6067 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
6068 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
6069 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
6070 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
6071 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
6072 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
6073 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
6074 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
6075 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
6076 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
6077 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
6078 #define SETUP_TOKEN_PLAYER_KEY_UP 12
6079 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
6080 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
6081 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
6083 #define NUM_PLAYER_SETUP_TOKENS 16
6086 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
6087 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
6089 #define NUM_SYSTEM_SETUP_TOKENS 2
6092 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
6094 #define NUM_OPTIONS_SETUP_TOKENS 1
6097 static struct SetupInfo si;
6098 static struct SetupEditorInfo sei;
6099 static struct SetupEditorCascadeInfo seci;
6100 static struct SetupShortcutInfo ssi;
6101 static struct SetupInputInfo sii;
6102 static struct SetupSystemInfo syi;
6103 static struct OptionInfo soi;
6105 static struct TokenInfo global_setup_tokens[] =
6107 { TYPE_STRING, &si.player_name, "player_name" },
6108 { TYPE_SWITCH, &si.sound, "sound" },
6109 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
6110 { TYPE_SWITCH, &si.sound_music, "background_music" },
6111 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
6112 { TYPE_SWITCH, &si.toons, "toons" },
6113 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
6114 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
6115 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
6116 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
6117 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
6118 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
6119 { TYPE_SWITCH, &si.team_mode, "team_mode" },
6120 { TYPE_SWITCH, &si.handicap, "handicap" },
6121 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
6122 { TYPE_SWITCH, &si.time_limit, "time_limit" },
6123 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
6124 { TYPE_STRING, &si.fullscreen_mode, "fullscreen_mode" },
6125 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
6126 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
6127 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
6128 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
6129 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
6130 { TYPE_STRING, &si.graphics_set, "graphics_set" },
6131 { TYPE_STRING, &si.sounds_set, "sounds_set" },
6132 { TYPE_STRING, &si.music_set, "music_set" },
6133 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
6134 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
6135 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
6138 static boolean not_used = FALSE;
6139 static struct TokenInfo editor_setup_tokens[] =
6142 { TYPE_SWITCH, ¬_used, "editor.el_boulderdash" },
6143 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine" },
6144 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine_club" },
6145 { TYPE_SWITCH, ¬_used, "editor.el_more" },
6146 { TYPE_SWITCH, ¬_used, "editor.el_sokoban" },
6147 { TYPE_SWITCH, ¬_used, "editor.el_supaplex" },
6148 { TYPE_SWITCH, ¬_used, "editor.el_diamond_caves" },
6149 { TYPE_SWITCH, ¬_used, "editor.el_dx_boulderdash" },
6151 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
6152 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
6153 { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
6154 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
6155 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
6156 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
6157 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
6158 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
6160 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
6161 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
6163 { TYPE_SWITCH, ¬_used, "editor.el_headlines" },
6165 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
6167 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
6168 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
6169 { TYPE_SWITCH, &sei.el_by_game, "editor.el_by_game" },
6170 { TYPE_SWITCH, &sei.el_by_type, "editor.el_by_type" },
6171 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
6174 static struct TokenInfo editor_cascade_setup_tokens[] =
6176 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
6177 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
6178 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
6179 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
6180 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
6181 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
6182 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
6183 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
6184 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
6185 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
6186 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
6187 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
6188 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
6189 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
6192 static struct TokenInfo shortcut_setup_tokens[] =
6194 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
6195 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
6196 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
6197 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
6198 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
6199 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
6200 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
6201 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
6204 static struct TokenInfo player_setup_tokens[] =
6206 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
6207 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
6208 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
6209 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
6210 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
6211 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
6212 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
6213 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
6214 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
6215 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
6216 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
6217 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
6218 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
6219 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
6220 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
6221 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
6224 static struct TokenInfo system_setup_tokens[] =
6226 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
6227 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
6230 static struct TokenInfo options_setup_tokens[] =
6232 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
6235 static char *get_corrected_login_name(char *login_name)
6237 /* needed because player name must be a fixed length string */
6238 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
6240 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
6241 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
6243 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
6244 if (strchr(login_name_new, ' '))
6245 *strchr(login_name_new, ' ') = '\0';
6247 return login_name_new;
6250 static void setSetupInfoToDefaults(struct SetupInfo *si)
6254 si->player_name = get_corrected_login_name(getLoginName());
6257 si->sound_loops = TRUE;
6258 si->sound_music = TRUE;
6259 si->sound_simple = TRUE;
6261 si->double_buffering = TRUE;
6262 si->direct_draw = !si->double_buffering;
6263 si->scroll_delay = TRUE;
6264 si->soft_scrolling = TRUE;
6265 si->fade_screens = TRUE;
6266 si->autorecord = TRUE;
6267 si->show_titlescreen = TRUE;
6268 si->quick_doors = FALSE;
6269 si->team_mode = FALSE;
6270 si->handicap = TRUE;
6271 si->skip_levels = TRUE;
6272 si->time_limit = TRUE;
6273 si->fullscreen = FALSE;
6274 si->fullscreen_mode = getStringCopy(DEFAULT_FULLSCREEN_MODE);
6275 si->ask_on_escape = TRUE;
6276 si->ask_on_escape_editor = TRUE;
6277 si->quick_switch = FALSE;
6278 si->input_on_focus = FALSE;
6279 si->prefer_aga_graphics = TRUE;
6281 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
6282 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
6283 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
6284 si->override_level_graphics = FALSE;
6285 si->override_level_sounds = FALSE;
6286 si->override_level_music = FALSE;
6288 si->editor.el_boulderdash = TRUE;
6289 si->editor.el_emerald_mine = TRUE;
6290 si->editor.el_emerald_mine_club = TRUE;
6291 si->editor.el_more = TRUE;
6292 si->editor.el_sokoban = TRUE;
6293 si->editor.el_supaplex = TRUE;
6294 si->editor.el_diamond_caves = TRUE;
6295 si->editor.el_dx_boulderdash = TRUE;
6296 si->editor.el_chars = TRUE;
6297 si->editor.el_custom = TRUE;
6299 si->editor.el_headlines = TRUE;
6300 si->editor.el_user_defined = FALSE;
6301 si->editor.el_dynamic = TRUE;
6303 si->editor.show_element_token = FALSE;
6305 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
6306 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
6307 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
6309 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
6310 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
6311 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
6312 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
6313 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
6315 for (i = 0; i < MAX_PLAYERS; i++)
6317 si->input[i].use_joystick = FALSE;
6318 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
6319 si->input[i].joy.xleft = JOYSTICK_XLEFT;
6320 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
6321 si->input[i].joy.xright = JOYSTICK_XRIGHT;
6322 si->input[i].joy.yupper = JOYSTICK_YUPPER;
6323 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
6324 si->input[i].joy.ylower = JOYSTICK_YLOWER;
6325 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
6326 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
6327 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
6328 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
6329 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
6330 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
6331 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
6332 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
6335 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
6336 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
6338 si->options.verbose = FALSE;
6341 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
6343 si->editor_cascade.el_bd = TRUE;
6344 si->editor_cascade.el_em = TRUE;
6345 si->editor_cascade.el_emc = TRUE;
6346 si->editor_cascade.el_rnd = TRUE;
6347 si->editor_cascade.el_sb = TRUE;
6348 si->editor_cascade.el_sp = TRUE;
6349 si->editor_cascade.el_dc = TRUE;
6350 si->editor_cascade.el_dx = TRUE;
6352 si->editor_cascade.el_chars = FALSE;
6353 si->editor_cascade.el_ce = FALSE;
6354 si->editor_cascade.el_ge = FALSE;
6355 si->editor_cascade.el_ref = FALSE;
6356 si->editor_cascade.el_user = FALSE;
6357 si->editor_cascade.el_dynamic = FALSE;
6360 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
6364 if (!setup_file_hash)
6369 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
6370 setSetupInfo(global_setup_tokens, i,
6371 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
6376 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
6377 setSetupInfo(editor_setup_tokens, i,
6378 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
6381 /* shortcut setup */
6382 ssi = setup.shortcut;
6383 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
6384 setSetupInfo(shortcut_setup_tokens, i,
6385 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
6386 setup.shortcut = ssi;
6389 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
6393 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
6395 sii = setup.input[pnr];
6396 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
6398 char full_token[100];
6400 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
6401 setSetupInfo(player_setup_tokens, i,
6402 getHashEntry(setup_file_hash, full_token));
6404 setup.input[pnr] = sii;
6409 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
6410 setSetupInfo(system_setup_tokens, i,
6411 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
6415 soi = setup.options;
6416 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
6417 setSetupInfo(options_setup_tokens, i,
6418 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
6419 setup.options = soi;
6422 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
6426 if (!setup_file_hash)
6429 /* editor cascade setup */
6430 seci = setup.editor_cascade;
6431 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
6432 setSetupInfo(editor_cascade_setup_tokens, i,
6433 getHashEntry(setup_file_hash,
6434 editor_cascade_setup_tokens[i].text));
6435 setup.editor_cascade = seci;
6440 char *filename = getSetupFilename();
6441 SetupFileHash *setup_file_hash = NULL;
6443 /* always start with reliable default values */
6444 setSetupInfoToDefaults(&setup);
6446 setup_file_hash = loadSetupFileHash(filename);
6448 if (setup_file_hash)
6450 char *player_name_new;
6452 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
6453 decodeSetupFileHash(setup_file_hash);
6455 setup.direct_draw = !setup.double_buffering;
6457 freeSetupFileHash(setup_file_hash);
6459 /* needed to work around problems with fixed length strings */
6460 player_name_new = get_corrected_login_name(setup.player_name);
6461 free(setup.player_name);
6462 setup.player_name = player_name_new;
6465 Error(ERR_WARN, "using default setup values");
6468 void LoadSetup_EditorCascade()
6470 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
6471 SetupFileHash *setup_file_hash = NULL;
6473 /* always start with reliable default values */
6474 setSetupInfoToDefaults_EditorCascade(&setup);
6476 setup_file_hash = loadSetupFileHash(filename);
6478 if (setup_file_hash)
6480 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
6481 decodeSetupFileHash_EditorCascade(setup_file_hash);
6483 freeSetupFileHash(setup_file_hash);
6491 char *filename = getSetupFilename();
6495 InitUserDataDirectory();
6497 if (!(file = fopen(filename, MODE_WRITE)))
6499 Error(ERR_WARN, "cannot write setup file '%s'", filename);
6503 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
6504 getCookie("SETUP")));
6505 fprintf(file, "\n");
6509 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
6511 /* just to make things nicer :) */
6512 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
6513 i == SETUP_TOKEN_GRAPHICS_SET)
6514 fprintf(file, "\n");
6516 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
6521 fprintf(file, "\n");
6522 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
6523 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
6525 /* shortcut setup */
6526 ssi = setup.shortcut;
6527 fprintf(file, "\n");
6528 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
6529 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
6532 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
6536 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
6537 fprintf(file, "\n");
6539 sii = setup.input[pnr];
6540 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
6541 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
6546 fprintf(file, "\n");
6547 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
6548 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
6551 soi = setup.options;
6552 fprintf(file, "\n");
6553 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
6554 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
6558 SetFilePermissions(filename, PERMS_PRIVATE);
6561 void SaveSetup_EditorCascade()
6563 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
6567 InitUserDataDirectory();
6569 if (!(file = fopen(filename, MODE_WRITE)))
6571 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
6576 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
6577 getCookie("SETUP")));
6578 fprintf(file, "\n");
6580 seci = setup.editor_cascade;
6581 fprintf(file, "\n");
6582 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
6583 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
6587 SetFilePermissions(filename, PERMS_PRIVATE);
6592 void LoadCustomElementDescriptions()
6594 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
6595 SetupFileHash *setup_file_hash;
6598 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6600 if (element_info[i].custom_description != NULL)
6602 free(element_info[i].custom_description);
6603 element_info[i].custom_description = NULL;
6607 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
6610 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6612 char *token = getStringCat2(element_info[i].token_name, ".name");
6613 char *value = getHashEntry(setup_file_hash, token);
6616 element_info[i].custom_description = getStringCopy(value);
6621 freeSetupFileHash(setup_file_hash);
6624 static void LoadSpecialMenuDesignSettingsFromFilename(char *filename)
6626 SetupFileHash *setup_file_hash;
6630 printf("LoadSpecialMenuDesignSettings from file '%s' ...\n", filename);
6633 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
6636 /* special case: initialize with default values that may be overwritten */
6637 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
6639 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
6640 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
6641 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
6643 if (value_x != NULL)
6644 menu.draw_xoffset[i] = get_integer_from_string(value_x);
6645 if (value_y != NULL)
6646 menu.draw_yoffset[i] = get_integer_from_string(value_y);
6647 if (list_size != NULL)
6648 menu.list_size[i] = get_integer_from_string(list_size);
6651 /* special case: initialize with default values that may be overwritten */
6652 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
6654 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
6655 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
6657 if (value_x != NULL)
6658 menu.draw_xoffset_info[i] = get_integer_from_string(value_x);
6659 if (value_y != NULL)
6660 menu.draw_yoffset_info[i] = get_integer_from_string(value_y);
6663 /* read (and overwrite with) values that may be specified in config file */
6664 for (i = 0; image_config_vars[i].token != NULL; i++)
6666 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
6669 *image_config_vars[i].value =
6670 get_auto_parameter_value(image_config_vars[i].token, value);
6673 freeSetupFileHash(setup_file_hash);
6676 void LoadSpecialMenuDesignSettings()
6678 char *filename_base = UNDEFINED_FILENAME, *filename_local;
6681 /* always start with reliable default values from default config */
6682 for (i = 0; image_config_vars[i].token != NULL; i++)
6683 for (j = 0; image_config[j].token != NULL; j++)
6684 if (strEqual(image_config_vars[i].token, image_config[j].token))
6685 *image_config_vars[i].value =
6686 get_auto_parameter_value(image_config_vars[i].token,
6687 image_config[j].value);
6689 if (!SETUP_OVERRIDE_ARTWORK(setup, ARTWORK_TYPE_GRAPHICS))
6691 /* first look for special settings configured in level series config */
6692 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
6694 if (fileExists(filename_base))
6695 LoadSpecialMenuDesignSettingsFromFilename(filename_base);
6698 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
6700 if (filename_local != NULL && !strEqual(filename_base, filename_local))
6701 LoadSpecialMenuDesignSettingsFromFilename(filename_local);
6704 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
6706 char *filename = getEditorSetupFilename();
6707 SetupFileList *setup_file_list, *list;
6708 SetupFileHash *element_hash;
6709 int num_unknown_tokens = 0;
6712 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
6715 element_hash = newSetupFileHash();
6717 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6718 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
6720 /* determined size may be larger than needed (due to unknown elements) */
6722 for (list = setup_file_list; list != NULL; list = list->next)
6725 /* add space for up to 3 more elements for padding that may be needed */
6728 /* free memory for old list of elements, if needed */
6729 checked_free(*elements);
6731 /* allocate memory for new list of elements */
6732 *elements = checked_malloc(*num_elements * sizeof(int));
6735 for (list = setup_file_list; list != NULL; list = list->next)
6737 char *value = getHashEntry(element_hash, list->token);
6739 if (value == NULL) /* try to find obsolete token mapping */
6741 char *mapped_token = get_mapped_token(list->token);
6743 if (mapped_token != NULL)
6745 value = getHashEntry(element_hash, mapped_token);
6753 (*elements)[(*num_elements)++] = atoi(value);
6757 if (num_unknown_tokens == 0)
6759 Error(ERR_RETURN_LINE, "-");
6760 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
6761 Error(ERR_RETURN, "- config file: '%s'", filename);
6763 num_unknown_tokens++;
6766 Error(ERR_RETURN, "- token: '%s'", list->token);
6770 if (num_unknown_tokens > 0)
6771 Error(ERR_RETURN_LINE, "-");
6773 while (*num_elements % 4) /* pad with empty elements, if needed */
6774 (*elements)[(*num_elements)++] = EL_EMPTY;
6776 freeSetupFileList(setup_file_list);
6777 freeSetupFileHash(element_hash);
6780 for (i = 0; i < *num_elements; i++)
6781 printf("editor: element '%s' [%d]\n",
6782 element_info[(*elements)[i]].token_name, (*elements)[i]);
6786 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
6789 SetupFileHash *setup_file_hash = NULL;
6790 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
6791 char *filename_music, *filename_prefix, *filename_info;
6797 token_to_value_ptr[] =
6799 { "title_header", &tmp_music_file_info.title_header },
6800 { "artist_header", &tmp_music_file_info.artist_header },
6801 { "album_header", &tmp_music_file_info.album_header },
6802 { "year_header", &tmp_music_file_info.year_header },
6804 { "title", &tmp_music_file_info.title },
6805 { "artist", &tmp_music_file_info.artist },
6806 { "album", &tmp_music_file_info.album },
6807 { "year", &tmp_music_file_info.year },
6813 filename_music = (is_sound ? getCustomSoundFilename(basename) :
6814 getCustomMusicFilename(basename));
6816 if (filename_music == NULL)
6819 /* ---------- try to replace file extension ---------- */
6821 filename_prefix = getStringCopy(filename_music);
6822 if (strrchr(filename_prefix, '.') != NULL)
6823 *strrchr(filename_prefix, '.') = '\0';
6824 filename_info = getStringCat2(filename_prefix, ".txt");
6827 printf("trying to load file '%s'...\n", filename_info);
6830 if (fileExists(filename_info))
6831 setup_file_hash = loadSetupFileHash(filename_info);
6833 free(filename_prefix);
6834 free(filename_info);
6836 if (setup_file_hash == NULL)
6838 /* ---------- try to add file extension ---------- */
6840 filename_prefix = getStringCopy(filename_music);
6841 filename_info = getStringCat2(filename_prefix, ".txt");
6844 printf("trying to load file '%s'...\n", filename_info);
6847 if (fileExists(filename_info))
6848 setup_file_hash = loadSetupFileHash(filename_info);
6850 free(filename_prefix);
6851 free(filename_info);
6854 if (setup_file_hash == NULL)
6857 /* ---------- music file info found ---------- */
6859 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
6861 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
6863 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
6865 *token_to_value_ptr[i].value_ptr =
6866 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
6869 tmp_music_file_info.basename = getStringCopy(basename);
6870 tmp_music_file_info.music = music;
6871 tmp_music_file_info.is_sound = is_sound;
6873 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
6874 *new_music_file_info = tmp_music_file_info;
6876 return new_music_file_info;
6879 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
6881 return get_music_file_info_ext(basename, music, FALSE);
6884 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
6886 return get_music_file_info_ext(basename, sound, TRUE);
6889 static boolean music_info_listed_ext(struct MusicFileInfo *list,
6890 char *basename, boolean is_sound)
6892 for (; list != NULL; list = list->next)
6893 if (list->is_sound == is_sound && strEqual(list->basename, basename))
6899 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
6901 return music_info_listed_ext(list, basename, FALSE);
6904 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
6906 return music_info_listed_ext(list, basename, TRUE);
6909 void LoadMusicInfo()
6911 char *music_directory = getCustomMusicDirectory();
6912 int num_music = getMusicListSize();
6913 int num_music_noconf = 0;
6914 int num_sounds = getSoundListSize();
6916 struct dirent *dir_entry;
6917 struct FileInfo *music, *sound;
6918 struct MusicFileInfo *next, **new;
6921 while (music_file_info != NULL)
6923 next = music_file_info->next;
6925 checked_free(music_file_info->basename);
6927 checked_free(music_file_info->title_header);
6928 checked_free(music_file_info->artist_header);
6929 checked_free(music_file_info->album_header);
6930 checked_free(music_file_info->year_header);
6932 checked_free(music_file_info->title);
6933 checked_free(music_file_info->artist);
6934 checked_free(music_file_info->album);
6935 checked_free(music_file_info->year);
6937 free(music_file_info);
6939 music_file_info = next;
6942 new = &music_file_info;
6944 for (i = 0; i < num_music; i++)
6946 music = getMusicListEntry(i);
6948 if (music->filename == NULL)
6951 if (strEqual(music->filename, UNDEFINED_FILENAME))
6954 /* a configured file may be not recognized as music */
6955 if (!FileIsMusic(music->filename))
6959 printf("::: -> '%s' (configured)\n", music->filename);
6962 if (!music_info_listed(music_file_info, music->filename))
6964 *new = get_music_file_info(music->filename, i);
6966 new = &(*new)->next;
6970 if ((dir = opendir(music_directory)) == NULL)
6972 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
6976 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
6978 char *basename = dir_entry->d_name;
6979 boolean music_already_used = FALSE;
6982 /* skip all music files that are configured in music config file */
6983 for (i = 0; i < num_music; i++)
6985 music = getMusicListEntry(i);
6987 if (music->filename == NULL)
6990 if (strEqual(basename, music->filename))
6992 music_already_used = TRUE;
6997 if (music_already_used)
7000 if (!FileIsMusic(basename))
7004 printf("::: -> '%s' (found in directory)\n", basename);
7007 if (!music_info_listed(music_file_info, basename))
7009 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
7011 new = &(*new)->next;
7019 for (i = 0; i < num_sounds; i++)
7021 sound = getSoundListEntry(i);
7023 if (sound->filename == NULL)
7026 if (strEqual(sound->filename, UNDEFINED_FILENAME))
7029 /* a configured file may be not recognized as sound */
7030 if (!FileIsSound(sound->filename))
7034 printf("::: -> '%s' (configured)\n", sound->filename);
7037 if (!sound_info_listed(music_file_info, sound->filename))
7039 *new = get_sound_file_info(sound->filename, i);
7041 new = &(*new)->next;
7046 for (next = music_file_info; next != NULL; next = next->next)
7047 printf("::: title == '%s'\n", next->title);
7051 void add_helpanim_entry(int element, int action, int direction, int delay,
7052 int *num_list_entries)
7054 struct HelpAnimInfo *new_list_entry;
7055 (*num_list_entries)++;
7058 checked_realloc(helpanim_info,
7059 *num_list_entries * sizeof(struct HelpAnimInfo));
7060 new_list_entry = &helpanim_info[*num_list_entries - 1];
7062 new_list_entry->element = element;
7063 new_list_entry->action = action;
7064 new_list_entry->direction = direction;
7065 new_list_entry->delay = delay;
7068 void print_unknown_token(char *filename, char *token, int token_nr)
7072 Error(ERR_RETURN_LINE, "-");
7073 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
7074 Error(ERR_RETURN, "- config file: '%s'", filename);
7077 Error(ERR_RETURN, "- token: '%s'", token);
7080 void print_unknown_token_end(int token_nr)
7083 Error(ERR_RETURN_LINE, "-");
7086 void LoadHelpAnimInfo()
7088 char *filename = getHelpAnimFilename();
7089 SetupFileList *setup_file_list = NULL, *list;
7090 SetupFileHash *element_hash, *action_hash, *direction_hash;
7091 int num_list_entries = 0;
7092 int num_unknown_tokens = 0;
7095 if (fileExists(filename))
7096 setup_file_list = loadSetupFileList(filename);
7098 if (setup_file_list == NULL)
7100 /* use reliable default values from static configuration */
7101 SetupFileList *insert_ptr;
7103 insert_ptr = setup_file_list =
7104 newSetupFileList(helpanim_config[0].token,
7105 helpanim_config[0].value);
7107 for (i = 1; helpanim_config[i].token; i++)
7108 insert_ptr = addListEntry(insert_ptr,
7109 helpanim_config[i].token,
7110 helpanim_config[i].value);
7113 element_hash = newSetupFileHash();
7114 action_hash = newSetupFileHash();
7115 direction_hash = newSetupFileHash();
7117 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
7118 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
7120 for (i = 0; i < NUM_ACTIONS; i++)
7121 setHashEntry(action_hash, element_action_info[i].suffix,
7122 i_to_a(element_action_info[i].value));
7124 /* do not store direction index (bit) here, but direction value! */
7125 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
7126 setHashEntry(direction_hash, element_direction_info[i].suffix,
7127 i_to_a(1 << element_direction_info[i].value));
7129 for (list = setup_file_list; list != NULL; list = list->next)
7131 char *element_token, *action_token, *direction_token;
7132 char *element_value, *action_value, *direction_value;
7133 int delay = atoi(list->value);
7135 if (strEqual(list->token, "end"))
7137 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
7142 /* first try to break element into element/action/direction parts;
7143 if this does not work, also accept combined "element[.act][.dir]"
7144 elements (like "dynamite.active"), which are unique elements */
7146 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
7148 element_value = getHashEntry(element_hash, list->token);
7149 if (element_value != NULL) /* element found */
7150 add_helpanim_entry(atoi(element_value), -1, -1, delay,
7154 /* no further suffixes found -- this is not an element */
7155 print_unknown_token(filename, list->token, num_unknown_tokens++);
7161 /* token has format "<prefix>.<something>" */
7163 action_token = strchr(list->token, '.'); /* suffix may be action ... */
7164 direction_token = action_token; /* ... or direction */
7166 element_token = getStringCopy(list->token);
7167 *strchr(element_token, '.') = '\0';
7169 element_value = getHashEntry(element_hash, element_token);
7171 if (element_value == NULL) /* this is no element */
7173 element_value = getHashEntry(element_hash, list->token);
7174 if (element_value != NULL) /* combined element found */
7175 add_helpanim_entry(atoi(element_value), -1, -1, delay,
7178 print_unknown_token(filename, list->token, num_unknown_tokens++);
7180 free(element_token);
7185 action_value = getHashEntry(action_hash, action_token);
7187 if (action_value != NULL) /* action found */
7189 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
7192 free(element_token);
7197 direction_value = getHashEntry(direction_hash, direction_token);
7199 if (direction_value != NULL) /* direction found */
7201 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
7204 free(element_token);
7209 if (strchr(action_token + 1, '.') == NULL)
7211 /* no further suffixes found -- this is not an action nor direction */
7213 element_value = getHashEntry(element_hash, list->token);
7214 if (element_value != NULL) /* combined element found */
7215 add_helpanim_entry(atoi(element_value), -1, -1, delay,
7218 print_unknown_token(filename, list->token, num_unknown_tokens++);
7220 free(element_token);
7225 /* token has format "<prefix>.<suffix>.<something>" */
7227 direction_token = strchr(action_token + 1, '.');
7229 action_token = getStringCopy(action_token);
7230 *strchr(action_token + 1, '.') = '\0';
7232 action_value = getHashEntry(action_hash, action_token);
7234 if (action_value == NULL) /* this is no action */
7236 element_value = getHashEntry(element_hash, list->token);
7237 if (element_value != NULL) /* combined element found */
7238 add_helpanim_entry(atoi(element_value), -1, -1, delay,
7241 print_unknown_token(filename, list->token, num_unknown_tokens++);
7243 free(element_token);
7249 direction_value = getHashEntry(direction_hash, direction_token);
7251 if (direction_value != NULL) /* direction found */
7253 add_helpanim_entry(atoi(element_value), atoi(action_value),
7254 atoi(direction_value), delay, &num_list_entries);
7256 free(element_token);
7262 /* this is no direction */
7264 element_value = getHashEntry(element_hash, list->token);
7265 if (element_value != NULL) /* combined element found */
7266 add_helpanim_entry(atoi(element_value), -1, -1, delay,
7269 print_unknown_token(filename, list->token, num_unknown_tokens++);
7271 free(element_token);
7275 print_unknown_token_end(num_unknown_tokens);
7277 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
7278 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
7280 freeSetupFileList(setup_file_list);
7281 freeSetupFileHash(element_hash);
7282 freeSetupFileHash(action_hash);
7283 freeSetupFileHash(direction_hash);
7286 for (i = 0; i < num_list_entries; i++)
7287 printf("::: '%s': %d, %d, %d => %d\n",
7288 EL_NAME(helpanim_info[i].element),
7289 helpanim_info[i].element,
7290 helpanim_info[i].action,
7291 helpanim_info[i].direction,
7292 helpanim_info[i].delay);
7296 void LoadHelpTextInfo()
7298 char *filename = getHelpTextFilename();
7301 if (helptext_info != NULL)
7303 freeSetupFileHash(helptext_info);
7304 helptext_info = NULL;
7307 if (fileExists(filename))
7308 helptext_info = loadSetupFileHash(filename);
7310 if (helptext_info == NULL)
7312 /* use reliable default values from static configuration */
7313 helptext_info = newSetupFileHash();
7315 for (i = 0; helptext_config[i].token; i++)
7316 setHashEntry(helptext_info,
7317 helptext_config[i].token,
7318 helptext_config[i].value);
7322 BEGIN_HASH_ITERATION(helptext_info, itr)
7324 printf("::: '%s' => '%s'\n",
7325 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
7327 END_HASH_ITERATION(hash, itr)
7332 /* ------------------------------------------------------------------------- *
7334 * ------------------------------------------------------------------------- */
7336 #define MAX_NUM_CONVERT_LEVELS 1000
7338 void ConvertLevels()
7340 static LevelDirTree *convert_leveldir = NULL;
7341 static int convert_level_nr = -1;
7342 static int num_levels_handled = 0;
7343 static int num_levels_converted = 0;
7344 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
7347 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7348 global.convert_leveldir);
7350 if (convert_leveldir == NULL)
7351 Error(ERR_EXIT, "no such level identifier: '%s'",
7352 global.convert_leveldir);
7354 leveldir_current = convert_leveldir;
7356 if (global.convert_level_nr != -1)
7358 convert_leveldir->first_level = global.convert_level_nr;
7359 convert_leveldir->last_level = global.convert_level_nr;
7362 convert_level_nr = convert_leveldir->first_level;
7364 printf_line("=", 79);
7365 printf("Converting levels\n");
7366 printf_line("-", 79);
7367 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
7368 printf("Level series name: '%s'\n", convert_leveldir->name);
7369 printf("Level series author: '%s'\n", convert_leveldir->author);
7370 printf("Number of levels: %d\n", convert_leveldir->levels);
7371 printf_line("=", 79);
7374 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
7375 levels_failed[i] = FALSE;
7377 while (convert_level_nr <= convert_leveldir->last_level)
7379 char *level_filename;
7382 level_nr = convert_level_nr++;
7384 printf("Level %03d: ", level_nr);
7386 LoadLevel(level_nr);
7387 if (level.no_valid_file)
7389 printf("(no level)\n");
7393 printf("converting level ... ");
7395 level_filename = getDefaultLevelFilename(level_nr);
7396 new_level = !fileExists(level_filename);
7400 SaveLevel(level_nr);
7402 num_levels_converted++;
7404 printf("converted.\n");
7408 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
7409 levels_failed[level_nr] = TRUE;
7411 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
7414 num_levels_handled++;
7418 printf_line("=", 79);
7419 printf("Number of levels handled: %d\n", num_levels_handled);
7420 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
7421 (num_levels_handled ?
7422 num_levels_converted * 100 / num_levels_handled : 0));
7423 printf_line("-", 79);
7424 printf("Summary (for automatic parsing by scripts):\n");
7425 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
7426 convert_leveldir->identifier, num_levels_converted,
7428 (num_levels_handled ?
7429 num_levels_converted * 100 / num_levels_handled : 0));
7431 if (num_levels_handled != num_levels_converted)
7433 printf(", FAILED:");
7434 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
7435 if (levels_failed[i])
7440 printf_line("=", 79);