1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 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 */
30 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
31 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
32 #define LEVEL_HEADER_UNUSED 0 /* unused level header bytes */
33 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
34 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
35 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
36 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
37 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
38 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
39 #define LEVEL_CHUNK_GRP1_SIZE 74 /* size of level GRP1 chunk */
40 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
41 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
43 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
44 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
45 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
47 /* file identifier strings */
48 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
49 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
50 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
52 /* values for "CONF" chunk */
53 #define CONF_MASK_1_BYTE 0x00
54 #define CONF_MASK_2_BYTE 0x40
55 #define CONF_MASK_4_BYTE 0x80
56 #define CONF_MASK_MULTI_BYTES 0xc0
58 #define CONF_MASK_BYTES 0xc0
59 #define CONF_MASK_TOKEN 0x3f
61 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
62 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
63 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
64 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
66 /* a sequence of configuration values can be terminated by this value */
67 #define CONF_LAST_ENTRY CONF_VALUE_1_BYTE(0)
69 /* these definitions are just for convenience of use and readability */
70 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
71 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
72 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
73 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
75 #define CONF_VALUE_INTEGER_1 CONF_VALUE_8_BIT(1)
76 #define CONF_VALUE_INTEGER_2 CONF_VALUE_8_BIT(2)
77 #define CONF_VALUE_INTEGER_3 CONF_VALUE_8_BIT(3)
78 #define CONF_VALUE_INTEGER_4 CONF_VALUE_8_BIT(4)
79 #define CONF_VALUE_INTEGER_5 CONF_VALUE_8_BIT(5)
80 #define CONF_VALUE_INTEGER_6 CONF_VALUE_8_BIT(6)
81 #define CONF_VALUE_INTEGER_7 CONF_VALUE_8_BIT(7)
82 #define CONF_VALUE_INTEGER_8 CONF_VALUE_8_BIT(8)
83 #define CONF_VALUE_BOOLEAN_1 CONF_VALUE_8_BIT(9)
84 #define CONF_VALUE_BOOLEAN_2 CONF_VALUE_8_BIT(10)
85 #define CONF_VALUE_BOOLEAN_3 CONF_VALUE_8_BIT(11)
86 #define CONF_VALUE_BOOLEAN_4 CONF_VALUE_8_BIT(12)
87 #define CONF_VALUE_BOOLEAN_5 CONF_VALUE_8_BIT(13)
88 #define CONF_VALUE_BOOLEAN_6 CONF_VALUE_8_BIT(14)
89 #define CONF_VALUE_BOOLEAN_7 CONF_VALUE_8_BIT(15)
90 #define CONF_VALUE_BOOLEAN_8 CONF_VALUE_8_BIT(16)
92 #define CONF_VALUE_ELEMENT_1 CONF_VALUE_16_BIT(1)
93 #define CONF_VALUE_ELEMENT_2 CONF_VALUE_16_BIT(2)
94 #define CONF_VALUE_ELEMENT_3 CONF_VALUE_16_BIT(3)
95 #define CONF_VALUE_ELEMENT_4 CONF_VALUE_16_BIT(4)
96 #define CONF_VALUE_ELEMENT_5 CONF_VALUE_16_BIT(5)
97 #define CONF_VALUE_ELEMENT_6 CONF_VALUE_16_BIT(6)
98 #define CONF_VALUE_ELEMENT_7 CONF_VALUE_16_BIT(7)
99 #define CONF_VALUE_ELEMENT_8 CONF_VALUE_16_BIT(8)
102 #define CONF_VALUE_ELEMENTS CONF_VALUE_BYTES(1)
103 #define CONF_VALUE_CONTENTS CONF_VALUE_BYTES(2)
107 #define CONF_VALUE_INTEGER(x) ((x) >= CONF_VALUE_INTEGER_1 && \
108 (x) <= CONF_VALUE_INTEGER_8)
110 #define CONF_VALUE_BOOLEAN(x) ((x) >= CONF_VALUE_BOOLEAN_1 && \
111 (x) <= CONF_VALUE_BOOLEAN_8)
114 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
115 (x) == CONF_MASK_2_BYTE ? 2 : \
116 (x) == CONF_MASK_4_BYTE ? 4 : 0)
119 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
120 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
121 #define CONF_ELEMENT_NUM_BYTES (2)
123 #define CONF_ENTITY_NUM_BYTES(t) ((t) == CONF_VALUE_ELEMENTS ? \
124 CONF_ELEMENT_NUM_BYTES : \
125 (t) == CONF_VALUE_CONTENTS ? \
126 CONF_CONTENT_NUM_BYTES : 1)
129 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
130 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
131 #define CONF_ELEMENT_NUM_BYTES (2)
133 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
134 (t) == TYPE_ELEMENT_LIST ? \
135 CONF_ELEMENT_NUM_BYTES : \
136 (t) == TYPE_CONTENT || \
137 (t) == TYPE_CONTENT_LIST ? \
138 CONF_CONTENT_NUM_BYTES : 1)
140 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
141 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
142 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
144 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
146 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
147 CONF_ELEMENT_NUM_BYTES)
148 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
149 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
152 static void LoadLevel_InitPlayfield(struct LevelInfo *, char *);
155 /* temporary variables used to store pointers to structure members */
156 static struct LevelInfo li;
157 static struct ElementInfo xx_ei;
158 static struct ElementChangeInfo xx_change;
159 static struct ElementGroupInfo xx_group;
160 static unsigned int xx_event_bits_0_31, xx_event_bits_32_63;
161 static int xx_num_description_bytes;
162 static int xx_num_contents;
163 static int xx_current_change_page;
165 struct ElementFileConfig
167 int element; /* element for which data is to be stored */
168 int data_type; /* internal type of data */
169 int conf_type; /* special type identifier stored in file */
172 void *value; /* variable that holds the data to be stored */
173 int default_value; /* initial default value for this variable */
176 void *num_entities; /* number of entities for multi-byte data */
177 int default_num_entities; /* default number of entities for this data */
178 int max_num_entities; /* maximal number of entities for this data */
181 static struct ElementFileConfig element_conf[] =
183 /* ---------- 1-byte values ---------------------------------------------- */
187 TYPE_INTEGER, CONF_VALUE_INTEGER_1,
188 &li.android_move_time, 10
192 TYPE_INTEGER, CONF_VALUE_INTEGER_2,
193 &li.android_clone_time, 10
197 TYPE_INTEGER, CONF_VALUE_INTEGER_1,
202 TYPE_INTEGER, CONF_VALUE_INTEGER_2,
207 TYPE_INTEGER, CONF_VALUE_INTEGER_1,
208 &li.magnify_score, 10
212 TYPE_INTEGER, CONF_VALUE_INTEGER_2,
217 TYPE_INTEGER, CONF_VALUE_INTEGER_1,
222 TYPE_INTEGER, CONF_VALUE_INTEGER_1,
223 &li.game_of_life[0], 2
227 TYPE_INTEGER, CONF_VALUE_INTEGER_2,
228 &li.game_of_life[1], 3
232 TYPE_INTEGER, CONF_VALUE_INTEGER_3,
233 &li.game_of_life[2], 3
237 TYPE_INTEGER, CONF_VALUE_INTEGER_4,
238 &li.game_of_life[3], 3
242 TYPE_INTEGER, CONF_VALUE_INTEGER_1,
247 TYPE_INTEGER, CONF_VALUE_INTEGER_2,
252 TYPE_INTEGER, CONF_VALUE_INTEGER_3,
257 TYPE_INTEGER, CONF_VALUE_INTEGER_4,
262 TYPE_BITFIELD, CONF_VALUE_INTEGER_1,
263 &li.wind_direction_initial, MV_NONE
267 TYPE_INTEGER, CONF_VALUE_INTEGER_1,
268 &li.time_timegate, 10
271 EL_LIGHT_SWITCH_ACTIVE,
272 TYPE_INTEGER, CONF_VALUE_INTEGER_1,
277 TYPE_INTEGER, CONF_VALUE_INTEGER_1,
278 &li.shield_normal_time, 10
282 TYPE_INTEGER, CONF_VALUE_INTEGER_1,
283 &li.shield_deadly_time, 10
287 TYPE_INTEGER, CONF_VALUE_INTEGER_1,
292 TYPE_INTEGER, CONF_VALUE_INTEGER_2,
293 &li.extra_time_score, 10
297 TYPE_INTEGER, CONF_VALUE_INTEGER_1,
298 &li.time_orb_time, 10
302 TYPE_BOOLEAN, CONF_VALUE_BOOLEAN_1,
303 &li.use_time_orb_bug, FALSE
307 TYPE_BOOLEAN, CONF_VALUE_BOOLEAN_1,
308 &li.block_snap_field, TRUE
312 TYPE_BOOLEAN, CONF_VALUE_BOOLEAN_2,
313 &li.use_start_element[0], FALSE
317 TYPE_BOOLEAN, CONF_VALUE_BOOLEAN_2,
318 &li.use_start_element[1], FALSE
322 TYPE_BOOLEAN, CONF_VALUE_BOOLEAN_2,
323 &li.use_start_element[2], FALSE
327 TYPE_BOOLEAN, CONF_VALUE_BOOLEAN_2,
328 &li.use_start_element[3], FALSE
332 TYPE_BOOLEAN, CONF_VALUE_BOOLEAN_3,
333 &li.use_artwork_element[0], FALSE
337 TYPE_BOOLEAN, CONF_VALUE_BOOLEAN_3,
338 &li.use_artwork_element[1], FALSE
342 TYPE_BOOLEAN, CONF_VALUE_BOOLEAN_3,
343 &li.use_artwork_element[2], FALSE
347 TYPE_BOOLEAN, CONF_VALUE_BOOLEAN_3,
348 &li.use_artwork_element[3], FALSE
352 TYPE_BOOLEAN, CONF_VALUE_BOOLEAN_4,
353 &li.use_explosion_element[0], FALSE
357 TYPE_BOOLEAN, CONF_VALUE_BOOLEAN_4,
358 &li.use_explosion_element[1], FALSE
362 TYPE_BOOLEAN, CONF_VALUE_BOOLEAN_4,
363 &li.use_explosion_element[2], FALSE
367 TYPE_BOOLEAN, CONF_VALUE_BOOLEAN_4,
368 &li.use_explosion_element[3], FALSE
372 TYPE_BOOLEAN, CONF_VALUE_BOOLEAN_5,
373 &li.continuous_snapping, TRUE
377 TYPE_INTEGER, CONF_VALUE_INTEGER_1,
378 &li.initial_player_stepsize, STEPSIZE_NORMAL
382 TYPE_INTEGER, CONF_VALUE_INTEGER_1,
387 TYPE_BOOLEAN, CONF_VALUE_BOOLEAN_1,
388 &li.ball_random, FALSE
392 TYPE_BOOLEAN, CONF_VALUE_BOOLEAN_2,
393 &li.ball_state_initial, FALSE
396 /* ---------- 2-byte values ---------------------------------------------- */
400 TYPE_ELEMENT, CONF_VALUE_ELEMENT_1,
401 &li.start_element[0], EL_PLAYER_1
405 TYPE_ELEMENT, CONF_VALUE_ELEMENT_1,
406 &li.start_element[1], EL_PLAYER_2
410 TYPE_ELEMENT, CONF_VALUE_ELEMENT_1,
411 &li.start_element[2], EL_PLAYER_3
415 TYPE_ELEMENT, CONF_VALUE_ELEMENT_1,
416 &li.start_element[3], EL_PLAYER_4
420 TYPE_ELEMENT, CONF_VALUE_ELEMENT_2,
421 &li.artwork_element[0], EL_PLAYER_1
425 TYPE_ELEMENT, CONF_VALUE_ELEMENT_2,
426 &li.artwork_element[1], EL_PLAYER_2
430 TYPE_ELEMENT, CONF_VALUE_ELEMENT_2,
431 &li.artwork_element[2], EL_PLAYER_3
435 TYPE_ELEMENT, CONF_VALUE_ELEMENT_2,
436 &li.artwork_element[3], EL_PLAYER_4
440 TYPE_ELEMENT, CONF_VALUE_ELEMENT_3,
441 &li.explosion_element[0], EL_PLAYER_1
445 TYPE_ELEMENT, CONF_VALUE_ELEMENT_3,
446 &li.explosion_element[1], EL_PLAYER_2
450 TYPE_ELEMENT, CONF_VALUE_ELEMENT_3,
451 &li.explosion_element[2], EL_PLAYER_3
455 TYPE_ELEMENT, CONF_VALUE_ELEMENT_3,
456 &li.explosion_element[3], EL_PLAYER_4
459 /* ---------- multi-byte values ------------------------------------------ */
463 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
464 &li.android_clone_element[0], EL_EMPTY,
465 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
469 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
470 &li.ball_content, EL_EMPTY,
471 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
481 static struct ElementFileConfig custom_element_conf[] =
485 TYPE_STRING, CONF_VALUE_BYTES(1),
486 &xx_ei.description[0], 0,
487 &xx_num_description_bytes, MAX_ELEMENT_NAME_LEN, MAX_ELEMENT_NAME_LEN,
492 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
493 &xx_ei.properties[EP_BITFIELD_BASE],
497 (1 << EP_CAN_MOVE_INTO_ACID)
504 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
505 &xx_ei.properties[EP_BITFIELD_BASE + 1], EP_BITMASK_DEFAULT
511 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
512 &xx_ei.use_gfx_element, FALSE
516 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
517 &xx_ei.gfx_element, EL_EMPTY_SPACE
522 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
523 &xx_ei.access_direction, MV_ALL_DIRECTIONS
528 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
529 &xx_ei.collect_score_initial, 10
533 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
534 &xx_ei.collect_count_initial, 1
539 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
540 &xx_ei.ce_value_fixed_initial, 0
544 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
545 &xx_ei.ce_value_random_initial, 0
549 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
550 &xx_ei.use_last_ce_value, FALSE
555 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
556 &xx_ei.push_delay_fixed, 8,
560 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
561 &xx_ei.push_delay_random, 8,
565 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
566 &xx_ei.drop_delay_fixed, 0
570 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
571 &xx_ei.drop_delay_random, 0
575 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
576 &xx_ei.move_delay_fixed, 0
580 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
581 &xx_ei.move_delay_random, 0
586 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
587 &xx_ei.move_pattern, MV_ALL_DIRECTIONS
591 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
592 &xx_ei.move_direction_initial, MV_START_AUTOMATIC
596 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
597 &xx_ei.move_stepsize, TILEX / 8
602 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
603 &xx_ei.move_enter_element, EL_EMPTY_SPACE
607 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
608 &xx_ei.move_leave_element, EL_EMPTY_SPACE
612 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
613 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED
618 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
619 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM
624 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
625 &xx_ei.explosion_type, EXPLODES_3X3
629 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
630 &xx_ei.explosion_delay, 16
634 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
635 &xx_ei.ignition_delay, 8
640 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
641 &xx_ei.content, EL_EMPTY_SPACE,
642 &xx_num_contents, 1, 1
645 /* ---------- "num_change_pages" must be the last entry ------------------- */
649 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
650 &xx_ei.num_change_pages, -1 /* always save this value */
660 static struct ElementFileConfig custom_element_change_conf[] =
662 /* ---------- "current_change_page" must be the first entry --------------- */
666 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
667 &xx_current_change_page, -1
672 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
673 &xx_change.can_change, FALSE
678 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
679 &xx_event_bits_0_31, 0
683 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
684 &xx_event_bits_32_63, 0
689 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
690 &xx_change.trigger_player, CH_PLAYER_ANY
694 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
695 &xx_change.trigger_side, CH_SIDE_ANY
699 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
700 &xx_change.trigger_page, CH_PAGE_ANY
705 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
706 &xx_change.target_element, EL_EMPTY_SPACE
711 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
712 &xx_change.delay_fixed, 0
716 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
717 &xx_change.delay_random, 0
721 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
722 &xx_change.delay_frames, FRAMES_PER_SECOND
727 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
728 &xx_change.trigger_element, EL_EMPTY_SPACE
733 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
734 &xx_change.explode, FALSE
738 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
739 &xx_change.use_target_content, FALSE
743 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
744 &xx_change.only_if_complete, FALSE
748 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
749 &xx_change.use_random_replace, FALSE
753 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
754 &xx_change.random_percentage, 100
758 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
759 &xx_change.replace_when, CP_WHEN_EMPTY
764 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
765 &xx_change.has_action, FALSE
769 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
770 &xx_change.action_type, CA_NO_ACTION
774 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
775 &xx_change.action_mode, CA_MODE_UNDEFINED
779 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
780 &xx_change.action_arg, CA_ARG_UNDEFINED
785 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
786 &xx_change.target_content, EL_EMPTY_SPACE,
787 &xx_num_contents, 1, 1
797 static struct ElementFileConfig group_element_conf[] =
801 TYPE_STRING, CONF_VALUE_BYTES(1),
802 &xx_ei.description[0], 0,
803 &xx_num_description_bytes, MAX_ELEMENT_NAME_LEN, MAX_ELEMENT_NAME_LEN,
808 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
809 &xx_ei.use_gfx_element, FALSE
813 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
814 &xx_ei.gfx_element, EL_EMPTY_SPACE
819 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
820 &xx_group.choice_mode, ANIM_RANDOM
825 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
826 &xx_group.element[0], EL_EMPTY_SPACE,
827 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
844 { LEVEL_FILE_TYPE_RND, "RND" },
845 { LEVEL_FILE_TYPE_BD, "BD" },
846 { LEVEL_FILE_TYPE_EM, "EM" },
847 { LEVEL_FILE_TYPE_SP, "SP" },
848 { LEVEL_FILE_TYPE_DX, "DX" },
849 { LEVEL_FILE_TYPE_SB, "SB" },
850 { LEVEL_FILE_TYPE_DC, "DC" },
855 /* ========================================================================= */
856 /* level file functions */
857 /* ========================================================================= */
859 static void setLevelInfoToDefaultsFromConfigList(struct LevelInfo *level)
863 li = *level; /* copy level data into temporary buffer */
865 for (i = 0; element_conf[i].data_type != -1; i++)
867 int default_value = element_conf[i].default_value;
868 int data_type = element_conf[i].data_type;
869 int conf_type = element_conf[i].conf_type;
870 int byte_mask = conf_type & CONF_MASK_BYTES;
872 if (byte_mask == CONF_MASK_MULTI_BYTES)
874 int default_num_entities = element_conf[i].default_num_entities;
875 int max_num_entities = element_conf[i].max_num_entities;
877 *(int *)(element_conf[i].num_entities) = default_num_entities;
879 if (data_type == TYPE_ELEMENT_LIST)
881 int *element_array = (int *)(element_conf[i].value);
884 for (j = 0; j < max_num_entities; j++)
885 element_array[j] = default_value;
887 else if (data_type == TYPE_CONTENT_LIST)
889 struct Content *content = (struct Content *)(element_conf[i].value);
892 for (c = 0; c < max_num_entities; c++)
893 for (y = 0; y < 3; y++)
894 for (x = 0; x < 3; x++)
895 content[c].e[x][y] = default_value;
898 else /* constant size configuration data (1, 2 or 4 bytes) */
900 if (data_type == TYPE_BOOLEAN)
901 *(boolean *)(element_conf[i].value) = default_value;
903 *(int *) (element_conf[i].value) = default_value;
907 *level = li; /* copy temporary buffer back to level data */
910 void setElementChangePages(struct ElementInfo *ei, int change_pages)
912 int change_page_size = sizeof(struct ElementChangeInfo);
914 ei->num_change_pages = MAX(1, change_pages);
917 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
919 if (ei->current_change_page >= ei->num_change_pages)
920 ei->current_change_page = ei->num_change_pages - 1;
922 ei->change = &ei->change_page[ei->current_change_page];
925 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
929 change->can_change = FALSE;
931 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
932 change->has_event[i] = FALSE;
934 change->trigger_player = CH_PLAYER_ANY;
935 change->trigger_side = CH_SIDE_ANY;
936 change->trigger_page = CH_PAGE_ANY;
938 change->target_element = EL_EMPTY_SPACE;
940 change->delay_fixed = 0;
941 change->delay_random = 0;
942 change->delay_frames = FRAMES_PER_SECOND;
944 change->trigger_element = EL_EMPTY_SPACE;
946 change->explode = FALSE;
947 change->use_target_content = FALSE;
948 change->only_if_complete = FALSE;
949 change->use_random_replace = FALSE;
950 change->random_percentage = 100;
951 change->replace_when = CP_WHEN_EMPTY;
953 change->has_action = FALSE;
954 change->action_type = CA_NO_ACTION;
955 change->action_mode = CA_MODE_UNDEFINED;
956 change->action_arg = CA_ARG_UNDEFINED;
958 for (x = 0; x < 3; x++)
959 for (y = 0; y < 3; y++)
960 change->target_content.e[x][y] = EL_EMPTY_SPACE;
962 change->direct_action = 0;
963 change->other_action = 0;
965 change->pre_change_function = NULL;
966 change->change_function = NULL;
967 change->post_change_function = NULL;
970 static void setLevelInfoToDefaults(struct LevelInfo *level)
972 static boolean clipboard_elements_initialized = FALSE;
976 InitElementPropertiesStatic();
979 setLevelInfoToDefaultsFromConfigList(level);
980 setLevelInfoToDefaults_EM();
982 level->native_em_level = &native_em_level;
984 level->game_engine_type = GAME_ENGINE_TYPE_RND;
986 level->file_version = FILE_VERSION_ACTUAL;
987 level->game_version = GAME_VERSION_ACTUAL;
989 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
990 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
991 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
993 level->fieldx = STD_LEV_FIELDX;
994 level->fieldy = STD_LEV_FIELDY;
996 for (x = 0; x < MAX_LEV_FIELDX; x++)
997 for (y = 0; y < MAX_LEV_FIELDY; y++)
998 level->field[x][y] = EL_SAND;
1001 level->gems_needed = 0;
1003 level->amoeba_speed = 10;
1005 level->time_magic_wall = 10;
1006 level->time_wheel = 10;
1008 level->time_light = 10;
1009 level->time_timegate = 10;
1012 level->amoeba_content = EL_DIAMOND;
1014 level->game_of_life[0] = 2;
1015 level->game_of_life[1] = 3;
1016 level->game_of_life[2] = 3;
1017 level->game_of_life[3] = 3;
1019 level->biomaze[0] = 2;
1020 level->biomaze[1] = 3;
1021 level->biomaze[2] = 3;
1022 level->biomaze[3] = 3;
1025 level->double_speed = FALSE;
1027 level->initial_gravity = FALSE;
1028 level->em_slippery_gems = FALSE;
1029 level->instant_relocation = FALSE;
1030 level->can_pass_to_walkable = FALSE;
1031 level->grow_into_diggable = TRUE;
1034 level->block_snap_field = TRUE;
1037 level->block_last_field = FALSE; /* EM does not block by default */
1038 level->sp_block_last_field = TRUE; /* SP blocks the last field */
1040 level->can_move_into_acid_bits = ~0; /* everything can move into acid */
1041 level->dont_collide_with_bits = ~0; /* always deadly when colliding */
1043 level->use_spring_bug = FALSE;
1044 level->use_time_orb_bug = FALSE;
1046 level->use_step_counter = FALSE;
1048 /* values for the new EMC elements */
1050 level->android_move_time = 10;
1051 level->android_clone_time = 10;
1052 level->ball_time = 10;
1053 level->lenses_score = 10;
1054 level->lenses_time = 10;
1055 level->magnify_score = 10;
1056 level->magnify_time = 10;
1057 level->slurp_score = 10;
1058 level->wind_direction_initial = MV_NONE;
1059 level->ball_random = FALSE;
1060 level->ball_state_initial = FALSE;
1061 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1062 for (x = 0; x < 3; x++)
1063 for (y = 0; y < 3; y++)
1064 level->ball_content[i].e[x][y] = EL_EMPTY;
1067 for (i = 0; i < 16; i++)
1068 level->android_array[i] = FALSE;
1071 level->use_custom_template = FALSE;
1073 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1074 level->name[i] = '\0';
1075 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1076 level->author[i] = '\0';
1078 strcpy(level->name, NAMELESS_LEVEL_NAME);
1079 strcpy(level->author, ANONYMOUS_NAME);
1081 for (i = 0; i < 4; i++)
1083 level->envelope_text[i][0] = '\0';
1084 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
1085 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
1088 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1089 level->score[i] = (i == SC_TIME_BONUS ? 1 : 10);
1091 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
1092 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1093 for (x = 0; x < 3; x++)
1094 for (y = 0; y < 3; y++)
1095 level->yamyam_content[i].e[x][y] =
1096 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
1098 level->field[0][0] = EL_PLAYER_1;
1099 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1101 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1104 struct ElementInfo *ei = &element_info[element];
1106 /* never initialize clipboard elements after the very first time */
1107 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1110 setElementChangePages(ei, 1);
1111 setElementChangeInfoToDefaults(ei->change);
1113 if (IS_CUSTOM_ELEMENT(element) ||
1114 IS_GROUP_ELEMENT(element) ||
1115 IS_INTERNAL_ELEMENT(element))
1117 for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
1118 ei->description[j] = '\0';
1120 if (ei->custom_description != NULL)
1121 strncpy(ei->description, ei->custom_description,MAX_ELEMENT_NAME_LEN);
1123 strcpy(ei->description, ei->editor_description);
1125 ei->use_gfx_element = FALSE;
1126 ei->gfx_element = EL_EMPTY_SPACE;
1128 ei->modified_settings = FALSE;
1131 if (IS_CUSTOM_ELEMENT(element) ||
1132 IS_INTERNAL_ELEMENT(element))
1134 ei->access_direction = MV_ALL_DIRECTIONS;
1136 ei->collect_score_initial = 10; /* special default */
1137 ei->collect_count_initial = 1; /* special default */
1139 ei->ce_value_fixed_initial = 0;
1140 ei->ce_value_random_initial = 0;
1141 ei->use_last_ce_value = FALSE;
1143 ei->push_delay_fixed = -1; /* initialize later */
1144 ei->push_delay_random = -1; /* initialize later */
1145 ei->drop_delay_fixed = 0;
1146 ei->drop_delay_random = 0;
1147 ei->move_delay_fixed = 0;
1148 ei->move_delay_random = 0;
1150 ei->move_pattern = MV_ALL_DIRECTIONS;
1151 ei->move_direction_initial = MV_START_AUTOMATIC;
1152 ei->move_stepsize = TILEX / 8;
1154 ei->move_enter_element = EL_EMPTY_SPACE;
1155 ei->move_leave_element = EL_EMPTY_SPACE;
1156 ei->move_leave_type = LEAVE_TYPE_UNLIMITED;
1158 ei->slippery_type = SLIPPERY_ANY_RANDOM;
1160 ei->explosion_type = EXPLODES_3X3;
1161 ei->explosion_delay = 16;
1162 ei->ignition_delay = 8;
1164 for (x = 0; x < 3; x++)
1165 for (y = 0; y < 3; y++)
1166 ei->content.e[x][y] = EL_EMPTY_SPACE;
1168 ei->access_type = 0;
1169 ei->access_layer = 0;
1170 ei->access_protected = 0;
1171 ei->walk_to_action = 0;
1172 ei->smash_targets = 0;
1175 ei->can_explode_by_fire = FALSE;
1176 ei->can_explode_smashed = FALSE;
1177 ei->can_explode_impact = FALSE;
1179 ei->current_change_page = 0;
1182 /* !!! now done in InitElementPropertiesStatic() (see above) !!! */
1183 /* !!! (else properties set there will be overwritten here) !!! */
1184 /* start with no properties at all */
1186 for (j = 0; j < NUM_EP_BITFIELDS; j++)
1187 ei->properties[j] = EP_BITMASK_DEFAULT;
1189 for (j = 0; j < NUM_EP_BITFIELDS; j++)
1190 Properties[element][j] = EP_BITMASK_DEFAULT;
1194 /* now set default properties */
1195 SET_PROPERTY(element, EP_CAN_MOVE_INTO_ACID, TRUE);
1198 if (IS_GROUP_ELEMENT(element) ||
1199 IS_INTERNAL_ELEMENT(element))
1201 struct ElementGroupInfo *group;
1203 /* initialize memory for list of elements in group */
1204 if (ei->group == NULL)
1205 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1209 for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
1210 group->element[j] = EL_EMPTY_SPACE;
1212 /* default: only one element in group */
1213 group->num_elements = 1;
1215 group->choice_mode = ANIM_RANDOM;
1219 clipboard_elements_initialized = TRUE;
1221 BorderElement = EL_STEELWALL;
1223 level->no_valid_file = FALSE;
1225 level->changed = FALSE;
1227 if (leveldir_current == NULL) /* only when dumping level */
1230 /* try to determine better author name than 'anonymous' */
1231 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1233 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1234 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1238 switch (LEVELCLASS(leveldir_current))
1240 case LEVELCLASS_TUTORIAL:
1241 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1244 case LEVELCLASS_CONTRIB:
1245 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1246 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1249 case LEVELCLASS_PRIVATE:
1250 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1251 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1255 /* keep default value */
1261 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1263 level_file_info->nr = 0;
1264 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1265 level_file_info->packed = FALSE;
1266 level_file_info->basename = NULL;
1267 level_file_info->filename = NULL;
1270 static void ActivateLevelTemplate()
1273 /* Currently there is no special action needed to activate the template
1274 data, because 'element_info' property settings overwrite the original
1275 level data, while all other variables do not change. */
1277 /* Currently there is no special action needed to activate the template
1278 data, because 'element_info' and 'Properties' overwrite the original
1279 level data, while all other variables do not change. */
1283 static char *getLevelFilenameFromBasename(char *basename)
1285 static char *filename = NULL;
1287 checked_free(filename);
1289 filename = getPath2(getCurrentLevelDir(), basename);
1294 static int getFileTypeFromBasename(char *basename)
1296 static char *filename = NULL;
1297 struct stat file_status;
1299 /* ---------- try to determine file type from filename ---------- */
1301 /* check for typical filename of a Supaplex level package file */
1302 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
1303 strncmp(basename, "LEVELS.D", 8) == 0))
1304 return LEVEL_FILE_TYPE_SP;
1306 /* ---------- try to determine file type from filesize ---------- */
1308 checked_free(filename);
1309 filename = getPath2(getCurrentLevelDir(), basename);
1311 if (stat(filename, &file_status) == 0)
1313 /* check for typical filesize of a Supaplex level package file */
1314 if (file_status.st_size == 170496)
1315 return LEVEL_FILE_TYPE_SP;
1318 return LEVEL_FILE_TYPE_UNKNOWN;
1321 static char *getSingleLevelBasename(int nr)
1323 static char basename[MAX_FILENAME_LEN];
1326 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
1328 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
1333 static char *getPackedLevelBasename(int type)
1335 static char basename[MAX_FILENAME_LEN];
1336 char *directory = getCurrentLevelDir();
1338 struct dirent *dir_entry;
1340 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
1342 if ((dir = opendir(directory)) == NULL)
1344 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
1349 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1351 char *entry_basename = dir_entry->d_name;
1352 int entry_type = getFileTypeFromBasename(entry_basename);
1354 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
1356 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
1359 strcpy(basename, entry_basename);
1371 static char *getSingleLevelFilename(int nr)
1373 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
1377 static char *getPackedLevelFilename(int type)
1379 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
1383 char *getDefaultLevelFilename(int nr)
1385 return getSingleLevelFilename(nr);
1389 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
1393 lfi->packed = FALSE;
1394 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
1395 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1399 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
1400 int type, char *format, ...)
1402 static char basename[MAX_FILENAME_LEN];
1405 va_start(ap, format);
1406 vsprintf(basename, format, ap);
1410 lfi->packed = FALSE;
1411 lfi->basename = basename;
1412 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1415 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
1420 lfi->basename = getPackedLevelBasename(lfi->type);
1421 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1424 static int getFiletypeFromID(char *filetype_id)
1426 char *filetype_id_lower;
1427 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
1430 if (filetype_id == NULL)
1431 return LEVEL_FILE_TYPE_UNKNOWN;
1433 filetype_id_lower = getStringToLower(filetype_id);
1435 for (i = 0; filetype_id_list[i].id != NULL; i++)
1437 char *id_lower = getStringToLower(filetype_id_list[i].id);
1439 if (strEqual(filetype_id_lower, id_lower))
1440 filetype = filetype_id_list[i].filetype;
1444 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
1448 free(filetype_id_lower);
1453 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
1457 /* special case: level number is negative => check for level template file */
1460 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
1461 "template.%s", LEVELFILE_EXTENSION);
1463 /* no fallback if template file not existing */
1467 /* special case: check for file name/pattern specified in "levelinfo.conf" */
1468 if (leveldir_current->level_filename != NULL)
1470 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
1472 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
1473 leveldir_current->level_filename, nr);
1474 if (fileExists(lfi->filename))
1478 /* check for native Rocks'n'Diamonds level file */
1479 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
1480 "%03d.%s", nr, LEVELFILE_EXTENSION);
1481 if (fileExists(lfi->filename))
1484 /* check for Emerald Mine level file (V1) */
1485 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
1486 'a' + (nr / 10) % 26, '0' + nr % 10);
1487 if (fileExists(lfi->filename))
1489 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
1490 'A' + (nr / 10) % 26, '0' + nr % 10);
1491 if (fileExists(lfi->filename))
1494 /* check for Emerald Mine level file (V2 to V5) */
1495 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
1496 if (fileExists(lfi->filename))
1499 /* check for Emerald Mine level file (V6 / single mode) */
1500 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
1501 if (fileExists(lfi->filename))
1503 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
1504 if (fileExists(lfi->filename))
1507 /* check for Emerald Mine level file (V6 / teamwork mode) */
1508 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
1509 if (fileExists(lfi->filename))
1511 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
1512 if (fileExists(lfi->filename))
1515 /* check for various packed level file formats */
1516 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
1517 if (fileExists(lfi->filename))
1520 /* no known level file found -- use default values (and fail later) */
1521 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
1522 "%03d.%s", nr, LEVELFILE_EXTENSION);
1525 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
1527 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
1528 lfi->type = getFileTypeFromBasename(lfi->basename);
1531 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
1533 /* always start with reliable default values */
1534 setFileInfoToDefaults(level_file_info);
1536 level_file_info->nr = nr; /* set requested level number */
1538 determineLevelFileInfo_Filename(level_file_info);
1539 determineLevelFileInfo_Filetype(level_file_info);
1542 /* ------------------------------------------------------------------------- */
1543 /* functions for loading R'n'D level */
1544 /* ------------------------------------------------------------------------- */
1546 int getMappedElement(int element)
1548 /* remap some (historic, now obsolete) elements */
1552 case EL_PLAYER_OBSOLETE:
1553 element = EL_PLAYER_1;
1556 case EL_KEY_OBSOLETE:
1559 case EL_EM_KEY_1_FILE_OBSOLETE:
1560 element = EL_EM_KEY_1;
1563 case EL_EM_KEY_2_FILE_OBSOLETE:
1564 element = EL_EM_KEY_2;
1567 case EL_EM_KEY_3_FILE_OBSOLETE:
1568 element = EL_EM_KEY_3;
1571 case EL_EM_KEY_4_FILE_OBSOLETE:
1572 element = EL_EM_KEY_4;
1575 case EL_ENVELOPE_OBSOLETE:
1576 element = EL_ENVELOPE_1;
1584 if (element >= NUM_FILE_ELEMENTS)
1586 Error(ERR_WARN, "invalid level element %d", element);
1588 element = EL_UNKNOWN;
1596 int getMappedElementByVersion(int element, int game_version)
1598 /* remap some elements due to certain game version */
1600 if (game_version <= VERSION_IDENT(2,2,0,0))
1602 /* map game font elements */
1603 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1604 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1605 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1606 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1609 if (game_version < VERSION_IDENT(3,0,0,0))
1611 /* map Supaplex gravity tube elements */
1612 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1613 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1614 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1615 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1622 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
1624 level->file_version = getFileVersion(file);
1625 level->game_version = getFileVersion(file);
1630 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
1634 level->fieldx = getFile8Bit(file);
1635 level->fieldy = getFile8Bit(file);
1637 level->time = getFile16BitBE(file);
1638 level->gems_needed = getFile16BitBE(file);
1640 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1641 level->name[i] = getFile8Bit(file);
1642 level->name[MAX_LEVEL_NAME_LEN] = 0;
1644 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1645 level->score[i] = getFile8Bit(file);
1647 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
1648 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
1649 for (y = 0; y < 3; y++)
1650 for (x = 0; x < 3; x++)
1651 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
1653 level->amoeba_speed = getFile8Bit(file);
1654 level->time_magic_wall = getFile8Bit(file);
1655 level->time_wheel = getFile8Bit(file);
1656 level->amoeba_content = getMappedElement(getFile8Bit(file));
1658 level->initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
1661 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1662 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1663 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1665 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1667 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1668 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1669 level->can_move_into_acid_bits = getFile32BitBE(file);
1670 level->dont_collide_with_bits = getFile8Bit(file);
1672 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1673 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1675 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1676 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1677 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1679 level->game_engine_type = getFile8Bit(file);
1681 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
1686 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
1690 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1691 level->author[i] = getFile8Bit(file);
1692 level->author[MAX_LEVEL_NAME_LEN] = 0;
1697 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
1700 int chunk_size_expected = level->fieldx * level->fieldy;
1702 /* Note: "chunk_size" was wrong before version 2.0 when elements are
1703 stored with 16-bit encoding (and should be twice as big then).
1704 Even worse, playfield data was stored 16-bit when only yamyam content
1705 contained 16-bit elements and vice versa. */
1707 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
1708 chunk_size_expected *= 2;
1710 if (chunk_size_expected != chunk_size)
1712 ReadUnusedBytesFromFile(file, chunk_size);
1713 return chunk_size_expected;
1716 for (y = 0; y < level->fieldy; y++)
1717 for (x = 0; x < level->fieldx; x++)
1718 level->field[x][y] =
1719 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
1724 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
1727 int header_size = 4;
1728 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
1729 int chunk_size_expected = header_size + content_size;
1731 /* Note: "chunk_size" was wrong before version 2.0 when elements are
1732 stored with 16-bit encoding (and should be twice as big then).
1733 Even worse, playfield data was stored 16-bit when only yamyam content
1734 contained 16-bit elements and vice versa. */
1736 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
1737 chunk_size_expected += content_size;
1739 if (chunk_size_expected != chunk_size)
1741 ReadUnusedBytesFromFile(file, chunk_size);
1742 return chunk_size_expected;
1746 level->num_yamyam_contents = getFile8Bit(file);
1750 /* correct invalid number of content fields -- should never happen */
1751 if (level->num_yamyam_contents < 1 ||
1752 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
1753 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
1755 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1756 for (y = 0; y < 3; y++)
1757 for (x = 0; x < 3; x++)
1758 level->yamyam_content[i].e[x][y] =
1759 getMappedElement(level->encoding_16bit_field ?
1760 getFile16BitBE(file) : getFile8Bit(file));
1764 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
1768 int num_contents, content_xsize, content_ysize;
1769 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1771 element = getMappedElement(getFile16BitBE(file));
1772 num_contents = getFile8Bit(file);
1773 content_xsize = getFile8Bit(file);
1774 content_ysize = getFile8Bit(file);
1776 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1778 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1779 for (y = 0; y < 3; y++)
1780 for (x = 0; x < 3; x++)
1781 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
1783 /* correct invalid number of content fields -- should never happen */
1784 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
1785 num_contents = STD_ELEMENT_CONTENTS;
1787 if (element == EL_YAMYAM)
1789 level->num_yamyam_contents = num_contents;
1791 for (i = 0; i < num_contents; i++)
1792 for (y = 0; y < 3; y++)
1793 for (x = 0; x < 3; x++)
1794 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
1796 else if (element == EL_BD_AMOEBA)
1798 level->amoeba_content = content_array[0][0][0];
1802 Error(ERR_WARN, "cannot load content for element '%d'", element);
1808 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
1814 int chunk_size_expected;
1816 element = getMappedElement(getFile16BitBE(file));
1817 if (!IS_ENVELOPE(element))
1818 element = EL_ENVELOPE_1;
1820 envelope_nr = element - EL_ENVELOPE_1;
1822 envelope_len = getFile16BitBE(file);
1824 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
1825 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
1827 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1829 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
1830 if (chunk_size_expected != chunk_size)
1832 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
1833 return chunk_size_expected;
1836 for (i = 0; i < envelope_len; i++)
1837 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
1842 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
1844 int num_changed_custom_elements = getFile16BitBE(file);
1845 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
1848 if (chunk_size_expected != chunk_size)
1850 ReadUnusedBytesFromFile(file, chunk_size - 2);
1851 return chunk_size_expected;
1854 for (i = 0; i < num_changed_custom_elements; i++)
1856 int element = getFile16BitBE(file);
1857 int properties = getFile32BitBE(file);
1860 if (IS_CUSTOM_ELEMENT(element))
1861 element_info[element].properties[EP_BITFIELD_BASE] = properties;
1863 Error(ERR_WARN, "invalid custom element number %d", element);
1865 if (IS_CUSTOM_ELEMENT(element))
1866 Properties[element][EP_BITFIELD_BASE] = properties;
1868 Error(ERR_WARN, "invalid custom element number %d", element);
1875 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
1877 int num_changed_custom_elements = getFile16BitBE(file);
1878 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
1881 if (chunk_size_expected != chunk_size)
1883 ReadUnusedBytesFromFile(file, chunk_size - 2);
1884 return chunk_size_expected;
1887 for (i = 0; i < num_changed_custom_elements; i++)
1889 int element = getFile16BitBE(file);
1890 int custom_target_element = getFile16BitBE(file);
1892 if (IS_CUSTOM_ELEMENT(element))
1893 element_info[element].change->target_element = custom_target_element;
1895 Error(ERR_WARN, "invalid custom element number %d", element);
1901 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
1903 int num_changed_custom_elements = getFile16BitBE(file);
1904 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1907 if (chunk_size_expected != chunk_size)
1909 ReadUnusedBytesFromFile(file, chunk_size - 2);
1910 return chunk_size_expected;
1913 for (i = 0; i < num_changed_custom_elements; i++)
1915 int element = getFile16BitBE(file);
1916 struct ElementInfo *ei = &element_info[element];
1917 unsigned int event_bits;
1919 if (!IS_CUSTOM_ELEMENT(element))
1921 Error(ERR_WARN, "invalid custom element number %d", element);
1923 element = EL_INTERNAL_DUMMY;
1926 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
1927 ei->description[j] = getFile8Bit(file);
1928 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1931 ei->properties[EP_BITFIELD_BASE] = getFile32BitBE(file);
1933 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
1936 /* some free bytes for future properties and padding */
1937 ReadUnusedBytesFromFile(file, 7);
1939 ei->use_gfx_element = getFile8Bit(file);
1940 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1942 ei->collect_score_initial = getFile8Bit(file);
1943 ei->collect_count_initial = getFile8Bit(file);
1945 ei->push_delay_fixed = getFile16BitBE(file);
1946 ei->push_delay_random = getFile16BitBE(file);
1947 ei->move_delay_fixed = getFile16BitBE(file);
1948 ei->move_delay_random = getFile16BitBE(file);
1950 ei->move_pattern = getFile16BitBE(file);
1951 ei->move_direction_initial = getFile8Bit(file);
1952 ei->move_stepsize = getFile8Bit(file);
1954 for (y = 0; y < 3; y++)
1955 for (x = 0; x < 3; x++)
1956 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
1958 event_bits = getFile32BitBE(file);
1959 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1960 if (event_bits & (1 << j))
1961 ei->change->has_event[j] = TRUE;
1963 ei->change->target_element = getMappedElement(getFile16BitBE(file));
1965 ei->change->delay_fixed = getFile16BitBE(file);
1966 ei->change->delay_random = getFile16BitBE(file);
1967 ei->change->delay_frames = getFile16BitBE(file);
1969 ei->change->trigger_element = getMappedElement(getFile16BitBE(file));
1971 ei->change->explode = getFile8Bit(file);
1972 ei->change->use_target_content = getFile8Bit(file);
1973 ei->change->only_if_complete = getFile8Bit(file);
1974 ei->change->use_random_replace = getFile8Bit(file);
1976 ei->change->random_percentage = getFile8Bit(file);
1977 ei->change->replace_when = getFile8Bit(file);
1979 for (y = 0; y < 3; y++)
1980 for (x = 0; x < 3; x++)
1981 ei->change->target_content.e[x][y] =
1982 getMappedElement(getFile16BitBE(file));
1984 ei->slippery_type = getFile8Bit(file);
1986 /* some free bytes for future properties and padding */
1987 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
1989 /* mark that this custom element has been modified */
1990 ei->modified_settings = TRUE;
1996 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
1998 struct ElementInfo *ei;
1999 int chunk_size_expected;
2003 /* ---------- custom element base property values (96 bytes) ------------- */
2005 element = getFile16BitBE(file);
2007 if (!IS_CUSTOM_ELEMENT(element))
2009 Error(ERR_WARN, "invalid custom element number %d", element);
2011 ReadUnusedBytesFromFile(file, chunk_size - 2);
2015 ei = &element_info[element];
2017 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2018 ei->description[i] = getFile8Bit(file);
2019 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2022 ei->properties[EP_BITFIELD_BASE] = getFile32BitBE(file);
2024 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
2026 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2028 ei->num_change_pages = getFile8Bit(file);
2030 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2031 if (chunk_size_expected != chunk_size)
2033 ReadUnusedBytesFromFile(file, chunk_size - 43);
2034 return chunk_size_expected;
2037 ei->ce_value_fixed_initial = getFile16BitBE(file);
2038 ei->ce_value_random_initial = getFile16BitBE(file);
2039 ei->use_last_ce_value = getFile8Bit(file);
2041 ei->use_gfx_element = getFile8Bit(file);
2042 ei->gfx_element = getMappedElement(getFile16BitBE(file));
2044 ei->collect_score_initial = getFile8Bit(file);
2045 ei->collect_count_initial = getFile8Bit(file);
2047 ei->drop_delay_fixed = getFile8Bit(file);
2048 ei->push_delay_fixed = getFile8Bit(file);
2049 ei->drop_delay_random = getFile8Bit(file);
2050 ei->push_delay_random = getFile8Bit(file);
2051 ei->move_delay_fixed = getFile16BitBE(file);
2052 ei->move_delay_random = getFile16BitBE(file);
2054 /* bits 0 - 15 of "move_pattern" ... */
2055 ei->move_pattern = getFile16BitBE(file);
2056 ei->move_direction_initial = getFile8Bit(file);
2057 ei->move_stepsize = getFile8Bit(file);
2059 ei->slippery_type = getFile8Bit(file);
2061 for (y = 0; y < 3; y++)
2062 for (x = 0; x < 3; x++)
2063 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2065 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2066 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2067 ei->move_leave_type = getFile8Bit(file);
2069 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2070 ei->move_pattern |= (getFile16BitBE(file) << 16);
2072 ei->access_direction = getFile8Bit(file);
2074 ei->explosion_delay = getFile8Bit(file);
2075 ei->ignition_delay = getFile8Bit(file);
2076 ei->explosion_type = getFile8Bit(file);
2078 /* some free bytes for future custom property values and padding */
2079 ReadUnusedBytesFromFile(file, 1);
2081 /* ---------- change page property values (48 bytes) --------------------- */
2083 setElementChangePages(ei, ei->num_change_pages);
2085 for (i = 0; i < ei->num_change_pages; i++)
2087 struct ElementChangeInfo *change = &ei->change_page[i];
2088 unsigned int event_bits;
2090 /* always start with reliable default values */
2091 setElementChangeInfoToDefaults(change);
2093 /* bits 0 - 31 of "has_event[]" ... */
2094 event_bits = getFile32BitBE(file);
2095 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2096 if (event_bits & (1 << j))
2097 change->has_event[j] = TRUE;
2099 change->target_element = getMappedElement(getFile16BitBE(file));
2101 change->delay_fixed = getFile16BitBE(file);
2102 change->delay_random = getFile16BitBE(file);
2103 change->delay_frames = getFile16BitBE(file);
2105 change->trigger_element = getMappedElement(getFile16BitBE(file));
2107 change->explode = getFile8Bit(file);
2108 change->use_target_content = getFile8Bit(file);
2109 change->only_if_complete = getFile8Bit(file);
2110 change->use_random_replace = getFile8Bit(file);
2112 change->random_percentage = getFile8Bit(file);
2113 change->replace_when = getFile8Bit(file);
2115 for (y = 0; y < 3; y++)
2116 for (x = 0; x < 3; x++)
2117 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2119 change->can_change = getFile8Bit(file);
2121 change->trigger_side = getFile8Bit(file);
2123 change->trigger_player = getFile8Bit(file);
2124 change->trigger_page = getFile8Bit(file);
2126 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2127 CH_PAGE_ANY : (1 << change->trigger_page));
2129 change->has_action = getFile8Bit(file);
2130 change->action_type = getFile8Bit(file);
2131 change->action_mode = getFile8Bit(file);
2132 change->action_arg = getFile16BitBE(file);
2134 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2135 event_bits = getFile8Bit(file);
2136 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2137 if (event_bits & (1 << (j - 32)))
2138 change->has_event[j] = TRUE;
2141 /* mark this custom element as modified */
2142 ei->modified_settings = TRUE;
2147 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
2149 struct ElementInfo *ei;
2150 struct ElementGroupInfo *group;
2154 element = getFile16BitBE(file);
2156 if (!IS_GROUP_ELEMENT(element))
2158 Error(ERR_WARN, "invalid group element number %d", element);
2160 ReadUnusedBytesFromFile(file, chunk_size - 2);
2164 ei = &element_info[element];
2166 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2167 ei->description[i] = getFile8Bit(file);
2168 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2170 group = element_info[element].group;
2172 group->num_elements = getFile8Bit(file);
2174 ei->use_gfx_element = getFile8Bit(file);
2175 ei->gfx_element = getMappedElement(getFile16BitBE(file));
2177 group->choice_mode = getFile8Bit(file);
2179 /* some free bytes for future values and padding */
2180 ReadUnusedBytesFromFile(file, 3);
2182 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2183 group->element[i] = getMappedElement(getFile16BitBE(file));
2185 /* mark this group element as modified */
2186 element_info[element].modified_settings = TRUE;
2191 static int LoadLevel_MicroChunk(FILE *file, struct ElementFileConfig *config,
2194 int micro_chunk_size = 0;
2195 int conf_type = getFile8Bit(file);
2196 int byte_mask = conf_type & CONF_MASK_BYTES;
2197 boolean element_found = FALSE;
2200 micro_chunk_size += 1;
2202 if (byte_mask == CONF_MASK_MULTI_BYTES)
2204 int num_bytes = getFile16BitBE(file);
2205 byte *buffer = checked_malloc(num_bytes);
2208 printf("::: - found multi bytes\n");
2211 ReadBytesFromFile(file, buffer, num_bytes);
2213 for (i = 0; config[i].data_type != -1; i++)
2215 if (config[i].element == element &&
2216 config[i].conf_type == conf_type)
2218 int data_type = config[i].data_type;
2219 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2220 int max_num_entities = config[i].max_num_entities;
2222 if (num_entities > max_num_entities)
2225 "truncating number of entities for element %d from %d to %d",
2226 element, num_entities, max_num_entities);
2228 num_entities = max_num_entities;
2231 *(int *)(config[i].num_entities) = num_entities;
2233 element_found = TRUE;
2235 if (data_type == TYPE_ELEMENT_LIST)
2237 int *element_array = (int *)(config[i].value);
2240 for (j = 0; j < num_entities; j++)
2242 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
2244 else if (data_type == TYPE_CONTENT_LIST)
2246 struct Content *content= (struct Content *)(config[i].value);
2249 for (c = 0; c < num_entities; c++)
2250 for (y = 0; y < 3; y++)
2251 for (x = 0; x < 3; x++)
2252 content[c].e[x][y] =
2253 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
2256 element_found = FALSE;
2262 checked_free(buffer);
2264 micro_chunk_size += 2 + num_bytes;
2266 else /* constant size configuration data (1, 2 or 4 bytes) */
2268 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
2269 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
2270 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
2273 printf("::: - found single bytes\n");
2276 for (i = 0; config[i].data_type != -1; i++)
2278 if (config[i].element == element &&
2279 config[i].conf_type == conf_type)
2281 int data_type = config[i].data_type;
2283 if (data_type == TYPE_BOOLEAN)
2284 *(boolean *)(config[i].value) = value;
2286 *(int *) (config[i].value) = value;
2288 element_found = TRUE;
2294 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
2298 Error(ERR_WARN, "cannot load micro chunk value for element %d", element);
2300 return micro_chunk_size;
2303 static int LoadLevel_CONF(FILE *file, int chunk_size, struct LevelInfo *level)
2305 int real_chunk_size = 0;
2308 li = *level; /* copy level data into temporary buffer */
2313 int element = getFile16BitBE(file);
2315 real_chunk_size += 2;
2316 real_chunk_size += LoadLevel_MicroChunk(file, element_conf, element);
2318 int conf_type = getFile8Bit(file);
2319 int byte_mask = conf_type & CONF_MASK_BYTES;
2320 boolean element_found = FALSE;
2323 real_chunk_size += 3;
2326 li = *level; /* copy level data into temporary buffer */
2329 if (byte_mask == CONF_MASK_MULTI_BYTES)
2331 int num_bytes = getFile16BitBE(file);
2332 byte *buffer = checked_malloc(num_bytes);
2334 ReadBytesFromFile(file, buffer, num_bytes);
2336 for (i = 0; element_conf[i].data_type != -1; i++)
2338 if (element_conf[i].element == element &&
2339 element_conf[i].conf_type == conf_type)
2341 int data_type = element_conf[i].data_type;
2342 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2343 int max_num_entities = element_conf[i].max_num_entities;
2345 if (num_entities > max_num_entities)
2348 "truncating number of entities for element %d from %d to %d",
2349 element, num_entities, max_num_entities);
2351 num_entities = max_num_entities;
2354 *(int *)(element_conf[i].num_entities) = num_entities;
2356 element_found = TRUE;
2358 if (data_type == TYPE_ELEMENT_LIST)
2360 int *element_array = (int *)(element_conf[i].value);
2363 for (j = 0; j < num_entities; j++)
2365 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
2367 else if (data_type == TYPE_CONTENT_LIST)
2369 struct Content *content= (struct Content *)(element_conf[i].value);
2372 for (c = 0; c < num_entities; c++)
2373 for (y = 0; y < 3; y++)
2374 for (x = 0; x < 3; x++)
2375 content[c].e[x][y] =
2376 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
2379 element_found = FALSE;
2385 checked_free(buffer);
2387 real_chunk_size += 2 + num_bytes;
2389 else /* constant size configuration data (1, 2 or 4 bytes) */
2391 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
2392 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
2393 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
2395 for (i = 0; element_conf[i].data_type != -1; i++)
2397 if (element_conf[i].element == element &&
2398 element_conf[i].conf_type == conf_type)
2400 int data_type = element_conf[i].data_type;
2402 if (data_type == TYPE_BOOLEAN)
2403 *(boolean *)(element_conf[i].value) = value;
2405 *(int *) (element_conf[i].value) = value;
2407 element_found = TRUE;
2413 real_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
2417 Error(ERR_WARN, "cannot load CONF value for element %d", element);
2421 *level = li; /* copy temporary buffer back to level data */
2425 if (real_chunk_size >= chunk_size)
2428 if (conf_type == CONF_LAST_ENTRY || real_chunk_size >= chunk_size)
2434 *level = li; /* copy temporary buffer back to level data */
2437 return real_chunk_size;
2440 static int LoadLevel_CUSX(FILE *file, int chunk_size, struct LevelInfo *level)
2442 int element = getFile16BitBE(file);
2443 int real_chunk_size = 2;
2444 struct ElementInfo *ei = &element_info[element];
2447 printf("::: CUSX: loading element '%s' ...\n", EL_NAME(element));
2450 xx_ei = *ei; /* copy element data into temporary buffer */
2452 xx_ei.num_change_pages = -1;
2456 real_chunk_size += LoadLevel_MicroChunk(file, custom_element_conf, -1);
2459 printf("::: - real_chunk_size now %d\n", real_chunk_size);
2462 if (xx_ei.num_change_pages != -1)
2465 if (real_chunk_size >= chunk_size)
2471 if (ei->num_change_pages == -1)
2473 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
2476 ei->num_change_pages = 1;
2477 setElementChangePages(ei, 1);
2479 return real_chunk_size;
2482 setElementChangePages(ei, ei->num_change_pages);
2484 xx_current_change_page = 0;
2486 xx_event_bits_0_31 = 0;
2487 xx_event_bits_32_63 = 0;
2491 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
2494 xx_change = *change; /* copy change data into temporary buffer */
2496 real_chunk_size += LoadLevel_MicroChunk(file,custom_element_change_conf,-1);
2498 *change = xx_change;
2500 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2501 if ((i < 32 && xx_event_bits_0_31 & (1 << i)) ||
2502 (i >= 32 && xx_event_bits_32_63 & (1 << (i - 32))))
2503 change->has_event[i] = TRUE;
2505 xx_event_bits_0_31 = 0;
2506 xx_event_bits_32_63 = 0;
2508 if (real_chunk_size >= chunk_size)
2512 return real_chunk_size;
2515 static int LoadLevel_GRPX(FILE *file, int chunk_size, struct LevelInfo *level)
2517 int element = getFile16BitBE(file);
2518 int real_chunk_size = 2;
2519 struct ElementInfo *ei = &element_info[element];
2520 struct ElementGroupInfo *group = ei->group;
2522 xx_ei = *ei; /* copy element data into temporary buffer */
2523 xx_group = *group; /* copy group data into temporary buffer */
2527 real_chunk_size += LoadLevel_MicroChunk(file, group_element_conf, -1);
2529 if (real_chunk_size >= chunk_size)
2536 return real_chunk_size;
2539 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
2540 struct LevelFileInfo *level_file_info)
2542 char *filename = level_file_info->filename;
2543 char cookie[MAX_LINE_LEN];
2544 char chunk_name[CHUNK_ID_LEN + 1];
2548 if (!(file = fopen(filename, MODE_READ)))
2550 level->no_valid_file = TRUE;
2552 if (level != &level_template)
2553 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
2558 getFileChunkBE(file, chunk_name, NULL);
2559 if (strEqual(chunk_name, "RND1"))
2561 getFile32BitBE(file); /* not used */
2563 getFileChunkBE(file, chunk_name, NULL);
2564 if (!strEqual(chunk_name, "CAVE"))
2566 level->no_valid_file = TRUE;
2568 Error(ERR_WARN, "unknown format of level file '%s'", filename);
2573 else /* check for pre-2.0 file format with cookie string */
2575 strcpy(cookie, chunk_name);
2576 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
2577 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2578 cookie[strlen(cookie) - 1] = '\0';
2580 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
2582 level->no_valid_file = TRUE;
2584 Error(ERR_WARN, "unknown format of level file '%s'", filename);
2589 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
2591 level->no_valid_file = TRUE;
2593 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
2598 /* pre-2.0 level files have no game version, so use file version here */
2599 level->game_version = level->file_version;
2602 if (level->file_version < FILE_VERSION_1_2)
2604 /* level files from versions before 1.2.0 without chunk structure */
2605 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
2606 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
2614 int (*loader)(FILE *, int, struct LevelInfo *);
2618 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
2619 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
2620 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
2621 { "BODY", -1, LoadLevel_BODY },
2622 { "CONT", -1, LoadLevel_CONT },
2623 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
2624 { "CNT3", -1, LoadLevel_CNT3 },
2625 { "CUS1", -1, LoadLevel_CUS1 },
2626 { "CUS2", -1, LoadLevel_CUS2 },
2627 { "CUS3", -1, LoadLevel_CUS3 },
2628 { "CUS4", -1, LoadLevel_CUS4 },
2629 { "GRP1", -1, LoadLevel_GRP1 },
2630 { "CONF", -1, LoadLevel_CONF },
2632 { "CUSX", -1, LoadLevel_CUSX },
2633 { "GRPX", -1, LoadLevel_GRPX },
2639 while (getFileChunkBE(file, chunk_name, &chunk_size))
2643 while (chunk_info[i].name != NULL &&
2644 !strEqual(chunk_name, chunk_info[i].name))
2647 if (chunk_info[i].name == NULL)
2649 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
2650 chunk_name, filename);
2651 ReadUnusedBytesFromFile(file, chunk_size);
2653 else if (chunk_info[i].size != -1 &&
2654 chunk_info[i].size != chunk_size)
2656 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
2657 chunk_size, chunk_name, filename);
2658 ReadUnusedBytesFromFile(file, chunk_size);
2662 /* call function to load this level chunk */
2663 int chunk_size_expected =
2664 (chunk_info[i].loader)(file, chunk_size, level);
2666 /* the size of some chunks cannot be checked before reading other
2667 chunks first (like "HEAD" and "BODY") that contain some header
2668 information, so check them here */
2669 if (chunk_size_expected != chunk_size)
2671 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
2672 chunk_size, chunk_name, filename);
2681 /* ------------------------------------------------------------------------- */
2682 /* functions for loading EM level */
2683 /* ------------------------------------------------------------------------- */
2687 static int map_em_element_yam(int element)
2691 case 0x00: return EL_EMPTY;
2692 case 0x01: return EL_EMERALD;
2693 case 0x02: return EL_DIAMOND;
2694 case 0x03: return EL_ROCK;
2695 case 0x04: return EL_ROBOT;
2696 case 0x05: return EL_SPACESHIP_UP;
2697 case 0x06: return EL_BOMB;
2698 case 0x07: return EL_BUG_UP;
2699 case 0x08: return EL_AMOEBA_DROP;
2700 case 0x09: return EL_NUT;
2701 case 0x0a: return EL_YAMYAM;
2702 case 0x0b: return EL_QUICKSAND_FULL;
2703 case 0x0c: return EL_SAND;
2704 case 0x0d: return EL_WALL_SLIPPERY;
2705 case 0x0e: return EL_STEELWALL;
2706 case 0x0f: return EL_WALL;
2707 case 0x10: return EL_EM_KEY_1;
2708 case 0x11: return EL_EM_KEY_2;
2709 case 0x12: return EL_EM_KEY_4;
2710 case 0x13: return EL_EM_KEY_3;
2711 case 0x14: return EL_MAGIC_WALL;
2712 case 0x15: return EL_ROBOT_WHEEL;
2713 case 0x16: return EL_DYNAMITE;
2715 case 0x17: return EL_EM_KEY_1; /* EMC */
2716 case 0x18: return EL_BUG_UP; /* EMC */
2717 case 0x1a: return EL_DIAMOND; /* EMC */
2718 case 0x1b: return EL_EMERALD; /* EMC */
2719 case 0x25: return EL_NUT; /* EMC */
2720 case 0x80: return EL_EMPTY; /* EMC */
2721 case 0x85: return EL_EM_KEY_1; /* EMC */
2722 case 0x86: return EL_EM_KEY_2; /* EMC */
2723 case 0x87: return EL_EM_KEY_4; /* EMC */
2724 case 0x88: return EL_EM_KEY_3; /* EMC */
2725 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
2726 case 0x9a: return EL_AMOEBA_WET; /* EMC */
2727 case 0xaf: return EL_DYNAMITE; /* EMC */
2728 case 0xbd: return EL_SAND; /* EMC */
2731 Error(ERR_WARN, "invalid level element %d", element);
2736 static int map_em_element_field(int element)
2738 if (element >= 0xc8 && element <= 0xe1)
2739 return EL_CHAR_A + (element - 0xc8);
2740 else if (element >= 0xe2 && element <= 0xeb)
2741 return EL_CHAR_0 + (element - 0xe2);
2745 case 0x00: return EL_ROCK;
2746 case 0x01: return EL_ROCK; /* EMC */
2747 case 0x02: return EL_DIAMOND;
2748 case 0x03: return EL_DIAMOND;
2749 case 0x04: return EL_ROBOT;
2750 case 0x05: return EL_ROBOT; /* EMC */
2751 case 0x06: return EL_EMPTY_SPACE; /* EMC */
2752 case 0x07: return EL_EMPTY_SPACE; /* EMC */
2753 case 0x08: return EL_SPACESHIP_UP;
2754 case 0x09: return EL_SPACESHIP_RIGHT;
2755 case 0x0a: return EL_SPACESHIP_DOWN;
2756 case 0x0b: return EL_SPACESHIP_LEFT;
2757 case 0x0c: return EL_SPACESHIP_UP;
2758 case 0x0d: return EL_SPACESHIP_RIGHT;
2759 case 0x0e: return EL_SPACESHIP_DOWN;
2760 case 0x0f: return EL_SPACESHIP_LEFT;
2762 case 0x10: return EL_BOMB;
2763 case 0x11: return EL_BOMB; /* EMC */
2764 case 0x12: return EL_EMERALD;
2765 case 0x13: return EL_EMERALD;
2766 case 0x14: return EL_BUG_UP;
2767 case 0x15: return EL_BUG_RIGHT;
2768 case 0x16: return EL_BUG_DOWN;
2769 case 0x17: return EL_BUG_LEFT;
2770 case 0x18: return EL_BUG_UP;
2771 case 0x19: return EL_BUG_RIGHT;
2772 case 0x1a: return EL_BUG_DOWN;
2773 case 0x1b: return EL_BUG_LEFT;
2774 case 0x1c: return EL_AMOEBA_DROP;
2775 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
2776 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
2777 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
2779 case 0x20: return EL_ROCK;
2780 case 0x21: return EL_BOMB; /* EMC */
2781 case 0x22: return EL_DIAMOND; /* EMC */
2782 case 0x23: return EL_EMERALD; /* EMC */
2783 case 0x24: return EL_MAGIC_WALL;
2784 case 0x25: return EL_NUT;
2785 case 0x26: return EL_NUT; /* EMC */
2786 case 0x27: return EL_NUT; /* EMC */
2788 /* looks like magic wheel, but is _always_ activated */
2789 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
2791 case 0x29: return EL_YAMYAM; /* up */
2792 case 0x2a: return EL_YAMYAM; /* down */
2793 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
2794 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
2795 case 0x2d: return EL_QUICKSAND_FULL;
2796 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
2797 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
2799 case 0x30: return EL_EMPTY_SPACE; /* EMC */
2800 case 0x31: return EL_SAND; /* EMC */
2801 case 0x32: return EL_SAND; /* EMC */
2802 case 0x33: return EL_SAND; /* EMC */
2803 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
2804 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
2805 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
2806 case 0x37: return EL_SAND; /* EMC */
2807 case 0x38: return EL_ROCK; /* EMC */
2808 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
2809 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
2810 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
2811 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
2812 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
2813 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
2814 case 0x3f: return EL_ACID_POOL_BOTTOM;
2816 case 0x40: return EL_EXIT_OPEN; /* 1 */
2817 case 0x41: return EL_EXIT_OPEN; /* 2 */
2818 case 0x42: return EL_EXIT_OPEN; /* 3 */
2819 case 0x43: return EL_BALLOON; /* EMC */
2820 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
2821 case 0x45: return EL_SPRING; /* EMC */
2822 case 0x46: return EL_SPRING; /* falling */ /* EMC */
2823 case 0x47: return EL_SPRING; /* left */ /* EMC */
2824 case 0x48: return EL_SPRING; /* right */ /* EMC */
2825 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
2826 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
2827 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
2828 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
2829 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
2830 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
2831 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
2833 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
2834 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
2835 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
2836 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
2837 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
2838 case 0x55: return EL_EMPTY_SPACE; /* EMC */
2839 case 0x56: return EL_EMPTY_SPACE; /* EMC */
2840 case 0x57: return EL_EMPTY_SPACE; /* EMC */
2841 case 0x58: return EL_EMPTY_SPACE; /* EMC */
2842 case 0x59: return EL_EMPTY_SPACE; /* EMC */
2843 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
2844 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
2845 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
2846 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
2847 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
2848 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
2850 case 0x60: return EL_EMPTY_SPACE; /* EMC */
2851 case 0x61: return EL_EMPTY_SPACE; /* EMC */
2852 case 0x62: return EL_EMPTY_SPACE; /* EMC */
2853 case 0x63: return EL_SPRING; /* left */ /* EMC */
2854 case 0x64: return EL_SPRING; /* right */ /* EMC */
2855 case 0x65: return EL_ACID; /* 1 */ /* EMC */
2856 case 0x66: return EL_ACID; /* 2 */ /* EMC */
2857 case 0x67: return EL_ACID; /* 3 */ /* EMC */
2858 case 0x68: return EL_ACID; /* 4 */ /* EMC */
2859 case 0x69: return EL_ACID; /* 5 */ /* EMC */
2860 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
2861 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
2862 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
2863 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
2864 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
2865 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
2867 case 0x70: return EL_EMPTY_SPACE; /* EMC */
2868 case 0x71: return EL_EMPTY_SPACE; /* EMC */
2869 case 0x72: return EL_NUT; /* left */ /* EMC */
2870 case 0x73: return EL_SAND; /* EMC (? "nut") */
2871 case 0x74: return EL_STEELWALL;
2872 case 0x75: return EL_EMPTY_SPACE; /* EMC */
2873 case 0x76: return EL_EMPTY_SPACE; /* EMC */
2874 case 0x77: return EL_BOMB; /* left */ /* EMC */
2875 case 0x78: return EL_BOMB; /* right */ /* EMC */
2876 case 0x79: return EL_ROCK; /* left */ /* EMC */
2877 case 0x7a: return EL_ROCK; /* right */ /* EMC */
2878 case 0x7b: return EL_ACID; /* (? EMC "blank") */
2879 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
2880 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
2881 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
2882 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
2884 case 0x80: return EL_EMPTY;
2885 case 0x81: return EL_WALL_SLIPPERY;
2886 case 0x82: return EL_SAND;
2887 case 0x83: return EL_STEELWALL;
2888 case 0x84: return EL_WALL;
2889 case 0x85: return EL_EM_KEY_1;
2890 case 0x86: return EL_EM_KEY_2;
2891 case 0x87: return EL_EM_KEY_4;
2892 case 0x88: return EL_EM_KEY_3;
2893 case 0x89: return EL_EM_GATE_1;
2894 case 0x8a: return EL_EM_GATE_2;
2895 case 0x8b: return EL_EM_GATE_4;
2896 case 0x8c: return EL_EM_GATE_3;
2897 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
2898 case 0x8e: return EL_EM_GATE_1_GRAY;
2899 case 0x8f: return EL_EM_GATE_2_GRAY;
2901 case 0x90: return EL_EM_GATE_4_GRAY;
2902 case 0x91: return EL_EM_GATE_3_GRAY;
2903 case 0x92: return EL_MAGIC_WALL;
2904 case 0x93: return EL_ROBOT_WHEEL;
2905 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
2906 case 0x95: return EL_ACID_POOL_TOPLEFT;
2907 case 0x96: return EL_ACID_POOL_TOPRIGHT;
2908 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
2909 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
2910 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
2911 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
2912 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
2913 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
2914 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
2915 case 0x9e: return EL_EXIT_CLOSED;
2916 case 0x9f: return EL_CHAR_LESS; /* arrow left */
2918 /* looks like normal sand, but behaves like wall */
2919 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
2920 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
2921 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
2922 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
2923 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
2924 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
2925 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
2926 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
2927 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
2928 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
2929 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
2930 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
2931 case 0xac: return EL_CHAR_COMMA; /* EMC */
2932 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
2933 case 0xae: return EL_CHAR_MINUS; /* EMC */
2934 case 0xaf: return EL_DYNAMITE;
2936 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
2937 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
2938 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
2939 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
2940 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
2941 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
2942 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
2943 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
2944 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
2945 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
2946 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
2947 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
2948 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
2949 case 0xbd: return EL_SAND; /* EMC ("dirt") */
2950 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
2951 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
2953 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
2954 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
2955 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
2956 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
2957 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
2958 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
2959 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
2960 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
2962 /* characters: see above */
2964 case 0xec: return EL_CHAR_PERIOD;
2965 case 0xed: return EL_CHAR_EXCLAM;
2966 case 0xee: return EL_CHAR_COLON;
2967 case 0xef: return EL_CHAR_QUESTION;
2969 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
2970 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
2971 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
2972 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
2973 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
2974 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
2975 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
2976 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
2978 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
2979 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
2980 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
2981 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
2982 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
2983 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
2985 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
2986 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
2989 /* should never happen (all 8-bit value cases should be handled) */
2990 Error(ERR_WARN, "invalid level element %d", element);
2995 #define EM_LEVEL_SIZE 2106
2996 #define EM_LEVEL_XSIZE 64
2997 #define EM_LEVEL_YSIZE 32
2999 static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
3000 struct LevelFileInfo *level_file_info)
3002 char *filename = level_file_info->filename;
3004 unsigned char leveldata[EM_LEVEL_SIZE];
3005 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
3006 int nr = level_file_info->nr;
3009 if (!(file = fopen(filename, MODE_READ)))
3011 level->no_valid_file = TRUE;
3013 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3018 for (i = 0; i < EM_LEVEL_SIZE; i++)
3019 leveldata[i] = fgetc(file);
3023 /* check if level data is crypted by testing against known starting bytes
3024 of the few existing crypted level files (from Emerald Mine 1 + 2) */
3026 if ((leveldata[0] == 0xf1 ||
3027 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
3029 unsigned char code0 = 0x65;
3030 unsigned char code1 = 0x11;
3032 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
3033 leveldata[0] = 0xf1;
3035 /* decode crypted level data */
3037 for (i = 0; i < EM_LEVEL_SIZE; i++)
3039 leveldata[i] ^= code0;
3040 leveldata[i] -= code1;
3042 code0 = (code0 + 7) & 0xff;
3046 level->fieldx = EM_LEVEL_XSIZE;
3047 level->fieldy = EM_LEVEL_YSIZE;
3049 level->time = header[46] * 10;
3050 level->gems_needed = header[47];
3052 /* The original Emerald Mine levels have their level number stored
3053 at the second byte of the level file...
3054 Do not trust this information at other level files, e.g. EMC,
3055 but correct it anyway (normally the first row is completely
3056 steel wall, so the correction does not hurt anyway). */
3058 if (leveldata[1] == nr)
3059 leveldata[1] = leveldata[2]; /* correct level number field */
3061 sprintf(level->name, "Level %d", nr); /* set level name */
3063 level->score[SC_EMERALD] = header[36];
3064 level->score[SC_DIAMOND] = header[37];
3065 level->score[SC_ROBOT] = header[38];
3066 level->score[SC_SPACESHIP] = header[39];
3067 level->score[SC_BUG] = header[40];
3068 level->score[SC_YAMYAM] = header[41];
3069 level->score[SC_NUT] = header[42];
3070 level->score[SC_DYNAMITE] = header[43];
3071 level->score[SC_TIME_BONUS] = header[44];
3073 level->num_yamyam_contents = 4;
3075 for (i = 0; i < level->num_yamyam_contents; i++)
3076 for (y = 0; y < 3; y++)
3077 for (x = 0; x < 3; x++)
3078 level->yamyam_content[i].e[x][y] =
3079 map_em_element_yam(header[i * 9 + y * 3 + x]);
3081 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
3082 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
3083 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
3084 level->amoeba_content = EL_DIAMOND;
3086 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3088 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
3090 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
3091 new_element = EL_AMOEBA_WET;
3093 level->field[x][y] = new_element;
3096 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
3097 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
3098 level->field[x][y] = EL_PLAYER_1;
3100 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
3101 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
3102 level->field[x][y] = EL_PLAYER_2;
3107 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3109 static int ball_xy[8][2] =
3120 struct LevelInfo_EM *level_em = level->native_em_level;
3121 struct LEVEL *lev = level_em->lev;
3122 struct PLAYER **ply = level_em->ply;
3127 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3128 for (j = 0; j < 8; j++)
3129 printf("::: ball %d, %d: %d\n", i, j,
3130 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3133 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3134 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3136 lev->time_seconds = level->time;
3137 lev->required_initial = level->gems_needed;
3139 lev->emerald_score = level->score[SC_EMERALD];
3140 lev->diamond_score = level->score[SC_DIAMOND];
3141 lev->alien_score = level->score[SC_ROBOT];
3142 lev->tank_score = level->score[SC_SPACESHIP];
3143 lev->bug_score = level->score[SC_BUG];
3144 lev->eater_score = level->score[SC_YAMYAM];
3145 lev->nut_score = level->score[SC_NUT];
3146 lev->dynamite_score = level->score[SC_DYNAMITE];
3147 lev->key_score = level->score[SC_KEY];
3148 lev->exit_score = level->score[SC_TIME_BONUS];
3150 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3151 for (y = 0; y < 3; y++)
3152 for (x = 0; x < 3; x++)
3153 lev->eater_array[i][y * 3 + x] =
3154 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3156 lev->amoeba_time = level->amoeba_speed;
3157 lev->wonderwall_time_initial = level->time_magic_wall;
3158 lev->wheel_time = level->time_wheel;
3160 lev->android_move_time = level->android_move_time;
3161 lev->android_clone_time = level->android_clone_time;
3162 lev->ball_random = level->ball_random;
3163 lev->ball_state_initial = level->ball_state_initial;
3164 lev->ball_time = level->ball_time;
3165 lev->num_ball_arrays = level->num_ball_contents;
3167 lev->lenses_score = level->lenses_score;
3168 lev->magnify_score = level->magnify_score;
3169 lev->slurp_score = level->slurp_score;
3171 lev->lenses_time = level->lenses_time;
3172 lev->magnify_time = level->magnify_time;
3174 lev->wind_direction_initial =
3175 map_direction_RND_to_EM(level->wind_direction_initial);
3176 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3177 lev->wind_time : 0);
3179 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3180 for (j = 0; j < 8; j++)
3181 lev->ball_array[i][j] =
3182 map_element_RND_to_EM(level->
3183 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3186 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3187 for (j = 0; j < 8; j++)
3188 printf("::: ball %d, %d: %d\n", i, j,
3189 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3192 map_android_clone_elements_RND_to_EM(level);
3195 for (i = 0; i < 16; i++)
3196 lev->android_array[i] = FALSE; /* !!! YET TO COME !!! */
3199 /* first fill the complete playfield with the default border element */
3200 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3201 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3202 level_em->cave[x][y] = ZBORDER;
3208 LoadLevel_InitPlayfield();
3210 lev_fieldx = lev->width; /* !!! also in LoadLevel_InitPlayfield() !!! */
3211 lev_fieldy = lev->height; /* !!! also in LoadLevel_InitPlayfield() !!! */
3212 SetBorderElement(); /* !!! also in LoadLevel_InitPlayfield() !!! */
3217 printf("::: BorderElement == %d\n", BorderElement);
3220 if (BorderElement == EL_STEELWALL)
3222 for (y = 0; y < lev->height + 2; y++)
3223 for (x = 0; x < lev->width + 2; x++)
3224 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3227 /* then copy the real level contents from level file into the playfield */
3228 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3230 int new_element = map_element_RND_to_EM(level->field[x][y]);
3231 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3232 int xx = x + 1 + offset;
3233 int yy = y + 1 + offset;
3235 if (level->field[x][y] == EL_AMOEBA_DEAD)
3236 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3238 level_em->cave[xx][yy] = new_element;
3243 /* then copy the real level contents from level file into the playfield */
3244 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3246 int new_element = map_element_RND_to_EM(level->field[x][y]);
3248 if (level->field[x][y] == EL_AMOEBA_DEAD)
3249 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3251 level_em->cave[x + 1][y + 1] = new_element;
3258 for (i = 0; i < MAX_PLAYERS; i++)
3260 ply[i]->x_initial = 0;
3261 ply[i]->y_initial = 0;
3266 ply1->x_initial = 0;
3267 ply1->y_initial = 0;
3269 ply2->x_initial = 0;
3270 ply2->y_initial = 0;
3274 /* initialize player positions and delete players from the playfield */
3275 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3279 if (ELEM_IS_PLAYER(level->field[x][y]))
3281 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3282 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3283 int xx = x + 1 + offset;
3284 int yy = y + 1 + offset;
3286 ply[player_nr]->x_initial = xx;
3287 ply[player_nr]->y_initial = yy;
3289 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3295 /* !!! CURRENTLY ONLY SUPPORT FOR ONE PLAYER !!! */
3296 if (ELEM_IS_PLAYER(level->field[x][y]))
3298 ply1->x_initial = x + 1;
3299 ply1->y_initial = y + 1;
3300 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
3303 /* !!! ADD SUPPORT FOR MORE THAN ONE PLAYER !!! */
3304 if (level->field[x][y] == EL_PLAYER_1)
3306 ply1->x_initial = x + 1;
3307 ply1->y_initial = y + 1;
3308 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
3310 else if (level->field[x][y] == EL_PLAYER_2)
3312 ply2->x_initial = x + 1;
3313 ply2->y_initial = y + 1;
3314 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
3322 if (BorderElement == EL_STEELWALL)
3331 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3333 static int ball_xy[8][2] =
3344 struct LevelInfo_EM *level_em = level->native_em_level;
3345 struct LEVEL *lev = level_em->lev;
3346 struct PLAYER **ply = level_em->ply;
3349 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3350 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3352 level->time = lev->time_seconds;
3353 level->gems_needed = lev->required_initial;
3355 sprintf(level->name, "Level %d", level->file_info.nr);
3357 level->score[SC_EMERALD] = lev->emerald_score;
3358 level->score[SC_DIAMOND] = lev->diamond_score;
3359 level->score[SC_ROBOT] = lev->alien_score;
3360 level->score[SC_SPACESHIP] = lev->tank_score;
3361 level->score[SC_BUG] = lev->bug_score;
3362 level->score[SC_YAMYAM] = lev->eater_score;
3363 level->score[SC_NUT] = lev->nut_score;
3364 level->score[SC_DYNAMITE] = lev->dynamite_score;
3365 level->score[SC_KEY] = lev->key_score;
3366 level->score[SC_TIME_BONUS] = lev->exit_score;
3368 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3370 for (i = 0; i < level->num_yamyam_contents; i++)
3371 for (y = 0; y < 3; y++)
3372 for (x = 0; x < 3; x++)
3373 level->yamyam_content[i].e[x][y] =
3374 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3376 level->amoeba_speed = lev->amoeba_time;
3377 level->time_magic_wall = lev->wonderwall_time_initial;
3378 level->time_wheel = lev->wheel_time;
3380 level->android_move_time = lev->android_move_time;
3381 level->android_clone_time = lev->android_clone_time;
3382 level->ball_random = lev->ball_random;
3383 level->ball_state_initial = lev->ball_state_initial;
3384 level->ball_time = lev->ball_time;
3385 level->num_ball_contents = lev->num_ball_arrays;
3387 level->lenses_score = lev->lenses_score;
3388 level->magnify_score = lev->magnify_score;
3389 level->slurp_score = lev->slurp_score;
3391 level->lenses_time = lev->lenses_time;
3392 level->magnify_time = lev->magnify_time;
3394 level->wind_direction_initial =
3395 map_direction_EM_to_RND(lev->wind_direction_initial);
3398 printf("::: foo\n");
3399 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3400 for (j = 0; j < 8; j++)
3401 printf("::: ball %d, %d: %d\n", i, j,
3402 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3405 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3406 for (j = 0; j < 8; j++)
3407 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3408 map_element_EM_to_RND(lev->ball_array[i][j]);
3411 printf("::: bar\n");
3412 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3413 for (j = 0; j < 8; j++)
3414 printf("::: ball %d, %d: %d\n", i, j,
3415 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3418 map_android_clone_elements_EM_to_RND(level);
3421 for (i = 0; i < 16; i++)
3422 level->android_array[i] = FALSE; /* !!! YET TO COME !!! */
3425 /* convert the playfield (some elements need special treatment) */
3426 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3428 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3430 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3431 new_element = EL_AMOEBA_DEAD;
3433 level->field[x][y] = new_element;
3437 printf("::: bar 0\n");
3438 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3439 for (j = 0; j < 8; j++)
3440 printf("::: ball %d, %d: %d\n", i, j,
3441 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3446 for (i = 0; i < MAX_PLAYERS; i++)
3448 /* in case of all players set to the same field, use the first player */
3449 int nr = MAX_PLAYERS - i - 1;
3450 int jx = ply[nr]->x_initial - 1;
3451 int jy = ply[nr]->y_initial - 1;
3454 printf("::: player %d: %d, %d\n", nr, jx, jy);
3457 if (jx != -1 && jy != -1)
3458 level->field[jx][jy] = EL_PLAYER_1 + nr;
3463 /* in case of both players set to the same field, use the first player */
3464 level->field[ply2->x_initial - 1][ply2->y_initial - 1] = EL_PLAYER_2;
3465 level->field[ply1->x_initial - 1][ply1->y_initial - 1] = EL_PLAYER_1;
3470 printf("::: native Emerald Mine file version: %d\n", level_em->file_version);
3474 printf("::: bar 2\n");
3475 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3476 for (j = 0; j < 8; j++)
3477 printf("::: ball %d, %d: %d\n", i, j,
3478 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3482 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
3483 struct LevelFileInfo *level_file_info)
3485 if (!LoadNativeLevel_EM(level_file_info->filename))
3486 level->no_valid_file = TRUE;
3489 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
3491 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
3492 CopyNativeLevel_RND_to_EM(level);
3495 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
3500 static int ball_xy[8][2] =
3514 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3515 for (j = 0; j < 8; j++)
3516 printf("::: ball %d, %d: %d\n", i, j,
3517 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3521 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
3522 CopyNativeLevel_EM_to_RND(level);
3526 /* ------------------------------------------------------------------------- */
3527 /* functions for loading SP level */
3528 /* ------------------------------------------------------------------------- */
3530 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
3531 #define SP_LEVEL_SIZE 1536
3532 #define SP_LEVEL_XSIZE 60
3533 #define SP_LEVEL_YSIZE 24
3534 #define SP_LEVEL_NAME_LEN 23
3536 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
3539 int num_special_ports;
3542 /* for details of the Supaplex level format, see Herman Perk's Supaplex
3543 documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
3545 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
3546 for (y = 0; y < SP_LEVEL_YSIZE; y++)
3548 for (x = 0; x < SP_LEVEL_XSIZE; x++)
3550 int element_old = fgetc(file);
3553 if (element_old <= 0x27)
3554 element_new = getMappedElement(EL_SP_START + element_old);
3555 else if (element_old == 0x28)
3556 element_new = EL_INVISIBLE_WALL;
3559 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
3560 Error(ERR_WARN, "invalid level element %d", element_old);
3562 element_new = EL_UNKNOWN;
3565 level->field[x][y] = element_new;
3569 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
3571 /* initial gravity: 1 == "on", anything else (0) == "off" */
3572 level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
3574 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
3576 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
3577 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3578 level->name[i] = fgetc(file);
3579 level->name[SP_LEVEL_NAME_LEN] = '\0';
3581 /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
3582 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
3584 /* number of infotrons needed; 0 means that Supaplex will count the total
3585 amount of infotrons in the level and use the low byte of that number
3586 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
3587 level->gems_needed = fgetc(file);
3589 /* number of special ("gravity") port entries below (maximum 10 allowed) */
3590 num_special_ports = fgetc(file);
3592 /* database of properties of up to 10 special ports (6 bytes per port) */
3593 for (i = 0; i < 10; i++)
3595 int port_location, port_x, port_y, port_element;
3598 /* high and low byte of the location of a special port; if (x, y) are the
3599 coordinates of a port in the field and (0, 0) is the top-left corner,
3600 the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
3601 of what may be expected: Supaplex works with a game field in memory
3602 which is 2 bytes per tile) */
3603 port_location = getFile16BitBE(file);
3605 /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
3606 gravity = fgetc(file);
3608 /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
3609 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
3611 /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
3612 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
3614 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
3616 if (i >= num_special_ports)
3619 port_x = (port_location / 2) % SP_LEVEL_XSIZE;
3620 port_y = (port_location / 2) / SP_LEVEL_XSIZE;
3622 if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
3623 port_y < 0 || port_y >= SP_LEVEL_YSIZE)
3625 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3631 port_element = level->field[port_x][port_y];
3633 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3634 port_element > EL_SP_GRAVITY_PORT_UP)
3636 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3641 /* change previous (wrong) gravity inverting special port to either
3642 gravity enabling special port or gravity disabling special port */
3643 level->field[port_x][port_y] +=
3644 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3645 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3648 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
3650 /* change special gravity ports without database entries to normal ports */
3651 for (y = 0; y < SP_LEVEL_YSIZE; y++)
3652 for (x = 0; x < SP_LEVEL_XSIZE; x++)
3653 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3654 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3655 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3657 /* auto-determine number of infotrons if it was stored as "0" -- see above */
3658 if (level->gems_needed == 0)
3660 for (y = 0; y < SP_LEVEL_YSIZE; y++)
3661 for (x = 0; x < SP_LEVEL_XSIZE; x++)
3662 if (level->field[x][y] == EL_SP_INFOTRON)
3663 level->gems_needed++;
3665 level->gems_needed &= 0xff; /* only use low byte -- see above */
3668 level->fieldx = SP_LEVEL_XSIZE;
3669 level->fieldy = SP_LEVEL_YSIZE;
3671 level->time = 0; /* no time limit */
3672 level->amoeba_speed = 0;
3673 level->time_magic_wall = 0;
3674 level->time_wheel = 0;
3675 level->amoeba_content = EL_EMPTY;
3678 /* original Supaplex does not use score values -- use default values */
3680 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3681 level->score[i] = 0; /* !!! CORRECT THIS !!! */
3684 /* there are no yamyams in supaplex levels */
3685 for (i = 0; i < level->num_yamyam_contents; i++)
3686 for (y = 0; y < 3; y++)
3687 for (x = 0; x < 3; x++)
3688 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3691 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
3692 struct LevelFileInfo *level_file_info)
3694 char *filename = level_file_info->filename;
3696 int nr = level_file_info->nr - leveldir_current->first_level;
3698 char name_first, name_last;
3699 struct LevelInfo multipart_level;
3700 int multipart_xpos, multipart_ypos;
3701 boolean is_multipart_level;
3702 boolean is_first_part;
3703 boolean reading_multipart_level = FALSE;
3704 boolean use_empty_level = FALSE;
3706 if (!(file = fopen(filename, MODE_READ)))
3708 level->no_valid_file = TRUE;
3710 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3715 /* position file stream to the requested level inside the level package */
3716 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
3718 level->no_valid_file = TRUE;
3720 Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
3725 /* there exist Supaplex level package files with multi-part levels which
3726 can be detected as follows: instead of leading and trailing dashes ('-')
3727 to pad the level name, they have leading and trailing numbers which are
3728 the x and y coordinations of the current part of the multi-part level;
3729 if there are '?' characters instead of numbers on the left or right side
3730 of the level name, the multi-part level consists of only horizontal or
3733 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
3735 LoadLevelFromFileStream_SP(file, level, l);
3737 /* check if this level is a part of a bigger multi-part level */
3739 name_first = level->name[0];
3740 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
3742 is_multipart_level =
3743 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
3744 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
3747 ((name_first == '?' || name_first == '1') &&
3748 (name_last == '?' || name_last == '1'));
3750 /* correct leading multipart level meta information in level name */
3751 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
3752 level->name[i] = '-';
3754 /* correct trailing multipart level meta information in level name */
3755 for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
3756 level->name[i] = '-';
3758 /* ---------- check for normal single level ---------- */
3760 if (!reading_multipart_level && !is_multipart_level)
3762 /* the current level is simply a normal single-part level, and we are
3763 not reading a multi-part level yet, so return the level as it is */
3768 /* ---------- check for empty level (unused multi-part) ---------- */
3770 if (!reading_multipart_level && is_multipart_level && !is_first_part)
3772 /* this is a part of a multi-part level, but not the first part
3773 (and we are not already reading parts of a multi-part level);
3774 in this case, use an empty level instead of the single part */
3776 use_empty_level = TRUE;
3781 /* ---------- check for finished multi-part level ---------- */
3783 if (reading_multipart_level &&
3784 (!is_multipart_level ||
3785 !strEqual(level->name, multipart_level.name)))
3787 /* we are already reading parts of a multi-part level, but this level is
3788 either not a multi-part level, or a part of a different multi-part
3789 level; in both cases, the multi-part level seems to be complete */
3794 /* ---------- here we have one part of a multi-part level ---------- */
3796 reading_multipart_level = TRUE;
3798 if (is_first_part) /* start with first part of new multi-part level */
3800 /* copy level info structure from first part */
3801 multipart_level = *level;
3803 /* clear playfield of new multi-part level */
3804 for (y = 0; y < MAX_LEV_FIELDY; y++)
3805 for (x = 0; x < MAX_LEV_FIELDX; x++)
3806 multipart_level.field[x][y] = EL_EMPTY;
3809 if (name_first == '?')
3811 if (name_last == '?')
3814 multipart_xpos = (int)(name_first - '0');
3815 multipart_ypos = (int)(name_last - '0');
3818 printf("----------> part (%d/%d) of multi-part level '%s'\n",
3819 multipart_xpos, multipart_ypos, multipart_level.name);
3822 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
3823 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
3825 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
3830 multipart_level.fieldx = MAX(multipart_level.fieldx,
3831 multipart_xpos * SP_LEVEL_XSIZE);
3832 multipart_level.fieldy = MAX(multipart_level.fieldy,
3833 multipart_ypos * SP_LEVEL_YSIZE);
3835 /* copy level part at the right position of multi-part level */
3836 for (y = 0; y < SP_LEVEL_YSIZE; y++)
3838 for (x = 0; x < SP_LEVEL_XSIZE; x++)
3840 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
3841 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
3843 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
3850 if (use_empty_level)
3852 setLevelInfoToDefaults(level);
3854 level->fieldx = SP_LEVEL_XSIZE;
3855 level->fieldy = SP_LEVEL_YSIZE;
3857 for (y = 0; y < SP_LEVEL_YSIZE; y++)
3858 for (x = 0; x < SP_LEVEL_XSIZE; x++)
3859 level->field[x][y] = EL_EMPTY;
3861 strcpy(level->name, "-------- EMPTY --------");
3863 Error(ERR_WARN, "single part of multi-part level -- using empty level");
3866 if (reading_multipart_level)
3867 *level = multipart_level;
3870 /* ------------------------------------------------------------------------- */
3871 /* functions for loading generic level */
3872 /* ------------------------------------------------------------------------- */
3874 void LoadLevelFromFileInfo(struct LevelInfo *level,
3875 struct LevelFileInfo *level_file_info)
3877 /* always start with reliable default values */
3878 setLevelInfoToDefaults(level);
3880 switch (level_file_info->type)
3882 case LEVEL_FILE_TYPE_RND:
3883 LoadLevelFromFileInfo_RND(level, level_file_info);
3886 case LEVEL_FILE_TYPE_EM:
3887 LoadLevelFromFileInfo_EM(level, level_file_info);
3888 level->game_engine_type = GAME_ENGINE_TYPE_EM;
3891 case LEVEL_FILE_TYPE_SP:
3892 LoadLevelFromFileInfo_SP(level, level_file_info);
3896 LoadLevelFromFileInfo_RND(level, level_file_info);
3900 /* if level file is invalid, restore level structure to default values */
3901 if (level->no_valid_file)
3902 setLevelInfoToDefaults(level);
3904 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
3905 level->game_engine_type = GAME_ENGINE_TYPE_RND;
3908 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
3909 CopyNativeLevel_Native_to_RND(level);
3911 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
3912 CopyNativeLevel_RND_to_Native(level);
3914 CopyNativeLevel_Native_to_RND(level);
3918 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
3920 static struct LevelFileInfo level_file_info;
3922 /* always start with reliable default values */
3923 setFileInfoToDefaults(&level_file_info);
3925 level_file_info.nr = 0; /* unknown level number */
3926 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
3927 level_file_info.filename = filename;
3929 LoadLevelFromFileInfo(level, &level_file_info);
3932 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
3934 if (leveldir_current == NULL) /* only when dumping level */
3937 /* all engine modifications also valid for levels which use latest engine */
3939 if (level->game_version < VERSION_IDENT(3,2,0,5))
3941 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
3942 level->score[SC_TIME_BONUS] /= 10;
3947 leveldir_current->latest_engine = TRUE; /* !!! TEST ONLY !!! */
3950 if (leveldir_current->latest_engine)
3952 /* ---------- use latest game engine ----------------------------------- */
3954 /* For all levels which are forced to use the latest game engine version
3955 (normally all but user contributed, private and undefined levels), set
3956 the game engine version to the actual version; this allows for actual
3957 corrections in the game engine to take effect for existing, converted
3958 levels (from "classic" or other existing games) to make the emulation
3959 of the corresponding game more accurate, while (hopefully) not breaking
3960 existing levels created from other players. */
3962 level->game_version = GAME_VERSION_ACTUAL;
3964 /* Set special EM style gems behaviour: EM style gems slip down from
3965 normal, steel and growing wall. As this is a more fundamental change,
3966 it seems better to set the default behaviour to "off" (as it is more
3967 natural) and make it configurable in the level editor (as a property
3968 of gem style elements). Already existing converted levels (neither
3969 private nor contributed levels) are changed to the new behaviour. */
3971 if (level->file_version < FILE_VERSION_2_0)
3972 level->em_slippery_gems = TRUE;
3977 /* ---------- use game engine the level was created with ----------------- */
3979 /* For all levels which are not forced to use the latest game engine
3980 version (normally user contributed, private and undefined levels),
3981 use the version of the game engine the levels were created for.
3983 Since 2.0.1, the game engine version is now directly stored
3984 in the level file (chunk "VERS"), so there is no need anymore
3985 to set the game version from the file version (except for old,
3986 pre-2.0 levels, where the game version is still taken from the
3987 file format version used to store the level -- see above). */
3989 /* player was faster than enemies in 1.0.0 and before */
3990 if (level->file_version == FILE_VERSION_1_0)
3991 level->initial_player_stepsize = STEPSIZE_FAST;
3993 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
3994 if (level->game_version == VERSION_IDENT(2,0,1,0))
3995 level->em_slippery_gems = TRUE;
3997 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
3998 if (level->game_version < VERSION_IDENT(2,2,0,0))
3999 level->use_spring_bug = TRUE;
4001 if (level->game_version < VERSION_IDENT(3,2,0,5))
4003 /* time orb caused limited time in endless time levels before 3.2.0-5 */
4004 level->use_time_orb_bug = TRUE;
4006 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
4007 level->block_snap_field = FALSE;
4009 /* extra time score was same value as time left score before 3.2.0-5 */
4010 level->extra_time_score = level->score[SC_TIME_BONUS];
4013 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
4014 level->score[SC_TIME_BONUS] /= 10;
4018 if (level->game_version < VERSION_IDENT(3,2,0,7))
4020 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
4021 level->continuous_snapping = FALSE;
4024 /* only few elements were able to actively move into acid before 3.1.0 */
4025 /* trigger settings did not exist before 3.1.0; set to default "any" */
4026 if (level->game_version < VERSION_IDENT(3,1,0,0))
4030 /* correct "can move into acid" settings (all zero in old levels) */
4032 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
4033 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
4035 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
4036 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
4037 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
4038 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
4040 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4041 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
4043 /* correct trigger settings (stored as zero == "none" in old levels) */
4045 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4047 int element = EL_CUSTOM_START + i;
4048 struct ElementInfo *ei = &element_info[element];
4050 for (j = 0; j < ei->num_change_pages; j++)
4052 struct ElementChangeInfo *change = &ei->change_page[j];
4054 change->trigger_player = CH_PLAYER_ANY;
4055 change->trigger_page = CH_PAGE_ANY;
4061 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
4065 /* map custom element change events that have changed in newer versions
4066 (these following values were accidentally changed in version 3.0.1)
4067 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
4068 if (level->game_version <= VERSION_IDENT(3,0,0,0))
4070 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4072 int element = EL_CUSTOM_START + i;
4074 /* order of checking and copying events to be mapped is important */
4075 /* (do not change the start and end value -- they are constant) */
4076 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
4078 if (HAS_CHANGE_EVENT(element, j - 2))
4080 SET_CHANGE_EVENT(element, j - 2, FALSE);
4081 SET_CHANGE_EVENT(element, j, TRUE);
4085 /* order of checking and copying events to be mapped is important */
4086 /* (do not change the start and end value -- they are constant) */
4087 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
4089 if (HAS_CHANGE_EVENT(element, j - 1))
4091 SET_CHANGE_EVENT(element, j - 1, FALSE);
4092 SET_CHANGE_EVENT(element, j, TRUE);
4098 /* initialize "can_change" field for old levels with only one change page */
4099 if (level->game_version <= VERSION_IDENT(3,0,2,0))
4101 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4103 int element = EL_CUSTOM_START + i;
4105 if (CAN_CHANGE(element))
4106 element_info[element].change->can_change = TRUE;
4110 /* correct custom element values (for old levels without these options) */
4111 if (level->game_version < VERSION_IDENT(3,1,1,0))
4113 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4115 int element = EL_CUSTOM_START + i;
4116 struct ElementInfo *ei = &element_info[element];
4118 if (ei->access_direction == MV_NO_DIRECTION)
4119 ei->access_direction = MV_ALL_DIRECTIONS;
4122 for (j = 0; j < ei->num_change_pages; j++)
4124 struct ElementChangeInfo *change = &ei->change_page[j];
4126 if (change->trigger_side == CH_SIDE_NONE)
4127 change->trigger_side = CH_SIDE_ANY;
4134 /* correct custom element values (fix invalid values for all versions) */
4137 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4139 int element = EL_CUSTOM_START + i;
4140 struct ElementInfo *ei = &element_info[element];
4142 for (j = 0; j < ei->num_change_pages; j++)
4144 struct ElementChangeInfo *change = &ei->change_page[j];
4146 if (change->trigger_player == CH_PLAYER_NONE)
4147 change->trigger_player = CH_PLAYER_ANY;
4149 if (change->trigger_side == CH_SIDE_NONE)
4150 change->trigger_side = CH_SIDE_ANY;
4156 /* initialize "can_explode" field for old levels which did not store this */
4157 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
4158 if (level->game_version <= VERSION_IDENT(3,1,0,0))
4160 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4162 int element = EL_CUSTOM_START + i;
4164 if (EXPLODES_1X1_OLD(element))
4165 element_info[element].explosion_type = EXPLODES_1X1;
4167 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
4168 EXPLODES_SMASHED(element) ||
4169 EXPLODES_IMPACT(element)));
4173 /* correct previously hard-coded move delay values for maze runner style */
4174 if (level->game_version < VERSION_IDENT(3,1,1,0))
4176 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4178 int element = EL_CUSTOM_START + i;
4180 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
4182 /* previously hard-coded and therefore ignored */
4183 element_info[element].move_delay_fixed = 9;
4184 element_info[element].move_delay_random = 0;
4189 /* map elements that have changed in newer versions */
4190 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
4191 level->game_version);
4192 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4193 for (x = 0; x < 3; x++)
4194 for (y = 0; y < 3; y++)
4195 level->yamyam_content[i].e[x][y] =
4196 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
4197 level->game_version);
4199 /* initialize element properties for level editor etc. */
4200 InitElementPropertiesEngine(level->game_version);
4203 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
4207 /* map elements that have changed in newer versions */
4208 for (y = 0; y < level->fieldy; y++)
4209 for (x = 0; x < level->fieldx; x++)
4210 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
4211 level->game_version);
4213 /* copy elements to runtime playfield array */
4214 for (x = 0; x < MAX_LEV_FIELDX; x++)
4215 for (y = 0; y < MAX_LEV_FIELDY; y++)
4216 Feld[x][y] = level->field[x][y];
4218 /* initialize level size variables for faster access */
4219 lev_fieldx = level->fieldx;
4220 lev_fieldy = level->fieldy;
4222 /* determine border element for this level */
4226 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
4228 struct LevelFileInfo *level_file_info = &level->file_info;
4231 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
4232 CopyNativeLevel_RND_to_Native(level);
4234 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
4235 CopyNativeLevel_RND_to_Native(level);
4237 CopyNativeLevel_Native_to_RND(level);
4241 void LoadLevelTemplate(int nr)
4245 setLevelFileInfo(&level_template.file_info, nr);
4246 filename = level_template.file_info.filename;
4248 LoadLevelFromFileInfo(&level_template, &level_template.file_info);
4250 LoadLevel_InitVersion(&level_template, filename);
4251 LoadLevel_InitElements(&level_template, filename);
4253 ActivateLevelTemplate();
4256 void LoadLevel(int nr)
4260 setLevelFileInfo(&level.file_info, nr);
4261 filename = level.file_info.filename;
4263 LoadLevelFromFileInfo(&level, &level.file_info);
4265 if (level.use_custom_template)
4266 LoadLevelTemplate(-1);
4268 LoadLevel_InitVersion(&level, filename);
4269 LoadLevel_InitElements(&level, filename);
4270 LoadLevel_InitPlayfield(&level, filename);
4272 LoadLevel_InitNativeEngines(&level, filename);
4275 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
4277 putFileVersion(file, level->file_version);
4278 putFileVersion(file, level->game_version);
4281 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
4285 putFile8Bit(file, level->fieldx);
4286 putFile8Bit(file, level->fieldy);
4288 putFile16BitBE(file, level->time);
4289 putFile16BitBE(file, level->gems_needed);
4291 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
4292 putFile8Bit(file, level->name[i]);
4294 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4295 putFile8Bit(file, level->score[i]);
4297 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
4298 for (y = 0; y < 3; y++)
4299 for (x = 0; x < 3; x++)
4300 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
4301 level->yamyam_content[i].e[x][y]));
4302 putFile8Bit(file, level->amoeba_speed);
4303 putFile8Bit(file, level->time_magic_wall);
4304 putFile8Bit(file, level->time_wheel);
4305 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
4306 level->amoeba_content));
4307 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
4308 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
4309 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
4310 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
4312 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
4314 putFile8Bit(file, (level->block_last_field ? 1 : 0));
4315 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
4316 putFile32BitBE(file, level->can_move_into_acid_bits);
4317 putFile8Bit(file, level->dont_collide_with_bits);
4319 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
4320 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
4322 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
4323 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
4324 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
4326 putFile8Bit(file, level->game_engine_type);
4328 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
4331 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
4335 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
4336 putFile8Bit(file, level->author[i]);
4339 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
4343 for (y = 0; y < level->fieldy; y++)
4344 for (x = 0; x < level->fieldx; x++)
4345 if (level->encoding_16bit_field)
4346 putFile16BitBE(file, level->field[x][y]);
4348 putFile8Bit(file, level->field[x][y]);
4352 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
4356 putFile8Bit(file, EL_YAMYAM);
4357 putFile8Bit(file, level->num_yamyam_contents);
4358 putFile8Bit(file, 0);
4359 putFile8Bit(file, 0);
4361 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4362 for (y = 0; y < 3; y++)
4363 for (x = 0; x < 3; x++)
4364 if (level->encoding_16bit_field)
4365 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
4367 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
4371 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
4374 int num_contents, content_xsize, content_ysize;
4375 int content_array[MAX_ELEMENT_CONTENTS][3][3];
4377 if (element == EL_YAMYAM)
4379 num_contents = level->num_yamyam_contents;
4383 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4384 for (y = 0; y < 3; y++)
4385 for (x = 0; x < 3; x++)
4386 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
4388 else if (element == EL_BD_AMOEBA)
4394 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4395 for (y = 0; y < 3; y++)
4396 for (x = 0; x < 3; x++)
4397 content_array[i][x][y] = EL_EMPTY;
4398 content_array[0][0][0] = level->amoeba_content;
4402 /* chunk header already written -- write empty chunk data */
4403 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
4405 Error(ERR_WARN, "cannot save content for element '%d'", element);
4409 putFile16BitBE(file, element);
4410 putFile8Bit(file, num_contents);
4411 putFile8Bit(file, content_xsize);
4412 putFile8Bit(file, content_ysize);
4414 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
4416 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4417 for (y = 0; y < 3; y++)
4418 for (x = 0; x < 3; x++)
4419 putFile16BitBE(file, content_array[i][x][y]);
4422 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
4425 int envelope_nr = element - EL_ENVELOPE_1;
4426 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
4428 putFile16BitBE(file, element);
4429 putFile16BitBE(file, envelope_len);
4430 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
4431 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
4433 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
4435 for (i = 0; i < envelope_len; i++)
4436 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
4440 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
4441 int num_changed_custom_elements)
4445 putFile16BitBE(file, num_changed_custom_elements);
4447 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4449 int element = EL_CUSTOM_START + i;
4452 struct ElementInfo *ei = &element_info[element];
4454 if (ei->properties[EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
4456 if (check < num_changed_custom_elements)
4458 putFile16BitBE(file, element);
4459 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE]);
4465 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
4467 if (check < num_changed_custom_elements)
4469 putFile16BitBE(file, element);
4470 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
4478 if (check != num_changed_custom_elements) /* should not happen */
4479 Error(ERR_WARN, "inconsistent number of custom element properties");
4484 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
4485 int num_changed_custom_elements)
4489 putFile16BitBE(file, num_changed_custom_elements);
4491 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4493 int element = EL_CUSTOM_START + i;
4495 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
4497 if (check < num_changed_custom_elements)
4499 putFile16BitBE(file, element);
4500 putFile16BitBE(file, element_info[element].change->target_element);
4507 if (check != num_changed_custom_elements) /* should not happen */
4508 Error(ERR_WARN, "inconsistent number of custom target elements");
4513 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
4514 int num_changed_custom_elements)
4516 int i, j, x, y, check = 0;
4518 putFile16BitBE(file, num_changed_custom_elements);
4520 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4522 int element = EL_CUSTOM_START + i;
4523 struct ElementInfo *ei = &element_info[element];
4525 if (ei->modified_settings)
4527 if (check < num_changed_custom_elements)
4529 putFile16BitBE(file, element);
4531 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
4532 putFile8Bit(file, ei->description[j]);
4535 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE]);
4537 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
4540 /* some free bytes for future properties and padding */
4541 WriteUnusedBytesToFile(file, 7);
4543 putFile8Bit(file, ei->use_gfx_element);
4544 putFile16BitBE(file, ei->gfx_element);
4546 putFile8Bit(file, ei->collect_score_initial);
4547 putFile8Bit(file, ei->collect_count_initial);
4549 putFile16BitBE(file, ei->push_delay_fixed);
4550 putFile16BitBE(file, ei->push_delay_random);
4551 putFile16BitBE(file, ei->move_delay_fixed);
4552 putFile16BitBE(file, ei->move_delay_random);
4554 putFile16BitBE(file, ei->move_pattern);
4555 putFile8Bit(file, ei->move_direction_initial);
4556 putFile8Bit(file, ei->move_stepsize);
4558 for (y = 0; y < 3; y++)
4559 for (x = 0; x < 3; x++)
4560 putFile16BitBE(file, ei->content.e[x][y]);
4562 putFile32BitBE(file, ei->change->events);
4564 putFile16BitBE(file, ei->change->target_element);
4566 putFile16BitBE(file, ei->change->delay_fixed);
4567 putFile16BitBE(file, ei->change->delay_random);
4568 putFile16BitBE(file, ei->change->delay_frames);
4570 putFile16BitBE(file, ei->change->trigger_element);
4572 putFile8Bit(file, ei->change->explode);
4573 putFile8Bit(file, ei->change->use_target_content);
4574 putFile8Bit(file, ei->change->only_if_complete);
4575 putFile8Bit(file, ei->change->use_random_replace);
4577 putFile8Bit(file, ei->change->random_percentage);
4578 putFile8Bit(file, ei->change->replace_when);
4580 for (y = 0; y < 3; y++)
4581 for (x = 0; x < 3; x++)
4582 putFile16BitBE(file, ei->change->content.e[x][y]);
4584 putFile8Bit(file, ei->slippery_type);
4586 /* some free bytes for future properties and padding */
4587 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
4594 if (check != num_changed_custom_elements) /* should not happen */
4595 Error(ERR_WARN, "inconsistent number of custom element properties");
4599 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
4601 struct ElementInfo *ei = &element_info[element];
4604 /* ---------- custom element base property values (96 bytes) ------------- */
4606 putFile16BitBE(file, element);
4608 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
4609 putFile8Bit(file, ei->description[i]);
4612 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE]);
4614 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
4616 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
4618 putFile8Bit(file, ei->num_change_pages);
4620 putFile16BitBE(file, ei->ce_value_fixed_initial);
4621 putFile16BitBE(file, ei->ce_value_random_initial);
4622 putFile8Bit(file, ei->use_last_ce_value);
4624 putFile8Bit(file, ei->use_gfx_element);
4625 putFile16BitBE(file, ei->gfx_element);
4627 putFile8Bit(file, ei->collect_score_initial);
4628 putFile8Bit(file, ei->collect_count_initial);
4630 putFile8Bit(file, ei->drop_delay_fixed);
4631 putFile8Bit(file, ei->push_delay_fixed);
4632 putFile8Bit(file, ei->drop_delay_random);
4633 putFile8Bit(file, ei->push_delay_random);
4634 putFile16BitBE(file, ei->move_delay_fixed);
4635 putFile16BitBE(file, ei->move_delay_random);
4637 /* bits 0 - 15 of "move_pattern" ... */
4638 putFile16BitBE(file, ei->move_pattern & 0xffff);
4639 putFile8Bit(file, ei->move_direction_initial);
4640 putFile8Bit(file, ei->move_stepsize);
4642 putFile8Bit(file, ei->slippery_type);
4644 for (y = 0; y < 3; y++)
4645 for (x = 0; x < 3; x++)
4646 putFile16BitBE(file, ei->content.e[x][y]);
4648 putFile16BitBE(file, ei->move_enter_element);
4649 putFile16BitBE(file, ei->move_leave_element);
4650 putFile8Bit(file, ei->move_leave_type);
4652 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
4653 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
4655 putFile8Bit(file, ei->access_direction);
4657 putFile8Bit(file, ei->explosion_delay);
4658 putFile8Bit(file, ei->ignition_delay);
4659 putFile8Bit(file, ei->explosion_type);
4661 /* some free bytes for future custom property values and padding */
4662 WriteUnusedBytesToFile(file, 1);
4664 /* ---------- change page property values (48 bytes) --------------------- */
4666 for (i = 0; i < ei->num_change_pages; i++)
4668 struct ElementChangeInfo *change = &ei->change_page[i];
4669 unsigned int event_bits;
4671 /* bits 0 - 31 of "has_event[]" ... */
4673 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
4674 if (change->has_event[j])
4675 event_bits |= (1 << j);
4676 putFile32BitBE(file, event_bits);
4678 putFile16BitBE(file, change->target_element);
4680 putFile16BitBE(file, change->delay_fixed);
4681 putFile16BitBE(file, change->delay_random);
4682 putFile16BitBE(file, change->delay_frames);
4684 putFile16BitBE(file, change->trigger_element);
4686 putFile8Bit(file, change->explode);
4687 putFile8Bit(file, change->use_target_content);
4688 putFile8Bit(file, change->only_if_complete);
4689 putFile8Bit(file, change->use_random_replace);
4691 putFile8Bit(file, change->random_percentage);
4692 putFile8Bit(file, change->replace_when);
4694 for (y = 0; y < 3; y++)
4695 for (x = 0; x < 3; x++)
4696 putFile16BitBE(file, change->target_content.e[x][y]);
4698 putFile8Bit(file, change->can_change);
4700 putFile8Bit(file, change->trigger_side);
4702 putFile8Bit(file, change->trigger_player);
4703 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
4704 log_2(change->trigger_page)));
4706 putFile8Bit(file, change->has_action);
4707 putFile8Bit(file, change->action_type);
4708 putFile8Bit(file, change->action_mode);
4709 putFile16BitBE(file, change->action_arg);
4711 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
4713 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
4714 if (change->has_event[j])
4715 event_bits |= (1 << (j - 32));
4716 putFile8Bit(file, event_bits);
4720 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
4722 struct ElementInfo *ei = &element_info[element];
4723 struct ElementGroupInfo *group = ei->group;
4726 putFile16BitBE(file, element);
4728 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
4729 putFile8Bit(file, ei->description[i]);
4731 putFile8Bit(file, group->num_elements);
4733 putFile8Bit(file, ei->use_gfx_element);
4734 putFile16BitBE(file, ei->gfx_element);
4736 putFile8Bit(file, group->choice_mode);
4738 /* some free bytes for future values and padding */
4739 WriteUnusedBytesToFile(file, 3);
4741 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
4742 putFile16BitBE(file, group->element[i]);
4745 static int SaveLevel_MicroChunk_SingleValue(FILE *file,
4746 struct ElementFileConfig *entry)
4748 int default_value = entry->default_value;
4749 int element = entry->element;
4750 int data_type = entry->data_type;
4751 int conf_type = entry->conf_type;
4752 int byte_mask = conf_type & CONF_MASK_BYTES;
4753 void *value_ptr = entry->value;
4754 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
4757 boolean modified = FALSE;
4759 /* check if any settings have been modified before saving them */
4760 if (value != default_value)
4763 if (!modified) /* do not save unmodified default settings */
4767 printf("::: %02x, %d: %d != %d\n",
4768 byte_mask, conf_type & CONF_MASK_TOKEN,
4769 value, default_value);
4773 num_bytes += putFile16BitBE(file, element);
4775 num_bytes += putFile8Bit(file, conf_type);
4776 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
4777 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
4778 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :0);
4783 static int SaveLevel_MicroChunk_ElementList(FILE *file,
4784 struct ElementFileConfig *entry)
4786 int *element_array = (int *)(entry->value);
4787 int num_elements = *(int *)(entry->num_entities);
4788 int default_value = entry->default_value;
4789 int element = entry->element;
4790 int conf_type = entry->conf_type;
4792 boolean modified = FALSE;
4795 /* check if any settings have been modified before saving them */
4796 for (i = 0; i < num_elements; i++)
4797 if (element_array[i] != default_value)
4800 if (!modified) /* do not save unmodified default settings */
4804 num_bytes += putFile16BitBE(file, element);
4806 num_bytes += putFile8Bit(file, conf_type);
4807 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
4809 for (i = 0; i < num_elements; i++)
4810 num_bytes += putFile16BitBE(file, element_array[i]);
4815 static int SaveLevel_MicroChunk_ContentList(FILE *file,
4816 struct ElementFileConfig *entry)
4818 struct Content *content = (struct Content *)(entry->value);
4819 int num_contents = *(int *)(entry->num_entities);
4820 int default_value = entry->default_value;
4821 int element = entry->element;
4822 int conf_type = entry->conf_type;
4824 boolean modified = FALSE;
4827 /* check if any settings have been modified before saving them */
4828 for (i = 0; i < num_contents; i++)
4829 for (y = 0; y < 3; y++)
4830 for (x = 0; x < 3; x++)
4831 if (content[i].e[x][y] != default_value)
4834 if (!modified) /* do not save unmodified default settings */
4838 num_bytes += putFile16BitBE(file, element);
4840 num_bytes += putFile8Bit(file, conf_type);
4841 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
4843 for (i = 0; i < num_contents; i++)
4844 for (y = 0; y < 3; y++)
4845 for (x = 0; x < 3; x++)
4846 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
4851 static int SaveLevel_CONF(FILE *file, struct LevelInfo *level)
4856 li = *level; /* copy level data into temporary buffer */
4858 for (i = 0; element_conf[i].data_type != -1; i++)
4860 struct ElementFileConfig *config = &element_conf[i];
4861 int data_type = config->data_type;
4862 int conf_type = config->conf_type;
4863 int byte_mask = conf_type & CONF_MASK_BYTES;
4865 if (byte_mask != CONF_MASK_MULTI_BYTES)
4866 chunk_size += SaveLevel_MicroChunk_SingleValue(file, config);
4867 else if (data_type == TYPE_ELEMENT_LIST)
4868 chunk_size += SaveLevel_MicroChunk_ElementList(file, config);
4869 else if (data_type == TYPE_CONTENT_LIST)
4870 chunk_size += SaveLevel_MicroChunk_ContentList(file, config);
4876 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
4878 struct ElementInfo *ei = &element_info[element];
4882 chunk_size += putFile16BitBE(file, element);
4884 xx_ei = *ei; /* copy element data into temporary buffer */
4886 xx_num_description_bytes = MAX_ELEMENT_NAME_LEN;
4887 xx_num_contents = 1;
4890 printf("::: - element config\n");
4893 for (i = 0; custom_element_conf[i].data_type != -1; i++)
4895 struct ElementFileConfig *config = &custom_element_conf[i];
4896 int data_type = config->data_type;
4897 int conf_type = config->conf_type;
4898 int byte_mask = conf_type & CONF_MASK_BYTES;
4900 if (byte_mask != CONF_MASK_MULTI_BYTES)
4901 chunk_size += SaveLevel_MicroChunk_SingleValue(file, config);
4902 else if (data_type == TYPE_ELEMENT_LIST)
4903 chunk_size += SaveLevel_MicroChunk_ElementList(file, config);
4904 else if (data_type == TYPE_CONTENT_LIST)
4905 chunk_size += SaveLevel_MicroChunk_ContentList(file, config);
4909 printf("::: - change pages\n");
4912 for (i = 0; i < ei->num_change_pages; i++)
4914 struct ElementChangeInfo *change = &ei->change_page[i];
4916 xx_current_change_page = i;
4918 xx_change = *change; /* copy change data into temporary buffer */
4921 printf("::: %d: xx_change.action_mode == %d\n",
4922 i, xx_change.action_mode);
4923 printf("::: %d: xx_change.action_arg == %d\n",
4924 i, xx_change.action_arg);
4927 xx_event_bits_0_31 = 0;
4928 xx_event_bits_32_63 = 0;
4930 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
4932 if (change->has_event[i])
4935 xx_event_bits_0_31 |= (1 << i);
4937 xx_event_bits_32_63 |= (1 << (i - 32));
4941 for (i = 0; custom_element_change_conf[i].data_type != -1; i++)
4943 struct ElementFileConfig *config = &custom_element_change_conf[i];
4944 int data_type = config->data_type;
4945 int conf_type = config->conf_type;
4946 int byte_mask = conf_type & CONF_MASK_BYTES;
4948 if (byte_mask != CONF_MASK_MULTI_BYTES)
4949 chunk_size += SaveLevel_MicroChunk_SingleValue(file, config);
4950 else if (data_type == TYPE_ELEMENT_LIST)
4951 chunk_size += SaveLevel_MicroChunk_ElementList(file, config);
4952 else if (data_type == TYPE_CONTENT_LIST)
4953 chunk_size += SaveLevel_MicroChunk_ContentList(file, config);
4960 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
4962 struct ElementInfo *ei = &element_info[element];
4963 struct ElementGroupInfo *group = ei->group;
4967 chunk_size += putFile16BitBE(file, element);
4969 xx_ei = *ei; /* copy element data into temporary buffer */
4970 xx_group = *group; /* copy group data into temporary buffer */
4972 xx_num_description_bytes = MAX_ELEMENT_NAME_LEN;
4973 xx_num_contents = 1;
4975 for (i = 0; group_element_conf[i].data_type != -1; i++)
4977 struct ElementFileConfig *config = &group_element_conf[i];
4978 int data_type = config->data_type;
4979 int conf_type = config->conf_type;
4980 int byte_mask = conf_type & CONF_MASK_BYTES;
4982 if (byte_mask != CONF_MASK_MULTI_BYTES)
4983 chunk_size += SaveLevel_MicroChunk_SingleValue(file, config);
4984 else if (data_type == TYPE_ELEMENT_LIST)
4985 chunk_size += SaveLevel_MicroChunk_ElementList(file, config);
4986 else if (data_type == TYPE_CONTENT_LIST)
4987 chunk_size += SaveLevel_MicroChunk_ContentList(file, config);
4993 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
4995 int body_chunk_size, conf_chunk_size;
4999 if (!(file = fopen(filename, MODE_WRITE)))
5001 Error(ERR_WARN, "cannot save level file '%s'", filename);
5005 level->file_version = FILE_VERSION_ACTUAL;
5006 level->game_version = GAME_VERSION_ACTUAL;
5008 /* check level field for 16-bit elements */
5009 level->encoding_16bit_field = FALSE;
5010 for (y = 0; y < level->fieldy; y++)
5011 for (x = 0; x < level->fieldx; x++)
5012 if (level->field[x][y] > 255)
5013 level->encoding_16bit_field = TRUE;
5015 /* check yamyam content for 16-bit elements */
5016 level->encoding_16bit_yamyam = FALSE;
5017 for (i = 0; i < level->num_yamyam_contents; i++)
5018 for (y = 0; y < 3; y++)
5019 for (x = 0; x < 3; x++)
5020 if (level->yamyam_content[i].e[x][y] > 255)
5021 level->encoding_16bit_yamyam = TRUE;
5023 /* check amoeba content for 16-bit elements */
5024 level->encoding_16bit_amoeba = FALSE;
5025 if (level->amoeba_content > 255)
5026 level->encoding_16bit_amoeba = TRUE;
5028 /* calculate size of "BODY" chunk */
5030 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
5032 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
5033 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
5035 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
5036 SaveLevel_VERS(file, level);
5038 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
5039 SaveLevel_HEAD(file, level);
5041 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
5042 SaveLevel_AUTH(file, level);
5044 putFileChunkBE(file, "BODY", body_chunk_size);
5045 SaveLevel_BODY(file, level);
5047 if (level->encoding_16bit_yamyam ||
5048 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
5050 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
5051 SaveLevel_CNT2(file, level, EL_YAMYAM);
5054 if (level->encoding_16bit_amoeba)
5056 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
5057 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
5060 /* check for envelope content */
5061 for (i = 0; i < 4; i++)
5063 if (strlen(level->envelope_text[i]) > 0)
5065 int envelope_len = strlen(level->envelope_text[i]) + 1;
5067 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
5068 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
5072 /* if not using template level, check for non-default custom/group elements */
5073 if (!level->use_custom_template)
5076 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5078 int element = EL_CUSTOM_START + i;
5080 if (element_info[element].modified_settings)
5082 int num_change_pages = element_info[element].num_change_pages;
5084 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
5085 SaveLevel_CUS4(file, level, element);
5091 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
5093 int element = EL_GROUP_START + i;
5095 if (element_info[element].modified_settings)
5097 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
5098 SaveLevel_GRP1(file, level, element);
5104 conf_chunk_size = SaveLevel_CONF(NULL, level);
5106 /* check if non-default element settings need to be saved */
5107 if (conf_chunk_size > 0)
5109 putFileChunkBE(file, "CONF", conf_chunk_size);
5110 SaveLevel_CONF(file, level);
5113 /* if not using template level, check for non-default custom/group elements */
5114 if (!level->use_custom_template)
5116 /* (element number, number of change pages, change page number) */
5117 int cusx_chunk_size_no_changes = (2) + (1 + 1) + (1 + 1);
5118 /* (element number only) */
5119 int grpx_chunk_size_no_changes = (2);
5122 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5124 int element = EL_CUSTOM_START + i;
5125 int cusx_chunk_size = SaveLevel_CUSX(NULL, level, element);
5127 /* check if non-default element settings need to be saved */
5128 if (cusx_chunk_size > cusx_chunk_size_no_changes)
5131 printf("::: SAVING CE %d\n", i + 1);
5134 putFileChunkBE(file, "CUSX", cusx_chunk_size);
5135 SaveLevel_CUSX(file, level, element);
5146 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
5148 int element = EL_GROUP_START + i;
5149 int grpx_chunk_size = SaveLevel_GRPX(NULL, level, element);
5151 /* check if non-default element settings need to be saved */
5152 if (grpx_chunk_size > grpx_chunk_size_no_changes)
5155 printf("::: SAVING GE %d\n", i + 1);
5158 putFileChunkBE(file, "GRPX", grpx_chunk_size);
5159 SaveLevel_GRPX(file, level, element);
5172 SetFilePermissions(filename, PERMS_PRIVATE);
5175 void SaveLevel(int nr)
5177 char *filename = getDefaultLevelFilename(nr);
5179 SaveLevelFromFilename(&level, filename);
5182 void SaveLevelTemplate()
5184 char *filename = getDefaultLevelFilename(-1);
5186 SaveLevelFromFilename(&level, filename);
5189 void DumpLevel(struct LevelInfo *level)
5191 if (level->no_valid_file)
5193 Error(ERR_WARN, "cannot dump -- no valid level file found");
5198 printf_line("-", 79);
5199 printf("Level xxx (file version %08d, game version %08d)\n",
5200 level->file_version, level->game_version);
5201 printf_line("-", 79);
5203 printf("Level author: '%s'\n", level->author);
5204 printf("Level title: '%s'\n", level->name);
5206 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
5208 printf("Level time: %d seconds\n", level->time);
5209 printf("Gems needed: %d\n", level->gems_needed);
5211 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
5212 printf("Time for wheel: %d seconds\n", level->time_wheel);
5213 printf("Time for light: %d seconds\n", level->time_light);
5214 printf("Time for timegate: %d seconds\n", level->time_timegate);
5216 printf("Amoeba speed: %d\n", level->amoeba_speed);
5218 printf("Initial gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
5219 printf("Initial player stepsize: %d\n", level->initial_player_stepsize);
5220 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
5221 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
5222 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
5223 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
5224 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
5226 printf_line("-", 79);
5230 /* ========================================================================= */
5231 /* tape file functions */
5232 /* ========================================================================= */
5234 static void setTapeInfoToDefaults()
5238 /* always start with reliable default values (empty tape) */
5241 /* default values (also for pre-1.2 tapes) with only the first player */
5242 tape.player_participates[0] = TRUE;
5243 for (i = 1; i < MAX_PLAYERS; i++)
5244 tape.player_participates[i] = FALSE;
5246 /* at least one (default: the first) player participates in every tape */
5247 tape.num_participating_players = 1;
5249 tape.level_nr = level_nr;
5251 tape.changed = FALSE;
5253 tape.recording = FALSE;
5254 tape.playing = FALSE;
5255 tape.pausing = FALSE;
5257 tape.no_valid_file = FALSE;
5260 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
5262 tape->file_version = getFileVersion(file);
5263 tape->game_version = getFileVersion(file);
5268 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
5272 tape->random_seed = getFile32BitBE(file);
5273 tape->date = getFile32BitBE(file);
5274 tape->length = getFile32BitBE(file);
5276 /* read header fields that are new since version 1.2 */
5277 if (tape->file_version >= FILE_VERSION_1_2)
5279 byte store_participating_players = getFile8Bit(file);
5282 /* since version 1.2, tapes store which players participate in the tape */
5283 tape->num_participating_players = 0;
5284 for (i = 0; i < MAX_PLAYERS; i++)
5286 tape->player_participates[i] = FALSE;
5288 if (store_participating_players & (1 << i))
5290 tape->player_participates[i] = TRUE;
5291 tape->num_participating_players++;
5295 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
5297 engine_version = getFileVersion(file);
5298 if (engine_version > 0)
5299 tape->engine_version = engine_version;
5301 tape->engine_version = tape->game_version;
5307 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
5309 int level_identifier_size;
5312 level_identifier_size = getFile16BitBE(file);
5314 tape->level_identifier =
5315 checked_realloc(tape->level_identifier, level_identifier_size);
5317 for (i = 0; i < level_identifier_size; i++)
5318 tape->level_identifier[i] = getFile8Bit(file);
5320 tape->level_nr = getFile16BitBE(file);
5322 chunk_size = 2 + level_identifier_size + 2;
5327 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
5330 int chunk_size_expected =
5331 (tape->num_participating_players + 1) * tape->length;
5333 if (chunk_size_expected != chunk_size)
5335 ReadUnusedBytesFromFile(file, chunk_size);
5336 return chunk_size_expected;
5339 for (i = 0; i < tape->length; i++)
5341 if (i >= MAX_TAPE_LEN)
5344 for (j = 0; j < MAX_PLAYERS; j++)
5346 tape->pos[i].action[j] = MV_NONE;
5348 if (tape->player_participates[j])
5349 tape->pos[i].action[j] = getFile8Bit(file);
5352 tape->pos[i].delay = getFile8Bit(file);
5354 if (tape->file_version == FILE_VERSION_1_0)
5356 /* eliminate possible diagonal moves in old tapes */
5357 /* this is only for backward compatibility */
5359 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
5360 byte action = tape->pos[i].action[0];
5361 int k, num_moves = 0;
5363 for (k = 0; k<4; k++)
5365 if (action & joy_dir[k])
5367 tape->pos[i + num_moves].action[0] = joy_dir[k];
5369 tape->pos[i + num_moves].delay = 0;
5378 tape->length += num_moves;
5381 else if (tape->file_version < FILE_VERSION_2_0)
5383 /* convert pre-2.0 tapes to new tape format */
5385 if (tape->pos[i].delay > 1)
5388 tape->pos[i + 1] = tape->pos[i];
5389 tape->pos[i + 1].delay = 1;
5392 for (j = 0; j < MAX_PLAYERS; j++)
5393 tape->pos[i].action[j] = MV_NONE;
5394 tape->pos[i].delay--;
5405 if (i != tape->length)
5406 chunk_size = (tape->num_participating_players + 1) * i;
5411 void LoadTapeFromFilename(char *filename)
5413 char cookie[MAX_LINE_LEN];
5414 char chunk_name[CHUNK_ID_LEN + 1];
5418 /* always start with reliable default values */
5419 setTapeInfoToDefaults();
5421 if (!(file = fopen(filename, MODE_READ)))
5423 tape.no_valid_file = TRUE;
5428 getFileChunkBE(file, chunk_name, NULL);
5429 if (strEqual(chunk_name, "RND1"))
5431 getFile32BitBE(file); /* not used */
5433 getFileChunkBE(file, chunk_name, NULL);
5434 if (!strEqual(chunk_name, "TAPE"))
5436 tape.no_valid_file = TRUE;
5438 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
5443 else /* check for pre-2.0 file format with cookie string */
5445 strcpy(cookie, chunk_name);
5446 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
5447 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
5448 cookie[strlen(cookie) - 1] = '\0';
5450 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
5452 tape.no_valid_file = TRUE;
5454 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
5459 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
5461 tape.no_valid_file = TRUE;
5463 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
5468 /* pre-2.0 tape files have no game version, so use file version here */
5469 tape.game_version = tape.file_version;
5472 if (tape.file_version < FILE_VERSION_1_2)
5474 /* tape files from versions before 1.2.0 without chunk structure */
5475 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
5476 LoadTape_BODY(file, 2 * tape.length, &tape);
5484 int (*loader)(FILE *, int, struct TapeInfo *);
5488 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
5489 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
5490 { "INFO", -1, LoadTape_INFO },
5491 { "BODY", -1, LoadTape_BODY },
5495 while (getFileChunkBE(file, chunk_name, &chunk_size))
5499 while (chunk_info[i].name != NULL &&
5500 !strEqual(chunk_name, chunk_info[i].name))
5503 if (chunk_info[i].name == NULL)
5505 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
5506 chunk_name, filename);
5507 ReadUnusedBytesFromFile(file, chunk_size);
5509 else if (chunk_info[i].size != -1 &&
5510 chunk_info[i].size != chunk_size)
5512 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
5513 chunk_size, chunk_name, filename);
5514 ReadUnusedBytesFromFile(file, chunk_size);
5518 /* call function to load this tape chunk */
5519 int chunk_size_expected =
5520 (chunk_info[i].loader)(file, chunk_size, &tape);
5522 /* the size of some chunks cannot be checked before reading other
5523 chunks first (like "HEAD" and "BODY") that contain some header
5524 information, so check them here */
5525 if (chunk_size_expected != chunk_size)
5527 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
5528 chunk_size, chunk_name, filename);
5536 tape.length_seconds = GetTapeLength();
5539 printf("::: tape game version: %d\n", tape.game_version);
5540 printf("::: tape engine version: %d\n", tape.engine_version);
5544 void LoadTape(int nr)
5546 char *filename = getTapeFilename(nr);
5548 LoadTapeFromFilename(filename);
5551 void LoadSolutionTape(int nr)
5553 char *filename = getSolutionTapeFilename(nr);
5555 LoadTapeFromFilename(filename);
5558 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
5560 putFileVersion(file, tape->file_version);
5561 putFileVersion(file, tape->game_version);
5564 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
5567 byte store_participating_players = 0;
5569 /* set bits for participating players for compact storage */
5570 for (i = 0; i < MAX_PLAYERS; i++)
5571 if (tape->player_participates[i])
5572 store_participating_players |= (1 << i);
5574 putFile32BitBE(file, tape->random_seed);
5575 putFile32BitBE(file, tape->date);
5576 putFile32BitBE(file, tape->length);
5578 putFile8Bit(file, store_participating_players);
5580 /* unused bytes not at the end here for 4-byte alignment of engine_version */
5581 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
5583 putFileVersion(file, tape->engine_version);
5586 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
5588 int level_identifier_size = strlen(tape->level_identifier) + 1;
5591 putFile16BitBE(file, level_identifier_size);
5593 for (i = 0; i < level_identifier_size; i++)
5594 putFile8Bit(file, tape->level_identifier[i]);
5596 putFile16BitBE(file, tape->level_nr);
5599 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
5603 for (i = 0; i < tape->length; i++)
5605 for (j = 0; j < MAX_PLAYERS; j++)
5606 if (tape->player_participates[j])
5607 putFile8Bit(file, tape->pos[i].action[j]);
5609 putFile8Bit(file, tape->pos[i].delay);
5613 void SaveTape(int nr)
5615 char *filename = getTapeFilename(nr);
5617 boolean new_tape = TRUE;
5618 int num_participating_players = 0;
5619 int info_chunk_size;
5620 int body_chunk_size;
5623 InitTapeDirectory(leveldir_current->subdir);
5625 /* if a tape still exists, ask to overwrite it */
5626 if (fileExists(filename))
5629 if (!Request("Replace old tape ?", REQ_ASK))
5633 if (!(file = fopen(filename, MODE_WRITE)))
5635 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
5639 tape.file_version = FILE_VERSION_ACTUAL;
5640 tape.game_version = GAME_VERSION_ACTUAL;
5642 /* count number of participating players */
5643 for (i = 0; i < MAX_PLAYERS; i++)
5644 if (tape.player_participates[i])
5645 num_participating_players++;
5647 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
5648 body_chunk_size = (num_participating_players + 1) * tape.length;
5650 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
5651 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
5653 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
5654 SaveTape_VERS(file, &tape);
5656 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
5657 SaveTape_HEAD(file, &tape);
5659 putFileChunkBE(file, "INFO", info_chunk_size);
5660 SaveTape_INFO(file, &tape);
5662 putFileChunkBE(file, "BODY", body_chunk_size);
5663 SaveTape_BODY(file, &tape);
5667 SetFilePermissions(filename, PERMS_PRIVATE);
5669 tape.changed = FALSE;
5672 Request("Tape saved !", REQ_CONFIRM);
5675 void DumpTape(struct TapeInfo *tape)
5677 int tape_frame_counter;
5680 if (tape->no_valid_file)
5682 Error(ERR_WARN, "cannot dump -- no valid tape file found");
5687 printf_line("-", 79);
5688 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
5689 tape->level_nr, tape->file_version, tape->game_version);
5690 printf(" (effective engine version %08d)\n",
5691 tape->engine_version);
5692 printf("Level series identifier: '%s'\n", tape->level_identifier);
5693 printf_line("-", 79);
5695 tape_frame_counter = 0;
5697 for (i = 0; i < tape->length; i++)
5699 if (i >= MAX_TAPE_LEN)
5702 printf("%04d: ", i);
5704 for (j = 0; j < MAX_PLAYERS; j++)
5706 if (tape->player_participates[j])
5708 int action = tape->pos[i].action[j];
5710 printf("%d:%02x ", j, action);
5711 printf("[%c%c%c%c|%c%c] - ",
5712 (action & JOY_LEFT ? '<' : ' '),
5713 (action & JOY_RIGHT ? '>' : ' '),
5714 (action & JOY_UP ? '^' : ' '),
5715 (action & JOY_DOWN ? 'v' : ' '),
5716 (action & JOY_BUTTON_1 ? '1' : ' '),
5717 (action & JOY_BUTTON_2 ? '2' : ' '));
5721 printf("(%03d) ", tape->pos[i].delay);
5722 printf("[%05d]\n", tape_frame_counter);
5724 tape_frame_counter += tape->pos[i].delay;
5727 printf_line("-", 79);
5731 /* ========================================================================= */
5732 /* score file functions */
5733 /* ========================================================================= */
5735 void LoadScore(int nr)
5738 char *filename = getScoreFilename(nr);
5739 char cookie[MAX_LINE_LEN];
5740 char line[MAX_LINE_LEN];
5744 /* always start with reliable default values */
5745 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5747 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
5748 highscore[i].Score = 0;
5751 if (!(file = fopen(filename, MODE_READ)))
5754 /* check file identifier */
5755 fgets(cookie, MAX_LINE_LEN, file);
5756 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
5757 cookie[strlen(cookie) - 1] = '\0';
5759 if (!checkCookieString(cookie, SCORE_COOKIE))
5761 Error(ERR_WARN, "unknown format of score file '%s'", filename);
5766 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5768 fscanf(file, "%d", &highscore[i].Score);
5769 fgets(line, MAX_LINE_LEN, file);
5771 if (line[strlen(line) - 1] == '\n')
5772 line[strlen(line) - 1] = '\0';
5774 for (line_ptr = line; *line_ptr; line_ptr++)
5776 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
5778 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
5779 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
5788 void SaveScore(int nr)
5791 char *filename = getScoreFilename(nr);
5794 InitScoreDirectory(leveldir_current->subdir);
5796 if (!(file = fopen(filename, MODE_WRITE)))
5798 Error(ERR_WARN, "cannot save score for level %d", nr);
5802 fprintf(file, "%s\n\n", SCORE_COOKIE);
5804 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5805 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
5809 SetFilePermissions(filename, PERMS_PUBLIC);
5813 /* ========================================================================= */
5814 /* setup file functions */
5815 /* ========================================================================= */
5817 #define TOKEN_STR_PLAYER_PREFIX "player_"
5820 #define SETUP_TOKEN_PLAYER_NAME 0
5821 #define SETUP_TOKEN_SOUND 1
5822 #define SETUP_TOKEN_SOUND_LOOPS 2
5823 #define SETUP_TOKEN_SOUND_MUSIC 3
5824 #define SETUP_TOKEN_SOUND_SIMPLE 4
5825 #define SETUP_TOKEN_TOONS 5
5826 #define SETUP_TOKEN_SCROLL_DELAY 6
5827 #define SETUP_TOKEN_SOFT_SCROLLING 7
5828 #define SETUP_TOKEN_FADING 8
5829 #define SETUP_TOKEN_AUTORECORD 9
5830 #define SETUP_TOKEN_SHOW_TITLESCREEN 10
5831 #define SETUP_TOKEN_QUICK_DOORS 11
5832 #define SETUP_TOKEN_TEAM_MODE 12
5833 #define SETUP_TOKEN_HANDICAP 13
5834 #define SETUP_TOKEN_SKIP_LEVELS 14
5835 #define SETUP_TOKEN_TIME_LIMIT 15
5836 #define SETUP_TOKEN_FULLSCREEN 16
5837 #define SETUP_TOKEN_ASK_ON_ESCAPE 17
5838 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 18
5839 #define SETUP_TOKEN_QUICK_SWITCH 19
5840 #define SETUP_TOKEN_INPUT_ON_FOCUS 20
5841 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 21
5842 #define SETUP_TOKEN_GRAPHICS_SET 22
5843 #define SETUP_TOKEN_SOUNDS_SET 23
5844 #define SETUP_TOKEN_MUSIC_SET 24
5845 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 25
5846 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 26
5847 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 27
5849 #define NUM_GLOBAL_SETUP_TOKENS 28
5852 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
5853 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
5854 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
5855 #define SETUP_TOKEN_EDITOR_EL_MORE 3
5856 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 4
5857 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 5
5858 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 6
5859 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 7
5860 #define SETUP_TOKEN_EDITOR_EL_CHARS 8
5861 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 9
5862 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
5863 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
5864 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 12
5865 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 13
5867 #define NUM_EDITOR_SETUP_TOKENS 14
5869 /* editor cascade setup */
5870 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
5871 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
5872 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
5873 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
5874 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
5875 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
5876 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
5877 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
5878 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
5879 #define SETUP_TOKEN_EDITOR_CASCADE_CE 9
5880 #define SETUP_TOKEN_EDITOR_CASCADE_GE 10
5881 #define SETUP_TOKEN_EDITOR_CASCADE_USER 11
5882 #define SETUP_TOKEN_EDITOR_CASCADE_GENERIC 12
5883 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 13
5885 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 14
5887 /* shortcut setup */
5888 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
5889 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
5890 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
5891 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
5892 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
5893 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
5894 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
5895 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
5897 #define NUM_SHORTCUT_SETUP_TOKENS 8
5900 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
5901 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
5902 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
5903 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
5904 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
5905 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
5906 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
5907 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
5908 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
5909 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
5910 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
5911 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
5912 #define SETUP_TOKEN_PLAYER_KEY_UP 12
5913 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
5914 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
5915 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
5917 #define NUM_PLAYER_SETUP_TOKENS 16
5920 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
5921 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
5923 #define NUM_SYSTEM_SETUP_TOKENS 2
5926 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
5928 #define NUM_OPTIONS_SETUP_TOKENS 1
5931 static struct SetupInfo si;
5932 static struct SetupEditorInfo sei;
5933 static struct SetupEditorCascadeInfo seci;
5934 static struct SetupShortcutInfo ssi;
5935 static struct SetupInputInfo sii;
5936 static struct SetupSystemInfo syi;
5937 static struct OptionInfo soi;
5939 static struct TokenInfo global_setup_tokens[] =
5941 { TYPE_STRING, &si.player_name, "player_name" },
5942 { TYPE_SWITCH, &si.sound, "sound" },
5943 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
5944 { TYPE_SWITCH, &si.sound_music, "background_music" },
5945 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
5946 { TYPE_SWITCH, &si.toons, "toons" },
5947 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
5948 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
5949 { TYPE_SWITCH, &si.fading, "screen_fading" },
5950 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
5951 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
5952 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
5953 { TYPE_SWITCH, &si.team_mode, "team_mode" },
5954 { TYPE_SWITCH, &si.handicap, "handicap" },
5955 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
5956 { TYPE_SWITCH, &si.time_limit, "time_limit" },
5957 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
5958 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
5959 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
5960 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
5961 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
5962 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
5963 { TYPE_STRING, &si.graphics_set, "graphics_set" },
5964 { TYPE_STRING, &si.sounds_set, "sounds_set" },
5965 { TYPE_STRING, &si.music_set, "music_set" },
5966 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
5967 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
5968 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
5971 static struct TokenInfo editor_setup_tokens[] =
5973 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
5974 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
5975 { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
5976 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
5977 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
5978 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
5979 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
5980 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
5981 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
5982 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
5983 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
5984 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
5985 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
5986 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
5989 static struct TokenInfo editor_cascade_setup_tokens[] =
5991 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
5992 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
5993 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
5994 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
5995 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
5996 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
5997 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
5998 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
5999 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
6000 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
6001 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
6002 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
6003 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
6006 static struct TokenInfo shortcut_setup_tokens[] =
6008 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
6009 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
6010 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
6011 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
6012 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
6013 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
6014 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
6015 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
6018 static struct TokenInfo player_setup_tokens[] =
6020 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
6021 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
6022 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
6023 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
6024 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
6025 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
6026 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
6027 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
6028 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
6029 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
6030 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
6031 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
6032 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
6033 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
6034 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
6035 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
6038 static struct TokenInfo system_setup_tokens[] =
6040 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
6041 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
6044 static struct TokenInfo options_setup_tokens[] =
6046 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
6049 static char *get_corrected_login_name(char *login_name)
6051 /* needed because player name must be a fixed length string */
6052 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
6054 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
6055 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
6057 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
6058 if (strchr(login_name_new, ' '))
6059 *strchr(login_name_new, ' ') = '\0';
6061 return login_name_new;
6064 static void setSetupInfoToDefaults(struct SetupInfo *si)
6068 si->player_name = get_corrected_login_name(getLoginName());
6071 si->sound_loops = TRUE;
6072 si->sound_music = TRUE;
6073 si->sound_simple = TRUE;
6075 si->double_buffering = TRUE;
6076 si->direct_draw = !si->double_buffering;
6077 si->scroll_delay = TRUE;
6078 si->soft_scrolling = TRUE;
6080 si->autorecord = TRUE;
6081 si->show_titlescreen = TRUE;
6082 si->quick_doors = FALSE;
6083 si->team_mode = FALSE;
6084 si->handicap = TRUE;
6085 si->skip_levels = TRUE;
6086 si->time_limit = TRUE;
6087 si->fullscreen = FALSE;
6088 si->ask_on_escape = TRUE;
6089 si->ask_on_escape_editor = TRUE;
6090 si->quick_switch = FALSE;
6091 si->input_on_focus = FALSE;
6092 si->prefer_aga_graphics = TRUE;
6094 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
6095 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
6096 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
6097 si->override_level_graphics = FALSE;
6098 si->override_level_sounds = FALSE;
6099 si->override_level_music = FALSE;
6101 si->editor.el_boulderdash = TRUE;
6102 si->editor.el_emerald_mine = TRUE;
6103 si->editor.el_emerald_mine_club = TRUE;
6104 si->editor.el_more = TRUE;
6105 si->editor.el_sokoban = TRUE;
6106 si->editor.el_supaplex = TRUE;
6107 si->editor.el_diamond_caves = TRUE;
6108 si->editor.el_dx_boulderdash = TRUE;
6109 si->editor.el_chars = TRUE;
6110 si->editor.el_custom = TRUE;
6112 si->editor.el_headlines = TRUE;
6113 si->editor.el_user_defined = FALSE;
6114 si->editor.el_dynamic = TRUE;
6116 si->editor.show_element_token = FALSE;
6118 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
6119 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
6120 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
6122 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
6123 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
6124 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
6125 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
6126 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
6128 for (i = 0; i < MAX_PLAYERS; i++)
6130 si->input[i].use_joystick = FALSE;
6131 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
6132 si->input[i].joy.xleft = JOYSTICK_XLEFT;
6133 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
6134 si->input[i].joy.xright = JOYSTICK_XRIGHT;
6135 si->input[i].joy.yupper = JOYSTICK_YUPPER;
6136 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
6137 si->input[i].joy.ylower = JOYSTICK_YLOWER;
6138 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
6139 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
6140 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
6141 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
6142 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
6143 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
6144 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
6145 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
6148 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
6149 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
6151 si->options.verbose = FALSE;
6154 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
6156 si->editor_cascade.el_bd = TRUE;
6157 si->editor_cascade.el_em = TRUE;
6158 si->editor_cascade.el_emc = TRUE;
6159 si->editor_cascade.el_rnd = TRUE;
6160 si->editor_cascade.el_sb = TRUE;
6161 si->editor_cascade.el_sp = TRUE;
6162 si->editor_cascade.el_dc = TRUE;
6163 si->editor_cascade.el_dx = TRUE;
6165 si->editor_cascade.el_chars = FALSE;
6166 si->editor_cascade.el_ce = FALSE;
6167 si->editor_cascade.el_ge = FALSE;
6168 si->editor_cascade.el_user = FALSE;
6169 si->editor_cascade.el_dynamic = FALSE;
6172 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
6176 if (!setup_file_hash)
6181 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
6182 setSetupInfo(global_setup_tokens, i,
6183 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
6188 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
6189 setSetupInfo(editor_setup_tokens, i,
6190 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
6193 /* shortcut setup */
6194 ssi = setup.shortcut;
6195 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
6196 setSetupInfo(shortcut_setup_tokens, i,
6197 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
6198 setup.shortcut = ssi;
6201 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
6205 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
6207 sii = setup.input[pnr];
6208 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
6210 char full_token[100];
6212 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
6213 setSetupInfo(player_setup_tokens, i,
6214 getHashEntry(setup_file_hash, full_token));
6216 setup.input[pnr] = sii;
6221 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
6222 setSetupInfo(system_setup_tokens, i,
6223 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
6227 soi = setup.options;
6228 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
6229 setSetupInfo(options_setup_tokens, i,
6230 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
6231 setup.options = soi;
6234 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
6238 if (!setup_file_hash)
6241 /* editor cascade setup */
6242 seci = setup.editor_cascade;
6243 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
6244 setSetupInfo(editor_cascade_setup_tokens, i,
6245 getHashEntry(setup_file_hash,
6246 editor_cascade_setup_tokens[i].text));
6247 setup.editor_cascade = seci;
6252 char *filename = getSetupFilename();
6253 SetupFileHash *setup_file_hash = NULL;
6255 /* always start with reliable default values */
6256 setSetupInfoToDefaults(&setup);
6258 setup_file_hash = loadSetupFileHash(filename);
6260 if (setup_file_hash)
6262 char *player_name_new;
6264 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
6265 decodeSetupFileHash(setup_file_hash);
6267 setup.direct_draw = !setup.double_buffering;
6269 freeSetupFileHash(setup_file_hash);
6271 /* needed to work around problems with fixed length strings */
6272 player_name_new = get_corrected_login_name(setup.player_name);
6273 free(setup.player_name);
6274 setup.player_name = player_name_new;
6277 Error(ERR_WARN, "using default setup values");
6280 void LoadSetup_EditorCascade()
6282 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
6283 SetupFileHash *setup_file_hash = NULL;
6285 /* always start with reliable default values */
6286 setSetupInfoToDefaults_EditorCascade(&setup);
6288 setup_file_hash = loadSetupFileHash(filename);
6290 if (setup_file_hash)
6292 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
6293 decodeSetupFileHash_EditorCascade(setup_file_hash);
6295 freeSetupFileHash(setup_file_hash);
6303 char *filename = getSetupFilename();
6307 InitUserDataDirectory();
6309 if (!(file = fopen(filename, MODE_WRITE)))
6311 Error(ERR_WARN, "cannot write setup file '%s'", filename);
6315 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
6316 getCookie("SETUP")));
6317 fprintf(file, "\n");
6321 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
6323 /* just to make things nicer :) */
6324 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
6325 i == SETUP_TOKEN_GRAPHICS_SET)
6326 fprintf(file, "\n");
6328 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
6333 fprintf(file, "\n");
6334 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
6335 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
6337 /* shortcut setup */
6338 ssi = setup.shortcut;
6339 fprintf(file, "\n");
6340 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
6341 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
6344 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
6348 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
6349 fprintf(file, "\n");
6351 sii = setup.input[pnr];
6352 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
6353 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
6358 fprintf(file, "\n");
6359 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
6360 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
6363 soi = setup.options;
6364 fprintf(file, "\n");
6365 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
6366 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
6370 SetFilePermissions(filename, PERMS_PRIVATE);
6373 void SaveSetup_EditorCascade()
6375 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
6379 InitUserDataDirectory();
6381 if (!(file = fopen(filename, MODE_WRITE)))
6383 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
6388 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
6389 getCookie("SETUP")));
6390 fprintf(file, "\n");
6392 seci = setup.editor_cascade;
6393 fprintf(file, "\n");
6394 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
6395 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
6399 SetFilePermissions(filename, PERMS_PRIVATE);
6404 void LoadCustomElementDescriptions()
6406 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
6407 SetupFileHash *setup_file_hash;
6410 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6412 if (element_info[i].custom_description != NULL)
6414 free(element_info[i].custom_description);
6415 element_info[i].custom_description = NULL;
6419 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
6422 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6424 char *token = getStringCat2(element_info[i].token_name, ".name");
6425 char *value = getHashEntry(setup_file_hash, token);
6428 element_info[i].custom_description = getStringCopy(value);
6433 freeSetupFileHash(setup_file_hash);
6436 static void LoadSpecialMenuDesignSettingsFromFilename(char *filename)
6438 SetupFileHash *setup_file_hash;
6442 printf("LoadSpecialMenuDesignSettings from file '%s' ...\n", filename);
6445 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
6448 /* special case: initialize with default values that may be overwritten */
6449 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
6451 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
6452 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
6453 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
6455 if (value_x != NULL)
6456 menu.draw_xoffset[i] = get_integer_from_string(value_x);
6457 if (value_y != NULL)
6458 menu.draw_yoffset[i] = get_integer_from_string(value_y);
6459 if (list_size != NULL)
6460 menu.list_size[i] = get_integer_from_string(list_size);
6463 /* read (and overwrite with) values that may be specified in config file */
6464 for (i = 0; image_config_vars[i].token != NULL; i++)
6466 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
6469 *image_config_vars[i].value =
6470 get_auto_parameter_value(image_config_vars[i].token, value);
6473 freeSetupFileHash(setup_file_hash);
6476 void LoadSpecialMenuDesignSettings()
6478 char *filename_base = UNDEFINED_FILENAME, *filename_local;
6481 /* always start with reliable default values from default config */
6482 for (i = 0; image_config_vars[i].token != NULL; i++)
6483 for (j = 0; image_config[j].token != NULL; j++)
6484 if (strEqual(image_config_vars[i].token, image_config[j].token))
6485 *image_config_vars[i].value =
6486 get_auto_parameter_value(image_config_vars[i].token,
6487 image_config[j].value);
6490 if (!SETUP_OVERRIDE_ARTWORK(setup, ARTWORK_TYPE_GRAPHICS))
6492 /* first look for special settings configured in level series config */
6493 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
6495 if (fileExists(filename_base))
6496 LoadSpecialMenuDesignSettingsFromFilename(filename_base);
6499 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
6501 if (filename_local != NULL && !strEqual(filename_base, filename_local))
6502 LoadSpecialMenuDesignSettingsFromFilename(filename_local);
6506 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
6508 LoadSpecialMenuDesignSettingsFromFilename(filename_local);
6512 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
6514 char *filename = getEditorSetupFilename();
6515 SetupFileList *setup_file_list, *list;
6516 SetupFileHash *element_hash;
6517 int num_unknown_tokens = 0;
6520 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
6523 element_hash = newSetupFileHash();
6525 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6526 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
6528 /* determined size may be larger than needed (due to unknown elements) */
6530 for (list = setup_file_list; list != NULL; list = list->next)
6533 /* add space for up to 3 more elements for padding that may be needed */
6536 /* free memory for old list of elements, if needed */
6537 checked_free(*elements);
6539 /* allocate memory for new list of elements */
6540 *elements = checked_malloc(*num_elements * sizeof(int));
6543 for (list = setup_file_list; list != NULL; list = list->next)
6545 char *value = getHashEntry(element_hash, list->token);
6547 if (value == NULL) /* try to find obsolete token mapping */
6549 char *mapped_token = get_mapped_token(list->token);
6551 if (mapped_token != NULL)
6553 value = getHashEntry(element_hash, mapped_token);
6561 (*elements)[(*num_elements)++] = atoi(value);
6565 if (num_unknown_tokens == 0)
6567 Error(ERR_RETURN_LINE, "-");
6568 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
6569 Error(ERR_RETURN, "- config file: '%s'", filename);
6571 num_unknown_tokens++;
6574 Error(ERR_RETURN, "- token: '%s'", list->token);
6578 if (num_unknown_tokens > 0)
6579 Error(ERR_RETURN_LINE, "-");
6581 while (*num_elements % 4) /* pad with empty elements, if needed */
6582 (*elements)[(*num_elements)++] = EL_EMPTY;
6584 freeSetupFileList(setup_file_list);
6585 freeSetupFileHash(element_hash);
6588 for (i = 0; i < *num_elements; i++)
6589 printf("editor: element '%s' [%d]\n",
6590 element_info[(*elements)[i]].token_name, (*elements)[i]);
6594 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
6597 SetupFileHash *setup_file_hash = NULL;
6598 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
6599 char *filename_music, *filename_prefix, *filename_info;
6605 token_to_value_ptr[] =
6607 { "title_header", &tmp_music_file_info.title_header },
6608 { "artist_header", &tmp_music_file_info.artist_header },
6609 { "album_header", &tmp_music_file_info.album_header },
6610 { "year_header", &tmp_music_file_info.year_header },
6612 { "title", &tmp_music_file_info.title },
6613 { "artist", &tmp_music_file_info.artist },
6614 { "album", &tmp_music_file_info.album },
6615 { "year", &tmp_music_file_info.year },
6621 filename_music = (is_sound ? getCustomSoundFilename(basename) :
6622 getCustomMusicFilename(basename));
6624 if (filename_music == NULL)
6627 /* ---------- try to replace file extension ---------- */
6629 filename_prefix = getStringCopy(filename_music);
6630 if (strrchr(filename_prefix, '.') != NULL)
6631 *strrchr(filename_prefix, '.') = '\0';
6632 filename_info = getStringCat2(filename_prefix, ".txt");
6635 printf("trying to load file '%s'...\n", filename_info);
6638 if (fileExists(filename_info))
6639 setup_file_hash = loadSetupFileHash(filename_info);
6641 free(filename_prefix);
6642 free(filename_info);
6644 if (setup_file_hash == NULL)
6646 /* ---------- try to add file extension ---------- */
6648 filename_prefix = getStringCopy(filename_music);
6649 filename_info = getStringCat2(filename_prefix, ".txt");
6652 printf("trying to load file '%s'...\n", filename_info);
6655 if (fileExists(filename_info))
6656 setup_file_hash = loadSetupFileHash(filename_info);
6658 free(filename_prefix);
6659 free(filename_info);
6662 if (setup_file_hash == NULL)
6665 /* ---------- music file info found ---------- */
6667 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
6669 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
6671 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
6673 *token_to_value_ptr[i].value_ptr =
6674 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
6677 tmp_music_file_info.basename = getStringCopy(basename);
6678 tmp_music_file_info.music = music;
6679 tmp_music_file_info.is_sound = is_sound;
6681 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
6682 *new_music_file_info = tmp_music_file_info;
6684 return new_music_file_info;
6687 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
6689 return get_music_file_info_ext(basename, music, FALSE);
6692 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
6694 return get_music_file_info_ext(basename, sound, TRUE);
6697 static boolean music_info_listed_ext(struct MusicFileInfo *list,
6698 char *basename, boolean is_sound)
6700 for (; list != NULL; list = list->next)
6701 if (list->is_sound == is_sound && strEqual(list->basename, basename))
6707 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
6709 return music_info_listed_ext(list, basename, FALSE);
6712 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
6714 return music_info_listed_ext(list, basename, TRUE);
6717 void LoadMusicInfo()
6719 char *music_directory = getCustomMusicDirectory();
6720 int num_music = getMusicListSize();
6721 int num_music_noconf = 0;
6722 int num_sounds = getSoundListSize();
6724 struct dirent *dir_entry;
6725 struct FileInfo *music, *sound;
6726 struct MusicFileInfo *next, **new;
6729 while (music_file_info != NULL)
6731 next = music_file_info->next;
6733 checked_free(music_file_info->basename);
6735 checked_free(music_file_info->title_header);
6736 checked_free(music_file_info->artist_header);
6737 checked_free(music_file_info->album_header);
6738 checked_free(music_file_info->year_header);
6740 checked_free(music_file_info->title);
6741 checked_free(music_file_info->artist);
6742 checked_free(music_file_info->album);
6743 checked_free(music_file_info->year);
6745 free(music_file_info);
6747 music_file_info = next;
6750 new = &music_file_info;
6752 for (i = 0; i < num_music; i++)
6754 music = getMusicListEntry(i);
6756 if (music->filename == NULL)
6759 if (strEqual(music->filename, UNDEFINED_FILENAME))
6762 /* a configured file may be not recognized as music */
6763 if (!FileIsMusic(music->filename))
6767 printf("::: -> '%s' (configured)\n", music->filename);
6770 if (!music_info_listed(music_file_info, music->filename))
6772 *new = get_music_file_info(music->filename, i);
6774 new = &(*new)->next;
6778 if ((dir = opendir(music_directory)) == NULL)
6780 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
6784 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
6786 char *basename = dir_entry->d_name;
6787 boolean music_already_used = FALSE;
6790 /* skip all music files that are configured in music config file */
6791 for (i = 0; i < num_music; i++)
6793 music = getMusicListEntry(i);
6795 if (music->filename == NULL)
6798 if (strEqual(basename, music->filename))
6800 music_already_used = TRUE;
6805 if (music_already_used)
6808 if (!FileIsMusic(basename))
6812 printf("::: -> '%s' (found in directory)\n", basename);
6815 if (!music_info_listed(music_file_info, basename))
6817 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
6819 new = &(*new)->next;
6827 for (i = 0; i < num_sounds; i++)
6829 sound = getSoundListEntry(i);
6831 if (sound->filename == NULL)
6834 if (strEqual(sound->filename, UNDEFINED_FILENAME))
6837 /* a configured file may be not recognized as sound */
6838 if (!FileIsSound(sound->filename))
6842 printf("::: -> '%s' (configured)\n", sound->filename);
6845 if (!sound_info_listed(music_file_info, sound->filename))
6847 *new = get_sound_file_info(sound->filename, i);
6849 new = &(*new)->next;
6854 for (next = music_file_info; next != NULL; next = next->next)
6855 printf("::: title == '%s'\n", next->title);
6859 void add_helpanim_entry(int element, int action, int direction, int delay,
6860 int *num_list_entries)
6862 struct HelpAnimInfo *new_list_entry;
6863 (*num_list_entries)++;
6866 checked_realloc(helpanim_info,
6867 *num_list_entries * sizeof(struct HelpAnimInfo));
6868 new_list_entry = &helpanim_info[*num_list_entries - 1];
6870 new_list_entry->element = element;
6871 new_list_entry->action = action;
6872 new_list_entry->direction = direction;
6873 new_list_entry->delay = delay;
6876 void print_unknown_token(char *filename, char *token, int token_nr)
6880 Error(ERR_RETURN_LINE, "-");
6881 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
6882 Error(ERR_RETURN, "- config file: '%s'", filename);
6885 Error(ERR_RETURN, "- token: '%s'", token);
6888 void print_unknown_token_end(int token_nr)
6891 Error(ERR_RETURN_LINE, "-");
6894 void LoadHelpAnimInfo()
6896 char *filename = getHelpAnimFilename();
6897 SetupFileList *setup_file_list = NULL, *list;
6898 SetupFileHash *element_hash, *action_hash, *direction_hash;
6899 int num_list_entries = 0;
6900 int num_unknown_tokens = 0;
6903 if (fileExists(filename))
6904 setup_file_list = loadSetupFileList(filename);
6906 if (setup_file_list == NULL)
6908 /* use reliable default values from static configuration */
6909 SetupFileList *insert_ptr;
6911 insert_ptr = setup_file_list =
6912 newSetupFileList(helpanim_config[0].token,
6913 helpanim_config[0].value);
6915 for (i = 1; helpanim_config[i].token; i++)
6916 insert_ptr = addListEntry(insert_ptr,
6917 helpanim_config[i].token,
6918 helpanim_config[i].value);
6921 element_hash = newSetupFileHash();
6922 action_hash = newSetupFileHash();
6923 direction_hash = newSetupFileHash();
6925 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
6926 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
6928 for (i = 0; i < NUM_ACTIONS; i++)
6929 setHashEntry(action_hash, element_action_info[i].suffix,
6930 i_to_a(element_action_info[i].value));
6932 /* do not store direction index (bit) here, but direction value! */
6933 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
6934 setHashEntry(direction_hash, element_direction_info[i].suffix,
6935 i_to_a(1 << element_direction_info[i].value));
6937 for (list = setup_file_list; list != NULL; list = list->next)
6939 char *element_token, *action_token, *direction_token;
6940 char *element_value, *action_value, *direction_value;
6941 int delay = atoi(list->value);
6943 if (strEqual(list->token, "end"))
6945 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
6950 /* first try to break element into element/action/direction parts;
6951 if this does not work, also accept combined "element[.act][.dir]"
6952 elements (like "dynamite.active"), which are unique elements */
6954 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
6956 element_value = getHashEntry(element_hash, list->token);
6957 if (element_value != NULL) /* element found */
6958 add_helpanim_entry(atoi(element_value), -1, -1, delay,
6962 /* no further suffixes found -- this is not an element */
6963 print_unknown_token(filename, list->token, num_unknown_tokens++);
6969 /* token has format "<prefix>.<something>" */
6971 action_token = strchr(list->token, '.'); /* suffix may be action ... */
6972 direction_token = action_token; /* ... or direction */
6974 element_token = getStringCopy(list->token);
6975 *strchr(element_token, '.') = '\0';
6977 element_value = getHashEntry(element_hash, element_token);
6979 if (element_value == NULL) /* this is no element */
6981 element_value = getHashEntry(element_hash, list->token);
6982 if (element_value != NULL) /* combined element found */
6983 add_helpanim_entry(atoi(element_value), -1, -1, delay,
6986 print_unknown_token(filename, list->token, num_unknown_tokens++);
6988 free(element_token);
6993 action_value = getHashEntry(action_hash, action_token);
6995 if (action_value != NULL) /* action found */
6997 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
7000 free(element_token);
7005 direction_value = getHashEntry(direction_hash, direction_token);
7007 if (direction_value != NULL) /* direction found */
7009 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
7012 free(element_token);
7017 if (strchr(action_token + 1, '.') == NULL)
7019 /* no further suffixes found -- this is not an action nor direction */
7021 element_value = getHashEntry(element_hash, list->token);
7022 if (element_value != NULL) /* combined element found */
7023 add_helpanim_entry(atoi(element_value), -1, -1, delay,
7026 print_unknown_token(filename, list->token, num_unknown_tokens++);
7028 free(element_token);
7033 /* token has format "<prefix>.<suffix>.<something>" */
7035 direction_token = strchr(action_token + 1, '.');
7037 action_token = getStringCopy(action_token);
7038 *strchr(action_token + 1, '.') = '\0';
7040 action_value = getHashEntry(action_hash, action_token);
7042 if (action_value == NULL) /* this is no action */
7044 element_value = getHashEntry(element_hash, list->token);
7045 if (element_value != NULL) /* combined element found */
7046 add_helpanim_entry(atoi(element_value), -1, -1, delay,
7049 print_unknown_token(filename, list->token, num_unknown_tokens++);
7051 free(element_token);
7057 direction_value = getHashEntry(direction_hash, direction_token);
7059 if (direction_value != NULL) /* direction found */
7061 add_helpanim_entry(atoi(element_value), atoi(action_value),
7062 atoi(direction_value), delay, &num_list_entries);
7064 free(element_token);
7070 /* this is no direction */
7072 element_value = getHashEntry(element_hash, list->token);
7073 if (element_value != NULL) /* combined element found */
7074 add_helpanim_entry(atoi(element_value), -1, -1, delay,
7077 print_unknown_token(filename, list->token, num_unknown_tokens++);
7079 free(element_token);
7083 print_unknown_token_end(num_unknown_tokens);
7085 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
7086 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
7088 freeSetupFileList(setup_file_list);
7089 freeSetupFileHash(element_hash);
7090 freeSetupFileHash(action_hash);
7091 freeSetupFileHash(direction_hash);
7094 for (i = 0; i < num_list_entries; i++)
7095 printf("::: '%s': %d, %d, %d => %d\n",
7096 EL_NAME(helpanim_info[i].element),
7097 helpanim_info[i].element,
7098 helpanim_info[i].action,
7099 helpanim_info[i].direction,
7100 helpanim_info[i].delay);
7104 void LoadHelpTextInfo()
7106 char *filename = getHelpTextFilename();
7109 if (helptext_info != NULL)
7111 freeSetupFileHash(helptext_info);
7112 helptext_info = NULL;
7115 if (fileExists(filename))
7116 helptext_info = loadSetupFileHash(filename);
7118 if (helptext_info == NULL)
7120 /* use reliable default values from static configuration */
7121 helptext_info = newSetupFileHash();
7123 for (i = 0; helptext_config[i].token; i++)
7124 setHashEntry(helptext_info,
7125 helptext_config[i].token,
7126 helptext_config[i].value);
7130 BEGIN_HASH_ITERATION(helptext_info, itr)
7132 printf("::: '%s' => '%s'\n",
7133 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
7135 END_HASH_ITERATION(hash, itr)
7140 /* ------------------------------------------------------------------------- *
7142 * ------------------------------------------------------------------------- */
7144 #define MAX_NUM_CONVERT_LEVELS 1000
7146 void ConvertLevels()
7148 static LevelDirTree *convert_leveldir = NULL;
7149 static int convert_level_nr = -1;
7150 static int num_levels_handled = 0;
7151 static int num_levels_converted = 0;
7152 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
7155 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7156 global.convert_leveldir);
7158 if (convert_leveldir == NULL)
7159 Error(ERR_EXIT, "no such level identifier: '%s'",
7160 global.convert_leveldir);
7162 leveldir_current = convert_leveldir;
7164 if (global.convert_level_nr != -1)
7166 convert_leveldir->first_level = global.convert_level_nr;
7167 convert_leveldir->last_level = global.convert_level_nr;
7170 convert_level_nr = convert_leveldir->first_level;
7172 printf_line("=", 79);
7173 printf("Converting levels\n");
7174 printf_line("-", 79);
7175 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
7176 printf("Level series name: '%s'\n", convert_leveldir->name);
7177 printf("Level series author: '%s'\n", convert_leveldir->author);
7178 printf("Number of levels: %d\n", convert_leveldir->levels);
7179 printf_line("=", 79);
7182 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
7183 levels_failed[i] = FALSE;
7185 while (convert_level_nr <= convert_leveldir->last_level)
7187 char *level_filename;
7190 level_nr = convert_level_nr++;
7192 printf("Level %03d: ", level_nr);
7194 LoadLevel(level_nr);
7195 if (level.no_valid_file)
7197 printf("(no level)\n");
7201 printf("converting level ... ");
7203 level_filename = getDefaultLevelFilename(level_nr);
7204 new_level = !fileExists(level_filename);
7208 SaveLevel(level_nr);
7210 num_levels_converted++;
7212 printf("converted.\n");
7216 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
7217 levels_failed[level_nr] = TRUE;
7219 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
7222 num_levels_handled++;
7226 printf_line("=", 79);
7227 printf("Number of levels handled: %d\n", num_levels_handled);
7228 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
7229 (num_levels_handled ?
7230 num_levels_converted * 100 / num_levels_handled : 0));
7231 printf_line("-", 79);
7232 printf("Summary (for automatic parsing by scripts):\n");
7233 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
7234 convert_leveldir->identifier, num_levels_converted,
7236 (num_levels_handled ?
7237 num_levels_converted * 100 / num_levels_handled : 0));
7239 if (num_levels_handled != num_levels_converted)
7241 printf(", FAILED:");
7242 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
7243 if (levels_failed[i])
7248 printf_line("=", 79);