1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
25 #define ENABLE_UNUSED_CODE 0 /* currently unused functions */
26 #define ENABLE_HISTORIC_CHUNKS 0 /* only for historic reference */
27 #define ENABLE_RESERVED_CODE 0 /* reserved for later use */
29 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
30 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
31 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
33 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
34 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
36 #define LEVEL_CHUNK_VERS_SIZE 8 /* size of file version chunk */
37 #define LEVEL_CHUNK_DATE_SIZE 4 /* size of file date chunk */
38 #define LEVEL_CHUNK_HEAD_SIZE 80 /* size of level file header */
39 #define LEVEL_CHUNK_HEAD_UNUSED 0 /* unused level header bytes */
40 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
41 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
42 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
43 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
44 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
45 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
46 #define LEVEL_CHUNK_GRP1_SIZE 74 /* size of level GRP1 chunk */
48 /* (element number, number of change pages, change page number) */
49 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
51 /* (element number only) */
52 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
53 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
55 /* (nothing at all if unchanged) */
56 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
58 #define TAPE_CHUNK_VERS_SIZE 8 /* size of file version chunk */
59 #define TAPE_CHUNK_HEAD_SIZE 20 /* size of tape file header */
60 #define TAPE_CHUNK_HEAD_UNUSED 3 /* unused tape header bytes */
62 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
63 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
64 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
66 /* file identifier strings */
67 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
68 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
69 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
71 /* values for deciding when (not) to save configuration data */
72 #define SAVE_CONF_NEVER 0
73 #define SAVE_CONF_ALWAYS 1
74 #define SAVE_CONF_WHEN_CHANGED -1
76 /* values for chunks using micro chunks */
77 #define CONF_MASK_1_BYTE 0x00
78 #define CONF_MASK_2_BYTE 0x40
79 #define CONF_MASK_4_BYTE 0x80
80 #define CONF_MASK_MULTI_BYTES 0xc0
82 #define CONF_MASK_BYTES 0xc0
83 #define CONF_MASK_TOKEN 0x3f
85 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
86 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
87 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
88 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
90 /* these definitions are just for convenience of use and readability */
91 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
92 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
93 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
94 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
96 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
97 (x) == CONF_MASK_2_BYTE ? 2 : \
98 (x) == CONF_MASK_4_BYTE ? 4 : 0)
100 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
101 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
102 #define CONF_ELEMENT_NUM_BYTES (2)
104 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
105 (t) == TYPE_ELEMENT_LIST ? \
106 CONF_ELEMENT_NUM_BYTES : \
107 (t) == TYPE_CONTENT || \
108 (t) == TYPE_CONTENT_LIST ? \
109 CONF_CONTENT_NUM_BYTES : 1)
111 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
112 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
113 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
115 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
117 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
118 CONF_ELEMENT_NUM_BYTES)
119 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
120 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
122 /* temporary variables used to store pointers to structure members */
123 static struct LevelInfo li;
124 static struct ElementInfo xx_ei, yy_ei;
125 static struct ElementChangeInfo xx_change;
126 static struct ElementGroupInfo xx_group;
127 static struct EnvelopeInfo xx_envelope;
128 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
129 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
130 static int xx_num_contents;
131 static int xx_current_change_page;
132 static char xx_default_string_empty[1] = "";
133 static int xx_string_length_unused;
135 struct LevelFileConfigInfo
137 int element; /* element for which data is to be stored */
138 int save_type; /* save data always, never or when changed */
139 int data_type; /* data type (used internally, not stored) */
140 int conf_type; /* micro chunk identifier (stored in file) */
143 void *value; /* variable that holds the data to be stored */
144 int default_value; /* initial default value for this variable */
147 void *value_copy; /* variable that holds the data to be copied */
148 void *num_entities; /* number of entities for multi-byte data */
149 int default_num_entities; /* default number of entities for this data */
150 int max_num_entities; /* maximal number of entities for this data */
151 char *default_string; /* optional default string for string data */
154 static struct LevelFileConfigInfo chunk_config_INFO[] =
156 /* ---------- values not related to single elements ----------------------- */
159 -1, SAVE_CONF_ALWAYS,
160 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
161 &li.game_engine_type, GAME_ENGINE_TYPE_RND
165 -1, SAVE_CONF_ALWAYS,
166 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
167 &li.fieldx, STD_LEV_FIELDX
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
172 &li.fieldy, STD_LEV_FIELDY
176 -1, SAVE_CONF_ALWAYS,
177 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
182 -1, SAVE_CONF_ALWAYS,
183 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
189 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
201 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
202 &li.wind_direction_initial, MV_NONE
207 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
208 &li.em_slippery_gems, FALSE
213 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
214 &li.use_custom_template, FALSE
219 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
220 &li.can_move_into_acid_bits, ~0 /* default: everything can */
225 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
226 &li.dont_collide_with_bits, ~0 /* default: always deadly */
231 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
232 &li.em_explodes_by_fire, FALSE
237 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
238 &li.score[SC_TIME_BONUS], 1
243 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
244 &li.auto_exit_sokoban, FALSE
254 static struct LevelFileConfigInfo chunk_config_ELEM[] =
256 /* (these values are the same for each player) */
259 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
260 &li.block_last_field, FALSE /* default case for EM levels */
264 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
265 &li.sp_block_last_field, TRUE /* default case for SP levels */
269 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
270 &li.instant_relocation, FALSE
274 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
275 &li.can_pass_to_walkable, FALSE
279 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
280 &li.block_snap_field, TRUE
284 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
285 &li.continuous_snapping, TRUE
289 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
290 &li.shifted_relocation, FALSE
294 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
295 &li.lazy_relocation, FALSE
298 /* (these values are different for each player) */
301 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
302 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
306 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
307 &li.initial_player_gravity[0], FALSE
311 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
312 &li.use_start_element[0], FALSE
316 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
317 &li.start_element[0], EL_PLAYER_1
321 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
322 &li.use_artwork_element[0], FALSE
326 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
327 &li.artwork_element[0], EL_PLAYER_1
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
332 &li.use_explosion_element[0], FALSE
336 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
337 &li.explosion_element[0], EL_PLAYER_1
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
342 &li.use_initial_inventory[0], FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
347 &li.initial_inventory_size[0], 1
351 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
352 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
353 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
358 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
359 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
363 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
364 &li.initial_player_gravity[1], FALSE
368 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
369 &li.use_start_element[1], FALSE
373 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
374 &li.start_element[1], EL_PLAYER_2
378 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
379 &li.use_artwork_element[1], FALSE
383 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
384 &li.artwork_element[1], EL_PLAYER_2
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
389 &li.use_explosion_element[1], FALSE
393 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
394 &li.explosion_element[1], EL_PLAYER_2
398 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
399 &li.use_initial_inventory[1], FALSE
403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
404 &li.initial_inventory_size[1], 1
408 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
409 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
410 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
415 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
416 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
420 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
421 &li.initial_player_gravity[2], FALSE
425 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
426 &li.use_start_element[2], FALSE
430 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
431 &li.start_element[2], EL_PLAYER_3
435 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
436 &li.use_artwork_element[2], FALSE
440 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
441 &li.artwork_element[2], EL_PLAYER_3
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
446 &li.use_explosion_element[2], FALSE
450 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
451 &li.explosion_element[2], EL_PLAYER_3
455 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
456 &li.use_initial_inventory[2], FALSE
460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
461 &li.initial_inventory_size[2], 1
465 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
466 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
467 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
472 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
473 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
477 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
478 &li.initial_player_gravity[3], FALSE
482 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
483 &li.use_start_element[3], FALSE
487 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
488 &li.start_element[3], EL_PLAYER_4
492 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
493 &li.use_artwork_element[3], FALSE
497 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
498 &li.artwork_element[3], EL_PLAYER_4
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
503 &li.use_explosion_element[3], FALSE
507 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
508 &li.explosion_element[3], EL_PLAYER_4
512 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
513 &li.use_initial_inventory[3], FALSE
517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
518 &li.initial_inventory_size[3], 1
522 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
523 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
524 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
529 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
530 &li.score[SC_EMERALD], 10
535 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
536 &li.score[SC_DIAMOND], 10
541 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
542 &li.score[SC_BUG], 10
547 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
548 &li.score[SC_SPACESHIP], 10
553 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
554 &li.score[SC_PACMAN], 10
559 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
560 &li.score[SC_NUT], 10
565 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
566 &li.score[SC_DYNAMITE], 10
571 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
572 &li.score[SC_KEY], 10
577 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
578 &li.score[SC_PEARL], 10
583 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
584 &li.score[SC_CRYSTAL], 10
589 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
590 &li.amoeba_content, EL_DIAMOND
594 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
599 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
600 &li.grow_into_diggable, TRUE
605 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
606 &li.yamyam_content, EL_ROCK, NULL,
607 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
611 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
612 &li.score[SC_YAMYAM], 10
617 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
618 &li.score[SC_ROBOT], 10
622 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
628 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
634 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
635 &li.time_magic_wall, 10
640 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
641 &li.game_of_life[0], 2
645 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
646 &li.game_of_life[1], 3
650 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
651 &li.game_of_life[2], 3
655 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
656 &li.game_of_life[3], 3
661 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
666 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
671 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
676 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
681 EL_TIMEGATE_SWITCH, -1,
682 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
683 &li.time_timegate, 10
687 EL_LIGHT_SWITCH_ACTIVE, -1,
688 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
693 EL_SHIELD_NORMAL, -1,
694 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
695 &li.shield_normal_time, 10
698 EL_SHIELD_NORMAL, -1,
699 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
700 &li.score[SC_SHIELD], 10
704 EL_SHIELD_DEADLY, -1,
705 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
706 &li.shield_deadly_time, 10
709 EL_SHIELD_DEADLY, -1,
710 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
711 &li.score[SC_SHIELD], 10
716 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
721 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
722 &li.extra_time_score, 10
726 EL_TIME_ORB_FULL, -1,
727 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
728 &li.time_orb_time, 10
731 EL_TIME_ORB_FULL, -1,
732 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
733 &li.use_time_orb_bug, FALSE
738 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
739 &li.use_spring_bug, FALSE
744 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
745 &li.android_move_time, 10
749 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
750 &li.android_clone_time, 10
754 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
755 &li.android_clone_element[0], EL_EMPTY, NULL,
756 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
761 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
766 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
771 EL_EMC_MAGNIFIER, -1,
772 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
773 &li.magnify_score, 10
776 EL_EMC_MAGNIFIER, -1,
777 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
782 EL_EMC_MAGIC_BALL, -1,
783 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
787 EL_EMC_MAGIC_BALL, -1,
788 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
789 &li.ball_random, FALSE
792 EL_EMC_MAGIC_BALL, -1,
793 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
794 &li.ball_state_initial, FALSE
797 EL_EMC_MAGIC_BALL, -1,
798 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
799 &li.ball_content, EL_EMPTY, NULL,
800 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
803 /* ---------- unused values ----------------------------------------------- */
806 EL_UNKNOWN, SAVE_CONF_NEVER,
807 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
808 &li.score[SC_UNKNOWN_14], 10
811 EL_UNKNOWN, SAVE_CONF_NEVER,
812 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
813 &li.score[SC_UNKNOWN_15], 10
823 static struct LevelFileConfigInfo chunk_config_NOTE[] =
827 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
828 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
832 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
833 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
838 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
839 &xx_envelope.autowrap, FALSE
843 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
844 &xx_envelope.centered, FALSE
849 TYPE_STRING, CONF_VALUE_BYTES(1),
850 &xx_envelope.text, -1, NULL,
851 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
852 &xx_default_string_empty[0]
862 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
866 TYPE_STRING, CONF_VALUE_BYTES(1),
867 &xx_ei.description[0], -1,
868 &yy_ei.description[0],
869 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
870 &xx_default_description[0]
875 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
876 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
877 &yy_ei.properties[EP_BITFIELD_BASE_NR]
879 #if ENABLE_RESERVED_CODE
880 /* (reserved for later use) */
883 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
884 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
885 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
891 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
892 &xx_ei.use_gfx_element, FALSE,
893 &yy_ei.use_gfx_element
897 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
898 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
899 &yy_ei.gfx_element_initial
904 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
905 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
906 &yy_ei.access_direction
911 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
912 &xx_ei.collect_score_initial, 10,
913 &yy_ei.collect_score_initial
917 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
918 &xx_ei.collect_count_initial, 1,
919 &yy_ei.collect_count_initial
924 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
925 &xx_ei.ce_value_fixed_initial, 0,
926 &yy_ei.ce_value_fixed_initial
930 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
931 &xx_ei.ce_value_random_initial, 0,
932 &yy_ei.ce_value_random_initial
936 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
937 &xx_ei.use_last_ce_value, FALSE,
938 &yy_ei.use_last_ce_value
943 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
944 &xx_ei.push_delay_fixed, 8,
945 &yy_ei.push_delay_fixed
949 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
950 &xx_ei.push_delay_random, 8,
951 &yy_ei.push_delay_random
955 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
956 &xx_ei.drop_delay_fixed, 0,
957 &yy_ei.drop_delay_fixed
961 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
962 &xx_ei.drop_delay_random, 0,
963 &yy_ei.drop_delay_random
967 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
968 &xx_ei.move_delay_fixed, 0,
969 &yy_ei.move_delay_fixed
973 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
974 &xx_ei.move_delay_random, 0,
975 &yy_ei.move_delay_random
980 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
981 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
986 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
987 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
988 &yy_ei.move_direction_initial
992 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
993 &xx_ei.move_stepsize, TILEX / 8,
999 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1000 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1001 &yy_ei.move_enter_element
1005 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1006 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1007 &yy_ei.move_leave_element
1011 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1012 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1013 &yy_ei.move_leave_type
1018 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1019 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1020 &yy_ei.slippery_type
1025 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1026 &xx_ei.explosion_type, EXPLODES_3X3,
1027 &yy_ei.explosion_type
1031 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1032 &xx_ei.explosion_delay, 16,
1033 &yy_ei.explosion_delay
1037 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1038 &xx_ei.ignition_delay, 8,
1039 &yy_ei.ignition_delay
1044 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1045 &xx_ei.content, EL_EMPTY_SPACE,
1047 &xx_num_contents, 1, 1
1050 /* ---------- "num_change_pages" must be the last entry ------------------- */
1053 -1, SAVE_CONF_ALWAYS,
1054 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1055 &xx_ei.num_change_pages, 1,
1056 &yy_ei.num_change_pages
1067 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1069 /* ---------- "current_change_page" must be the first entry --------------- */
1072 -1, SAVE_CONF_ALWAYS,
1073 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1074 &xx_current_change_page, -1
1077 /* ---------- (the remaining entries can be in any order) ----------------- */
1081 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1082 &xx_change.can_change, FALSE
1087 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1088 &xx_event_bits[0], 0
1092 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1093 &xx_event_bits[1], 0
1098 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1099 &xx_change.trigger_player, CH_PLAYER_ANY
1103 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1104 &xx_change.trigger_side, CH_SIDE_ANY
1108 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1109 &xx_change.trigger_page, CH_PAGE_ANY
1114 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1115 &xx_change.target_element, EL_EMPTY_SPACE
1120 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1121 &xx_change.delay_fixed, 0
1125 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1126 &xx_change.delay_random, 0
1130 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1131 &xx_change.delay_frames, FRAMES_PER_SECOND
1136 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1137 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1142 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1143 &xx_change.explode, FALSE
1147 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1148 &xx_change.use_target_content, FALSE
1152 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1153 &xx_change.only_if_complete, FALSE
1157 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1158 &xx_change.use_random_replace, FALSE
1162 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1163 &xx_change.random_percentage, 100
1167 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1168 &xx_change.replace_when, CP_WHEN_EMPTY
1173 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1174 &xx_change.has_action, FALSE
1178 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1179 &xx_change.action_type, CA_NO_ACTION
1183 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1184 &xx_change.action_mode, CA_MODE_UNDEFINED
1188 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1189 &xx_change.action_arg, CA_ARG_UNDEFINED
1194 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1195 &xx_change.action_element, EL_EMPTY_SPACE
1200 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1201 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1202 &xx_num_contents, 1, 1
1212 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1216 TYPE_STRING, CONF_VALUE_BYTES(1),
1217 &xx_ei.description[0], -1, NULL,
1218 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1219 &xx_default_description[0]
1224 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1225 &xx_ei.use_gfx_element, FALSE
1229 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1230 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1235 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1236 &xx_group.choice_mode, ANIM_RANDOM
1241 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1242 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1243 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1253 static struct LevelFileConfigInfo chunk_config_CONF[] = /* (OBSOLETE) */
1257 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1258 &li.block_snap_field, TRUE
1262 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1263 &li.continuous_snapping, TRUE
1267 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1268 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1273 &li.use_start_element[0], FALSE
1277 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1278 &li.start_element[0], EL_PLAYER_1
1282 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1283 &li.use_artwork_element[0], FALSE
1287 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1288 &li.artwork_element[0], EL_PLAYER_1
1292 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1293 &li.use_explosion_element[0], FALSE
1297 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1298 &li.explosion_element[0], EL_PLAYER_1
1313 filetype_id_list[] =
1315 { LEVEL_FILE_TYPE_RND, "RND" },
1316 { LEVEL_FILE_TYPE_BD, "BD" },
1317 { LEVEL_FILE_TYPE_EM, "EM" },
1318 { LEVEL_FILE_TYPE_SP, "SP" },
1319 { LEVEL_FILE_TYPE_DX, "DX" },
1320 { LEVEL_FILE_TYPE_SB, "SB" },
1321 { LEVEL_FILE_TYPE_DC, "DC" },
1322 { LEVEL_FILE_TYPE_MM, "MM" },
1323 { LEVEL_FILE_TYPE_MM, "DF" },
1328 /* ========================================================================= */
1329 /* level file functions */
1330 /* ========================================================================= */
1332 static boolean check_special_flags(char *flag)
1334 if (strEqual(options.special_flags, flag) ||
1335 strEqual(leveldir_current->special_flags, flag))
1341 static struct DateInfo getCurrentDate()
1343 time_t epoch_seconds = time(NULL);
1344 struct tm *now = localtime(&epoch_seconds);
1345 struct DateInfo date;
1347 date.year = now->tm_year + 1900;
1348 date.month = now->tm_mon + 1;
1349 date.day = now->tm_mday;
1351 date.src = DATE_SRC_CLOCK;
1356 static void resetEventFlags(struct ElementChangeInfo *change)
1360 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1361 change->has_event[i] = FALSE;
1364 static void resetEventBits()
1368 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1369 xx_event_bits[i] = 0;
1372 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1376 /* important: only change event flag if corresponding event bit is set
1377 (this is because all xx_event_bits[] values are loaded separately,
1378 and all xx_event_bits[] values are set back to zero before loading
1379 another value xx_event_bits[x] (each value representing 32 flags)) */
1381 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1382 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1383 change->has_event[i] = TRUE;
1386 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1390 /* in contrast to the above function setEventFlagsFromEventBits(), it
1391 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1392 depending on the corresponding change->has_event[i] values here, as
1393 all xx_event_bits[] values are reset in resetEventBits() before */
1395 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1396 if (change->has_event[i])
1397 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1400 static char *getDefaultElementDescription(struct ElementInfo *ei)
1402 static char description[MAX_ELEMENT_NAME_LEN + 1];
1403 char *default_description = (ei->custom_description != NULL ?
1404 ei->custom_description :
1405 ei->editor_description);
1408 /* always start with reliable default values */
1409 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1410 description[i] = '\0';
1412 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1413 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1415 return &description[0];
1418 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1420 char *default_description = getDefaultElementDescription(ei);
1423 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1424 ei->description[i] = default_description[i];
1427 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1431 for (i = 0; conf[i].data_type != -1; i++)
1433 int default_value = conf[i].default_value;
1434 int data_type = conf[i].data_type;
1435 int conf_type = conf[i].conf_type;
1436 int byte_mask = conf_type & CONF_MASK_BYTES;
1438 if (byte_mask == CONF_MASK_MULTI_BYTES)
1440 int default_num_entities = conf[i].default_num_entities;
1441 int max_num_entities = conf[i].max_num_entities;
1443 *(int *)(conf[i].num_entities) = default_num_entities;
1445 if (data_type == TYPE_STRING)
1447 char *default_string = conf[i].default_string;
1448 char *string = (char *)(conf[i].value);
1450 strncpy(string, default_string, max_num_entities);
1452 else if (data_type == TYPE_ELEMENT_LIST)
1454 int *element_array = (int *)(conf[i].value);
1457 for (j = 0; j < max_num_entities; j++)
1458 element_array[j] = default_value;
1460 else if (data_type == TYPE_CONTENT_LIST)
1462 struct Content *content = (struct Content *)(conf[i].value);
1465 for (c = 0; c < max_num_entities; c++)
1466 for (y = 0; y < 3; y++)
1467 for (x = 0; x < 3; x++)
1468 content[c].e[x][y] = default_value;
1471 else /* constant size configuration data (1, 2 or 4 bytes) */
1473 if (data_type == TYPE_BOOLEAN)
1474 *(boolean *)(conf[i].value) = default_value;
1476 *(int *) (conf[i].value) = default_value;
1481 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1485 for (i = 0; conf[i].data_type != -1; i++)
1487 int data_type = conf[i].data_type;
1488 int conf_type = conf[i].conf_type;
1489 int byte_mask = conf_type & CONF_MASK_BYTES;
1491 if (byte_mask == CONF_MASK_MULTI_BYTES)
1493 int max_num_entities = conf[i].max_num_entities;
1495 if (data_type == TYPE_STRING)
1497 char *string = (char *)(conf[i].value);
1498 char *string_copy = (char *)(conf[i].value_copy);
1500 strncpy(string_copy, string, max_num_entities);
1502 else if (data_type == TYPE_ELEMENT_LIST)
1504 int *element_array = (int *)(conf[i].value);
1505 int *element_array_copy = (int *)(conf[i].value_copy);
1508 for (j = 0; j < max_num_entities; j++)
1509 element_array_copy[j] = element_array[j];
1511 else if (data_type == TYPE_CONTENT_LIST)
1513 struct Content *content = (struct Content *)(conf[i].value);
1514 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1517 for (c = 0; c < max_num_entities; c++)
1518 for (y = 0; y < 3; y++)
1519 for (x = 0; x < 3; x++)
1520 content_copy[c].e[x][y] = content[c].e[x][y];
1523 else /* constant size configuration data (1, 2 or 4 bytes) */
1525 if (data_type == TYPE_BOOLEAN)
1526 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1528 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1533 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1537 xx_ei = *ei_from; /* copy element data into temporary buffer */
1538 yy_ei = *ei_to; /* copy element data into temporary buffer */
1540 copyConfigFromConfigList(chunk_config_CUSX_base);
1545 /* ---------- reinitialize and copy change pages ---------- */
1547 ei_to->num_change_pages = ei_from->num_change_pages;
1548 ei_to->current_change_page = ei_from->current_change_page;
1550 setElementChangePages(ei_to, ei_to->num_change_pages);
1552 for (i = 0; i < ei_to->num_change_pages; i++)
1553 ei_to->change_page[i] = ei_from->change_page[i];
1555 /* ---------- copy group element info ---------- */
1556 if (ei_from->group != NULL && ei_to->group != NULL) /* group or internal */
1557 *ei_to->group = *ei_from->group;
1559 /* mark this custom element as modified */
1560 ei_to->modified_settings = TRUE;
1563 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1565 int change_page_size = sizeof(struct ElementChangeInfo);
1567 ei->num_change_pages = MAX(1, change_pages);
1570 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1572 if (ei->current_change_page >= ei->num_change_pages)
1573 ei->current_change_page = ei->num_change_pages - 1;
1575 ei->change = &ei->change_page[ei->current_change_page];
1578 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1580 xx_change = *change; /* copy change data into temporary buffer */
1582 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1584 *change = xx_change;
1586 resetEventFlags(change);
1588 change->direct_action = 0;
1589 change->other_action = 0;
1591 change->pre_change_function = NULL;
1592 change->change_function = NULL;
1593 change->post_change_function = NULL;
1596 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1600 li = *level; /* copy level data into temporary buffer */
1601 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1602 *level = li; /* copy temporary buffer back to level data */
1604 setLevelInfoToDefaults_EM();
1605 setLevelInfoToDefaults_SP();
1606 setLevelInfoToDefaults_MM();
1608 level->native_em_level = &native_em_level;
1609 level->native_sp_level = &native_sp_level;
1610 level->native_mm_level = &native_mm_level;
1612 level->file_version = FILE_VERSION_ACTUAL;
1613 level->game_version = GAME_VERSION_ACTUAL;
1615 level->creation_date = getCurrentDate();
1617 level->encoding_16bit_field = TRUE;
1618 level->encoding_16bit_yamyam = TRUE;
1619 level->encoding_16bit_amoeba = TRUE;
1621 /* clear level name and level author string buffers */
1622 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1623 level->name[i] = '\0';
1624 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1625 level->author[i] = '\0';
1627 /* set level name and level author to default values */
1628 strcpy(level->name, NAMELESS_LEVEL_NAME);
1629 strcpy(level->author, ANONYMOUS_NAME);
1631 /* set level playfield to playable default level with player and exit */
1632 for (x = 0; x < MAX_LEV_FIELDX; x++)
1633 for (y = 0; y < MAX_LEV_FIELDY; y++)
1634 level->field[x][y] = EL_SAND;
1636 level->field[0][0] = EL_PLAYER_1;
1637 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1639 BorderElement = EL_STEELWALL;
1641 /* detect custom elements when loading them */
1642 level->file_has_custom_elements = FALSE;
1644 /* set all bug compatibility flags to "false" => do not emulate this bug */
1645 level->use_action_after_change_bug = FALSE;
1647 if (leveldir_current)
1649 /* try to determine better author name than 'anonymous' */
1650 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1652 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1653 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1657 switch (LEVELCLASS(leveldir_current))
1659 case LEVELCLASS_TUTORIAL:
1660 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1663 case LEVELCLASS_CONTRIB:
1664 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1665 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1668 case LEVELCLASS_PRIVATE:
1669 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1670 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1674 /* keep default value */
1681 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1683 static boolean clipboard_elements_initialized = FALSE;
1686 InitElementPropertiesStatic();
1688 li = *level; /* copy level data into temporary buffer */
1689 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1690 *level = li; /* copy temporary buffer back to level data */
1692 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1695 struct ElementInfo *ei = &element_info[element];
1697 /* never initialize clipboard elements after the very first time */
1698 /* (to be able to use clipboard elements between several levels) */
1699 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1702 if (IS_ENVELOPE(element))
1704 int envelope_nr = element - EL_ENVELOPE_1;
1706 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1708 level->envelope[envelope_nr] = xx_envelope;
1711 if (IS_CUSTOM_ELEMENT(element) ||
1712 IS_GROUP_ELEMENT(element) ||
1713 IS_INTERNAL_ELEMENT(element))
1715 xx_ei = *ei; /* copy element data into temporary buffer */
1717 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1722 setElementChangePages(ei, 1);
1723 setElementChangeInfoToDefaults(ei->change);
1725 if (IS_CUSTOM_ELEMENT(element) ||
1726 IS_GROUP_ELEMENT(element) ||
1727 IS_INTERNAL_ELEMENT(element))
1729 setElementDescriptionToDefault(ei);
1731 ei->modified_settings = FALSE;
1734 if (IS_CUSTOM_ELEMENT(element) ||
1735 IS_INTERNAL_ELEMENT(element))
1737 /* internal values used in level editor */
1739 ei->access_type = 0;
1740 ei->access_layer = 0;
1741 ei->access_protected = 0;
1742 ei->walk_to_action = 0;
1743 ei->smash_targets = 0;
1746 ei->can_explode_by_fire = FALSE;
1747 ei->can_explode_smashed = FALSE;
1748 ei->can_explode_impact = FALSE;
1750 ei->current_change_page = 0;
1753 if (IS_GROUP_ELEMENT(element) ||
1754 IS_INTERNAL_ELEMENT(element))
1756 struct ElementGroupInfo *group;
1758 /* initialize memory for list of elements in group */
1759 if (ei->group == NULL)
1760 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1764 xx_group = *group; /* copy group data into temporary buffer */
1766 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1772 clipboard_elements_initialized = TRUE;
1775 static void setLevelInfoToDefaults(struct LevelInfo *level,
1776 boolean level_info_only,
1777 boolean reset_file_status)
1779 setLevelInfoToDefaults_Level(level);
1781 if (!level_info_only)
1782 setLevelInfoToDefaults_Elements(level);
1784 if (reset_file_status)
1786 level->no_valid_file = FALSE;
1787 level->no_level_file = FALSE;
1790 level->changed = FALSE;
1793 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1795 level_file_info->nr = 0;
1796 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1797 level_file_info->packed = FALSE;
1798 level_file_info->basename = NULL;
1799 level_file_info->filename = NULL;
1802 int getMappedElement_SB(int, boolean);
1804 static void ActivateLevelTemplate()
1808 if (check_special_flags("load_xsb_to_ces"))
1810 /* fill smaller playfields with padding "beyond border wall" elements */
1811 if (level.fieldx < level_template.fieldx ||
1812 level.fieldy < level_template.fieldy)
1814 short field[level.fieldx][level.fieldy];
1815 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1816 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1817 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1818 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1820 /* copy old playfield (which is smaller than the visible area) */
1821 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1822 field[x][y] = level.field[x][y];
1824 /* fill new, larger playfield with "beyond border wall" elements */
1825 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1826 level.field[x][y] = getMappedElement_SB('_', TRUE);
1828 /* copy the old playfield to the middle of the new playfield */
1829 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1830 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1832 level.fieldx = new_fieldx;
1833 level.fieldy = new_fieldy;
1837 /* Currently there is no special action needed to activate the template
1838 data, because 'element_info' property settings overwrite the original
1839 level data, while all other variables do not change. */
1841 /* Exception: 'from_level_template' elements in the original level playfield
1842 are overwritten with the corresponding elements at the same position in
1843 playfield from the level template. */
1845 for (x = 0; x < level.fieldx; x++)
1846 for (y = 0; y < level.fieldy; y++)
1847 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1848 level.field[x][y] = level_template.field[x][y];
1850 if (check_special_flags("load_xsb_to_ces"))
1852 struct LevelInfo level_backup = level;
1854 /* overwrite all individual level settings from template level settings */
1855 level = level_template;
1857 /* restore playfield size */
1858 level.fieldx = level_backup.fieldx;
1859 level.fieldy = level_backup.fieldy;
1861 /* restore playfield content */
1862 for (x = 0; x < level.fieldx; x++)
1863 for (y = 0; y < level.fieldy; y++)
1864 level.field[x][y] = level_backup.field[x][y];
1866 /* restore name and author from individual level */
1867 strcpy(level.name, level_backup.name);
1868 strcpy(level.author, level_backup.author);
1870 /* restore flag "use_custom_template" */
1871 level.use_custom_template = level_backup.use_custom_template;
1875 static char *getLevelFilenameFromBasename(char *basename)
1877 static char *filename[2] = { NULL, NULL };
1878 int pos = (strEqual(basename, LEVELTEMPLATE_FILENAME) ? 0 : 1);
1880 checked_free(filename[pos]);
1882 filename[pos] = getPath2(getCurrentLevelDir(), basename);
1884 return filename[pos];
1887 static int getFileTypeFromBasename(char *basename)
1889 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1891 static char *filename = NULL;
1892 struct stat file_status;
1894 /* ---------- try to determine file type from filename ---------- */
1896 /* check for typical filename of a Supaplex level package file */
1897 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1898 return LEVEL_FILE_TYPE_SP;
1900 /* check for typical filename of a Diamond Caves II level package file */
1901 if (strSuffixLower(basename, ".dc") ||
1902 strSuffixLower(basename, ".dc2"))
1903 return LEVEL_FILE_TYPE_DC;
1905 /* check for typical filename of a Sokoban level package file */
1906 if (strSuffixLower(basename, ".xsb") &&
1907 strchr(basename, '%') == NULL)
1908 return LEVEL_FILE_TYPE_SB;
1910 /* ---------- try to determine file type from filesize ---------- */
1912 checked_free(filename);
1913 filename = getPath2(getCurrentLevelDir(), basename);
1915 if (stat(filename, &file_status) == 0)
1917 /* check for typical filesize of a Supaplex level package file */
1918 if (file_status.st_size == 170496)
1919 return LEVEL_FILE_TYPE_SP;
1922 return LEVEL_FILE_TYPE_UNKNOWN;
1925 static int getFileTypeFromMagicBytes(char *filename, int type)
1929 if ((file = openFile(filename, MODE_READ)))
1931 char chunk_name[CHUNK_ID_LEN + 1];
1933 getFileChunkBE(file, chunk_name, NULL);
1935 if (strEqual(chunk_name, "MMII") ||
1936 strEqual(chunk_name, "MIRR"))
1937 type = LEVEL_FILE_TYPE_MM;
1945 static boolean checkForPackageFromBasename(char *basename)
1947 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
1948 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
1950 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
1953 static char *getSingleLevelBasenameExt(int nr, char *extension)
1955 static char basename[MAX_FILENAME_LEN];
1958 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
1960 sprintf(basename, "%03d.%s", nr, extension);
1965 static char *getSingleLevelBasename(int nr)
1967 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
1970 static char *getPackedLevelBasename(int type)
1972 static char basename[MAX_FILENAME_LEN];
1973 char *directory = getCurrentLevelDir();
1975 DirectoryEntry *dir_entry;
1977 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
1979 if ((dir = openDirectory(directory)) == NULL)
1981 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
1986 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
1988 char *entry_basename = dir_entry->basename;
1989 int entry_type = getFileTypeFromBasename(entry_basename);
1991 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
1993 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
1996 strcpy(basename, entry_basename);
2003 closeDirectory(dir);
2008 static char *getSingleLevelFilename(int nr)
2010 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2013 #if ENABLE_UNUSED_CODE
2014 static char *getPackedLevelFilename(int type)
2016 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2020 char *getDefaultLevelFilename(int nr)
2022 return getSingleLevelFilename(nr);
2025 #if ENABLE_UNUSED_CODE
2026 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2030 lfi->packed = FALSE;
2031 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
2032 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2036 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2037 int type, char *format, ...)
2039 static char basename[MAX_FILENAME_LEN];
2042 va_start(ap, format);
2043 vsprintf(basename, format, ap);
2047 lfi->packed = FALSE;
2048 lfi->basename = basename;
2049 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2052 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2057 lfi->basename = getPackedLevelBasename(lfi->type);
2058 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2061 static int getFiletypeFromID(char *filetype_id)
2063 char *filetype_id_lower;
2064 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2067 if (filetype_id == NULL)
2068 return LEVEL_FILE_TYPE_UNKNOWN;
2070 filetype_id_lower = getStringToLower(filetype_id);
2072 for (i = 0; filetype_id_list[i].id != NULL; i++)
2074 char *id_lower = getStringToLower(filetype_id_list[i].id);
2076 if (strEqual(filetype_id_lower, id_lower))
2077 filetype = filetype_id_list[i].filetype;
2081 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2085 free(filetype_id_lower);
2090 char *getLocalLevelTemplateFilename()
2092 return getDefaultLevelFilename(-1);
2095 char *getGlobalLevelTemplateFilename()
2097 /* global variable "leveldir_current" must be modified in the loop below */
2098 LevelDirTree *leveldir_current_last = leveldir_current;
2099 char *filename = NULL;
2101 /* check for template level in path from current to topmost tree node */
2103 while (leveldir_current != NULL)
2105 filename = getDefaultLevelFilename(-1);
2107 if (fileExists(filename))
2110 leveldir_current = leveldir_current->node_parent;
2113 /* restore global variable "leveldir_current" modified in above loop */
2114 leveldir_current = leveldir_current_last;
2119 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2123 /* special case: level number is negative => check for level template file */
2126 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2127 getSingleLevelBasename(-1));
2129 /* replace local level template filename with global template filename */
2130 lfi->filename = getGlobalLevelTemplateFilename();
2132 /* no fallback if template file not existing */
2136 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2137 if (leveldir_current->level_filename != NULL)
2139 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2141 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2142 leveldir_current->level_filename, nr);
2144 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2146 if (fileExists(lfi->filename))
2149 else if (leveldir_current->level_filetype != NULL)
2151 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2153 /* check for specified native level file with standard file name */
2154 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2155 "%03d.%s", nr, LEVELFILE_EXTENSION);
2156 if (fileExists(lfi->filename))
2160 /* check for native Rocks'n'Diamonds level file */
2161 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2162 "%03d.%s", nr, LEVELFILE_EXTENSION);
2163 if (fileExists(lfi->filename))
2166 /* check for Emerald Mine level file (V1) */
2167 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2168 'a' + (nr / 10) % 26, '0' + nr % 10);
2169 if (fileExists(lfi->filename))
2171 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2172 'A' + (nr / 10) % 26, '0' + nr % 10);
2173 if (fileExists(lfi->filename))
2176 /* check for Emerald Mine level file (V2 to V5) */
2177 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2178 if (fileExists(lfi->filename))
2181 /* check for Emerald Mine level file (V6 / single mode) */
2182 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2183 if (fileExists(lfi->filename))
2185 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2186 if (fileExists(lfi->filename))
2189 /* check for Emerald Mine level file (V6 / teamwork mode) */
2190 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2191 if (fileExists(lfi->filename))
2193 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2194 if (fileExists(lfi->filename))
2197 /* check for various packed level file formats */
2198 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2199 if (fileExists(lfi->filename))
2202 /* no known level file found -- use default values (and fail later) */
2203 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2204 "%03d.%s", nr, LEVELFILE_EXTENSION);
2207 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2209 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2210 lfi->type = getFileTypeFromBasename(lfi->basename);
2212 if (lfi->type == LEVEL_FILE_TYPE_RND)
2213 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2216 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2218 /* always start with reliable default values */
2219 setFileInfoToDefaults(level_file_info);
2221 level_file_info->nr = nr; /* set requested level number */
2223 determineLevelFileInfo_Filename(level_file_info);
2224 determineLevelFileInfo_Filetype(level_file_info);
2227 /* ------------------------------------------------------------------------- */
2228 /* functions for loading R'n'D level */
2229 /* ------------------------------------------------------------------------- */
2231 int getMappedElement(int element)
2233 /* remap some (historic, now obsolete) elements */
2237 case EL_PLAYER_OBSOLETE:
2238 element = EL_PLAYER_1;
2241 case EL_KEY_OBSOLETE:
2245 case EL_EM_KEY_1_FILE_OBSOLETE:
2246 element = EL_EM_KEY_1;
2249 case EL_EM_KEY_2_FILE_OBSOLETE:
2250 element = EL_EM_KEY_2;
2253 case EL_EM_KEY_3_FILE_OBSOLETE:
2254 element = EL_EM_KEY_3;
2257 case EL_EM_KEY_4_FILE_OBSOLETE:
2258 element = EL_EM_KEY_4;
2261 case EL_ENVELOPE_OBSOLETE:
2262 element = EL_ENVELOPE_1;
2270 if (element >= NUM_FILE_ELEMENTS)
2272 Error(ERR_WARN, "invalid level element %d", element);
2274 element = EL_UNKNOWN;
2282 int getMappedElementByVersion(int element, int game_version)
2284 /* remap some elements due to certain game version */
2286 if (game_version <= VERSION_IDENT(2,2,0,0))
2288 /* map game font elements */
2289 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2290 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2291 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2292 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2295 if (game_version < VERSION_IDENT(3,0,0,0))
2297 /* map Supaplex gravity tube elements */
2298 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2299 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2300 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2301 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2308 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2310 level->file_version = getFileVersion(file);
2311 level->game_version = getFileVersion(file);
2316 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2318 level->creation_date.year = getFile16BitBE(file);
2319 level->creation_date.month = getFile8Bit(file);
2320 level->creation_date.day = getFile8Bit(file);
2322 level->creation_date.src = DATE_SRC_LEVELFILE;
2327 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2329 int initial_player_stepsize;
2330 int initial_player_gravity;
2333 level->fieldx = getFile8Bit(file);
2334 level->fieldy = getFile8Bit(file);
2336 level->time = getFile16BitBE(file);
2337 level->gems_needed = getFile16BitBE(file);
2339 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2340 level->name[i] = getFile8Bit(file);
2341 level->name[MAX_LEVEL_NAME_LEN] = 0;
2343 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2344 level->score[i] = getFile8Bit(file);
2346 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2347 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2348 for (y = 0; y < 3; y++)
2349 for (x = 0; x < 3; x++)
2350 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2352 level->amoeba_speed = getFile8Bit(file);
2353 level->time_magic_wall = getFile8Bit(file);
2354 level->time_wheel = getFile8Bit(file);
2355 level->amoeba_content = getMappedElement(getFile8Bit(file));
2357 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2360 for (i = 0; i < MAX_PLAYERS; i++)
2361 level->initial_player_stepsize[i] = initial_player_stepsize;
2363 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2365 for (i = 0; i < MAX_PLAYERS; i++)
2366 level->initial_player_gravity[i] = initial_player_gravity;
2368 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2369 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2371 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2373 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2374 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2375 level->can_move_into_acid_bits = getFile32BitBE(file);
2376 level->dont_collide_with_bits = getFile8Bit(file);
2378 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2379 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2381 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2382 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2383 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2385 level->game_engine_type = getFile8Bit(file);
2387 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2392 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2396 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2397 level->name[i] = getFile8Bit(file);
2398 level->name[MAX_LEVEL_NAME_LEN] = 0;
2403 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2407 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2408 level->author[i] = getFile8Bit(file);
2409 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2414 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2417 int chunk_size_expected = level->fieldx * level->fieldy;
2419 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2420 stored with 16-bit encoding (and should be twice as big then).
2421 Even worse, playfield data was stored 16-bit when only yamyam content
2422 contained 16-bit elements and vice versa. */
2424 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2425 chunk_size_expected *= 2;
2427 if (chunk_size_expected != chunk_size)
2429 ReadUnusedBytesFromFile(file, chunk_size);
2430 return chunk_size_expected;
2433 for (y = 0; y < level->fieldy; y++)
2434 for (x = 0; x < level->fieldx; x++)
2435 level->field[x][y] =
2436 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2441 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2444 int header_size = 4;
2445 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2446 int chunk_size_expected = header_size + content_size;
2448 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2449 stored with 16-bit encoding (and should be twice as big then).
2450 Even worse, playfield data was stored 16-bit when only yamyam content
2451 contained 16-bit elements and vice versa. */
2453 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2454 chunk_size_expected += content_size;
2456 if (chunk_size_expected != chunk_size)
2458 ReadUnusedBytesFromFile(file, chunk_size);
2459 return chunk_size_expected;
2463 level->num_yamyam_contents = getFile8Bit(file);
2467 /* correct invalid number of content fields -- should never happen */
2468 if (level->num_yamyam_contents < 1 ||
2469 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2470 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2472 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2473 for (y = 0; y < 3; y++)
2474 for (x = 0; x < 3; x++)
2475 level->yamyam_content[i].e[x][y] =
2476 getMappedElement(level->encoding_16bit_field ?
2477 getFile16BitBE(file) : getFile8Bit(file));
2481 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2486 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2488 element = getMappedElement(getFile16BitBE(file));
2489 num_contents = getFile8Bit(file);
2491 getFile8Bit(file); /* content x size (unused) */
2492 getFile8Bit(file); /* content y size (unused) */
2494 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2496 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2497 for (y = 0; y < 3; y++)
2498 for (x = 0; x < 3; x++)
2499 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2501 /* correct invalid number of content fields -- should never happen */
2502 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2503 num_contents = STD_ELEMENT_CONTENTS;
2505 if (element == EL_YAMYAM)
2507 level->num_yamyam_contents = num_contents;
2509 for (i = 0; i < num_contents; i++)
2510 for (y = 0; y < 3; y++)
2511 for (x = 0; x < 3; x++)
2512 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2514 else if (element == EL_BD_AMOEBA)
2516 level->amoeba_content = content_array[0][0][0];
2520 Error(ERR_WARN, "cannot load content for element '%d'", element);
2526 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2532 int chunk_size_expected;
2534 element = getMappedElement(getFile16BitBE(file));
2535 if (!IS_ENVELOPE(element))
2536 element = EL_ENVELOPE_1;
2538 envelope_nr = element - EL_ENVELOPE_1;
2540 envelope_len = getFile16BitBE(file);
2542 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2543 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2545 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2547 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2548 if (chunk_size_expected != chunk_size)
2550 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2551 return chunk_size_expected;
2554 for (i = 0; i < envelope_len; i++)
2555 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2560 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2562 int num_changed_custom_elements = getFile16BitBE(file);
2563 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2566 if (chunk_size_expected != chunk_size)
2568 ReadUnusedBytesFromFile(file, chunk_size - 2);
2569 return chunk_size_expected;
2572 for (i = 0; i < num_changed_custom_elements; i++)
2574 int element = getMappedElement(getFile16BitBE(file));
2575 int properties = getFile32BitBE(file);
2577 if (IS_CUSTOM_ELEMENT(element))
2578 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2580 Error(ERR_WARN, "invalid custom element number %d", element);
2582 /* older game versions that wrote level files with CUS1 chunks used
2583 different default push delay values (not yet stored in level file) */
2584 element_info[element].push_delay_fixed = 2;
2585 element_info[element].push_delay_random = 8;
2588 level->file_has_custom_elements = TRUE;
2593 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2595 int num_changed_custom_elements = getFile16BitBE(file);
2596 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2599 if (chunk_size_expected != chunk_size)
2601 ReadUnusedBytesFromFile(file, chunk_size - 2);
2602 return chunk_size_expected;
2605 for (i = 0; i < num_changed_custom_elements; i++)
2607 int element = getMappedElement(getFile16BitBE(file));
2608 int custom_target_element = getMappedElement(getFile16BitBE(file));
2610 if (IS_CUSTOM_ELEMENT(element))
2611 element_info[element].change->target_element = custom_target_element;
2613 Error(ERR_WARN, "invalid custom element number %d", element);
2616 level->file_has_custom_elements = TRUE;
2621 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2623 int num_changed_custom_elements = getFile16BitBE(file);
2624 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2627 if (chunk_size_expected != chunk_size)
2629 ReadUnusedBytesFromFile(file, chunk_size - 2);
2630 return chunk_size_expected;
2633 for (i = 0; i < num_changed_custom_elements; i++)
2635 int element = getMappedElement(getFile16BitBE(file));
2636 struct ElementInfo *ei = &element_info[element];
2637 unsigned int event_bits;
2639 if (!IS_CUSTOM_ELEMENT(element))
2641 Error(ERR_WARN, "invalid custom element number %d", element);
2643 element = EL_INTERNAL_DUMMY;
2646 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2647 ei->description[j] = getFile8Bit(file);
2648 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2650 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2652 /* some free bytes for future properties and padding */
2653 ReadUnusedBytesFromFile(file, 7);
2655 ei->use_gfx_element = getFile8Bit(file);
2656 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2658 ei->collect_score_initial = getFile8Bit(file);
2659 ei->collect_count_initial = getFile8Bit(file);
2661 ei->push_delay_fixed = getFile16BitBE(file);
2662 ei->push_delay_random = getFile16BitBE(file);
2663 ei->move_delay_fixed = getFile16BitBE(file);
2664 ei->move_delay_random = getFile16BitBE(file);
2666 ei->move_pattern = getFile16BitBE(file);
2667 ei->move_direction_initial = getFile8Bit(file);
2668 ei->move_stepsize = getFile8Bit(file);
2670 for (y = 0; y < 3; y++)
2671 for (x = 0; x < 3; x++)
2672 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2674 event_bits = getFile32BitBE(file);
2675 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2676 if (event_bits & (1 << j))
2677 ei->change->has_event[j] = TRUE;
2679 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2681 ei->change->delay_fixed = getFile16BitBE(file);
2682 ei->change->delay_random = getFile16BitBE(file);
2683 ei->change->delay_frames = getFile16BitBE(file);
2685 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2687 ei->change->explode = getFile8Bit(file);
2688 ei->change->use_target_content = getFile8Bit(file);
2689 ei->change->only_if_complete = getFile8Bit(file);
2690 ei->change->use_random_replace = getFile8Bit(file);
2692 ei->change->random_percentage = getFile8Bit(file);
2693 ei->change->replace_when = getFile8Bit(file);
2695 for (y = 0; y < 3; y++)
2696 for (x = 0; x < 3; x++)
2697 ei->change->target_content.e[x][y] =
2698 getMappedElement(getFile16BitBE(file));
2700 ei->slippery_type = getFile8Bit(file);
2702 /* some free bytes for future properties and padding */
2703 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2705 /* mark that this custom element has been modified */
2706 ei->modified_settings = TRUE;
2709 level->file_has_custom_elements = TRUE;
2714 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2716 struct ElementInfo *ei;
2717 int chunk_size_expected;
2721 /* ---------- custom element base property values (96 bytes) ------------- */
2723 element = getMappedElement(getFile16BitBE(file));
2725 if (!IS_CUSTOM_ELEMENT(element))
2727 Error(ERR_WARN, "invalid custom element number %d", element);
2729 ReadUnusedBytesFromFile(file, chunk_size - 2);
2733 ei = &element_info[element];
2735 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2736 ei->description[i] = getFile8Bit(file);
2737 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2739 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2741 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2743 ei->num_change_pages = getFile8Bit(file);
2745 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2746 if (chunk_size_expected != chunk_size)
2748 ReadUnusedBytesFromFile(file, chunk_size - 43);
2749 return chunk_size_expected;
2752 ei->ce_value_fixed_initial = getFile16BitBE(file);
2753 ei->ce_value_random_initial = getFile16BitBE(file);
2754 ei->use_last_ce_value = getFile8Bit(file);
2756 ei->use_gfx_element = getFile8Bit(file);
2757 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2759 ei->collect_score_initial = getFile8Bit(file);
2760 ei->collect_count_initial = getFile8Bit(file);
2762 ei->drop_delay_fixed = getFile8Bit(file);
2763 ei->push_delay_fixed = getFile8Bit(file);
2764 ei->drop_delay_random = getFile8Bit(file);
2765 ei->push_delay_random = getFile8Bit(file);
2766 ei->move_delay_fixed = getFile16BitBE(file);
2767 ei->move_delay_random = getFile16BitBE(file);
2769 /* bits 0 - 15 of "move_pattern" ... */
2770 ei->move_pattern = getFile16BitBE(file);
2771 ei->move_direction_initial = getFile8Bit(file);
2772 ei->move_stepsize = getFile8Bit(file);
2774 ei->slippery_type = getFile8Bit(file);
2776 for (y = 0; y < 3; y++)
2777 for (x = 0; x < 3; x++)
2778 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2780 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2781 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2782 ei->move_leave_type = getFile8Bit(file);
2784 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2785 ei->move_pattern |= (getFile16BitBE(file) << 16);
2787 ei->access_direction = getFile8Bit(file);
2789 ei->explosion_delay = getFile8Bit(file);
2790 ei->ignition_delay = getFile8Bit(file);
2791 ei->explosion_type = getFile8Bit(file);
2793 /* some free bytes for future custom property values and padding */
2794 ReadUnusedBytesFromFile(file, 1);
2796 /* ---------- change page property values (48 bytes) --------------------- */
2798 setElementChangePages(ei, ei->num_change_pages);
2800 for (i = 0; i < ei->num_change_pages; i++)
2802 struct ElementChangeInfo *change = &ei->change_page[i];
2803 unsigned int event_bits;
2805 /* always start with reliable default values */
2806 setElementChangeInfoToDefaults(change);
2808 /* bits 0 - 31 of "has_event[]" ... */
2809 event_bits = getFile32BitBE(file);
2810 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2811 if (event_bits & (1 << j))
2812 change->has_event[j] = TRUE;
2814 change->target_element = getMappedElement(getFile16BitBE(file));
2816 change->delay_fixed = getFile16BitBE(file);
2817 change->delay_random = getFile16BitBE(file);
2818 change->delay_frames = getFile16BitBE(file);
2820 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2822 change->explode = getFile8Bit(file);
2823 change->use_target_content = getFile8Bit(file);
2824 change->only_if_complete = getFile8Bit(file);
2825 change->use_random_replace = getFile8Bit(file);
2827 change->random_percentage = getFile8Bit(file);
2828 change->replace_when = getFile8Bit(file);
2830 for (y = 0; y < 3; y++)
2831 for (x = 0; x < 3; x++)
2832 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2834 change->can_change = getFile8Bit(file);
2836 change->trigger_side = getFile8Bit(file);
2838 change->trigger_player = getFile8Bit(file);
2839 change->trigger_page = getFile8Bit(file);
2841 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2842 CH_PAGE_ANY : (1 << change->trigger_page));
2844 change->has_action = getFile8Bit(file);
2845 change->action_type = getFile8Bit(file);
2846 change->action_mode = getFile8Bit(file);
2847 change->action_arg = getFile16BitBE(file);
2849 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2850 event_bits = getFile8Bit(file);
2851 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2852 if (event_bits & (1 << (j - 32)))
2853 change->has_event[j] = TRUE;
2856 /* mark this custom element as modified */
2857 ei->modified_settings = TRUE;
2859 level->file_has_custom_elements = TRUE;
2864 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2866 struct ElementInfo *ei;
2867 struct ElementGroupInfo *group;
2871 element = getMappedElement(getFile16BitBE(file));
2873 if (!IS_GROUP_ELEMENT(element))
2875 Error(ERR_WARN, "invalid group element number %d", element);
2877 ReadUnusedBytesFromFile(file, chunk_size - 2);
2881 ei = &element_info[element];
2883 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2884 ei->description[i] = getFile8Bit(file);
2885 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2887 group = element_info[element].group;
2889 group->num_elements = getFile8Bit(file);
2891 ei->use_gfx_element = getFile8Bit(file);
2892 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2894 group->choice_mode = getFile8Bit(file);
2896 /* some free bytes for future values and padding */
2897 ReadUnusedBytesFromFile(file, 3);
2899 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2900 group->element[i] = getMappedElement(getFile16BitBE(file));
2902 /* mark this group element as modified */
2903 element_info[element].modified_settings = TRUE;
2905 level->file_has_custom_elements = TRUE;
2910 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2911 int element, int real_element)
2913 int micro_chunk_size = 0;
2914 int conf_type = getFile8Bit(file);
2915 int byte_mask = conf_type & CONF_MASK_BYTES;
2916 boolean element_found = FALSE;
2919 micro_chunk_size += 1;
2921 if (byte_mask == CONF_MASK_MULTI_BYTES)
2923 int num_bytes = getFile16BitBE(file);
2924 byte *buffer = checked_malloc(num_bytes);
2926 ReadBytesFromFile(file, buffer, num_bytes);
2928 for (i = 0; conf[i].data_type != -1; i++)
2930 if (conf[i].element == element &&
2931 conf[i].conf_type == conf_type)
2933 int data_type = conf[i].data_type;
2934 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2935 int max_num_entities = conf[i].max_num_entities;
2937 if (num_entities > max_num_entities)
2940 "truncating number of entities for element %d from %d to %d",
2941 element, num_entities, max_num_entities);
2943 num_entities = max_num_entities;
2946 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
2947 data_type == TYPE_CONTENT_LIST))
2949 /* for element and content lists, zero entities are not allowed */
2950 Error(ERR_WARN, "found empty list of entities for element %d",
2953 /* do not set "num_entities" here to prevent reading behind buffer */
2955 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
2959 *(int *)(conf[i].num_entities) = num_entities;
2962 element_found = TRUE;
2964 if (data_type == TYPE_STRING)
2966 char *string = (char *)(conf[i].value);
2969 for (j = 0; j < max_num_entities; j++)
2970 string[j] = (j < num_entities ? buffer[j] : '\0');
2972 else if (data_type == TYPE_ELEMENT_LIST)
2974 int *element_array = (int *)(conf[i].value);
2977 for (j = 0; j < num_entities; j++)
2979 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
2981 else if (data_type == TYPE_CONTENT_LIST)
2983 struct Content *content= (struct Content *)(conf[i].value);
2986 for (c = 0; c < num_entities; c++)
2987 for (y = 0; y < 3; y++)
2988 for (x = 0; x < 3; x++)
2989 content[c].e[x][y] =
2990 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
2993 element_found = FALSE;
2999 checked_free(buffer);
3001 micro_chunk_size += 2 + num_bytes;
3003 else /* constant size configuration data (1, 2 or 4 bytes) */
3005 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3006 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3007 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3009 for (i = 0; conf[i].data_type != -1; i++)
3011 if (conf[i].element == element &&
3012 conf[i].conf_type == conf_type)
3014 int data_type = conf[i].data_type;
3016 if (data_type == TYPE_ELEMENT)
3017 value = getMappedElement(value);
3019 if (data_type == TYPE_BOOLEAN)
3020 *(boolean *)(conf[i].value) = value;
3022 *(int *) (conf[i].value) = value;
3024 element_found = TRUE;
3030 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3035 char *error_conf_chunk_bytes =
3036 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3037 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3038 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3039 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3040 int error_element = real_element;
3042 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3043 error_conf_chunk_bytes, error_conf_chunk_token,
3044 error_element, EL_NAME(error_element));
3047 return micro_chunk_size;
3050 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3052 int real_chunk_size = 0;
3054 li = *level; /* copy level data into temporary buffer */
3056 while (!checkEndOfFile(file))
3058 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3060 if (real_chunk_size >= chunk_size)
3064 *level = li; /* copy temporary buffer back to level data */
3066 return real_chunk_size;
3069 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3071 int real_chunk_size = 0;
3073 li = *level; /* copy level data into temporary buffer */
3075 while (!checkEndOfFile(file))
3077 int element = getMappedElement(getFile16BitBE(file));
3079 real_chunk_size += 2;
3080 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3082 if (real_chunk_size >= chunk_size)
3086 *level = li; /* copy temporary buffer back to level data */
3088 return real_chunk_size;
3091 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3093 int real_chunk_size = 0;
3095 li = *level; /* copy level data into temporary buffer */
3097 while (!checkEndOfFile(file))
3099 int element = getMappedElement(getFile16BitBE(file));
3101 real_chunk_size += 2;
3102 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3104 if (real_chunk_size >= chunk_size)
3108 *level = li; /* copy temporary buffer back to level data */
3110 return real_chunk_size;
3113 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3115 int element = getMappedElement(getFile16BitBE(file));
3116 int envelope_nr = element - EL_ENVELOPE_1;
3117 int real_chunk_size = 2;
3119 xx_envelope = level->envelope[envelope_nr]; /* copy into temporary buffer */
3121 while (!checkEndOfFile(file))
3123 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3126 if (real_chunk_size >= chunk_size)
3130 level->envelope[envelope_nr] = xx_envelope; /* copy from temporary buffer */
3132 return real_chunk_size;
3135 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3137 int element = getMappedElement(getFile16BitBE(file));
3138 int real_chunk_size = 2;
3139 struct ElementInfo *ei = &element_info[element];
3142 xx_ei = *ei; /* copy element data into temporary buffer */
3144 xx_ei.num_change_pages = -1;
3146 while (!checkEndOfFile(file))
3148 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3150 if (xx_ei.num_change_pages != -1)
3153 if (real_chunk_size >= chunk_size)
3159 if (ei->num_change_pages == -1)
3161 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3164 ei->num_change_pages = 1;
3166 setElementChangePages(ei, 1);
3167 setElementChangeInfoToDefaults(ei->change);
3169 return real_chunk_size;
3172 /* initialize number of change pages stored for this custom element */
3173 setElementChangePages(ei, ei->num_change_pages);
3174 for (i = 0; i < ei->num_change_pages; i++)
3175 setElementChangeInfoToDefaults(&ei->change_page[i]);
3177 /* start with reading properties for the first change page */
3178 xx_current_change_page = 0;
3180 while (!checkEndOfFile(file))
3182 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3184 xx_change = *change; /* copy change data into temporary buffer */
3186 resetEventBits(); /* reset bits; change page might have changed */
3188 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3191 *change = xx_change;
3193 setEventFlagsFromEventBits(change);
3195 if (real_chunk_size >= chunk_size)
3199 level->file_has_custom_elements = TRUE;
3201 return real_chunk_size;
3204 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3206 int element = getMappedElement(getFile16BitBE(file));
3207 int real_chunk_size = 2;
3208 struct ElementInfo *ei = &element_info[element];
3209 struct ElementGroupInfo *group = ei->group;
3211 xx_ei = *ei; /* copy element data into temporary buffer */
3212 xx_group = *group; /* copy group data into temporary buffer */
3214 while (!checkEndOfFile(file))
3216 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3219 if (real_chunk_size >= chunk_size)
3226 level->file_has_custom_elements = TRUE;
3228 return real_chunk_size;
3231 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3232 struct LevelFileInfo *level_file_info,
3233 boolean level_info_only)
3235 char *filename = level_file_info->filename;
3236 char cookie[MAX_LINE_LEN];
3237 char chunk_name[CHUNK_ID_LEN + 1];
3241 if (!(file = openFile(filename, MODE_READ)))
3243 level->no_valid_file = TRUE;
3244 level->no_level_file = TRUE;
3246 if (level_info_only)
3249 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3251 if (!setup.editor.use_template_for_new_levels)
3254 /* if level file not found, try to initialize level data from template */
3255 filename = getGlobalLevelTemplateFilename();
3257 if (!(file = openFile(filename, MODE_READ)))
3260 /* default: for empty levels, use level template for custom elements */
3261 level->use_custom_template = TRUE;
3263 level->no_valid_file = FALSE;
3266 getFileChunkBE(file, chunk_name, NULL);
3267 if (strEqual(chunk_name, "RND1"))
3269 getFile32BitBE(file); /* not used */
3271 getFileChunkBE(file, chunk_name, NULL);
3272 if (!strEqual(chunk_name, "CAVE"))
3274 level->no_valid_file = TRUE;
3276 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3283 else /* check for pre-2.0 file format with cookie string */
3285 strcpy(cookie, chunk_name);
3286 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3288 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3289 cookie[strlen(cookie) - 1] = '\0';
3291 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3293 level->no_valid_file = TRUE;
3295 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3302 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3304 level->no_valid_file = TRUE;
3306 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3313 /* pre-2.0 level files have no game version, so use file version here */
3314 level->game_version = level->file_version;
3317 if (level->file_version < FILE_VERSION_1_2)
3319 /* level files from versions before 1.2.0 without chunk structure */
3320 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3321 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3329 int (*loader)(File *, int, struct LevelInfo *);
3333 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3334 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3335 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3336 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3337 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3338 { "INFO", -1, LoadLevel_INFO },
3339 { "BODY", -1, LoadLevel_BODY },
3340 { "CONT", -1, LoadLevel_CONT },
3341 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3342 { "CNT3", -1, LoadLevel_CNT3 },
3343 { "CUS1", -1, LoadLevel_CUS1 },
3344 { "CUS2", -1, LoadLevel_CUS2 },
3345 { "CUS3", -1, LoadLevel_CUS3 },
3346 { "CUS4", -1, LoadLevel_CUS4 },
3347 { "GRP1", -1, LoadLevel_GRP1 },
3348 { "CONF", -1, LoadLevel_CONF },
3349 { "ELEM", -1, LoadLevel_ELEM },
3350 { "NOTE", -1, LoadLevel_NOTE },
3351 { "CUSX", -1, LoadLevel_CUSX },
3352 { "GRPX", -1, LoadLevel_GRPX },
3357 while (getFileChunkBE(file, chunk_name, &chunk_size))
3361 while (chunk_info[i].name != NULL &&
3362 !strEqual(chunk_name, chunk_info[i].name))
3365 if (chunk_info[i].name == NULL)
3367 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3368 chunk_name, filename);
3369 ReadUnusedBytesFromFile(file, chunk_size);
3371 else if (chunk_info[i].size != -1 &&
3372 chunk_info[i].size != chunk_size)
3374 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3375 chunk_size, chunk_name, filename);
3376 ReadUnusedBytesFromFile(file, chunk_size);
3380 /* call function to load this level chunk */
3381 int chunk_size_expected =
3382 (chunk_info[i].loader)(file, chunk_size, level);
3384 /* the size of some chunks cannot be checked before reading other
3385 chunks first (like "HEAD" and "BODY") that contain some header
3386 information, so check them here */
3387 if (chunk_size_expected != chunk_size)
3389 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3390 chunk_size, chunk_name, filename);
3400 /* ------------------------------------------------------------------------- */
3401 /* functions for loading EM level */
3402 /* ------------------------------------------------------------------------- */
3404 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3406 static int ball_xy[8][2] =
3417 struct LevelInfo_EM *level_em = level->native_em_level;
3418 struct LEVEL *lev = level_em->lev;
3419 struct PLAYER **ply = level_em->ply;
3422 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3423 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3425 lev->time_seconds = level->time;
3426 lev->required_initial = level->gems_needed;
3428 lev->emerald_score = level->score[SC_EMERALD];
3429 lev->diamond_score = level->score[SC_DIAMOND];
3430 lev->alien_score = level->score[SC_ROBOT];
3431 lev->tank_score = level->score[SC_SPACESHIP];
3432 lev->bug_score = level->score[SC_BUG];
3433 lev->eater_score = level->score[SC_YAMYAM];
3434 lev->nut_score = level->score[SC_NUT];
3435 lev->dynamite_score = level->score[SC_DYNAMITE];
3436 lev->key_score = level->score[SC_KEY];
3437 lev->exit_score = level->score[SC_TIME_BONUS];
3439 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3440 for (y = 0; y < 3; y++)
3441 for (x = 0; x < 3; x++)
3442 lev->eater_array[i][y * 3 + x] =
3443 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3445 lev->amoeba_time = level->amoeba_speed;
3446 lev->wonderwall_time_initial = level->time_magic_wall;
3447 lev->wheel_time = level->time_wheel;
3449 lev->android_move_time = level->android_move_time;
3450 lev->android_clone_time = level->android_clone_time;
3451 lev->ball_random = level->ball_random;
3452 lev->ball_state_initial = level->ball_state_initial;
3453 lev->ball_time = level->ball_time;
3454 lev->num_ball_arrays = level->num_ball_contents;
3456 lev->lenses_score = level->lenses_score;
3457 lev->magnify_score = level->magnify_score;
3458 lev->slurp_score = level->slurp_score;
3460 lev->lenses_time = level->lenses_time;
3461 lev->magnify_time = level->magnify_time;
3463 lev->wind_direction_initial =
3464 map_direction_RND_to_EM(level->wind_direction_initial);
3465 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3466 lev->wind_time : 0);
3468 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3469 for (j = 0; j < 8; j++)
3470 lev->ball_array[i][j] =
3471 map_element_RND_to_EM(level->
3472 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3474 map_android_clone_elements_RND_to_EM(level);
3476 /* first fill the complete playfield with the default border element */
3477 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3478 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3479 level_em->cave[x][y] = ZBORDER;
3481 if (BorderElement == EL_STEELWALL)
3483 for (y = 0; y < lev->height + 2; y++)
3484 for (x = 0; x < lev->width + 2; x++)
3485 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3488 /* then copy the real level contents from level file into the playfield */
3489 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3491 int new_element = map_element_RND_to_EM(level->field[x][y]);
3492 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3493 int xx = x + 1 + offset;
3494 int yy = y + 1 + offset;
3496 if (level->field[x][y] == EL_AMOEBA_DEAD)
3497 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3499 level_em->cave[xx][yy] = new_element;
3502 for (i = 0; i < MAX_PLAYERS; i++)
3504 ply[i]->x_initial = 0;
3505 ply[i]->y_initial = 0;
3508 /* initialize player positions and delete players from the playfield */
3509 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3511 if (ELEM_IS_PLAYER(level->field[x][y]))
3513 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3514 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3515 int xx = x + 1 + offset;
3516 int yy = y + 1 + offset;
3518 ply[player_nr]->x_initial = xx;
3519 ply[player_nr]->y_initial = yy;
3521 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3525 if (BorderElement == EL_STEELWALL)
3532 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3534 static int ball_xy[8][2] =
3545 struct LevelInfo_EM *level_em = level->native_em_level;
3546 struct LEVEL *lev = level_em->lev;
3547 struct PLAYER **ply = level_em->ply;
3550 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3551 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3553 level->time = lev->time_seconds;
3554 level->gems_needed = lev->required_initial;
3556 sprintf(level->name, "Level %d", level->file_info.nr);
3558 level->score[SC_EMERALD] = lev->emerald_score;
3559 level->score[SC_DIAMOND] = lev->diamond_score;
3560 level->score[SC_ROBOT] = lev->alien_score;
3561 level->score[SC_SPACESHIP] = lev->tank_score;
3562 level->score[SC_BUG] = lev->bug_score;
3563 level->score[SC_YAMYAM] = lev->eater_score;
3564 level->score[SC_NUT] = lev->nut_score;
3565 level->score[SC_DYNAMITE] = lev->dynamite_score;
3566 level->score[SC_KEY] = lev->key_score;
3567 level->score[SC_TIME_BONUS] = lev->exit_score;
3569 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3571 for (i = 0; i < level->num_yamyam_contents; i++)
3572 for (y = 0; y < 3; y++)
3573 for (x = 0; x < 3; x++)
3574 level->yamyam_content[i].e[x][y] =
3575 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3577 level->amoeba_speed = lev->amoeba_time;
3578 level->time_magic_wall = lev->wonderwall_time_initial;
3579 level->time_wheel = lev->wheel_time;
3581 level->android_move_time = lev->android_move_time;
3582 level->android_clone_time = lev->android_clone_time;
3583 level->ball_random = lev->ball_random;
3584 level->ball_state_initial = lev->ball_state_initial;
3585 level->ball_time = lev->ball_time;
3586 level->num_ball_contents = lev->num_ball_arrays;
3588 level->lenses_score = lev->lenses_score;
3589 level->magnify_score = lev->magnify_score;
3590 level->slurp_score = lev->slurp_score;
3592 level->lenses_time = lev->lenses_time;
3593 level->magnify_time = lev->magnify_time;
3595 level->wind_direction_initial =
3596 map_direction_EM_to_RND(lev->wind_direction_initial);
3598 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3599 for (j = 0; j < 8; j++)
3600 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3601 map_element_EM_to_RND(lev->ball_array[i][j]);
3603 map_android_clone_elements_EM_to_RND(level);
3605 /* convert the playfield (some elements need special treatment) */
3606 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3608 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3610 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3611 new_element = EL_AMOEBA_DEAD;
3613 level->field[x][y] = new_element;
3616 for (i = 0; i < MAX_PLAYERS; i++)
3618 /* in case of all players set to the same field, use the first player */
3619 int nr = MAX_PLAYERS - i - 1;
3620 int jx = ply[nr]->x_initial - 1;
3621 int jy = ply[nr]->y_initial - 1;
3623 if (jx != -1 && jy != -1)
3624 level->field[jx][jy] = EL_PLAYER_1 + nr;
3629 /* ------------------------------------------------------------------------- */
3630 /* functions for loading SP level */
3631 /* ------------------------------------------------------------------------- */
3633 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3635 struct LevelInfo_SP *level_sp = level->native_sp_level;
3636 LevelInfoType *header = &level_sp->header;
3639 level_sp->width = level->fieldx;
3640 level_sp->height = level->fieldy;
3642 for (x = 0; x < level->fieldx; x++)
3643 for (y = 0; y < level->fieldy; y++)
3644 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3646 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3648 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3649 header->LevelTitle[i] = level->name[i];
3650 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3652 header->InfotronsNeeded = level->gems_needed;
3654 header->SpecialPortCount = 0;
3656 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3658 boolean gravity_port_found = FALSE;
3659 boolean gravity_port_valid = FALSE;
3660 int gravity_port_flag;
3661 int gravity_port_base_element;
3662 int element = level->field[x][y];
3664 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3665 element <= EL_SP_GRAVITY_ON_PORT_UP)
3667 gravity_port_found = TRUE;
3668 gravity_port_valid = TRUE;
3669 gravity_port_flag = 1;
3670 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3672 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3673 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3675 gravity_port_found = TRUE;
3676 gravity_port_valid = TRUE;
3677 gravity_port_flag = 0;
3678 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3680 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3681 element <= EL_SP_GRAVITY_PORT_UP)
3683 /* change R'n'D style gravity inverting special port to normal port
3684 (there are no gravity inverting ports in native Supaplex engine) */
3686 gravity_port_found = TRUE;
3687 gravity_port_valid = FALSE;
3688 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3691 if (gravity_port_found)
3693 if (gravity_port_valid &&
3694 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3696 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3698 port->PortLocation = (y * level->fieldx + x) * 2;
3699 port->Gravity = gravity_port_flag;
3701 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3703 header->SpecialPortCount++;
3707 /* change special gravity port to normal port */
3709 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3712 level_sp->playfield[x][y] = element - EL_SP_START;
3717 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3719 struct LevelInfo_SP *level_sp = level->native_sp_level;
3720 LevelInfoType *header = &level_sp->header;
3721 boolean num_invalid_elements = 0;
3724 level->fieldx = level_sp->width;
3725 level->fieldy = level_sp->height;
3727 for (x = 0; x < level->fieldx; x++)
3729 for (y = 0; y < level->fieldy; y++)
3731 int element_old = level_sp->playfield[x][y];
3732 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3734 if (element_new == EL_UNKNOWN)
3736 num_invalid_elements++;
3738 Error(ERR_DEBUG, "invalid element %d at position %d, %d",
3742 level->field[x][y] = element_new;
3746 if (num_invalid_elements > 0)
3747 Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
3748 (!options.debug ? " (use '--debug' for more details)" : ""));
3750 for (i = 0; i < MAX_PLAYERS; i++)
3751 level->initial_player_gravity[i] =
3752 (header->InitialGravity == 1 ? TRUE : FALSE);
3754 /* skip leading spaces */
3755 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3756 if (header->LevelTitle[i] != ' ')
3759 /* copy level title */
3760 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3761 level->name[j] = header->LevelTitle[i];
3762 level->name[j] = '\0';
3764 /* cut trailing spaces */
3766 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3767 level->name[j - 1] = '\0';
3769 level->gems_needed = header->InfotronsNeeded;
3771 for (i = 0; i < header->SpecialPortCount; i++)
3773 SpecialPortType *port = &header->SpecialPort[i];
3774 int port_location = port->PortLocation;
3775 int gravity = port->Gravity;
3776 int port_x, port_y, port_element;
3778 port_x = (port_location / 2) % level->fieldx;
3779 port_y = (port_location / 2) / level->fieldx;
3781 if (port_x < 0 || port_x >= level->fieldx ||
3782 port_y < 0 || port_y >= level->fieldy)
3784 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3790 port_element = level->field[port_x][port_y];
3792 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3793 port_element > EL_SP_GRAVITY_PORT_UP)
3795 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3800 /* change previous (wrong) gravity inverting special port to either
3801 gravity enabling special port or gravity disabling special port */
3802 level->field[port_x][port_y] +=
3803 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3804 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3807 /* change special gravity ports without database entries to normal ports */
3808 for (x = 0; x < level->fieldx; x++)
3809 for (y = 0; y < level->fieldy; y++)
3810 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3811 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3812 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3814 level->time = 0; /* no time limit */
3815 level->amoeba_speed = 0;
3816 level->time_magic_wall = 0;
3817 level->time_wheel = 0;
3818 level->amoeba_content = EL_EMPTY;
3821 /* original Supaplex does not use score values -- use default values */
3823 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3824 level->score[i] = 0;
3827 /* there are no yamyams in supaplex levels */
3828 for (i = 0; i < level->num_yamyam_contents; i++)
3829 for (x = 0; x < 3; x++)
3830 for (y = 0; y < 3; y++)
3831 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3834 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3836 struct LevelInfo_SP *level_sp = level->native_sp_level;
3837 struct DemoInfo_SP *demo = &level_sp->demo;
3840 /* always start with reliable default values */
3841 demo->is_available = FALSE;
3844 if (TAPE_IS_EMPTY(tape))
3847 demo->level_nr = tape.level_nr; /* (currently not used) */
3849 level_sp->header.DemoRandomSeed = tape.random_seed;
3853 for (i = 0; i < tape.length; i++)
3855 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3856 int demo_repeat = tape.pos[i].delay;
3857 int demo_entries = (demo_repeat + 15) / 16;
3859 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3861 Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
3867 for (j = 0; j < demo_repeat / 16; j++)
3868 demo->data[demo->length++] = 0xf0 | demo_action;
3870 if (demo_repeat % 16)
3871 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3874 demo->is_available = TRUE;
3877 static void setTapeInfoToDefaults();
3879 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3881 struct LevelInfo_SP *level_sp = level->native_sp_level;
3882 struct DemoInfo_SP *demo = &level_sp->demo;
3883 char *filename = level->file_info.filename;
3886 /* always start with reliable default values */
3887 setTapeInfoToDefaults();
3889 if (!demo->is_available)
3892 tape.level_nr = demo->level_nr; /* (currently not used) */
3893 tape.random_seed = level_sp->header.DemoRandomSeed;
3895 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3898 tape.pos[tape.counter].delay = 0;
3900 for (i = 0; i < demo->length; i++)
3902 int demo_action = demo->data[i] & 0x0f;
3903 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3904 int tape_action = map_key_SP_to_RND(demo_action);
3905 int tape_repeat = demo_repeat + 1;
3906 byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
3907 boolean success = 0;
3910 for (j = 0; j < tape_repeat; j++)
3911 success = TapeAddAction(action);
3915 Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
3922 TapeHaltRecording();
3926 /* ------------------------------------------------------------------------- */
3927 /* functions for loading MM level */
3928 /* ------------------------------------------------------------------------- */
3930 void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
3932 struct LevelInfo_MM *level_mm = level->native_mm_level;
3935 level_mm->file_version = level->file_version;
3936 level_mm->game_version = level->game_version;
3937 level_mm->encoding_16bit_field = level->encoding_16bit_field;
3939 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
3940 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
3942 level_mm->time = level->time;
3943 level_mm->kettles_needed = level->gems_needed;
3944 level_mm->auto_count_kettles = FALSE;
3945 level_mm->laser_red = FALSE;
3946 level_mm->laser_green = FALSE;
3947 level_mm->laser_blue = TRUE;
3949 strcpy(level_mm->name, level->name);
3950 strcpy(level_mm->author, level->author);
3952 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
3953 level_mm->score[SC_KEY] = level->score[SC_PACMAN];
3954 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
3956 level_mm->amoeba_speed = level->amoeba_speed;
3957 level_mm->time_fuse = 0;
3959 for (x = 0; x < level->fieldx; x++)
3960 for (y = 0; y < level->fieldy; y++)
3962 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
3965 void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
3967 struct LevelInfo_MM *level_mm = level->native_mm_level;
3970 level->file_version = level_mm->file_version;
3971 level->game_version = level_mm->game_version;
3972 level->encoding_16bit_field = level_mm->encoding_16bit_field;
3974 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
3975 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
3977 level->time = level_mm->time;
3978 level->gems_needed = level_mm->kettles_needed;
3980 strcpy(level->name, level_mm->name);
3982 /* only overwrite author from 'levelinfo.conf' if author defined in level */
3983 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
3984 strcpy(level->author, level_mm->author);
3986 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
3987 level->score[SC_KEY] = level_mm->score[SC_PACMAN];
3988 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
3990 level->amoeba_speed = level_mm->amoeba_speed;
3992 for (x = 0; x < level->fieldx; x++)
3993 for (y = 0; y < level->fieldy; y++)
3994 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
3996 if (level_mm->auto_count_kettles)
3998 level->gems_needed = 0;
4000 for (x = 0; x < level->fieldx; x++)
4001 for (y = 0; y < level->fieldy; y++)
4002 if (level->field[x][y] == EL_MM_KETTLE ||
4003 level->field[x][y] == EL_DF_CELL)
4004 level->gems_needed++;
4009 /* ------------------------------------------------------------------------- */
4010 /* functions for loading DC level */
4011 /* ------------------------------------------------------------------------- */
4013 #define DC_LEVEL_HEADER_SIZE 344
4015 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
4017 static int last_data_encoded;
4021 int diff_hi, diff_lo;
4022 int data_hi, data_lo;
4023 unsigned short data_decoded;
4027 last_data_encoded = 0;
4034 diff = data_encoded - last_data_encoded;
4035 diff_hi = diff & ~0xff;
4036 diff_lo = diff & 0xff;
4040 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4041 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4042 data_hi = data_hi & 0xff00;
4044 data_decoded = data_hi | data_lo;
4046 last_data_encoded = data_encoded;
4048 offset1 = (offset1 + 1) % 31;
4049 offset2 = offset2 & 0xff;
4051 return data_decoded;
4054 int getMappedElement_DC(int element)
4062 /* 0x0117 - 0x036e: (?) */
4065 /* 0x042d - 0x0684: (?) */
4081 element = EL_CRYSTAL;
4084 case 0x0e77: /* quicksand (boulder) */
4085 element = EL_QUICKSAND_FAST_FULL;
4088 case 0x0e99: /* slow quicksand (boulder) */
4089 element = EL_QUICKSAND_FULL;
4093 element = EL_EM_EXIT_OPEN;
4097 element = EL_EM_EXIT_CLOSED;
4101 element = EL_EM_STEEL_EXIT_OPEN;
4105 element = EL_EM_STEEL_EXIT_CLOSED;
4108 case 0x0f4f: /* dynamite (lit 1) */
4109 element = EL_EM_DYNAMITE_ACTIVE;
4112 case 0x0f57: /* dynamite (lit 2) */
4113 element = EL_EM_DYNAMITE_ACTIVE;
4116 case 0x0f5f: /* dynamite (lit 3) */
4117 element = EL_EM_DYNAMITE_ACTIVE;
4120 case 0x0f67: /* dynamite (lit 4) */
4121 element = EL_EM_DYNAMITE_ACTIVE;
4128 element = EL_AMOEBA_WET;
4132 element = EL_AMOEBA_DROP;
4136 element = EL_DC_MAGIC_WALL;
4140 element = EL_SPACESHIP_UP;
4144 element = EL_SPACESHIP_DOWN;
4148 element = EL_SPACESHIP_LEFT;
4152 element = EL_SPACESHIP_RIGHT;
4156 element = EL_BUG_UP;
4160 element = EL_BUG_DOWN;
4164 element = EL_BUG_LEFT;
4168 element = EL_BUG_RIGHT;
4172 element = EL_MOLE_UP;
4176 element = EL_MOLE_DOWN;
4180 element = EL_MOLE_LEFT;
4184 element = EL_MOLE_RIGHT;
4192 element = EL_YAMYAM;
4196 element = EL_SWITCHGATE_OPEN;
4200 element = EL_SWITCHGATE_CLOSED;
4204 element = EL_DC_SWITCHGATE_SWITCH_UP;
4208 element = EL_TIMEGATE_CLOSED;
4211 case 0x144c: /* conveyor belt switch (green) */
4212 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4215 case 0x144f: /* conveyor belt switch (red) */
4216 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4219 case 0x1452: /* conveyor belt switch (blue) */
4220 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4224 element = EL_CONVEYOR_BELT_3_MIDDLE;
4228 element = EL_CONVEYOR_BELT_3_LEFT;
4232 element = EL_CONVEYOR_BELT_3_RIGHT;
4236 element = EL_CONVEYOR_BELT_1_MIDDLE;
4240 element = EL_CONVEYOR_BELT_1_LEFT;
4244 element = EL_CONVEYOR_BELT_1_RIGHT;
4248 element = EL_CONVEYOR_BELT_4_MIDDLE;
4252 element = EL_CONVEYOR_BELT_4_LEFT;
4256 element = EL_CONVEYOR_BELT_4_RIGHT;
4260 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4264 element = EL_EXPANDABLE_WALL_VERTICAL;
4268 element = EL_EXPANDABLE_WALL_ANY;
4271 case 0x14ce: /* growing steel wall (left/right) */
4272 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4275 case 0x14df: /* growing steel wall (up/down) */
4276 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4279 case 0x14e8: /* growing steel wall (up/down/left/right) */
4280 element = EL_EXPANDABLE_STEELWALL_ANY;
4284 element = EL_SHIELD_DEADLY;
4288 element = EL_EXTRA_TIME;
4296 element = EL_EMPTY_SPACE;
4299 case 0x1578: /* quicksand (empty) */
4300 element = EL_QUICKSAND_FAST_EMPTY;
4303 case 0x1579: /* slow quicksand (empty) */
4304 element = EL_QUICKSAND_EMPTY;
4307 /* 0x157c - 0x158b: */
4310 /* 0x1590 - 0x159f: */
4311 /* EL_DC_LANDMINE */
4314 element = EL_EM_DYNAMITE;
4317 case 0x15a1: /* key (red) */
4318 element = EL_EM_KEY_1;
4321 case 0x15a2: /* key (yellow) */
4322 element = EL_EM_KEY_2;
4325 case 0x15a3: /* key (blue) */
4326 element = EL_EM_KEY_4;
4329 case 0x15a4: /* key (green) */
4330 element = EL_EM_KEY_3;
4333 case 0x15a5: /* key (white) */
4334 element = EL_DC_KEY_WHITE;
4338 element = EL_WALL_SLIPPERY;
4345 case 0x15a8: /* wall (not round) */
4349 case 0x15a9: /* (blue) */
4350 element = EL_CHAR_A;
4353 case 0x15aa: /* (blue) */
4354 element = EL_CHAR_B;
4357 case 0x15ab: /* (blue) */
4358 element = EL_CHAR_C;
4361 case 0x15ac: /* (blue) */
4362 element = EL_CHAR_D;
4365 case 0x15ad: /* (blue) */
4366 element = EL_CHAR_E;
4369 case 0x15ae: /* (blue) */
4370 element = EL_CHAR_F;
4373 case 0x15af: /* (blue) */
4374 element = EL_CHAR_G;
4377 case 0x15b0: /* (blue) */
4378 element = EL_CHAR_H;
4381 case 0x15b1: /* (blue) */
4382 element = EL_CHAR_I;
4385 case 0x15b2: /* (blue) */
4386 element = EL_CHAR_J;
4389 case 0x15b3: /* (blue) */
4390 element = EL_CHAR_K;
4393 case 0x15b4: /* (blue) */
4394 element = EL_CHAR_L;
4397 case 0x15b5: /* (blue) */
4398 element = EL_CHAR_M;
4401 case 0x15b6: /* (blue) */
4402 element = EL_CHAR_N;
4405 case 0x15b7: /* (blue) */
4406 element = EL_CHAR_O;
4409 case 0x15b8: /* (blue) */
4410 element = EL_CHAR_P;
4413 case 0x15b9: /* (blue) */
4414 element = EL_CHAR_Q;
4417 case 0x15ba: /* (blue) */
4418 element = EL_CHAR_R;
4421 case 0x15bb: /* (blue) */
4422 element = EL_CHAR_S;
4425 case 0x15bc: /* (blue) */
4426 element = EL_CHAR_T;
4429 case 0x15bd: /* (blue) */
4430 element = EL_CHAR_U;
4433 case 0x15be: /* (blue) */
4434 element = EL_CHAR_V;
4437 case 0x15bf: /* (blue) */
4438 element = EL_CHAR_W;
4441 case 0x15c0: /* (blue) */
4442 element = EL_CHAR_X;
4445 case 0x15c1: /* (blue) */
4446 element = EL_CHAR_Y;
4449 case 0x15c2: /* (blue) */
4450 element = EL_CHAR_Z;
4453 case 0x15c3: /* (blue) */
4454 element = EL_CHAR_AUMLAUT;
4457 case 0x15c4: /* (blue) */
4458 element = EL_CHAR_OUMLAUT;
4461 case 0x15c5: /* (blue) */
4462 element = EL_CHAR_UUMLAUT;
4465 case 0x15c6: /* (blue) */
4466 element = EL_CHAR_0;
4469 case 0x15c7: /* (blue) */
4470 element = EL_CHAR_1;
4473 case 0x15c8: /* (blue) */
4474 element = EL_CHAR_2;
4477 case 0x15c9: /* (blue) */
4478 element = EL_CHAR_3;
4481 case 0x15ca: /* (blue) */
4482 element = EL_CHAR_4;
4485 case 0x15cb: /* (blue) */
4486 element = EL_CHAR_5;
4489 case 0x15cc: /* (blue) */
4490 element = EL_CHAR_6;
4493 case 0x15cd: /* (blue) */
4494 element = EL_CHAR_7;
4497 case 0x15ce: /* (blue) */
4498 element = EL_CHAR_8;
4501 case 0x15cf: /* (blue) */
4502 element = EL_CHAR_9;
4505 case 0x15d0: /* (blue) */
4506 element = EL_CHAR_PERIOD;
4509 case 0x15d1: /* (blue) */
4510 element = EL_CHAR_EXCLAM;
4513 case 0x15d2: /* (blue) */
4514 element = EL_CHAR_COLON;
4517 case 0x15d3: /* (blue) */
4518 element = EL_CHAR_LESS;
4521 case 0x15d4: /* (blue) */
4522 element = EL_CHAR_GREATER;
4525 case 0x15d5: /* (blue) */
4526 element = EL_CHAR_QUESTION;
4529 case 0x15d6: /* (blue) */
4530 element = EL_CHAR_COPYRIGHT;
4533 case 0x15d7: /* (blue) */
4534 element = EL_CHAR_UP;
4537 case 0x15d8: /* (blue) */
4538 element = EL_CHAR_DOWN;
4541 case 0x15d9: /* (blue) */
4542 element = EL_CHAR_BUTTON;
4545 case 0x15da: /* (blue) */
4546 element = EL_CHAR_PLUS;
4549 case 0x15db: /* (blue) */
4550 element = EL_CHAR_MINUS;
4553 case 0x15dc: /* (blue) */
4554 element = EL_CHAR_APOSTROPHE;
4557 case 0x15dd: /* (blue) */
4558 element = EL_CHAR_PARENLEFT;
4561 case 0x15de: /* (blue) */
4562 element = EL_CHAR_PARENRIGHT;
4565 case 0x15df: /* (green) */
4566 element = EL_CHAR_A;
4569 case 0x15e0: /* (green) */
4570 element = EL_CHAR_B;
4573 case 0x15e1: /* (green) */
4574 element = EL_CHAR_C;
4577 case 0x15e2: /* (green) */
4578 element = EL_CHAR_D;
4581 case 0x15e3: /* (green) */
4582 element = EL_CHAR_E;
4585 case 0x15e4: /* (green) */
4586 element = EL_CHAR_F;
4589 case 0x15e5: /* (green) */
4590 element = EL_CHAR_G;
4593 case 0x15e6: /* (green) */
4594 element = EL_CHAR_H;
4597 case 0x15e7: /* (green) */
4598 element = EL_CHAR_I;
4601 case 0x15e8: /* (green) */
4602 element = EL_CHAR_J;
4605 case 0x15e9: /* (green) */
4606 element = EL_CHAR_K;
4609 case 0x15ea: /* (green) */
4610 element = EL_CHAR_L;
4613 case 0x15eb: /* (green) */
4614 element = EL_CHAR_M;
4617 case 0x15ec: /* (green) */
4618 element = EL_CHAR_N;
4621 case 0x15ed: /* (green) */
4622 element = EL_CHAR_O;
4625 case 0x15ee: /* (green) */
4626 element = EL_CHAR_P;
4629 case 0x15ef: /* (green) */
4630 element = EL_CHAR_Q;
4633 case 0x15f0: /* (green) */
4634 element = EL_CHAR_R;
4637 case 0x15f1: /* (green) */
4638 element = EL_CHAR_S;
4641 case 0x15f2: /* (green) */
4642 element = EL_CHAR_T;
4645 case 0x15f3: /* (green) */
4646 element = EL_CHAR_U;
4649 case 0x15f4: /* (green) */
4650 element = EL_CHAR_V;
4653 case 0x15f5: /* (green) */
4654 element = EL_CHAR_W;
4657 case 0x15f6: /* (green) */
4658 element = EL_CHAR_X;
4661 case 0x15f7: /* (green) */
4662 element = EL_CHAR_Y;
4665 case 0x15f8: /* (green) */
4666 element = EL_CHAR_Z;
4669 case 0x15f9: /* (green) */
4670 element = EL_CHAR_AUMLAUT;
4673 case 0x15fa: /* (green) */
4674 element = EL_CHAR_OUMLAUT;
4677 case 0x15fb: /* (green) */
4678 element = EL_CHAR_UUMLAUT;
4681 case 0x15fc: /* (green) */
4682 element = EL_CHAR_0;
4685 case 0x15fd: /* (green) */
4686 element = EL_CHAR_1;
4689 case 0x15fe: /* (green) */
4690 element = EL_CHAR_2;
4693 case 0x15ff: /* (green) */
4694 element = EL_CHAR_3;
4697 case 0x1600: /* (green) */
4698 element = EL_CHAR_4;
4701 case 0x1601: /* (green) */
4702 element = EL_CHAR_5;
4705 case 0x1602: /* (green) */
4706 element = EL_CHAR_6;
4709 case 0x1603: /* (green) */
4710 element = EL_CHAR_7;
4713 case 0x1604: /* (green) */
4714 element = EL_CHAR_8;
4717 case 0x1605: /* (green) */
4718 element = EL_CHAR_9;
4721 case 0x1606: /* (green) */
4722 element = EL_CHAR_PERIOD;
4725 case 0x1607: /* (green) */
4726 element = EL_CHAR_EXCLAM;
4729 case 0x1608: /* (green) */
4730 element = EL_CHAR_COLON;
4733 case 0x1609: /* (green) */
4734 element = EL_CHAR_LESS;
4737 case 0x160a: /* (green) */
4738 element = EL_CHAR_GREATER;
4741 case 0x160b: /* (green) */
4742 element = EL_CHAR_QUESTION;
4745 case 0x160c: /* (green) */
4746 element = EL_CHAR_COPYRIGHT;
4749 case 0x160d: /* (green) */
4750 element = EL_CHAR_UP;
4753 case 0x160e: /* (green) */
4754 element = EL_CHAR_DOWN;
4757 case 0x160f: /* (green) */
4758 element = EL_CHAR_BUTTON;
4761 case 0x1610: /* (green) */
4762 element = EL_CHAR_PLUS;
4765 case 0x1611: /* (green) */
4766 element = EL_CHAR_MINUS;
4769 case 0x1612: /* (green) */
4770 element = EL_CHAR_APOSTROPHE;
4773 case 0x1613: /* (green) */
4774 element = EL_CHAR_PARENLEFT;
4777 case 0x1614: /* (green) */
4778 element = EL_CHAR_PARENRIGHT;
4781 case 0x1615: /* (blue steel) */
4782 element = EL_STEEL_CHAR_A;
4785 case 0x1616: /* (blue steel) */
4786 element = EL_STEEL_CHAR_B;
4789 case 0x1617: /* (blue steel) */
4790 element = EL_STEEL_CHAR_C;
4793 case 0x1618: /* (blue steel) */
4794 element = EL_STEEL_CHAR_D;
4797 case 0x1619: /* (blue steel) */
4798 element = EL_STEEL_CHAR_E;
4801 case 0x161a: /* (blue steel) */
4802 element = EL_STEEL_CHAR_F;
4805 case 0x161b: /* (blue steel) */
4806 element = EL_STEEL_CHAR_G;
4809 case 0x161c: /* (blue steel) */
4810 element = EL_STEEL_CHAR_H;
4813 case 0x161d: /* (blue steel) */
4814 element = EL_STEEL_CHAR_I;
4817 case 0x161e: /* (blue steel) */
4818 element = EL_STEEL_CHAR_J;
4821 case 0x161f: /* (blue steel) */
4822 element = EL_STEEL_CHAR_K;
4825 case 0x1620: /* (blue steel) */
4826 element = EL_STEEL_CHAR_L;
4829 case 0x1621: /* (blue steel) */
4830 element = EL_STEEL_CHAR_M;
4833 case 0x1622: /* (blue steel) */
4834 element = EL_STEEL_CHAR_N;
4837 case 0x1623: /* (blue steel) */
4838 element = EL_STEEL_CHAR_O;
4841 case 0x1624: /* (blue steel) */
4842 element = EL_STEEL_CHAR_P;
4845 case 0x1625: /* (blue steel) */
4846 element = EL_STEEL_CHAR_Q;
4849 case 0x1626: /* (blue steel) */
4850 element = EL_STEEL_CHAR_R;
4853 case 0x1627: /* (blue steel) */
4854 element = EL_STEEL_CHAR_S;
4857 case 0x1628: /* (blue steel) */
4858 element = EL_STEEL_CHAR_T;
4861 case 0x1629: /* (blue steel) */
4862 element = EL_STEEL_CHAR_U;
4865 case 0x162a: /* (blue steel) */
4866 element = EL_STEEL_CHAR_V;
4869 case 0x162b: /* (blue steel) */
4870 element = EL_STEEL_CHAR_W;
4873 case 0x162c: /* (blue steel) */
4874 element = EL_STEEL_CHAR_X;
4877 case 0x162d: /* (blue steel) */
4878 element = EL_STEEL_CHAR_Y;
4881 case 0x162e: /* (blue steel) */
4882 element = EL_STEEL_CHAR_Z;
4885 case 0x162f: /* (blue steel) */
4886 element = EL_STEEL_CHAR_AUMLAUT;
4889 case 0x1630: /* (blue steel) */
4890 element = EL_STEEL_CHAR_OUMLAUT;
4893 case 0x1631: /* (blue steel) */
4894 element = EL_STEEL_CHAR_UUMLAUT;
4897 case 0x1632: /* (blue steel) */
4898 element = EL_STEEL_CHAR_0;
4901 case 0x1633: /* (blue steel) */
4902 element = EL_STEEL_CHAR_1;
4905 case 0x1634: /* (blue steel) */
4906 element = EL_STEEL_CHAR_2;
4909 case 0x1635: /* (blue steel) */
4910 element = EL_STEEL_CHAR_3;
4913 case 0x1636: /* (blue steel) */
4914 element = EL_STEEL_CHAR_4;
4917 case 0x1637: /* (blue steel) */
4918 element = EL_STEEL_CHAR_5;
4921 case 0x1638: /* (blue steel) */
4922 element = EL_STEEL_CHAR_6;
4925 case 0x1639: /* (blue steel) */
4926 element = EL_STEEL_CHAR_7;
4929 case 0x163a: /* (blue steel) */
4930 element = EL_STEEL_CHAR_8;
4933 case 0x163b: /* (blue steel) */
4934 element = EL_STEEL_CHAR_9;
4937 case 0x163c: /* (blue steel) */
4938 element = EL_STEEL_CHAR_PERIOD;
4941 case 0x163d: /* (blue steel) */
4942 element = EL_STEEL_CHAR_EXCLAM;
4945 case 0x163e: /* (blue steel) */
4946 element = EL_STEEL_CHAR_COLON;
4949 case 0x163f: /* (blue steel) */
4950 element = EL_STEEL_CHAR_LESS;
4953 case 0x1640: /* (blue steel) */
4954 element = EL_STEEL_CHAR_GREATER;
4957 case 0x1641: /* (blue steel) */
4958 element = EL_STEEL_CHAR_QUESTION;
4961 case 0x1642: /* (blue steel) */
4962 element = EL_STEEL_CHAR_COPYRIGHT;
4965 case 0x1643: /* (blue steel) */
4966 element = EL_STEEL_CHAR_UP;
4969 case 0x1644: /* (blue steel) */
4970 element = EL_STEEL_CHAR_DOWN;
4973 case 0x1645: /* (blue steel) */
4974 element = EL_STEEL_CHAR_BUTTON;
4977 case 0x1646: /* (blue steel) */
4978 element = EL_STEEL_CHAR_PLUS;
4981 case 0x1647: /* (blue steel) */
4982 element = EL_STEEL_CHAR_MINUS;
4985 case 0x1648: /* (blue steel) */
4986 element = EL_STEEL_CHAR_APOSTROPHE;
4989 case 0x1649: /* (blue steel) */
4990 element = EL_STEEL_CHAR_PARENLEFT;
4993 case 0x164a: /* (blue steel) */
4994 element = EL_STEEL_CHAR_PARENRIGHT;
4997 case 0x164b: /* (green steel) */
4998 element = EL_STEEL_CHAR_A;
5001 case 0x164c: /* (green steel) */
5002 element = EL_STEEL_CHAR_B;
5005 case 0x164d: /* (green steel) */
5006 element = EL_STEEL_CHAR_C;
5009 case 0x164e: /* (green steel) */
5010 element = EL_STEEL_CHAR_D;
5013 case 0x164f: /* (green steel) */
5014 element = EL_STEEL_CHAR_E;
5017 case 0x1650: /* (green steel) */
5018 element = EL_STEEL_CHAR_F;
5021 case 0x1651: /* (green steel) */
5022 element = EL_STEEL_CHAR_G;
5025 case 0x1652: /* (green steel) */
5026 element = EL_STEEL_CHAR_H;
5029 case 0x1653: /* (green steel) */
5030 element = EL_STEEL_CHAR_I;
5033 case 0x1654: /* (green steel) */
5034 element = EL_STEEL_CHAR_J;
5037 case 0x1655: /* (green steel) */
5038 element = EL_STEEL_CHAR_K;
5041 case 0x1656: /* (green steel) */
5042 element = EL_STEEL_CHAR_L;
5045 case 0x1657: /* (green steel) */
5046 element = EL_STEEL_CHAR_M;
5049 case 0x1658: /* (green steel) */
5050 element = EL_STEEL_CHAR_N;
5053 case 0x1659: /* (green steel) */
5054 element = EL_STEEL_CHAR_O;
5057 case 0x165a: /* (green steel) */
5058 element = EL_STEEL_CHAR_P;
5061 case 0x165b: /* (green steel) */
5062 element = EL_STEEL_CHAR_Q;
5065 case 0x165c: /* (green steel) */
5066 element = EL_STEEL_CHAR_R;
5069 case 0x165d: /* (green steel) */
5070 element = EL_STEEL_CHAR_S;
5073 case 0x165e: /* (green steel) */
5074 element = EL_STEEL_CHAR_T;
5077 case 0x165f: /* (green steel) */
5078 element = EL_STEEL_CHAR_U;
5081 case 0x1660: /* (green steel) */
5082 element = EL_STEEL_CHAR_V;
5085 case 0x1661: /* (green steel) */
5086 element = EL_STEEL_CHAR_W;
5089 case 0x1662: /* (green steel) */
5090 element = EL_STEEL_CHAR_X;
5093 case 0x1663: /* (green steel) */
5094 element = EL_STEEL_CHAR_Y;
5097 case 0x1664: /* (green steel) */
5098 element = EL_STEEL_CHAR_Z;
5101 case 0x1665: /* (green steel) */
5102 element = EL_STEEL_CHAR_AUMLAUT;
5105 case 0x1666: /* (green steel) */
5106 element = EL_STEEL_CHAR_OUMLAUT;
5109 case 0x1667: /* (green steel) */
5110 element = EL_STEEL_CHAR_UUMLAUT;
5113 case 0x1668: /* (green steel) */
5114 element = EL_STEEL_CHAR_0;
5117 case 0x1669: /* (green steel) */
5118 element = EL_STEEL_CHAR_1;
5121 case 0x166a: /* (green steel) */
5122 element = EL_STEEL_CHAR_2;
5125 case 0x166b: /* (green steel) */
5126 element = EL_STEEL_CHAR_3;
5129 case 0x166c: /* (green steel) */
5130 element = EL_STEEL_CHAR_4;
5133 case 0x166d: /* (green steel) */
5134 element = EL_STEEL_CHAR_5;
5137 case 0x166e: /* (green steel) */
5138 element = EL_STEEL_CHAR_6;
5141 case 0x166f: /* (green steel) */
5142 element = EL_STEEL_CHAR_7;
5145 case 0x1670: /* (green steel) */
5146 element = EL_STEEL_CHAR_8;
5149 case 0x1671: /* (green steel) */
5150 element = EL_STEEL_CHAR_9;
5153 case 0x1672: /* (green steel) */
5154 element = EL_STEEL_CHAR_PERIOD;
5157 case 0x1673: /* (green steel) */
5158 element = EL_STEEL_CHAR_EXCLAM;
5161 case 0x1674: /* (green steel) */
5162 element = EL_STEEL_CHAR_COLON;
5165 case 0x1675: /* (green steel) */
5166 element = EL_STEEL_CHAR_LESS;
5169 case 0x1676: /* (green steel) */
5170 element = EL_STEEL_CHAR_GREATER;
5173 case 0x1677: /* (green steel) */
5174 element = EL_STEEL_CHAR_QUESTION;
5177 case 0x1678: /* (green steel) */
5178 element = EL_STEEL_CHAR_COPYRIGHT;
5181 case 0x1679: /* (green steel) */
5182 element = EL_STEEL_CHAR_UP;
5185 case 0x167a: /* (green steel) */
5186 element = EL_STEEL_CHAR_DOWN;
5189 case 0x167b: /* (green steel) */
5190 element = EL_STEEL_CHAR_BUTTON;
5193 case 0x167c: /* (green steel) */
5194 element = EL_STEEL_CHAR_PLUS;
5197 case 0x167d: /* (green steel) */
5198 element = EL_STEEL_CHAR_MINUS;
5201 case 0x167e: /* (green steel) */
5202 element = EL_STEEL_CHAR_APOSTROPHE;
5205 case 0x167f: /* (green steel) */
5206 element = EL_STEEL_CHAR_PARENLEFT;
5209 case 0x1680: /* (green steel) */
5210 element = EL_STEEL_CHAR_PARENRIGHT;
5213 case 0x1681: /* gate (red) */
5214 element = EL_EM_GATE_1;
5217 case 0x1682: /* secret gate (red) */
5218 element = EL_GATE_1_GRAY;
5221 case 0x1683: /* gate (yellow) */
5222 element = EL_EM_GATE_2;
5225 case 0x1684: /* secret gate (yellow) */
5226 element = EL_GATE_2_GRAY;
5229 case 0x1685: /* gate (blue) */
5230 element = EL_EM_GATE_4;
5233 case 0x1686: /* secret gate (blue) */
5234 element = EL_GATE_4_GRAY;
5237 case 0x1687: /* gate (green) */
5238 element = EL_EM_GATE_3;
5241 case 0x1688: /* secret gate (green) */
5242 element = EL_GATE_3_GRAY;
5245 case 0x1689: /* gate (white) */
5246 element = EL_DC_GATE_WHITE;
5249 case 0x168a: /* secret gate (white) */
5250 element = EL_DC_GATE_WHITE_GRAY;
5253 case 0x168b: /* secret gate (no key) */
5254 element = EL_DC_GATE_FAKE_GRAY;
5258 element = EL_ROBOT_WHEEL;
5262 element = EL_DC_TIMEGATE_SWITCH;
5266 element = EL_ACID_POOL_BOTTOM;
5270 element = EL_ACID_POOL_TOPLEFT;
5274 element = EL_ACID_POOL_TOPRIGHT;
5278 element = EL_ACID_POOL_BOTTOMLEFT;
5282 element = EL_ACID_POOL_BOTTOMRIGHT;
5286 element = EL_STEELWALL;
5290 element = EL_STEELWALL_SLIPPERY;
5293 case 0x1695: /* steel wall (not round) */
5294 element = EL_STEELWALL;
5297 case 0x1696: /* steel wall (left) */
5298 element = EL_DC_STEELWALL_1_LEFT;
5301 case 0x1697: /* steel wall (bottom) */
5302 element = EL_DC_STEELWALL_1_BOTTOM;
5305 case 0x1698: /* steel wall (right) */
5306 element = EL_DC_STEELWALL_1_RIGHT;
5309 case 0x1699: /* steel wall (top) */
5310 element = EL_DC_STEELWALL_1_TOP;
5313 case 0x169a: /* steel wall (left/bottom) */
5314 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5317 case 0x169b: /* steel wall (right/bottom) */
5318 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5321 case 0x169c: /* steel wall (right/top) */
5322 element = EL_DC_STEELWALL_1_TOPRIGHT;
5325 case 0x169d: /* steel wall (left/top) */
5326 element = EL_DC_STEELWALL_1_TOPLEFT;
5329 case 0x169e: /* steel wall (right/bottom small) */
5330 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5333 case 0x169f: /* steel wall (left/bottom small) */
5334 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5337 case 0x16a0: /* steel wall (right/top small) */
5338 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5341 case 0x16a1: /* steel wall (left/top small) */
5342 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5345 case 0x16a2: /* steel wall (left/right) */
5346 element = EL_DC_STEELWALL_1_VERTICAL;
5349 case 0x16a3: /* steel wall (top/bottom) */
5350 element = EL_DC_STEELWALL_1_HORIZONTAL;
5353 case 0x16a4: /* steel wall 2 (left end) */
5354 element = EL_DC_STEELWALL_2_LEFT;
5357 case 0x16a5: /* steel wall 2 (right end) */
5358 element = EL_DC_STEELWALL_2_RIGHT;
5361 case 0x16a6: /* steel wall 2 (top end) */
5362 element = EL_DC_STEELWALL_2_TOP;
5365 case 0x16a7: /* steel wall 2 (bottom end) */
5366 element = EL_DC_STEELWALL_2_BOTTOM;
5369 case 0x16a8: /* steel wall 2 (left/right) */
5370 element = EL_DC_STEELWALL_2_HORIZONTAL;
5373 case 0x16a9: /* steel wall 2 (up/down) */
5374 element = EL_DC_STEELWALL_2_VERTICAL;
5377 case 0x16aa: /* steel wall 2 (mid) */
5378 element = EL_DC_STEELWALL_2_MIDDLE;
5382 element = EL_SIGN_EXCLAMATION;
5386 element = EL_SIGN_RADIOACTIVITY;
5390 element = EL_SIGN_STOP;
5394 element = EL_SIGN_WHEELCHAIR;
5398 element = EL_SIGN_PARKING;
5402 element = EL_SIGN_NO_ENTRY;
5406 element = EL_SIGN_HEART;
5410 element = EL_SIGN_GIVE_WAY;
5414 element = EL_SIGN_ENTRY_FORBIDDEN;
5418 element = EL_SIGN_EMERGENCY_EXIT;
5422 element = EL_SIGN_YIN_YANG;
5426 element = EL_WALL_EMERALD;
5430 element = EL_WALL_DIAMOND;
5434 element = EL_WALL_PEARL;
5438 element = EL_WALL_CRYSTAL;
5442 element = EL_INVISIBLE_WALL;
5446 element = EL_INVISIBLE_STEELWALL;
5449 /* 0x16bc - 0x16cb: */
5450 /* EL_INVISIBLE_SAND */
5453 element = EL_LIGHT_SWITCH;
5457 element = EL_ENVELOPE_1;
5461 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5462 element = EL_DIAMOND;
5463 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5464 element = EL_EMERALD;
5465 else if (element >= 0x157c && element <= 0x158b)
5467 else if (element >= 0x1590 && element <= 0x159f)
5468 element = EL_DC_LANDMINE;
5469 else if (element >= 0x16bc && element <= 0x16cb)
5470 element = EL_INVISIBLE_SAND;
5473 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5474 element = EL_UNKNOWN;
5479 return getMappedElement(element);
5482 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5485 byte header[DC_LEVEL_HEADER_SIZE];
5487 int envelope_header_pos = 62;
5488 int envelope_content_pos = 94;
5489 int level_name_pos = 251;
5490 int level_author_pos = 292;
5491 int envelope_header_len;
5492 int envelope_content_len;
5494 int level_author_len;
5496 int num_yamyam_contents;
5499 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5501 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5503 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5505 header[i * 2 + 0] = header_word >> 8;
5506 header[i * 2 + 1] = header_word & 0xff;
5509 /* read some values from level header to check level decoding integrity */
5510 fieldx = header[6] | (header[7] << 8);
5511 fieldy = header[8] | (header[9] << 8);
5512 num_yamyam_contents = header[60] | (header[61] << 8);
5514 /* do some simple sanity checks to ensure that level was correctly decoded */
5515 if (fieldx < 1 || fieldx > 256 ||
5516 fieldy < 1 || fieldy > 256 ||
5517 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5519 level->no_valid_file = TRUE;
5521 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5526 /* maximum envelope header size is 31 bytes */
5527 envelope_header_len = header[envelope_header_pos];
5528 /* maximum envelope content size is 110 (156?) bytes */
5529 envelope_content_len = header[envelope_content_pos];
5531 /* maximum level title size is 40 bytes */
5532 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5533 /* maximum level author size is 30 (51?) bytes */
5534 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5538 for (i = 0; i < envelope_header_len; i++)
5539 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5540 level->envelope[0].text[envelope_size++] =
5541 header[envelope_header_pos + 1 + i];
5543 if (envelope_header_len > 0 && envelope_content_len > 0)
5545 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5546 level->envelope[0].text[envelope_size++] = '\n';
5547 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5548 level->envelope[0].text[envelope_size++] = '\n';
5551 for (i = 0; i < envelope_content_len; i++)
5552 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5553 level->envelope[0].text[envelope_size++] =
5554 header[envelope_content_pos + 1 + i];
5556 level->envelope[0].text[envelope_size] = '\0';
5558 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5559 level->envelope[0].ysize = 10;
5560 level->envelope[0].autowrap = TRUE;
5561 level->envelope[0].centered = TRUE;
5563 for (i = 0; i < level_name_len; i++)
5564 level->name[i] = header[level_name_pos + 1 + i];
5565 level->name[level_name_len] = '\0';
5567 for (i = 0; i < level_author_len; i++)
5568 level->author[i] = header[level_author_pos + 1 + i];
5569 level->author[level_author_len] = '\0';
5571 num_yamyam_contents = header[60] | (header[61] << 8);
5572 level->num_yamyam_contents =
5573 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5575 for (i = 0; i < num_yamyam_contents; i++)
5577 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5579 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5580 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5582 if (i < MAX_ELEMENT_CONTENTS)
5583 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5587 fieldx = header[6] | (header[7] << 8);
5588 fieldy = header[8] | (header[9] << 8);
5589 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5590 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5592 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5594 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5595 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5597 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5598 level->field[x][y] = getMappedElement_DC(element_dc);
5601 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5602 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5603 level->field[x][y] = EL_PLAYER_1;
5605 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5606 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5607 level->field[x][y] = EL_PLAYER_2;
5609 level->gems_needed = header[18] | (header[19] << 8);
5611 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5612 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5613 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5614 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5615 level->score[SC_NUT] = header[28] | (header[29] << 8);
5616 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5617 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5618 level->score[SC_BUG] = header[34] | (header[35] << 8);
5619 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5620 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5621 level->score[SC_KEY] = header[40] | (header[41] << 8);
5622 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5624 level->time = header[44] | (header[45] << 8);
5626 level->amoeba_speed = header[46] | (header[47] << 8);
5627 level->time_light = header[48] | (header[49] << 8);
5628 level->time_timegate = header[50] | (header[51] << 8);
5629 level->time_wheel = header[52] | (header[53] << 8);
5630 level->time_magic_wall = header[54] | (header[55] << 8);
5631 level->extra_time = header[56] | (header[57] << 8);
5632 level->shield_normal_time = header[58] | (header[59] << 8);
5634 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5635 can slip down from flat walls, like normal walls and steel walls */
5636 level->em_slippery_gems = TRUE;
5639 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5640 struct LevelFileInfo *level_file_info,
5641 boolean level_info_only)
5643 char *filename = level_file_info->filename;
5645 int num_magic_bytes = 8;
5646 char magic_bytes[num_magic_bytes + 1];
5647 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5649 if (!(file = openFile(filename, MODE_READ)))
5651 level->no_valid_file = TRUE;
5653 if (!level_info_only)
5654 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5659 // fseek(file, 0x0000, SEEK_SET);
5661 if (level_file_info->packed)
5663 /* read "magic bytes" from start of file */
5664 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5665 magic_bytes[0] = '\0';
5667 /* check "magic bytes" for correct file format */
5668 if (!strPrefix(magic_bytes, "DC2"))
5670 level->no_valid_file = TRUE;
5672 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5678 if (strPrefix(magic_bytes, "DC2Win95") ||
5679 strPrefix(magic_bytes, "DC2Win98"))
5681 int position_first_level = 0x00fa;
5682 int extra_bytes = 4;
5685 /* advance file stream to first level inside the level package */
5686 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5688 /* each block of level data is followed by block of non-level data */
5689 num_levels_to_skip *= 2;
5691 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5692 while (num_levels_to_skip >= 0)
5694 /* advance file stream to next level inside the level package */
5695 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5697 level->no_valid_file = TRUE;
5699 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5705 /* skip apparently unused extra bytes following each level */
5706 ReadUnusedBytesFromFile(file, extra_bytes);
5708 /* read size of next level in level package */
5709 skip_bytes = getFile32BitLE(file);
5711 num_levels_to_skip--;
5716 level->no_valid_file = TRUE;
5718 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5725 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5731 /* ------------------------------------------------------------------------- */
5732 /* functions for loading SB level */
5733 /* ------------------------------------------------------------------------- */
5735 int getMappedElement_SB(int element_ascii, boolean use_ces)
5743 sb_element_mapping[] =
5745 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5746 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5747 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5748 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5749 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5750 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5751 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5752 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5759 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5760 if (element_ascii == sb_element_mapping[i].ascii)
5761 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5763 return EL_UNDEFINED;
5766 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5767 struct LevelFileInfo *level_file_info,
5768 boolean level_info_only)
5770 char *filename = level_file_info->filename;
5771 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5772 char last_comment[MAX_LINE_LEN];
5773 char level_name[MAX_LINE_LEN];
5776 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5777 boolean read_continued_line = FALSE;
5778 boolean reading_playfield = FALSE;
5779 boolean got_valid_playfield_line = FALSE;
5780 boolean invalid_playfield_char = FALSE;
5781 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5782 int file_level_nr = 0;
5784 int x = 0, y = 0; /* initialized to make compilers happy */
5786 last_comment[0] = '\0';
5787 level_name[0] = '\0';
5789 if (!(file = openFile(filename, MODE_READ)))
5791 level->no_valid_file = TRUE;
5793 if (!level_info_only)
5794 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5799 while (!checkEndOfFile(file))
5801 /* level successfully read, but next level may follow here */
5802 if (!got_valid_playfield_line && reading_playfield)
5804 /* read playfield from single level file -- skip remaining file */
5805 if (!level_file_info->packed)
5808 if (file_level_nr >= num_levels_to_skip)
5813 last_comment[0] = '\0';
5814 level_name[0] = '\0';
5816 reading_playfield = FALSE;
5819 got_valid_playfield_line = FALSE;
5821 /* read next line of input file */
5822 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5825 /* check if line was completely read and is terminated by line break */
5826 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5829 /* cut trailing line break (this can be newline and/or carriage return) */
5830 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5831 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5834 /* copy raw input line for later use (mainly debugging output) */
5835 strcpy(line_raw, line);
5837 if (read_continued_line)
5839 /* append new line to existing line, if there is enough space */
5840 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5841 strcat(previous_line, line_ptr);
5843 strcpy(line, previous_line); /* copy storage buffer to line */
5845 read_continued_line = FALSE;
5848 /* if the last character is '\', continue at next line */
5849 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5851 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5852 strcpy(previous_line, line); /* copy line to storage buffer */
5854 read_continued_line = TRUE;
5859 /* skip empty lines */
5860 if (line[0] == '\0')
5863 /* extract comment text from comment line */
5866 for (line_ptr = line; *line_ptr; line_ptr++)
5867 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5870 strcpy(last_comment, line_ptr);
5875 /* extract level title text from line containing level title */
5876 if (line[0] == '\'')
5878 strcpy(level_name, &line[1]);
5880 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5881 level_name[strlen(level_name) - 1] = '\0';
5886 /* skip lines containing only spaces (or empty lines) */
5887 for (line_ptr = line; *line_ptr; line_ptr++)
5888 if (*line_ptr != ' ')
5890 if (*line_ptr == '\0')
5893 /* at this point, we have found a line containing part of a playfield */
5895 got_valid_playfield_line = TRUE;
5897 if (!reading_playfield)
5899 reading_playfield = TRUE;
5900 invalid_playfield_char = FALSE;
5902 for (x = 0; x < MAX_LEV_FIELDX; x++)
5903 for (y = 0; y < MAX_LEV_FIELDY; y++)
5904 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5909 /* start with topmost tile row */
5913 /* skip playfield line if larger row than allowed */
5914 if (y >= MAX_LEV_FIELDY)
5917 /* start with leftmost tile column */
5920 /* read playfield elements from line */
5921 for (line_ptr = line; *line_ptr; line_ptr++)
5923 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5925 /* stop parsing playfield line if larger column than allowed */
5926 if (x >= MAX_LEV_FIELDX)
5929 if (mapped_sb_element == EL_UNDEFINED)
5931 invalid_playfield_char = TRUE;
5936 level->field[x][y] = mapped_sb_element;
5938 /* continue with next tile column */
5941 level->fieldx = MAX(x, level->fieldx);
5944 if (invalid_playfield_char)
5946 /* if first playfield line, treat invalid lines as comment lines */
5948 reading_playfield = FALSE;
5953 /* continue with next tile row */
5961 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
5962 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
5964 if (!reading_playfield)
5966 level->no_valid_file = TRUE;
5968 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5973 if (*level_name != '\0')
5975 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
5976 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5978 else if (*last_comment != '\0')
5980 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
5981 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5985 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
5988 /* set all empty fields beyond the border walls to invisible steel wall */
5989 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5991 if ((x == 0 || x == level->fieldx - 1 ||
5992 y == 0 || y == level->fieldy - 1) &&
5993 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
5994 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
5995 level->field, level->fieldx, level->fieldy);
5998 /* set special level settings for Sokoban levels */
6001 level->use_step_counter = TRUE;
6003 if (load_xsb_to_ces)
6005 /* special global settings can now be set in level template */
6007 level->use_custom_template = TRUE;
6012 /* ------------------------------------------------------------------------- */
6013 /* functions for handling native levels */
6014 /* ------------------------------------------------------------------------- */
6016 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6017 struct LevelFileInfo *level_file_info,
6018 boolean level_info_only)
6020 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6021 level->no_valid_file = TRUE;
6024 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6025 struct LevelFileInfo *level_file_info,
6026 boolean level_info_only)
6030 /* determine position of requested level inside level package */
6031 if (level_file_info->packed)
6032 pos = level_file_info->nr - leveldir_current->first_level;
6034 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6035 level->no_valid_file = TRUE;
6038 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6039 struct LevelFileInfo *level_file_info,
6040 boolean level_info_only)
6042 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6043 level->no_valid_file = TRUE;
6046 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6048 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6049 CopyNativeLevel_RND_to_EM(level);
6050 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6051 CopyNativeLevel_RND_to_SP(level);
6052 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6053 CopyNativeLevel_RND_to_MM(level);
6056 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6058 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6059 CopyNativeLevel_EM_to_RND(level);
6060 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6061 CopyNativeLevel_SP_to_RND(level);
6062 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6063 CopyNativeLevel_MM_to_RND(level);
6066 void SaveNativeLevel(struct LevelInfo *level)
6068 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6070 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6071 char *filename = getLevelFilenameFromBasename(basename);
6073 CopyNativeLevel_RND_to_SP(level);
6074 CopyNativeTape_RND_to_SP(level);
6076 SaveNativeLevel_SP(filename);
6081 /* ------------------------------------------------------------------------- */
6082 /* functions for loading generic level */
6083 /* ------------------------------------------------------------------------- */
6085 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6086 struct LevelFileInfo *level_file_info,
6087 boolean level_info_only)
6089 /* always start with reliable default values */
6090 setLevelInfoToDefaults(level, level_info_only, TRUE);
6092 switch (level_file_info->type)
6094 case LEVEL_FILE_TYPE_RND:
6095 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6098 case LEVEL_FILE_TYPE_EM:
6099 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6100 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6103 case LEVEL_FILE_TYPE_SP:
6104 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6105 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6108 case LEVEL_FILE_TYPE_MM:
6109 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6110 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6113 case LEVEL_FILE_TYPE_DC:
6114 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6117 case LEVEL_FILE_TYPE_SB:
6118 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6122 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6126 /* if level file is invalid, restore level structure to default values */
6127 if (level->no_valid_file)
6128 setLevelInfoToDefaults(level, level_info_only, FALSE);
6130 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6131 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6133 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6134 CopyNativeLevel_Native_to_RND(level);
6137 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6139 static struct LevelFileInfo level_file_info;
6141 /* always start with reliable default values */
6142 setFileInfoToDefaults(&level_file_info);
6144 level_file_info.nr = 0; /* unknown level number */
6145 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
6146 level_file_info.filename = filename;
6148 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6151 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
6155 if (leveldir_current == NULL) /* only when dumping level */
6158 /* all engine modifications also valid for levels which use latest engine */
6159 if (level->game_version < VERSION_IDENT(3,2,0,5))
6161 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6162 level->score[SC_TIME_BONUS] /= 10;
6165 if (leveldir_current->latest_engine)
6167 /* ---------- use latest game engine ----------------------------------- */
6169 /* For all levels which are forced to use the latest game engine version
6170 (normally all but user contributed, private and undefined levels), set
6171 the game engine version to the actual version; this allows for actual
6172 corrections in the game engine to take effect for existing, converted
6173 levels (from "classic" or other existing games) to make the emulation
6174 of the corresponding game more accurate, while (hopefully) not breaking
6175 existing levels created from other players. */
6177 level->game_version = GAME_VERSION_ACTUAL;
6179 /* Set special EM style gems behaviour: EM style gems slip down from
6180 normal, steel and growing wall. As this is a more fundamental change,
6181 it seems better to set the default behaviour to "off" (as it is more
6182 natural) and make it configurable in the level editor (as a property
6183 of gem style elements). Already existing converted levels (neither
6184 private nor contributed levels) are changed to the new behaviour. */
6186 if (level->file_version < FILE_VERSION_2_0)
6187 level->em_slippery_gems = TRUE;
6192 /* ---------- use game engine the level was created with ----------------- */
6194 /* For all levels which are not forced to use the latest game engine
6195 version (normally user contributed, private and undefined levels),
6196 use the version of the game engine the levels were created for.
6198 Since 2.0.1, the game engine version is now directly stored
6199 in the level file (chunk "VERS"), so there is no need anymore
6200 to set the game version from the file version (except for old,
6201 pre-2.0 levels, where the game version is still taken from the
6202 file format version used to store the level -- see above). */
6204 /* player was faster than enemies in 1.0.0 and before */
6205 if (level->file_version == FILE_VERSION_1_0)
6206 for (i = 0; i < MAX_PLAYERS; i++)
6207 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6209 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6210 if (level->game_version == VERSION_IDENT(2,0,1,0))
6211 level->em_slippery_gems = TRUE;
6213 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6214 if (level->game_version < VERSION_IDENT(2,2,0,0))
6215 level->use_spring_bug = TRUE;
6217 if (level->game_version < VERSION_IDENT(3,2,0,5))
6219 /* time orb caused limited time in endless time levels before 3.2.0-5 */
6220 level->use_time_orb_bug = TRUE;
6222 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6223 level->block_snap_field = FALSE;
6225 /* extra time score was same value as time left score before 3.2.0-5 */
6226 level->extra_time_score = level->score[SC_TIME_BONUS];
6229 if (level->game_version < VERSION_IDENT(3,2,0,7))
6231 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6232 level->continuous_snapping = FALSE;
6235 /* only few elements were able to actively move into acid before 3.1.0 */
6236 /* trigger settings did not exist before 3.1.0; set to default "any" */
6237 if (level->game_version < VERSION_IDENT(3,1,0,0))
6239 /* correct "can move into acid" settings (all zero in old levels) */
6241 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6242 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6244 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6245 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6246 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6247 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6249 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6250 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6252 /* correct trigger settings (stored as zero == "none" in old levels) */
6254 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6256 int element = EL_CUSTOM_START + i;
6257 struct ElementInfo *ei = &element_info[element];
6259 for (j = 0; j < ei->num_change_pages; j++)
6261 struct ElementChangeInfo *change = &ei->change_page[j];
6263 change->trigger_player = CH_PLAYER_ANY;
6264 change->trigger_page = CH_PAGE_ANY;
6269 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6271 int element = EL_CUSTOM_256;
6272 struct ElementInfo *ei = &element_info[element];
6273 struct ElementChangeInfo *change = &ei->change_page[0];
6275 /* This is needed to fix a problem that was caused by a bugfix in function
6276 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6277 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6278 not replace walkable elements, but instead just placed the player on it,
6279 without placing the Sokoban field under the player). Unfortunately, this
6280 breaks "Snake Bite" style levels when the snake is halfway through a door
6281 that just closes (the snake head is still alive and can be moved in this
6282 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6283 player (without Sokoban element) which then gets killed as designed). */
6285 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6286 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6287 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6288 change->target_element = EL_PLAYER_1;
6291 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6292 if (level->game_version < VERSION_IDENT(3,2,5,0))
6294 /* This is needed to fix a problem that was caused by a bugfix in function
6295 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6296 corrects the behaviour when a custom element changes to another custom
6297 element with a higher element number that has change actions defined.
6298 Normally, only one change per frame is allowed for custom elements.
6299 Therefore, it is checked if a custom element already changed in the
6300 current frame; if it did, subsequent changes are suppressed.
6301 Unfortunately, this is only checked for element changes, but not for
6302 change actions, which are still executed. As the function above loops
6303 through all custom elements from lower to higher, an element change
6304 resulting in a lower CE number won't be checked again, while a target
6305 element with a higher number will also be checked, and potential change
6306 actions will get executed for this CE, too (which is wrong), while
6307 further changes are ignored (which is correct). As this bugfix breaks
6308 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6309 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6310 behaviour for existing levels and tapes that make use of this bug */
6312 level->use_action_after_change_bug = TRUE;
6315 /* not centering level after relocating player was default only in 3.2.3 */
6316 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6317 level->shifted_relocation = TRUE;
6319 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6320 if (level->game_version < VERSION_IDENT(3,2,6,0))
6321 level->em_explodes_by_fire = TRUE;
6324 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6328 /* map elements that have changed in newer versions */
6329 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6330 level->game_version);
6331 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6332 for (x = 0; x < 3; x++)
6333 for (y = 0; y < 3; y++)
6334 level->yamyam_content[i].e[x][y] =
6335 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6336 level->game_version);
6340 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6344 /* map custom element change events that have changed in newer versions
6345 (these following values were accidentally changed in version 3.0.1)
6346 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6347 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6349 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6351 int element = EL_CUSTOM_START + i;
6353 /* order of checking and copying events to be mapped is important */
6354 /* (do not change the start and end value -- they are constant) */
6355 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6357 if (HAS_CHANGE_EVENT(element, j - 2))
6359 SET_CHANGE_EVENT(element, j - 2, FALSE);
6360 SET_CHANGE_EVENT(element, j, TRUE);
6364 /* order of checking and copying events to be mapped is important */
6365 /* (do not change the start and end value -- they are constant) */
6366 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6368 if (HAS_CHANGE_EVENT(element, j - 1))
6370 SET_CHANGE_EVENT(element, j - 1, FALSE);
6371 SET_CHANGE_EVENT(element, j, TRUE);
6377 /* initialize "can_change" field for old levels with only one change page */
6378 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6380 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6382 int element = EL_CUSTOM_START + i;
6384 if (CAN_CHANGE(element))
6385 element_info[element].change->can_change = TRUE;
6389 /* correct custom element values (for old levels without these options) */
6390 if (level->game_version < VERSION_IDENT(3,1,1,0))
6392 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6394 int element = EL_CUSTOM_START + i;
6395 struct ElementInfo *ei = &element_info[element];
6397 if (ei->access_direction == MV_NO_DIRECTION)
6398 ei->access_direction = MV_ALL_DIRECTIONS;
6402 /* correct custom element values (fix invalid values for all versions) */
6405 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6407 int element = EL_CUSTOM_START + i;
6408 struct ElementInfo *ei = &element_info[element];
6410 for (j = 0; j < ei->num_change_pages; j++)
6412 struct ElementChangeInfo *change = &ei->change_page[j];
6414 if (change->trigger_player == CH_PLAYER_NONE)
6415 change->trigger_player = CH_PLAYER_ANY;
6417 if (change->trigger_side == CH_SIDE_NONE)
6418 change->trigger_side = CH_SIDE_ANY;
6423 /* initialize "can_explode" field for old levels which did not store this */
6424 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6425 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6427 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6429 int element = EL_CUSTOM_START + i;
6431 if (EXPLODES_1X1_OLD(element))
6432 element_info[element].explosion_type = EXPLODES_1X1;
6434 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6435 EXPLODES_SMASHED(element) ||
6436 EXPLODES_IMPACT(element)));
6440 /* correct previously hard-coded move delay values for maze runner style */
6441 if (level->game_version < VERSION_IDENT(3,1,1,0))
6443 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6445 int element = EL_CUSTOM_START + i;
6447 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6449 /* previously hard-coded and therefore ignored */
6450 element_info[element].move_delay_fixed = 9;
6451 element_info[element].move_delay_random = 0;
6457 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6459 LoadLevel_InitStandardElements(level);
6461 if (level->file_has_custom_elements)
6462 LoadLevel_InitCustomElements(level);
6464 if (level->file_has_custom_elements)
6465 InitElementPropertiesAfterLoading(level->game_version);
6467 /* initialize element properties for level editor etc. */
6468 InitElementPropertiesEngine(level->game_version);
6469 InitElementPropertiesGfxElement();
6472 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6476 /* map elements that have changed in newer versions */
6477 for (y = 0; y < level->fieldy; y++)
6478 for (x = 0; x < level->fieldx; x++)
6479 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6480 level->game_version);
6482 /* clear unused playfield data (nicer if level gets resized in editor) */
6483 for (x = 0; x < MAX_LEV_FIELDX; x++)
6484 for (y = 0; y < MAX_LEV_FIELDY; y++)
6485 if (x >= level->fieldx || y >= level->fieldy)
6486 level->field[x][y] = EL_EMPTY;
6488 /* copy elements to runtime playfield array */
6489 for (x = 0; x < MAX_LEV_FIELDX; x++)
6490 for (y = 0; y < MAX_LEV_FIELDY; y++)
6491 Feld[x][y] = level->field[x][y];
6493 /* initialize level size variables for faster access */
6494 lev_fieldx = level->fieldx;
6495 lev_fieldy = level->fieldy;
6497 /* determine border element for this level */
6498 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6499 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6504 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6506 struct LevelFileInfo *level_file_info = &level->file_info;
6508 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6509 CopyNativeLevel_RND_to_Native(level);
6512 void LoadLevelTemplate(int nr)
6516 setLevelFileInfo(&level_template.file_info, nr);
6517 filename = level_template.file_info.filename;
6519 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6521 LoadLevel_InitVersion(&level_template, filename);
6522 LoadLevel_InitElements(&level_template, filename);
6524 ActivateLevelTemplate();
6527 void LoadLevel(int nr)
6531 setLevelFileInfo(&level.file_info, nr);
6532 filename = level.file_info.filename;
6534 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6536 if (level.use_custom_template)
6537 LoadLevelTemplate(-1);
6539 LoadLevel_InitVersion(&level, filename);
6540 LoadLevel_InitElements(&level, filename);
6541 LoadLevel_InitPlayfield(&level, filename);
6543 LoadLevel_InitNativeEngines(&level, filename);
6546 void LoadLevelInfoOnly(int nr)
6548 setLevelFileInfo(&level.file_info, nr);
6550 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6553 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6557 chunk_size += putFileVersion(file, level->file_version);
6558 chunk_size += putFileVersion(file, level->game_version);
6563 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6567 chunk_size += putFile16BitBE(file, level->creation_date.year);
6568 chunk_size += putFile8Bit(file, level->creation_date.month);
6569 chunk_size += putFile8Bit(file, level->creation_date.day);
6574 #if ENABLE_HISTORIC_CHUNKS
6575 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6579 putFile8Bit(file, level->fieldx);
6580 putFile8Bit(file, level->fieldy);
6582 putFile16BitBE(file, level->time);
6583 putFile16BitBE(file, level->gems_needed);
6585 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6586 putFile8Bit(file, level->name[i]);
6588 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6589 putFile8Bit(file, level->score[i]);
6591 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6592 for (y = 0; y < 3; y++)
6593 for (x = 0; x < 3; x++)
6594 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6595 level->yamyam_content[i].e[x][y]));
6596 putFile8Bit(file, level->amoeba_speed);
6597 putFile8Bit(file, level->time_magic_wall);
6598 putFile8Bit(file, level->time_wheel);
6599 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6600 level->amoeba_content));
6601 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6602 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6603 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6604 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6606 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6608 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6609 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6610 putFile32BitBE(file, level->can_move_into_acid_bits);
6611 putFile8Bit(file, level->dont_collide_with_bits);
6613 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6614 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6616 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6617 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6618 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6620 putFile8Bit(file, level->game_engine_type);
6622 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6626 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6631 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6632 chunk_size += putFile8Bit(file, level->name[i]);
6637 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6642 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6643 chunk_size += putFile8Bit(file, level->author[i]);
6648 #if ENABLE_HISTORIC_CHUNKS
6649 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6654 for (y = 0; y < level->fieldy; y++)
6655 for (x = 0; x < level->fieldx; x++)
6656 if (level->encoding_16bit_field)
6657 chunk_size += putFile16BitBE(file, level->field[x][y]);
6659 chunk_size += putFile8Bit(file, level->field[x][y]);
6665 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6670 for (y = 0; y < level->fieldy; y++)
6671 for (x = 0; x < level->fieldx; x++)
6672 chunk_size += putFile16BitBE(file, level->field[x][y]);
6677 #if ENABLE_HISTORIC_CHUNKS
6678 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6682 putFile8Bit(file, EL_YAMYAM);
6683 putFile8Bit(file, level->num_yamyam_contents);
6684 putFile8Bit(file, 0);
6685 putFile8Bit(file, 0);
6687 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6688 for (y = 0; y < 3; y++)
6689 for (x = 0; x < 3; x++)
6690 if (level->encoding_16bit_field)
6691 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6693 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6697 #if ENABLE_HISTORIC_CHUNKS
6698 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6701 int num_contents, content_xsize, content_ysize;
6702 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6704 if (element == EL_YAMYAM)
6706 num_contents = level->num_yamyam_contents;
6710 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6711 for (y = 0; y < 3; y++)
6712 for (x = 0; x < 3; x++)
6713 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6715 else if (element == EL_BD_AMOEBA)
6721 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6722 for (y = 0; y < 3; y++)
6723 for (x = 0; x < 3; x++)
6724 content_array[i][x][y] = EL_EMPTY;
6725 content_array[0][0][0] = level->amoeba_content;
6729 /* chunk header already written -- write empty chunk data */
6730 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6732 Error(ERR_WARN, "cannot save content for element '%d'", element);
6736 putFile16BitBE(file, element);
6737 putFile8Bit(file, num_contents);
6738 putFile8Bit(file, content_xsize);
6739 putFile8Bit(file, content_ysize);
6741 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6743 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6744 for (y = 0; y < 3; y++)
6745 for (x = 0; x < 3; x++)
6746 putFile16BitBE(file, content_array[i][x][y]);
6750 #if ENABLE_HISTORIC_CHUNKS
6751 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6753 int envelope_nr = element - EL_ENVELOPE_1;
6754 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6758 chunk_size += putFile16BitBE(file, element);
6759 chunk_size += putFile16BitBE(file, envelope_len);
6760 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6761 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6763 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6764 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6766 for (i = 0; i < envelope_len; i++)
6767 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6773 #if ENABLE_HISTORIC_CHUNKS
6774 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6775 int num_changed_custom_elements)
6779 putFile16BitBE(file, num_changed_custom_elements);
6781 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6783 int element = EL_CUSTOM_START + i;
6785 struct ElementInfo *ei = &element_info[element];
6787 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6789 if (check < num_changed_custom_elements)
6791 putFile16BitBE(file, element);
6792 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6799 if (check != num_changed_custom_elements) /* should not happen */
6800 Error(ERR_WARN, "inconsistent number of custom element properties");
6804 #if ENABLE_HISTORIC_CHUNKS
6805 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6806 int num_changed_custom_elements)
6810 putFile16BitBE(file, num_changed_custom_elements);
6812 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6814 int element = EL_CUSTOM_START + i;
6816 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6818 if (check < num_changed_custom_elements)
6820 putFile16BitBE(file, element);
6821 putFile16BitBE(file, element_info[element].change->target_element);
6828 if (check != num_changed_custom_elements) /* should not happen */
6829 Error(ERR_WARN, "inconsistent number of custom target elements");
6833 #if ENABLE_HISTORIC_CHUNKS
6834 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6835 int num_changed_custom_elements)
6837 int i, j, x, y, check = 0;
6839 putFile16BitBE(file, num_changed_custom_elements);
6841 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6843 int element = EL_CUSTOM_START + i;
6844 struct ElementInfo *ei = &element_info[element];
6846 if (ei->modified_settings)
6848 if (check < num_changed_custom_elements)
6850 putFile16BitBE(file, element);
6852 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6853 putFile8Bit(file, ei->description[j]);
6855 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6857 /* some free bytes for future properties and padding */
6858 WriteUnusedBytesToFile(file, 7);
6860 putFile8Bit(file, ei->use_gfx_element);
6861 putFile16BitBE(file, ei->gfx_element_initial);
6863 putFile8Bit(file, ei->collect_score_initial);
6864 putFile8Bit(file, ei->collect_count_initial);
6866 putFile16BitBE(file, ei->push_delay_fixed);
6867 putFile16BitBE(file, ei->push_delay_random);
6868 putFile16BitBE(file, ei->move_delay_fixed);
6869 putFile16BitBE(file, ei->move_delay_random);
6871 putFile16BitBE(file, ei->move_pattern);
6872 putFile8Bit(file, ei->move_direction_initial);
6873 putFile8Bit(file, ei->move_stepsize);
6875 for (y = 0; y < 3; y++)
6876 for (x = 0; x < 3; x++)
6877 putFile16BitBE(file, ei->content.e[x][y]);
6879 putFile32BitBE(file, ei->change->events);
6881 putFile16BitBE(file, ei->change->target_element);
6883 putFile16BitBE(file, ei->change->delay_fixed);
6884 putFile16BitBE(file, ei->change->delay_random);
6885 putFile16BitBE(file, ei->change->delay_frames);
6887 putFile16BitBE(file, ei->change->initial_trigger_element);
6889 putFile8Bit(file, ei->change->explode);
6890 putFile8Bit(file, ei->change->use_target_content);
6891 putFile8Bit(file, ei->change->only_if_complete);
6892 putFile8Bit(file, ei->change->use_random_replace);
6894 putFile8Bit(file, ei->change->random_percentage);
6895 putFile8Bit(file, ei->change->replace_when);
6897 for (y = 0; y < 3; y++)
6898 for (x = 0; x < 3; x++)
6899 putFile16BitBE(file, ei->change->content.e[x][y]);
6901 putFile8Bit(file, ei->slippery_type);
6903 /* some free bytes for future properties and padding */
6904 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6911 if (check != num_changed_custom_elements) /* should not happen */
6912 Error(ERR_WARN, "inconsistent number of custom element properties");
6916 #if ENABLE_HISTORIC_CHUNKS
6917 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6919 struct ElementInfo *ei = &element_info[element];
6922 /* ---------- custom element base property values (96 bytes) ------------- */
6924 putFile16BitBE(file, element);
6926 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6927 putFile8Bit(file, ei->description[i]);
6929 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6931 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
6933 putFile8Bit(file, ei->num_change_pages);
6935 putFile16BitBE(file, ei->ce_value_fixed_initial);
6936 putFile16BitBE(file, ei->ce_value_random_initial);
6937 putFile8Bit(file, ei->use_last_ce_value);
6939 putFile8Bit(file, ei->use_gfx_element);
6940 putFile16BitBE(file, ei->gfx_element_initial);
6942 putFile8Bit(file, ei->collect_score_initial);
6943 putFile8Bit(file, ei->collect_count_initial);
6945 putFile8Bit(file, ei->drop_delay_fixed);
6946 putFile8Bit(file, ei->push_delay_fixed);
6947 putFile8Bit(file, ei->drop_delay_random);
6948 putFile8Bit(file, ei->push_delay_random);
6949 putFile16BitBE(file, ei->move_delay_fixed);
6950 putFile16BitBE(file, ei->move_delay_random);
6952 /* bits 0 - 15 of "move_pattern" ... */
6953 putFile16BitBE(file, ei->move_pattern & 0xffff);
6954 putFile8Bit(file, ei->move_direction_initial);
6955 putFile8Bit(file, ei->move_stepsize);
6957 putFile8Bit(file, ei->slippery_type);
6959 for (y = 0; y < 3; y++)
6960 for (x = 0; x < 3; x++)
6961 putFile16BitBE(file, ei->content.e[x][y]);
6963 putFile16BitBE(file, ei->move_enter_element);
6964 putFile16BitBE(file, ei->move_leave_element);
6965 putFile8Bit(file, ei->move_leave_type);
6967 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
6968 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
6970 putFile8Bit(file, ei->access_direction);
6972 putFile8Bit(file, ei->explosion_delay);
6973 putFile8Bit(file, ei->ignition_delay);
6974 putFile8Bit(file, ei->explosion_type);
6976 /* some free bytes for future custom property values and padding */
6977 WriteUnusedBytesToFile(file, 1);
6979 /* ---------- change page property values (48 bytes) --------------------- */
6981 for (i = 0; i < ei->num_change_pages; i++)
6983 struct ElementChangeInfo *change = &ei->change_page[i];
6984 unsigned int event_bits;
6986 /* bits 0 - 31 of "has_event[]" ... */
6988 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
6989 if (change->has_event[j])
6990 event_bits |= (1 << j);
6991 putFile32BitBE(file, event_bits);
6993 putFile16BitBE(file, change->target_element);
6995 putFile16BitBE(file, change->delay_fixed);
6996 putFile16BitBE(file, change->delay_random);
6997 putFile16BitBE(file, change->delay_frames);
6999 putFile16BitBE(file, change->initial_trigger_element);
7001 putFile8Bit(file, change->explode);
7002 putFile8Bit(file, change->use_target_content);
7003 putFile8Bit(file, change->only_if_complete);
7004 putFile8Bit(file, change->use_random_replace);
7006 putFile8Bit(file, change->random_percentage);
7007 putFile8Bit(file, change->replace_when);
7009 for (y = 0; y < 3; y++)
7010 for (x = 0; x < 3; x++)
7011 putFile16BitBE(file, change->target_content.e[x][y]);
7013 putFile8Bit(file, change->can_change);
7015 putFile8Bit(file, change->trigger_side);
7017 putFile8Bit(file, change->trigger_player);
7018 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7019 log_2(change->trigger_page)));
7021 putFile8Bit(file, change->has_action);
7022 putFile8Bit(file, change->action_type);
7023 putFile8Bit(file, change->action_mode);
7024 putFile16BitBE(file, change->action_arg);
7026 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7028 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7029 if (change->has_event[j])
7030 event_bits |= (1 << (j - 32));
7031 putFile8Bit(file, event_bits);
7036 #if ENABLE_HISTORIC_CHUNKS
7037 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7039 struct ElementInfo *ei = &element_info[element];
7040 struct ElementGroupInfo *group = ei->group;
7043 putFile16BitBE(file, element);
7045 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7046 putFile8Bit(file, ei->description[i]);
7048 putFile8Bit(file, group->num_elements);
7050 putFile8Bit(file, ei->use_gfx_element);
7051 putFile16BitBE(file, ei->gfx_element_initial);
7053 putFile8Bit(file, group->choice_mode);
7055 /* some free bytes for future values and padding */
7056 WriteUnusedBytesToFile(file, 3);
7058 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7059 putFile16BitBE(file, group->element[i]);
7063 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7064 boolean write_element)
7066 int save_type = entry->save_type;
7067 int data_type = entry->data_type;
7068 int conf_type = entry->conf_type;
7069 int byte_mask = conf_type & CONF_MASK_BYTES;
7070 int element = entry->element;
7071 int default_value = entry->default_value;
7073 boolean modified = FALSE;
7075 if (byte_mask != CONF_MASK_MULTI_BYTES)
7077 void *value_ptr = entry->value;
7078 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7081 /* check if any settings have been modified before saving them */
7082 if (value != default_value)
7085 /* do not save if explicitly told or if unmodified default settings */
7086 if ((save_type == SAVE_CONF_NEVER) ||
7087 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7091 num_bytes += putFile16BitBE(file, element);
7093 num_bytes += putFile8Bit(file, conf_type);
7094 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7095 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7096 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7099 else if (data_type == TYPE_STRING)
7101 char *default_string = entry->default_string;
7102 char *string = (char *)(entry->value);
7103 int string_length = strlen(string);
7106 /* check if any settings have been modified before saving them */
7107 if (!strEqual(string, default_string))
7110 /* do not save if explicitly told or if unmodified default settings */
7111 if ((save_type == SAVE_CONF_NEVER) ||
7112 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7116 num_bytes += putFile16BitBE(file, element);
7118 num_bytes += putFile8Bit(file, conf_type);
7119 num_bytes += putFile16BitBE(file, string_length);
7121 for (i = 0; i < string_length; i++)
7122 num_bytes += putFile8Bit(file, string[i]);
7124 else if (data_type == TYPE_ELEMENT_LIST)
7126 int *element_array = (int *)(entry->value);
7127 int num_elements = *(int *)(entry->num_entities);
7130 /* check if any settings have been modified before saving them */
7131 for (i = 0; i < num_elements; i++)
7132 if (element_array[i] != default_value)
7135 /* do not save if explicitly told or if unmodified default settings */
7136 if ((save_type == SAVE_CONF_NEVER) ||
7137 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7141 num_bytes += putFile16BitBE(file, element);
7143 num_bytes += putFile8Bit(file, conf_type);
7144 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7146 for (i = 0; i < num_elements; i++)
7147 num_bytes += putFile16BitBE(file, element_array[i]);
7149 else if (data_type == TYPE_CONTENT_LIST)
7151 struct Content *content = (struct Content *)(entry->value);
7152 int num_contents = *(int *)(entry->num_entities);
7155 /* check if any settings have been modified before saving them */
7156 for (i = 0; i < num_contents; i++)
7157 for (y = 0; y < 3; y++)
7158 for (x = 0; x < 3; x++)
7159 if (content[i].e[x][y] != default_value)
7162 /* do not save if explicitly told or if unmodified default settings */
7163 if ((save_type == SAVE_CONF_NEVER) ||
7164 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7168 num_bytes += putFile16BitBE(file, element);
7170 num_bytes += putFile8Bit(file, conf_type);
7171 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7173 for (i = 0; i < num_contents; i++)
7174 for (y = 0; y < 3; y++)
7175 for (x = 0; x < 3; x++)
7176 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7182 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7187 li = *level; /* copy level data into temporary buffer */
7189 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7190 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7195 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7200 li = *level; /* copy level data into temporary buffer */
7202 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7203 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7208 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7210 int envelope_nr = element - EL_ENVELOPE_1;
7214 chunk_size += putFile16BitBE(file, element);
7216 /* copy envelope data into temporary buffer */
7217 xx_envelope = level->envelope[envelope_nr];
7219 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7220 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7225 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7227 struct ElementInfo *ei = &element_info[element];
7231 chunk_size += putFile16BitBE(file, element);
7233 xx_ei = *ei; /* copy element data into temporary buffer */
7235 /* set default description string for this specific element */
7236 strcpy(xx_default_description, getDefaultElementDescription(ei));
7238 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7239 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7241 for (i = 0; i < ei->num_change_pages; i++)
7243 struct ElementChangeInfo *change = &ei->change_page[i];
7245 xx_current_change_page = i;
7247 xx_change = *change; /* copy change data into temporary buffer */
7250 setEventBitsFromEventFlags(change);
7252 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7253 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7260 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7262 struct ElementInfo *ei = &element_info[element];
7263 struct ElementGroupInfo *group = ei->group;
7267 chunk_size += putFile16BitBE(file, element);
7269 xx_ei = *ei; /* copy element data into temporary buffer */
7270 xx_group = *group; /* copy group data into temporary buffer */
7272 /* set default description string for this specific element */
7273 strcpy(xx_default_description, getDefaultElementDescription(ei));
7275 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7276 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7281 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7282 boolean save_as_template)
7288 if (!(file = fopen(filename, MODE_WRITE)))
7290 Error(ERR_WARN, "cannot save level file '%s'", filename);
7294 level->file_version = FILE_VERSION_ACTUAL;
7295 level->game_version = GAME_VERSION_ACTUAL;
7297 level->creation_date = getCurrentDate();
7299 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7300 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7302 chunk_size = SaveLevel_VERS(NULL, level);
7303 putFileChunkBE(file, "VERS", chunk_size);
7304 SaveLevel_VERS(file, level);
7306 chunk_size = SaveLevel_DATE(NULL, level);
7307 putFileChunkBE(file, "DATE", chunk_size);
7308 SaveLevel_DATE(file, level);
7310 chunk_size = SaveLevel_NAME(NULL, level);
7311 putFileChunkBE(file, "NAME", chunk_size);
7312 SaveLevel_NAME(file, level);
7314 chunk_size = SaveLevel_AUTH(NULL, level);
7315 putFileChunkBE(file, "AUTH", chunk_size);
7316 SaveLevel_AUTH(file, level);
7318 chunk_size = SaveLevel_INFO(NULL, level);
7319 putFileChunkBE(file, "INFO", chunk_size);
7320 SaveLevel_INFO(file, level);
7322 chunk_size = SaveLevel_BODY(NULL, level);
7323 putFileChunkBE(file, "BODY", chunk_size);
7324 SaveLevel_BODY(file, level);
7326 chunk_size = SaveLevel_ELEM(NULL, level);
7327 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7329 putFileChunkBE(file, "ELEM", chunk_size);
7330 SaveLevel_ELEM(file, level);
7333 for (i = 0; i < NUM_ENVELOPES; i++)
7335 int element = EL_ENVELOPE_1 + i;
7337 chunk_size = SaveLevel_NOTE(NULL, level, element);
7338 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7340 putFileChunkBE(file, "NOTE", chunk_size);
7341 SaveLevel_NOTE(file, level, element);
7345 /* if not using template level, check for non-default custom/group elements */
7346 if (!level->use_custom_template || save_as_template)
7348 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7350 int element = EL_CUSTOM_START + i;
7352 chunk_size = SaveLevel_CUSX(NULL, level, element);
7353 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7355 putFileChunkBE(file, "CUSX", chunk_size);
7356 SaveLevel_CUSX(file, level, element);
7360 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7362 int element = EL_GROUP_START + i;
7364 chunk_size = SaveLevel_GRPX(NULL, level, element);
7365 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7367 putFileChunkBE(file, "GRPX", chunk_size);
7368 SaveLevel_GRPX(file, level, element);
7375 SetFilePermissions(filename, PERMS_PRIVATE);
7378 void SaveLevel(int nr)
7380 char *filename = getDefaultLevelFilename(nr);
7382 SaveLevelFromFilename(&level, filename, FALSE);
7385 void SaveLevelTemplate()
7387 char *filename = getLocalLevelTemplateFilename();
7389 SaveLevelFromFilename(&level, filename, TRUE);
7392 boolean SaveLevelChecked(int nr)
7394 char *filename = getDefaultLevelFilename(nr);
7395 boolean new_level = !fileExists(filename);
7396 boolean level_saved = FALSE;
7398 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7403 Request("Level saved!", REQ_CONFIRM);
7411 void DumpLevel(struct LevelInfo *level)
7413 if (level->no_level_file || level->no_valid_file)
7415 Error(ERR_WARN, "cannot dump -- no valid level file found");
7421 Print("Level xxx (file version %08d, game version %08d)\n",
7422 level->file_version, level->game_version);
7425 Print("Level author: '%s'\n", level->author);
7426 Print("Level title: '%s'\n", level->name);
7428 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7430 Print("Level time: %d seconds\n", level->time);
7431 Print("Gems needed: %d\n", level->gems_needed);
7433 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7434 Print("Time for wheel: %d seconds\n", level->time_wheel);
7435 Print("Time for light: %d seconds\n", level->time_light);
7436 Print("Time for timegate: %d seconds\n", level->time_timegate);
7438 Print("Amoeba speed: %d\n", level->amoeba_speed);
7441 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7442 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7443 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7444 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7445 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7451 /* ========================================================================= */
7452 /* tape file functions */
7453 /* ========================================================================= */
7455 static void setTapeInfoToDefaults()
7459 /* always start with reliable default values (empty tape) */
7462 /* default values (also for pre-1.2 tapes) with only the first player */
7463 tape.player_participates[0] = TRUE;
7464 for (i = 1; i < MAX_PLAYERS; i++)
7465 tape.player_participates[i] = FALSE;
7467 /* at least one (default: the first) player participates in every tape */
7468 tape.num_participating_players = 1;
7470 tape.level_nr = level_nr;
7472 tape.changed = FALSE;
7474 tape.recording = FALSE;
7475 tape.playing = FALSE;
7476 tape.pausing = FALSE;
7478 tape.no_valid_file = FALSE;
7481 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7483 tape->file_version = getFileVersion(file);
7484 tape->game_version = getFileVersion(file);
7489 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7493 tape->random_seed = getFile32BitBE(file);
7494 tape->date = getFile32BitBE(file);
7495 tape->length = getFile32BitBE(file);
7497 /* read header fields that are new since version 1.2 */
7498 if (tape->file_version >= FILE_VERSION_1_2)
7500 byte store_participating_players = getFile8Bit(file);
7503 /* since version 1.2, tapes store which players participate in the tape */
7504 tape->num_participating_players = 0;
7505 for (i = 0; i < MAX_PLAYERS; i++)
7507 tape->player_participates[i] = FALSE;
7509 if (store_participating_players & (1 << i))
7511 tape->player_participates[i] = TRUE;
7512 tape->num_participating_players++;
7516 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7518 engine_version = getFileVersion(file);
7519 if (engine_version > 0)
7520 tape->engine_version = engine_version;
7522 tape->engine_version = tape->game_version;
7528 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7530 int level_identifier_size;
7533 level_identifier_size = getFile16BitBE(file);
7535 tape->level_identifier =
7536 checked_realloc(tape->level_identifier, level_identifier_size);
7538 for (i = 0; i < level_identifier_size; i++)
7539 tape->level_identifier[i] = getFile8Bit(file);
7541 tape->level_nr = getFile16BitBE(file);
7543 chunk_size = 2 + level_identifier_size + 2;
7548 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7551 int chunk_size_expected =
7552 (tape->num_participating_players + 1) * tape->length;
7554 if (chunk_size_expected != chunk_size)
7556 ReadUnusedBytesFromFile(file, chunk_size);
7557 return chunk_size_expected;
7560 for (i = 0; i < tape->length; i++)
7562 if (i >= MAX_TAPE_LEN)
7564 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7567 // tape too large; read and ignore remaining tape data from this chunk
7568 for (;i < tape->length; i++)
7569 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7574 for (j = 0; j < MAX_PLAYERS; j++)
7576 tape->pos[i].action[j] = MV_NONE;
7578 if (tape->player_participates[j])
7579 tape->pos[i].action[j] = getFile8Bit(file);
7582 tape->pos[i].delay = getFile8Bit(file);
7584 if (tape->file_version == FILE_VERSION_1_0)
7586 /* eliminate possible diagonal moves in old tapes */
7587 /* this is only for backward compatibility */
7589 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7590 byte action = tape->pos[i].action[0];
7591 int k, num_moves = 0;
7593 for (k = 0; k<4; k++)
7595 if (action & joy_dir[k])
7597 tape->pos[i + num_moves].action[0] = joy_dir[k];
7599 tape->pos[i + num_moves].delay = 0;
7608 tape->length += num_moves;
7611 else if (tape->file_version < FILE_VERSION_2_0)
7613 /* convert pre-2.0 tapes to new tape format */
7615 if (tape->pos[i].delay > 1)
7618 tape->pos[i + 1] = tape->pos[i];
7619 tape->pos[i + 1].delay = 1;
7622 for (j = 0; j < MAX_PLAYERS; j++)
7623 tape->pos[i].action[j] = MV_NONE;
7624 tape->pos[i].delay--;
7631 if (checkEndOfFile(file))
7635 if (i != tape->length)
7636 chunk_size = (tape->num_participating_players + 1) * i;
7641 void LoadTape_SokobanSolution(char *filename)
7644 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7646 if (!(file = openFile(filename, MODE_READ)))
7648 tape.no_valid_file = TRUE;
7653 while (!checkEndOfFile(file))
7655 unsigned char c = getByteFromFile(file);
7657 if (checkEndOfFile(file))
7664 tape.pos[tape.length].action[0] = MV_UP;
7665 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7671 tape.pos[tape.length].action[0] = MV_DOWN;
7672 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7678 tape.pos[tape.length].action[0] = MV_LEFT;
7679 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7685 tape.pos[tape.length].action[0] = MV_RIGHT;
7686 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7694 /* ignore white-space characters */
7698 tape.no_valid_file = TRUE;
7700 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7708 if (tape.no_valid_file)
7711 tape.length_frames = GetTapeLengthFrames();
7712 tape.length_seconds = GetTapeLengthSeconds();
7715 void LoadTapeFromFilename(char *filename)
7717 char cookie[MAX_LINE_LEN];
7718 char chunk_name[CHUNK_ID_LEN + 1];
7722 /* always start with reliable default values */
7723 setTapeInfoToDefaults();
7725 if (strSuffix(filename, ".sln"))
7727 LoadTape_SokobanSolution(filename);
7732 if (!(file = openFile(filename, MODE_READ)))
7734 tape.no_valid_file = TRUE;
7739 getFileChunkBE(file, chunk_name, NULL);
7740 if (strEqual(chunk_name, "RND1"))
7742 getFile32BitBE(file); /* not used */
7744 getFileChunkBE(file, chunk_name, NULL);
7745 if (!strEqual(chunk_name, "TAPE"))
7747 tape.no_valid_file = TRUE;
7749 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7756 else /* check for pre-2.0 file format with cookie string */
7758 strcpy(cookie, chunk_name);
7759 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7761 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7762 cookie[strlen(cookie) - 1] = '\0';
7764 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7766 tape.no_valid_file = TRUE;
7768 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7775 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7777 tape.no_valid_file = TRUE;
7779 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7786 /* pre-2.0 tape files have no game version, so use file version here */
7787 tape.game_version = tape.file_version;
7790 if (tape.file_version < FILE_VERSION_1_2)
7792 /* tape files from versions before 1.2.0 without chunk structure */
7793 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7794 LoadTape_BODY(file, 2 * tape.length, &tape);
7802 int (*loader)(File *, int, struct TapeInfo *);
7806 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7807 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7808 { "INFO", -1, LoadTape_INFO },
7809 { "BODY", -1, LoadTape_BODY },
7813 while (getFileChunkBE(file, chunk_name, &chunk_size))
7817 while (chunk_info[i].name != NULL &&
7818 !strEqual(chunk_name, chunk_info[i].name))
7821 if (chunk_info[i].name == NULL)
7823 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7824 chunk_name, filename);
7825 ReadUnusedBytesFromFile(file, chunk_size);
7827 else if (chunk_info[i].size != -1 &&
7828 chunk_info[i].size != chunk_size)
7830 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7831 chunk_size, chunk_name, filename);
7832 ReadUnusedBytesFromFile(file, chunk_size);
7836 /* call function to load this tape chunk */
7837 int chunk_size_expected =
7838 (chunk_info[i].loader)(file, chunk_size, &tape);
7840 /* the size of some chunks cannot be checked before reading other
7841 chunks first (like "HEAD" and "BODY") that contain some header
7842 information, so check them here */
7843 if (chunk_size_expected != chunk_size)
7845 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7846 chunk_size, chunk_name, filename);
7854 tape.length_frames = GetTapeLengthFrames();
7855 tape.length_seconds = GetTapeLengthSeconds();
7858 printf("::: tape file version: %d\n", tape.file_version);
7859 printf("::: tape game version: %d\n", tape.game_version);
7860 printf("::: tape engine version: %d\n", tape.engine_version);
7864 void LoadTape(int nr)
7866 char *filename = getTapeFilename(nr);
7868 LoadTapeFromFilename(filename);
7871 void LoadSolutionTape(int nr)
7873 char *filename = getSolutionTapeFilename(nr);
7875 LoadTapeFromFilename(filename);
7877 if (TAPE_IS_EMPTY(tape) &&
7878 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7879 level.native_sp_level->demo.is_available)
7880 CopyNativeTape_SP_to_RND(&level);
7883 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7885 putFileVersion(file, tape->file_version);
7886 putFileVersion(file, tape->game_version);
7889 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7892 byte store_participating_players = 0;
7894 /* set bits for participating players for compact storage */
7895 for (i = 0; i < MAX_PLAYERS; i++)
7896 if (tape->player_participates[i])
7897 store_participating_players |= (1 << i);
7899 putFile32BitBE(file, tape->random_seed);
7900 putFile32BitBE(file, tape->date);
7901 putFile32BitBE(file, tape->length);
7903 putFile8Bit(file, store_participating_players);
7905 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7906 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7908 putFileVersion(file, tape->engine_version);
7911 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7913 int level_identifier_size = strlen(tape->level_identifier) + 1;
7916 putFile16BitBE(file, level_identifier_size);
7918 for (i = 0; i < level_identifier_size; i++)
7919 putFile8Bit(file, tape->level_identifier[i]);
7921 putFile16BitBE(file, tape->level_nr);
7924 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7928 for (i = 0; i < tape->length; i++)
7930 for (j = 0; j < MAX_PLAYERS; j++)
7931 if (tape->player_participates[j])
7932 putFile8Bit(file, tape->pos[i].action[j]);
7934 putFile8Bit(file, tape->pos[i].delay);
7938 void SaveTape(int nr)
7940 char *filename = getTapeFilename(nr);
7942 int num_participating_players = 0;
7943 int info_chunk_size;
7944 int body_chunk_size;
7947 InitTapeDirectory(leveldir_current->subdir);
7949 if (!(file = fopen(filename, MODE_WRITE)))
7951 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7955 tape.file_version = FILE_VERSION_ACTUAL;
7956 tape.game_version = GAME_VERSION_ACTUAL;
7958 /* count number of participating players */
7959 for (i = 0; i < MAX_PLAYERS; i++)
7960 if (tape.player_participates[i])
7961 num_participating_players++;
7963 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7964 body_chunk_size = (num_participating_players + 1) * tape.length;
7966 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7967 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7969 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7970 SaveTape_VERS(file, &tape);
7972 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7973 SaveTape_HEAD(file, &tape);
7975 putFileChunkBE(file, "INFO", info_chunk_size);
7976 SaveTape_INFO(file, &tape);
7978 putFileChunkBE(file, "BODY", body_chunk_size);
7979 SaveTape_BODY(file, &tape);
7983 SetFilePermissions(filename, PERMS_PRIVATE);
7985 tape.changed = FALSE;
7988 boolean SaveTapeChecked(int nr)
7990 char *filename = getTapeFilename(nr);
7991 boolean new_tape = !fileExists(filename);
7992 boolean tape_saved = FALSE;
7994 if (new_tape || Request("Replace old tape?", REQ_ASK))
7999 Request("Tape saved!", REQ_CONFIRM);
8007 void DumpTape(struct TapeInfo *tape)
8009 int tape_frame_counter;
8012 if (tape->no_valid_file)
8014 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8020 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8021 tape->level_nr, tape->file_version, tape->game_version);
8022 Print(" (effective engine version %08d)\n",
8023 tape->engine_version);
8024 Print("Level series identifier: '%s'\n", tape->level_identifier);
8027 tape_frame_counter = 0;
8029 for (i = 0; i < tape->length; i++)
8031 if (i >= MAX_TAPE_LEN)
8036 for (j = 0; j < MAX_PLAYERS; j++)
8038 if (tape->player_participates[j])
8040 int action = tape->pos[i].action[j];
8042 Print("%d:%02x ", j, action);
8043 Print("[%c%c%c%c|%c%c] - ",
8044 (action & JOY_LEFT ? '<' : ' '),
8045 (action & JOY_RIGHT ? '>' : ' '),
8046 (action & JOY_UP ? '^' : ' '),
8047 (action & JOY_DOWN ? 'v' : ' '),
8048 (action & JOY_BUTTON_1 ? '1' : ' '),
8049 (action & JOY_BUTTON_2 ? '2' : ' '));
8053 Print("(%03d) ", tape->pos[i].delay);
8054 Print("[%05d]\n", tape_frame_counter);
8056 tape_frame_counter += tape->pos[i].delay;
8063 /* ========================================================================= */
8064 /* score file functions */
8065 /* ========================================================================= */
8067 void LoadScore(int nr)
8070 char *filename = getScoreFilename(nr);
8071 char cookie[MAX_LINE_LEN];
8072 char line[MAX_LINE_LEN];
8076 /* always start with reliable default values */
8077 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8079 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8080 highscore[i].Score = 0;
8083 if (!(file = fopen(filename, MODE_READ)))
8086 /* check file identifier */
8087 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8089 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8090 cookie[strlen(cookie) - 1] = '\0';
8092 if (!checkCookieString(cookie, SCORE_COOKIE))
8094 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8099 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8101 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8102 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8103 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8106 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8107 line[strlen(line) - 1] = '\0';
8109 for (line_ptr = line; *line_ptr; line_ptr++)
8111 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8113 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8114 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8123 void SaveScore(int nr)
8126 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8127 char *filename = getScoreFilename(nr);
8130 InitScoreDirectory(leveldir_current->subdir);
8132 if (!(file = fopen(filename, MODE_WRITE)))
8134 Error(ERR_WARN, "cannot save score for level %d", nr);
8138 fprintf(file, "%s\n\n", SCORE_COOKIE);
8140 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8141 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8145 SetFilePermissions(filename, permissions);
8149 /* ========================================================================= */
8150 /* setup file functions */
8151 /* ========================================================================= */
8153 #define TOKEN_STR_PLAYER_PREFIX "player_"
8156 #define SETUP_TOKEN_PLAYER_NAME 0
8157 #define SETUP_TOKEN_SOUND 1
8158 #define SETUP_TOKEN_SOUND_LOOPS 2
8159 #define SETUP_TOKEN_SOUND_MUSIC 3
8160 #define SETUP_TOKEN_SOUND_SIMPLE 4
8161 #define SETUP_TOKEN_TOONS 5
8162 #define SETUP_TOKEN_SCROLL_DELAY 6
8163 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
8164 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE 8
8165 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY 9
8166 #define SETUP_TOKEN_FADE_SCREENS 10
8167 #define SETUP_TOKEN_AUTORECORD 11
8168 #define SETUP_TOKEN_SHOW_TITLESCREEN 12
8169 #define SETUP_TOKEN_QUICK_DOORS 13
8170 #define SETUP_TOKEN_TEAM_MODE 14
8171 #define SETUP_TOKEN_HANDICAP 15
8172 #define SETUP_TOKEN_SKIP_LEVELS 16
8173 #define SETUP_TOKEN_INCREMENT_LEVELS 17
8174 #define SETUP_TOKEN_TIME_LIMIT 18
8175 #define SETUP_TOKEN_FULLSCREEN 19
8176 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT 20
8177 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY 21
8178 #define SETUP_TOKEN_SCREEN_RENDERING_MODE 22
8179 #define SETUP_TOKEN_ASK_ON_ESCAPE 23
8180 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 24
8181 #define SETUP_TOKEN_QUICK_SWITCH 25
8182 #define SETUP_TOKEN_INPUT_ON_FOCUS 26
8183 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 27
8184 #define SETUP_TOKEN_GAME_FRAME_DELAY 28
8185 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 29
8186 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 30
8187 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS 31
8188 #define SETUP_TOKEN_GRAPHICS_SET 32
8189 #define SETUP_TOKEN_SOUNDS_SET 33
8190 #define SETUP_TOKEN_MUSIC_SET 34
8191 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 35
8192 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 36
8193 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 37
8194 #define SETUP_TOKEN_VOLUME_SIMPLE 38
8195 #define SETUP_TOKEN_VOLUME_LOOPS 39
8196 #define SETUP_TOKEN_VOLUME_MUSIC 40
8197 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE 41
8198 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 42
8199 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE 43
8201 #define NUM_GLOBAL_SETUP_TOKENS 44
8204 #define SETUP_TOKEN_EDITOR_EL_CLASSIC 0
8205 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 1
8206 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 2
8207 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 3
8208 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 4
8209 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 5
8211 #define NUM_EDITOR_SETUP_TOKENS 6
8213 /* editor cascade setup */
8214 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
8215 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
8216 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
8217 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
8218 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
8219 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
8220 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
8221 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
8222 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
8223 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
8224 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
8225 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
8226 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
8227 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
8228 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
8230 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
8232 /* shortcut setup */
8233 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
8234 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
8235 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
8236 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
8237 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
8238 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
8239 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
8240 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
8241 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
8242 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
8243 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
8244 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
8245 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
8246 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
8247 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
8248 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
8249 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
8250 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
8251 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
8252 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
8253 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
8255 #define NUM_SHORTCUT_SETUP_TOKENS 21
8258 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
8259 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
8260 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
8261 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
8262 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
8263 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
8264 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
8265 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
8266 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
8267 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
8268 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
8269 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
8270 #define SETUP_TOKEN_PLAYER_KEY_UP 12
8271 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
8272 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8273 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8275 #define NUM_PLAYER_SETUP_TOKENS 16
8278 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8279 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8280 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8282 #define NUM_SYSTEM_SETUP_TOKENS 3
8284 /* internal setup */
8285 #define SETUP_TOKEN_INT_PROGRAM_TITLE 0
8286 #define SETUP_TOKEN_INT_PROGRAM_VERSION 1
8287 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR 2
8288 #define SETUP_TOKEN_INT_PROGRAM_EMAIL 3
8289 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE 4
8290 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 5
8291 #define SETUP_TOKEN_INT_PROGRAM_COMPANY 6
8292 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 7
8293 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 8
8294 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 9
8295 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 10
8296 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 11
8297 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 12
8298 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 13
8299 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 14
8300 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
8301 #define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE 16
8302 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH 17
8303 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT 18
8305 #define NUM_INTERNAL_SETUP_TOKENS 19
8308 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0 0
8309 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1 1
8310 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2 2
8311 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3 3
8312 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4 4
8313 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5 5
8314 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6 6
8315 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7 7
8316 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8 8
8317 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9 9
8318 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0 10
8319 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1 11
8320 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2 12
8321 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3 13
8322 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4 14
8323 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5 15
8324 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6 16
8325 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7 17
8326 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8 18
8327 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9 19
8328 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8329 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8330 #define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
8332 #define NUM_DEBUG_SETUP_TOKENS 23
8335 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8337 #define NUM_OPTIONS_SETUP_TOKENS 1
8340 static struct SetupInfo si;
8341 static struct SetupEditorInfo sei;
8342 static struct SetupEditorCascadeInfo seci;
8343 static struct SetupShortcutInfo ssi;
8344 static struct SetupInputInfo sii;
8345 static struct SetupSystemInfo syi;
8346 static struct SetupInternalInfo sxi;
8347 static struct SetupDebugInfo sdi;
8348 static struct OptionInfo soi;
8350 static struct TokenInfo global_setup_tokens[] =
8352 { TYPE_STRING, &si.player_name, "player_name" },
8353 { TYPE_SWITCH, &si.sound, "sound" },
8354 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8355 { TYPE_SWITCH, &si.sound_music, "background_music" },
8356 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8357 { TYPE_SWITCH, &si.toons, "toons" },
8358 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8359 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8360 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8361 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8362 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8363 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8364 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8365 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8366 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8367 { TYPE_SWITCH, &si.handicap, "handicap" },
8368 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8369 { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
8370 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8371 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8372 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8373 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8374 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8375 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8376 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8377 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8378 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8379 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8380 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8381 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8382 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8383 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8384 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8385 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8386 { TYPE_STRING, &si.music_set, "music_set" },
8387 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8388 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8389 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8390 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8391 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8392 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8393 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8394 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8395 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8398 static struct TokenInfo editor_setup_tokens[] =
8400 { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
8401 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8402 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8403 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8404 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8405 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8408 static struct TokenInfo editor_cascade_setup_tokens[] =
8410 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8411 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8412 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8413 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8414 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8415 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8416 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8417 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8418 { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
8419 { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
8420 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8421 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8422 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8423 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8424 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8425 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8426 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8429 static struct TokenInfo shortcut_setup_tokens[] =
8431 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8432 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8433 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8434 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8435 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8436 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8437 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8438 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8439 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8440 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8441 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8442 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8443 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8444 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8445 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8446 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8447 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8448 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8449 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8450 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8451 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8454 static struct TokenInfo player_setup_tokens[] =
8456 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8457 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8458 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8459 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8460 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8461 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8462 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8463 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8464 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8465 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8466 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8467 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8468 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8469 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8470 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8471 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8474 static struct TokenInfo system_setup_tokens[] =
8476 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8477 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8478 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8481 static struct TokenInfo internal_setup_tokens[] =
8483 { TYPE_STRING, &sxi.program_title, "program_title" },
8484 { TYPE_STRING, &sxi.program_version, "program_version" },
8485 { TYPE_STRING, &sxi.program_author, "program_author" },
8486 { TYPE_STRING, &sxi.program_email, "program_email" },
8487 { TYPE_STRING, &sxi.program_website, "program_website" },
8488 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8489 { TYPE_STRING, &sxi.program_company, "program_company" },
8490 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8491 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8492 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8493 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8494 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8495 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8496 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8497 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8498 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8499 { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" },
8500 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8501 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8504 static struct TokenInfo debug_setup_tokens[] =
8506 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8507 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8508 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8509 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8510 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8511 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8512 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8513 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8514 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8515 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8516 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8517 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8518 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8519 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8520 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8521 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8522 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8523 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8524 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8525 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8526 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8527 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8528 { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8531 static struct TokenInfo options_setup_tokens[] =
8533 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8536 static char *get_corrected_login_name(char *login_name)
8538 /* needed because player name must be a fixed length string */
8539 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8541 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8542 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8544 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8545 if (strchr(login_name_new, ' '))
8546 *strchr(login_name_new, ' ') = '\0';
8548 return login_name_new;
8551 static void setSetupInfoToDefaults(struct SetupInfo *si)
8555 si->player_name = get_corrected_login_name(getLoginName());
8558 si->sound_loops = TRUE;
8559 si->sound_music = TRUE;
8560 si->sound_simple = TRUE;
8562 si->scroll_delay = TRUE;
8563 si->scroll_delay_value = STD_SCROLL_DELAY;
8564 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8565 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8566 si->fade_screens = TRUE;
8567 si->autorecord = TRUE;
8568 si->show_titlescreen = TRUE;
8569 si->quick_doors = FALSE;
8570 si->team_mode = FALSE;
8571 si->handicap = TRUE;
8572 si->skip_levels = TRUE;
8573 si->increment_levels = TRUE;
8574 si->time_limit = TRUE;
8575 si->fullscreen = FALSE;
8576 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8577 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8578 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8579 si->ask_on_escape = TRUE;
8580 si->ask_on_escape_editor = TRUE;
8581 si->quick_switch = FALSE;
8582 si->input_on_focus = FALSE;
8583 si->prefer_aga_graphics = TRUE;
8584 si->game_frame_delay = GAME_FRAME_DELAY;
8585 si->sp_show_border_elements = FALSE;
8586 si->small_game_graphics = FALSE;
8587 si->show_snapshot_buttons = FALSE;
8589 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8590 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8591 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8593 si->override_level_graphics = FALSE;
8594 si->override_level_sounds = FALSE;
8595 si->override_level_music = FALSE;
8597 si->volume_simple = 100; /* percent */
8598 si->volume_loops = 100; /* percent */
8599 si->volume_music = 100; /* percent */
8601 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8602 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8603 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8605 si->editor.el_boulderdash = TRUE;
8606 si->editor.el_emerald_mine = TRUE;
8607 si->editor.el_emerald_mine_club = TRUE;
8608 si->editor.el_more = TRUE;
8609 si->editor.el_sokoban = TRUE;
8610 si->editor.el_supaplex = TRUE;
8611 si->editor.el_diamond_caves = TRUE;
8612 si->editor.el_dx_boulderdash = TRUE;
8614 si->editor.el_mirror_magic = TRUE;
8615 si->editor.el_deflektor = TRUE;
8617 si->editor.el_chars = TRUE;
8618 si->editor.el_steel_chars = TRUE;
8620 si->editor.el_classic = TRUE;
8621 si->editor.el_custom = TRUE;
8623 si->editor.el_user_defined = FALSE;
8624 si->editor.el_dynamic = TRUE;
8626 si->editor.el_headlines = TRUE;
8628 si->editor.show_element_token = FALSE;
8630 si->editor.use_template_for_new_levels = TRUE;
8632 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8633 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8634 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8636 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8637 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8638 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8639 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8640 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8642 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8643 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8644 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8645 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8646 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8647 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8649 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8650 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8651 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8653 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8654 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8655 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8656 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8658 for (i = 0; i < MAX_PLAYERS; i++)
8660 si->input[i].use_joystick = FALSE;
8661 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8662 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8663 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8664 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8665 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8666 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8667 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8668 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8669 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8670 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8671 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8672 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8673 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8674 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8675 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8678 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8679 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8680 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8682 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8683 si->internal.program_version = getStringCopy(getProgramRealVersionString());
8684 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8685 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8686 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8687 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8688 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8690 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8692 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8693 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8694 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8696 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8697 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8698 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8700 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8701 si->internal.choose_from_top_leveldir = FALSE;
8702 si->internal.show_scaling_in_title = TRUE;
8704 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
8705 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8707 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8708 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8709 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8710 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8711 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8712 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8713 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8714 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8715 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8716 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8718 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8719 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8720 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8721 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8722 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8723 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8724 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8725 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8726 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8727 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8729 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8730 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
8732 si->debug.show_frames_per_second = FALSE;
8734 si->options.verbose = FALSE;
8736 #if defined(PLATFORM_ANDROID)
8737 si->fullscreen = TRUE;
8741 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8743 si->editor_cascade.el_bd = TRUE;
8744 si->editor_cascade.el_em = TRUE;
8745 si->editor_cascade.el_emc = TRUE;
8746 si->editor_cascade.el_rnd = TRUE;
8747 si->editor_cascade.el_sb = TRUE;
8748 si->editor_cascade.el_sp = TRUE;
8749 si->editor_cascade.el_dc = TRUE;
8750 si->editor_cascade.el_dx = TRUE;
8752 si->editor_cascade.el_mm = TRUE;
8753 si->editor_cascade.el_df = TRUE;
8755 si->editor_cascade.el_chars = FALSE;
8756 si->editor_cascade.el_steel_chars = FALSE;
8757 si->editor_cascade.el_ce = FALSE;
8758 si->editor_cascade.el_ge = FALSE;
8759 si->editor_cascade.el_ref = FALSE;
8760 si->editor_cascade.el_user = FALSE;
8761 si->editor_cascade.el_dynamic = FALSE;
8764 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
8766 static char *getHideSetupToken(void *setup_value)
8768 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
8770 if (setup_value != NULL)
8771 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
8773 return hide_setup_token;
8776 static void setHideSetupEntry(void *setup_value_raw)
8778 /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
8779 void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
8781 char *hide_setup_token = getHideSetupToken(setup_value);
8783 if (setup_value != NULL)
8784 setHashEntry(hide_setup_hash, hide_setup_token, "");
8787 boolean hideSetupEntry(void *setup_value)
8789 char *hide_setup_token = getHideSetupToken(setup_value);
8791 return (setup_value != NULL &&
8792 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
8795 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
8796 struct TokenInfo *token_info,
8797 int token_nr, char *token_text)
8799 char *token_hide_text = getStringCat2(token_text, ".hide");
8800 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
8802 /* set the value of this setup option in the setup option structure */
8803 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
8805 /* check if this setup option should be hidden in the setup menu */
8806 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
8807 setHideSetupEntry(token_info[token_nr].value);
8810 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
8811 struct TokenInfo *token_info,
8814 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
8815 token_info[token_nr].text);
8818 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8822 if (!setup_file_hash)
8825 if (hide_setup_hash == NULL)
8826 hide_setup_hash = newSetupFileHash();
8830 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8831 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
8836 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8837 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
8840 /* shortcut setup */
8841 ssi = setup.shortcut;
8842 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8843 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
8844 setup.shortcut = ssi;
8847 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8851 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8853 sii = setup.input[pnr];
8854 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8856 char full_token[100];
8858 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8859 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
8862 setup.input[pnr] = sii;
8867 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8868 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
8871 /* internal setup */
8872 sxi = setup.internal;
8873 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8874 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
8875 setup.internal = sxi;
8879 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8880 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
8884 soi = setup.options;
8885 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8886 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
8887 setup.options = soi;
8890 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8894 if (!setup_file_hash)
8897 /* editor cascade setup */
8898 seci = setup.editor_cascade;
8899 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8900 setSetupInfo(editor_cascade_setup_tokens, i,
8901 getHashEntry(setup_file_hash,
8902 editor_cascade_setup_tokens[i].text));
8903 setup.editor_cascade = seci;
8906 void LoadSetupFromFilename(char *filename)
8908 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
8910 if (setup_file_hash)
8912 decodeSetupFileHash(setup_file_hash);
8914 freeSetupFileHash(setup_file_hash);
8918 Error(ERR_DEBUG, "using default setup values");
8922 static void LoadSetup_SpecialPostProcessing()
8924 char *player_name_new;
8926 /* needed to work around problems with fixed length strings */
8927 player_name_new = get_corrected_login_name(setup.player_name);
8928 free(setup.player_name);
8929 setup.player_name = player_name_new;
8931 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
8932 if (setup.scroll_delay == FALSE)
8934 setup.scroll_delay_value = MIN_SCROLL_DELAY;
8935 setup.scroll_delay = TRUE; /* now always "on" */
8938 /* make sure that scroll delay value stays inside valid range */
8939 setup.scroll_delay_value =
8940 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
8947 /* always start with reliable default values */
8948 setSetupInfoToDefaults(&setup);
8950 /* try to load setup values from default setup file */
8951 filename = getDefaultSetupFilename();
8953 if (fileExists(filename))
8954 LoadSetupFromFilename(filename);
8956 /* try to load setup values from user setup file */
8957 filename = getSetupFilename();
8959 LoadSetupFromFilename(filename);
8961 LoadSetup_SpecialPostProcessing();
8964 void LoadSetup_EditorCascade()
8966 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8967 SetupFileHash *setup_file_hash = NULL;
8969 /* always start with reliable default values */
8970 setSetupInfoToDefaults_EditorCascade(&setup);
8972 setup_file_hash = loadSetupFileHash(filename);
8974 if (setup_file_hash)
8976 decodeSetupFileHash_EditorCascade(setup_file_hash);
8978 freeSetupFileHash(setup_file_hash);
8984 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
8987 char mapping_guid[MAX_LINE_LEN];
8988 char *mapping_start, *mapping_end;
8990 // get GUID from game controller mapping line: copy complete line
8991 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
8992 mapping_guid[MAX_LINE_LEN - 1] = '\0';
8994 // get GUID from game controller mapping line: cut after GUID part
8995 mapping_start = strchr(mapping_guid, ',');
8996 if (mapping_start != NULL)
8997 *mapping_start = '\0';
8999 // cut newline from game controller mapping line
9000 mapping_end = strchr(mapping_line, '\n');
9001 if (mapping_end != NULL)
9002 *mapping_end = '\0';
9004 // add mapping entry to game controller mappings hash
9005 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9008 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9013 if (!(file = fopen(filename, MODE_READ)))
9015 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9022 char line[MAX_LINE_LEN];
9024 if (!fgets(line, MAX_LINE_LEN, file))
9027 addGameControllerMappingToHash(mappings_hash, line);
9035 char *filename = getSetupFilename();
9039 InitUserDataDirectory();
9041 if (!(file = fopen(filename, MODE_WRITE)))
9043 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9047 fprintFileHeader(file, SETUP_FILENAME);
9051 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9053 /* just to make things nicer :) */
9054 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9055 i == SETUP_TOKEN_GRAPHICS_SET ||
9056 i == SETUP_TOKEN_VOLUME_SIMPLE ||
9057 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
9058 fprintf(file, "\n");
9060 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9065 fprintf(file, "\n");
9066 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9067 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9069 /* shortcut setup */
9070 ssi = setup.shortcut;
9071 fprintf(file, "\n");
9072 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9073 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9076 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9080 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9081 fprintf(file, "\n");
9083 sii = setup.input[pnr];
9084 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9085 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9090 fprintf(file, "\n");
9091 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9092 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9094 /* internal setup */
9095 /* (internal setup values not saved to user setup file) */
9099 fprintf(file, "\n");
9100 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9101 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9104 soi = setup.options;
9105 fprintf(file, "\n");
9106 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9107 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9111 SetFilePermissions(filename, PERMS_PRIVATE);
9114 void SaveSetup_EditorCascade()
9116 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9120 InitUserDataDirectory();
9122 if (!(file = fopen(filename, MODE_WRITE)))
9124 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9129 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9131 seci = setup.editor_cascade;
9132 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9133 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9137 SetFilePermissions(filename, PERMS_PRIVATE);
9142 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9147 if (!(file = fopen(filename, MODE_WRITE)))
9149 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9154 BEGIN_HASH_ITERATION(mappings_hash, itr)
9156 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9158 END_HASH_ITERATION(mappings_hash, itr)
9163 void SaveSetup_AddGameControllerMapping(char *mapping)
9165 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9166 SetupFileHash *mappings_hash = newSetupFileHash();
9168 InitUserDataDirectory();
9170 // load existing personal game controller mappings
9171 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9173 // add new mapping to personal game controller mappings
9174 addGameControllerMappingToHash(mappings_hash, mapping);
9176 // save updated personal game controller mappings
9177 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9179 freeSetupFileHash(mappings_hash);
9183 void LoadCustomElementDescriptions()
9185 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9186 SetupFileHash *setup_file_hash;
9189 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9191 if (element_info[i].custom_description != NULL)
9193 free(element_info[i].custom_description);
9194 element_info[i].custom_description = NULL;
9198 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9201 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9203 char *token = getStringCat2(element_info[i].token_name, ".name");
9204 char *value = getHashEntry(setup_file_hash, token);
9207 element_info[i].custom_description = getStringCopy(value);
9212 freeSetupFileHash(setup_file_hash);
9215 static int getElementFromToken(char *token)
9217 char *value = getHashEntry(element_token_hash, token);
9222 Error(ERR_WARN, "unknown element token '%s'", token);
9224 return EL_UNDEFINED;
9227 static int get_token_parameter_value(char *token, char *value_raw)
9231 if (token == NULL || value_raw == NULL)
9232 return ARG_UNDEFINED_VALUE;
9234 suffix = strrchr(token, '.');
9238 if (strEqual(suffix, ".element"))
9239 return getElementFromToken(value_raw);
9241 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9242 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9245 void InitMenuDesignSettings_Static()
9249 /* always start with reliable default values from static default config */
9250 for (i = 0; image_config_vars[i].token != NULL; i++)
9252 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9255 *image_config_vars[i].value =
9256 get_token_parameter_value(image_config_vars[i].token, value);
9260 static void InitMenuDesignSettings_SpecialPreProcessing()
9264 /* the following initializes hierarchical values from static configuration */
9266 /* special case: initialize "ARG_DEFAULT" values in static default config */
9267 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9268 titlescreen_initial_first_default.fade_mode =
9269 title_initial_first_default.fade_mode;
9270 titlescreen_initial_first_default.fade_delay =
9271 title_initial_first_default.fade_delay;
9272 titlescreen_initial_first_default.post_delay =
9273 title_initial_first_default.post_delay;
9274 titlescreen_initial_first_default.auto_delay =
9275 title_initial_first_default.auto_delay;
9276 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
9277 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9278 titlescreen_first_default.post_delay = title_first_default.post_delay;
9279 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9280 titlemessage_initial_first_default.fade_mode =
9281 title_initial_first_default.fade_mode;
9282 titlemessage_initial_first_default.fade_delay =
9283 title_initial_first_default.fade_delay;
9284 titlemessage_initial_first_default.post_delay =
9285 title_initial_first_default.post_delay;
9286 titlemessage_initial_first_default.auto_delay =
9287 title_initial_first_default.auto_delay;
9288 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
9289 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9290 titlemessage_first_default.post_delay = title_first_default.post_delay;
9291 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9293 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
9294 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9295 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9296 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9297 titlescreen_default.fade_mode = title_default.fade_mode;
9298 titlescreen_default.fade_delay = title_default.fade_delay;
9299 titlescreen_default.post_delay = title_default.post_delay;
9300 titlescreen_default.auto_delay = title_default.auto_delay;
9301 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9302 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9303 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9304 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9305 titlemessage_default.fade_mode = title_default.fade_mode;
9306 titlemessage_default.fade_delay = title_default.fade_delay;
9307 titlemessage_default.post_delay = title_default.post_delay;
9308 titlemessage_default.auto_delay = title_default.auto_delay;
9310 /* special case: initialize "ARG_DEFAULT" values in static default config */
9311 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9312 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9314 titlescreen_initial_first[i] = titlescreen_initial_first_default;
9315 titlescreen_first[i] = titlescreen_first_default;
9316 titlemessage_initial_first[i] = titlemessage_initial_first_default;
9317 titlemessage_first[i] = titlemessage_first_default;
9319 titlescreen_initial[i] = titlescreen_initial_default;
9320 titlescreen[i] = titlescreen_default;
9321 titlemessage_initial[i] = titlemessage_initial_default;
9322 titlemessage[i] = titlemessage_default;
9325 /* special case: initialize "ARG_DEFAULT" values in static default config */
9326 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9327 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9329 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
9332 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9333 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9334 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9337 /* special case: initialize "ARG_DEFAULT" values in static default config */
9338 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9339 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9341 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9342 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9343 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9345 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
9348 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9352 static void InitMenuDesignSettings_SpecialPostProcessing()
9356 struct XY *dst, *src;
9360 { &game.button.save, &game.button.stop },
9361 { &game.button.pause2, &game.button.pause },
9362 { &game.button.load, &game.button.play },
9363 { &game.button.undo, &game.button.stop },
9364 { &game.button.redo, &game.button.play },
9370 /* special case: initialize later added SETUP list size from LEVELS value */
9371 if (menu.list_size[GAME_MODE_SETUP] == -1)
9372 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9374 /* set default position for snapshot buttons to stop/pause/play buttons */
9375 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9376 if ((*game_buttons_xy[i].dst).x == -1 &&
9377 (*game_buttons_xy[i].dst).y == -1)
9378 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9381 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9385 struct XYTileSize *dst, *src;
9388 editor_buttons_xy[] =
9391 &editor.button.element_left, &editor.palette.element_left,
9392 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9395 &editor.button.element_middle, &editor.palette.element_middle,
9396 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9399 &editor.button.element_right, &editor.palette.element_right,
9400 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9407 /* set default position for element buttons to element graphics */
9408 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9410 if ((*editor_buttons_xy[i].dst).x == -1 &&
9411 (*editor_buttons_xy[i].dst).y == -1)
9413 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9415 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9417 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9422 static void LoadMenuDesignSettingsFromFilename(char *filename)
9424 static struct TitleFadingInfo tfi;
9425 static struct TitleMessageInfo tmi;
9426 static struct TokenInfo title_tokens[] =
9428 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9429 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9430 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9431 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9435 static struct TokenInfo titlemessage_tokens[] =
9437 { TYPE_INTEGER, &tmi.x, ".x" },
9438 { TYPE_INTEGER, &tmi.y, ".y" },
9439 { TYPE_INTEGER, &tmi.width, ".width" },
9440 { TYPE_INTEGER, &tmi.height, ".height" },
9441 { TYPE_INTEGER, &tmi.chars, ".chars" },
9442 { TYPE_INTEGER, &tmi.lines, ".lines" },
9443 { TYPE_INTEGER, &tmi.align, ".align" },
9444 { TYPE_INTEGER, &tmi.valign, ".valign" },
9445 { TYPE_INTEGER, &tmi.font, ".font" },
9446 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9447 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9448 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9449 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9450 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9451 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9452 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9453 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9459 struct TitleFadingInfo *info;
9464 /* initialize first titles from "enter screen" definitions, if defined */
9465 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9466 { &title_first_default, "menu.enter_screen.TITLE" },
9468 /* initialize title screens from "next screen" definitions, if defined */
9469 { &title_initial_default, "menu.next_screen.TITLE" },
9470 { &title_default, "menu.next_screen.TITLE" },
9476 struct TitleMessageInfo *array;
9479 titlemessage_arrays[] =
9481 /* initialize first titles from "enter screen" definitions, if defined */
9482 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9483 { titlescreen_first, "menu.enter_screen.TITLE" },
9484 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9485 { titlemessage_first, "menu.enter_screen.TITLE" },
9487 /* initialize titles from "next screen" definitions, if defined */
9488 { titlescreen_initial, "menu.next_screen.TITLE" },
9489 { titlescreen, "menu.next_screen.TITLE" },
9490 { titlemessage_initial, "menu.next_screen.TITLE" },
9491 { titlemessage, "menu.next_screen.TITLE" },
9493 /* overwrite titles with title definitions, if defined */
9494 { titlescreen_initial_first, "[title_initial]" },
9495 { titlescreen_first, "[title]" },
9496 { titlemessage_initial_first, "[title_initial]" },
9497 { titlemessage_first, "[title]" },
9499 { titlescreen_initial, "[title_initial]" },
9500 { titlescreen, "[title]" },
9501 { titlemessage_initial, "[title_initial]" },
9502 { titlemessage, "[title]" },
9504 /* overwrite titles with title screen/message definitions, if defined */
9505 { titlescreen_initial_first, "[titlescreen_initial]" },
9506 { titlescreen_first, "[titlescreen]" },
9507 { titlemessage_initial_first, "[titlemessage_initial]" },
9508 { titlemessage_first, "[titlemessage]" },
9510 { titlescreen_initial, "[titlescreen_initial]" },
9511 { titlescreen, "[titlescreen]" },
9512 { titlemessage_initial, "[titlemessage_initial]" },
9513 { titlemessage, "[titlemessage]" },
9517 SetupFileHash *setup_file_hash;
9520 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9523 /* the following initializes hierarchical values from dynamic configuration */
9525 /* special case: initialize with default values that may be overwritten */
9526 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9527 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9529 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9530 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9531 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9533 if (value_1 != NULL)
9534 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9535 if (value_2 != NULL)
9536 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9537 if (value_3 != NULL)
9538 menu.list_size[i] = get_integer_from_string(value_3);
9541 /* special case: initialize with default values that may be overwritten */
9542 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9543 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9545 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9546 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9548 if (value_1 != NULL)
9549 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9550 if (value_2 != NULL)
9551 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9553 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9555 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9557 if (value_1 != NULL)
9558 menu.list_size_info[i] = get_integer_from_string(value_1);
9562 /* special case: initialize with default values that may be overwritten */
9563 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9564 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9566 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9567 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9569 if (value_1 != NULL)
9570 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9571 if (value_2 != NULL)
9572 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9575 /* special case: initialize with default values that may be overwritten */
9576 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9577 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9579 char *token_1 = "menu.enter_screen.fade_mode";
9580 char *token_2 = "menu.enter_screen.fade_delay";
9581 char *token_3 = "menu.enter_screen.post_delay";
9582 char *token_4 = "menu.leave_screen.fade_mode";
9583 char *token_5 = "menu.leave_screen.fade_delay";
9584 char *token_6 = "menu.leave_screen.post_delay";
9585 char *token_7 = "menu.next_screen.fade_mode";
9586 char *token_8 = "menu.next_screen.fade_delay";
9587 char *token_9 = "menu.next_screen.post_delay";
9588 char *value_1 = getHashEntry(setup_file_hash, token_1);
9589 char *value_2 = getHashEntry(setup_file_hash, token_2);
9590 char *value_3 = getHashEntry(setup_file_hash, token_3);
9591 char *value_4 = getHashEntry(setup_file_hash, token_4);
9592 char *value_5 = getHashEntry(setup_file_hash, token_5);
9593 char *value_6 = getHashEntry(setup_file_hash, token_6);
9594 char *value_7 = getHashEntry(setup_file_hash, token_7);
9595 char *value_8 = getHashEntry(setup_file_hash, token_8);
9596 char *value_9 = getHashEntry(setup_file_hash, token_9);
9598 if (value_1 != NULL)
9599 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9601 if (value_2 != NULL)
9602 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9604 if (value_3 != NULL)
9605 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9607 if (value_4 != NULL)
9608 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9610 if (value_5 != NULL)
9611 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9613 if (value_6 != NULL)
9614 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9616 if (value_7 != NULL)
9617 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9619 if (value_8 != NULL)
9620 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9622 if (value_9 != NULL)
9623 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9627 /* special case: initialize with default values that may be overwritten */
9628 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9629 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9631 char *token_w1 = "viewport.window.width";
9632 char *token_w2 = "viewport.window.height";
9633 char *token_01 = "viewport.playfield.x";
9634 char *token_02 = "viewport.playfield.y";
9635 char *token_03 = "viewport.playfield.width";
9636 char *token_04 = "viewport.playfield.height";
9637 char *token_05 = "viewport.playfield.border_size";
9638 char *token_06 = "viewport.door_1.x";
9639 char *token_07 = "viewport.door_1.y";
9640 char *token_08 = "viewport.door_1.width";
9641 char *token_09 = "viewport.door_1.height";
9642 char *token_10 = "viewport.door_1.border_size";
9643 char *token_11 = "viewport.door_2.x";
9644 char *token_12 = "viewport.door_2.y";
9645 char *token_13 = "viewport.door_2.width";
9646 char *token_14 = "viewport.door_2.height";
9647 char *token_15 = "viewport.door_2.border_size";
9648 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9649 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9650 char *value_01 = getHashEntry(setup_file_hash, token_01);
9651 char *value_02 = getHashEntry(setup_file_hash, token_02);
9652 char *value_03 = getHashEntry(setup_file_hash, token_03);
9653 char *value_04 = getHashEntry(setup_file_hash, token_04);
9654 char *value_05 = getHashEntry(setup_file_hash, token_05);
9655 char *value_06 = getHashEntry(setup_file_hash, token_06);
9656 char *value_07 = getHashEntry(setup_file_hash, token_07);
9657 char *value_08 = getHashEntry(setup_file_hash, token_08);
9658 char *value_09 = getHashEntry(setup_file_hash, token_09);
9659 char *value_10 = getHashEntry(setup_file_hash, token_10);
9660 char *value_11 = getHashEntry(setup_file_hash, token_11);
9661 char *value_12 = getHashEntry(setup_file_hash, token_12);
9662 char *value_13 = getHashEntry(setup_file_hash, token_13);
9663 char *value_14 = getHashEntry(setup_file_hash, token_14);
9664 char *value_15 = getHashEntry(setup_file_hash, token_15);
9666 if (value_w1 != NULL)
9667 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9668 if (value_w2 != NULL)
9669 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9670 if (value_01 != NULL)
9671 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9672 if (value_02 != NULL)
9673 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9674 if (value_03 != NULL)
9675 viewport.playfield[i].width = get_token_parameter_value(token_03,
9677 if (value_04 != NULL)
9678 viewport.playfield[i].height = get_token_parameter_value(token_04,
9680 if (value_05 != NULL)
9681 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9683 if (value_06 != NULL)
9684 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9685 if (value_07 != NULL)
9686 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9687 if (value_08 != NULL)
9688 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9689 if (value_09 != NULL)
9690 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9691 if (value_10 != NULL)
9692 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9694 if (value_11 != NULL)
9695 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9696 if (value_12 != NULL)
9697 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9698 if (value_13 != NULL)
9699 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9700 if (value_14 != NULL)
9701 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9702 if (value_15 != NULL)
9703 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9707 /* special case: initialize with default values that may be overwritten */
9708 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9709 for (i = 0; title_info[i].info != NULL; i++)
9711 struct TitleFadingInfo *info = title_info[i].info;
9712 char *base_token = title_info[i].text;
9714 for (j = 0; title_tokens[j].type != -1; j++)
9716 char *token = getStringCat2(base_token, title_tokens[j].text);
9717 char *value = getHashEntry(setup_file_hash, token);
9721 int parameter_value = get_token_parameter_value(token, value);
9725 *(int *)title_tokens[j].value = (int)parameter_value;
9734 /* special case: initialize with default values that may be overwritten */
9735 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9736 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9738 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9739 char *base_token = titlemessage_arrays[i].text;
9741 for (j = 0; titlemessage_tokens[j].type != -1; j++)
9743 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9744 char *value = getHashEntry(setup_file_hash, token);
9748 int parameter_value = get_token_parameter_value(token, value);
9750 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9754 if (titlemessage_tokens[j].type == TYPE_INTEGER)
9755 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
9757 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9767 /* read (and overwrite with) values that may be specified in config file */
9768 for (i = 0; image_config_vars[i].token != NULL; i++)
9770 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9772 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9773 if (value != NULL && !strEqual(value, ARG_DEFAULT))
9774 *image_config_vars[i].value =
9775 get_token_parameter_value(image_config_vars[i].token, value);
9778 freeSetupFileHash(setup_file_hash);
9781 void LoadMenuDesignSettings()
9783 char *filename_base = UNDEFINED_FILENAME, *filename_local;
9785 InitMenuDesignSettings_Static();
9786 InitMenuDesignSettings_SpecialPreProcessing();
9788 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9790 /* first look for special settings configured in level series config */
9791 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9793 if (fileExists(filename_base))
9794 LoadMenuDesignSettingsFromFilename(filename_base);
9797 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9799 if (filename_local != NULL && !strEqual(filename_base, filename_local))
9800 LoadMenuDesignSettingsFromFilename(filename_local);
9802 InitMenuDesignSettings_SpecialPostProcessing();
9805 void LoadMenuDesignSettings_AfterGraphics()
9807 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9810 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9812 char *filename = getEditorSetupFilename();
9813 SetupFileList *setup_file_list, *list;
9814 SetupFileHash *element_hash;
9815 int num_unknown_tokens = 0;
9818 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9821 element_hash = newSetupFileHash();
9823 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9824 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9826 /* determined size may be larger than needed (due to unknown elements) */
9828 for (list = setup_file_list; list != NULL; list = list->next)
9831 /* add space for up to 3 more elements for padding that may be needed */
9834 /* free memory for old list of elements, if needed */
9835 checked_free(*elements);
9837 /* allocate memory for new list of elements */
9838 *elements = checked_malloc(*num_elements * sizeof(int));
9841 for (list = setup_file_list; list != NULL; list = list->next)
9843 char *value = getHashEntry(element_hash, list->token);
9845 if (value == NULL) /* try to find obsolete token mapping */
9847 char *mapped_token = get_mapped_token(list->token);
9849 if (mapped_token != NULL)
9851 value = getHashEntry(element_hash, mapped_token);
9859 (*elements)[(*num_elements)++] = atoi(value);
9863 if (num_unknown_tokens == 0)
9865 Error(ERR_INFO_LINE, "-");
9866 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9867 Error(ERR_INFO, "- config file: '%s'", filename);
9869 num_unknown_tokens++;
9872 Error(ERR_INFO, "- token: '%s'", list->token);
9876 if (num_unknown_tokens > 0)
9877 Error(ERR_INFO_LINE, "-");
9879 while (*num_elements % 4) /* pad with empty elements, if needed */
9880 (*elements)[(*num_elements)++] = EL_EMPTY;
9882 freeSetupFileList(setup_file_list);
9883 freeSetupFileHash(element_hash);
9886 for (i = 0; i < *num_elements; i++)
9887 printf("editor: element '%s' [%d]\n",
9888 element_info[(*elements)[i]].token_name, (*elements)[i]);
9892 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9895 SetupFileHash *setup_file_hash = NULL;
9896 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9897 char *filename_music, *filename_prefix, *filename_info;
9903 token_to_value_ptr[] =
9905 { "title_header", &tmp_music_file_info.title_header },
9906 { "artist_header", &tmp_music_file_info.artist_header },
9907 { "album_header", &tmp_music_file_info.album_header },
9908 { "year_header", &tmp_music_file_info.year_header },
9910 { "title", &tmp_music_file_info.title },
9911 { "artist", &tmp_music_file_info.artist },
9912 { "album", &tmp_music_file_info.album },
9913 { "year", &tmp_music_file_info.year },
9919 filename_music = (is_sound ? getCustomSoundFilename(basename) :
9920 getCustomMusicFilename(basename));
9922 if (filename_music == NULL)
9925 /* ---------- try to replace file extension ---------- */
9927 filename_prefix = getStringCopy(filename_music);
9928 if (strrchr(filename_prefix, '.') != NULL)
9929 *strrchr(filename_prefix, '.') = '\0';
9930 filename_info = getStringCat2(filename_prefix, ".txt");
9932 if (fileExists(filename_info))
9933 setup_file_hash = loadSetupFileHash(filename_info);
9935 free(filename_prefix);
9936 free(filename_info);
9938 if (setup_file_hash == NULL)
9940 /* ---------- try to add file extension ---------- */
9942 filename_prefix = getStringCopy(filename_music);
9943 filename_info = getStringCat2(filename_prefix, ".txt");
9945 if (fileExists(filename_info))
9946 setup_file_hash = loadSetupFileHash(filename_info);
9948 free(filename_prefix);
9949 free(filename_info);
9952 if (setup_file_hash == NULL)
9955 /* ---------- music file info found ---------- */
9957 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
9959 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
9961 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
9963 *token_to_value_ptr[i].value_ptr =
9964 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
9967 tmp_music_file_info.basename = getStringCopy(basename);
9968 tmp_music_file_info.music = music;
9969 tmp_music_file_info.is_sound = is_sound;
9971 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
9972 *new_music_file_info = tmp_music_file_info;
9974 return new_music_file_info;
9977 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
9979 return get_music_file_info_ext(basename, music, FALSE);
9982 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
9984 return get_music_file_info_ext(basename, sound, TRUE);
9987 static boolean music_info_listed_ext(struct MusicFileInfo *list,
9988 char *basename, boolean is_sound)
9990 for (; list != NULL; list = list->next)
9991 if (list->is_sound == is_sound && strEqual(list->basename, basename))
9997 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
9999 return music_info_listed_ext(list, basename, FALSE);
10002 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10004 return music_info_listed_ext(list, basename, TRUE);
10007 void LoadMusicInfo()
10009 char *music_directory = getCustomMusicDirectory();
10010 int num_music = getMusicListSize();
10011 int num_music_noconf = 0;
10012 int num_sounds = getSoundListSize();
10014 DirectoryEntry *dir_entry;
10015 struct FileInfo *music, *sound;
10016 struct MusicFileInfo *next, **new;
10019 while (music_file_info != NULL)
10021 next = music_file_info->next;
10023 checked_free(music_file_info->basename);
10025 checked_free(music_file_info->title_header);
10026 checked_free(music_file_info->artist_header);
10027 checked_free(music_file_info->album_header);
10028 checked_free(music_file_info->year_header);
10030 checked_free(music_file_info->title);
10031 checked_free(music_file_info->artist);
10032 checked_free(music_file_info->album);
10033 checked_free(music_file_info->year);
10035 free(music_file_info);
10037 music_file_info = next;
10040 new = &music_file_info;
10042 for (i = 0; i < num_music; i++)
10044 music = getMusicListEntry(i);
10046 if (music->filename == NULL)
10049 if (strEqual(music->filename, UNDEFINED_FILENAME))
10052 /* a configured file may be not recognized as music */
10053 if (!FileIsMusic(music->filename))
10056 if (!music_info_listed(music_file_info, music->filename))
10058 *new = get_music_file_info(music->filename, i);
10061 new = &(*new)->next;
10065 if ((dir = openDirectory(music_directory)) == NULL)
10067 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10071 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
10073 char *basename = dir_entry->basename;
10074 boolean music_already_used = FALSE;
10077 /* skip all music files that are configured in music config file */
10078 for (i = 0; i < num_music; i++)
10080 music = getMusicListEntry(i);
10082 if (music->filename == NULL)
10085 if (strEqual(basename, music->filename))
10087 music_already_used = TRUE;
10092 if (music_already_used)
10095 if (!FileIsMusic(dir_entry->filename))
10098 if (!music_info_listed(music_file_info, basename))
10100 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10103 new = &(*new)->next;
10106 num_music_noconf++;
10109 closeDirectory(dir);
10111 for (i = 0; i < num_sounds; i++)
10113 sound = getSoundListEntry(i);
10115 if (sound->filename == NULL)
10118 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10121 /* a configured file may be not recognized as sound */
10122 if (!FileIsSound(sound->filename))
10125 if (!sound_info_listed(music_file_info, sound->filename))
10127 *new = get_sound_file_info(sound->filename, i);
10129 new = &(*new)->next;
10134 void add_helpanim_entry(int element, int action, int direction, int delay,
10135 int *num_list_entries)
10137 struct HelpAnimInfo *new_list_entry;
10138 (*num_list_entries)++;
10141 checked_realloc(helpanim_info,
10142 *num_list_entries * sizeof(struct HelpAnimInfo));
10143 new_list_entry = &helpanim_info[*num_list_entries - 1];
10145 new_list_entry->element = element;
10146 new_list_entry->action = action;
10147 new_list_entry->direction = direction;
10148 new_list_entry->delay = delay;
10151 void print_unknown_token(char *filename, char *token, int token_nr)
10155 Error(ERR_INFO_LINE, "-");
10156 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10157 Error(ERR_INFO, "- config file: '%s'", filename);
10160 Error(ERR_INFO, "- token: '%s'", token);
10163 void print_unknown_token_end(int token_nr)
10166 Error(ERR_INFO_LINE, "-");
10169 void LoadHelpAnimInfo()
10171 char *filename = getHelpAnimFilename();
10172 SetupFileList *setup_file_list = NULL, *list;
10173 SetupFileHash *element_hash, *action_hash, *direction_hash;
10174 int num_list_entries = 0;
10175 int num_unknown_tokens = 0;
10178 if (fileExists(filename))
10179 setup_file_list = loadSetupFileList(filename);
10181 if (setup_file_list == NULL)
10183 /* use reliable default values from static configuration */
10184 SetupFileList *insert_ptr;
10186 insert_ptr = setup_file_list =
10187 newSetupFileList(helpanim_config[0].token,
10188 helpanim_config[0].value);
10190 for (i = 1; helpanim_config[i].token; i++)
10191 insert_ptr = addListEntry(insert_ptr,
10192 helpanim_config[i].token,
10193 helpanim_config[i].value);
10196 element_hash = newSetupFileHash();
10197 action_hash = newSetupFileHash();
10198 direction_hash = newSetupFileHash();
10200 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10201 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10203 for (i = 0; i < NUM_ACTIONS; i++)
10204 setHashEntry(action_hash, element_action_info[i].suffix,
10205 i_to_a(element_action_info[i].value));
10207 /* do not store direction index (bit) here, but direction value! */
10208 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10209 setHashEntry(direction_hash, element_direction_info[i].suffix,
10210 i_to_a(1 << element_direction_info[i].value));
10212 for (list = setup_file_list; list != NULL; list = list->next)
10214 char *element_token, *action_token, *direction_token;
10215 char *element_value, *action_value, *direction_value;
10216 int delay = atoi(list->value);
10218 if (strEqual(list->token, "end"))
10220 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10225 /* first try to break element into element/action/direction parts;
10226 if this does not work, also accept combined "element[.act][.dir]"
10227 elements (like "dynamite.active"), which are unique elements */
10229 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10231 element_value = getHashEntry(element_hash, list->token);
10232 if (element_value != NULL) /* element found */
10233 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10234 &num_list_entries);
10237 /* no further suffixes found -- this is not an element */
10238 print_unknown_token(filename, list->token, num_unknown_tokens++);
10244 /* token has format "<prefix>.<something>" */
10246 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10247 direction_token = action_token; /* ... or direction */
10249 element_token = getStringCopy(list->token);
10250 *strchr(element_token, '.') = '\0';
10252 element_value = getHashEntry(element_hash, element_token);
10254 if (element_value == NULL) /* this is no element */
10256 element_value = getHashEntry(element_hash, list->token);
10257 if (element_value != NULL) /* combined element found */
10258 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10259 &num_list_entries);
10261 print_unknown_token(filename, list->token, num_unknown_tokens++);
10263 free(element_token);
10268 action_value = getHashEntry(action_hash, action_token);
10270 if (action_value != NULL) /* action found */
10272 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10273 &num_list_entries);
10275 free(element_token);
10280 direction_value = getHashEntry(direction_hash, direction_token);
10282 if (direction_value != NULL) /* direction found */
10284 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10285 &num_list_entries);
10287 free(element_token);
10292 if (strchr(action_token + 1, '.') == NULL)
10294 /* no further suffixes found -- this is not an action nor direction */
10296 element_value = getHashEntry(element_hash, list->token);
10297 if (element_value != NULL) /* combined element found */
10298 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10299 &num_list_entries);
10301 print_unknown_token(filename, list->token, num_unknown_tokens++);
10303 free(element_token);
10308 /* token has format "<prefix>.<suffix>.<something>" */
10310 direction_token = strchr(action_token + 1, '.');
10312 action_token = getStringCopy(action_token);
10313 *strchr(action_token + 1, '.') = '\0';
10315 action_value = getHashEntry(action_hash, action_token);
10317 if (action_value == NULL) /* this is no action */
10319 element_value = getHashEntry(element_hash, list->token);
10320 if (element_value != NULL) /* combined element found */
10321 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10322 &num_list_entries);
10324 print_unknown_token(filename, list->token, num_unknown_tokens++);
10326 free(element_token);
10327 free(action_token);
10332 direction_value = getHashEntry(direction_hash, direction_token);
10334 if (direction_value != NULL) /* direction found */
10336 add_helpanim_entry(atoi(element_value), atoi(action_value),
10337 atoi(direction_value), delay, &num_list_entries);
10339 free(element_token);
10340 free(action_token);
10345 /* this is no direction */
10347 element_value = getHashEntry(element_hash, list->token);
10348 if (element_value != NULL) /* combined element found */
10349 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10350 &num_list_entries);
10352 print_unknown_token(filename, list->token, num_unknown_tokens++);
10354 free(element_token);
10355 free(action_token);
10358 print_unknown_token_end(num_unknown_tokens);
10360 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10361 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10363 freeSetupFileList(setup_file_list);
10364 freeSetupFileHash(element_hash);
10365 freeSetupFileHash(action_hash);
10366 freeSetupFileHash(direction_hash);
10369 for (i = 0; i < num_list_entries; i++)
10370 printf("::: '%s': %d, %d, %d => %d\n",
10371 EL_NAME(helpanim_info[i].element),
10372 helpanim_info[i].element,
10373 helpanim_info[i].action,
10374 helpanim_info[i].direction,
10375 helpanim_info[i].delay);
10379 void LoadHelpTextInfo()
10381 char *filename = getHelpTextFilename();
10384 if (helptext_info != NULL)
10386 freeSetupFileHash(helptext_info);
10387 helptext_info = NULL;
10390 if (fileExists(filename))
10391 helptext_info = loadSetupFileHash(filename);
10393 if (helptext_info == NULL)
10395 /* use reliable default values from static configuration */
10396 helptext_info = newSetupFileHash();
10398 for (i = 0; helptext_config[i].token; i++)
10399 setHashEntry(helptext_info,
10400 helptext_config[i].token,
10401 helptext_config[i].value);
10405 BEGIN_HASH_ITERATION(helptext_info, itr)
10407 printf("::: '%s' => '%s'\n",
10408 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10410 END_HASH_ITERATION(hash, itr)
10415 /* ------------------------------------------------------------------------- */
10416 /* convert levels */
10417 /* ------------------------------------------------------------------------- */
10419 #define MAX_NUM_CONVERT_LEVELS 1000
10421 void ConvertLevels()
10423 static LevelDirTree *convert_leveldir = NULL;
10424 static int convert_level_nr = -1;
10425 static int num_levels_handled = 0;
10426 static int num_levels_converted = 0;
10427 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10430 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10431 global.convert_leveldir);
10433 if (convert_leveldir == NULL)
10434 Error(ERR_EXIT, "no such level identifier: '%s'",
10435 global.convert_leveldir);
10437 leveldir_current = convert_leveldir;
10439 if (global.convert_level_nr != -1)
10441 convert_leveldir->first_level = global.convert_level_nr;
10442 convert_leveldir->last_level = global.convert_level_nr;
10445 convert_level_nr = convert_leveldir->first_level;
10447 PrintLine("=", 79);
10448 Print("Converting levels\n");
10449 PrintLine("-", 79);
10450 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10451 Print("Level series name: '%s'\n", convert_leveldir->name);
10452 Print("Level series author: '%s'\n", convert_leveldir->author);
10453 Print("Number of levels: %d\n", convert_leveldir->levels);
10454 PrintLine("=", 79);
10457 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10458 levels_failed[i] = FALSE;
10460 while (convert_level_nr <= convert_leveldir->last_level)
10462 char *level_filename;
10465 level_nr = convert_level_nr++;
10467 Print("Level %03d: ", level_nr);
10469 LoadLevel(level_nr);
10470 if (level.no_level_file || level.no_valid_file)
10472 Print("(no level)\n");
10476 Print("converting level ... ");
10478 level_filename = getDefaultLevelFilename(level_nr);
10479 new_level = !fileExists(level_filename);
10483 SaveLevel(level_nr);
10485 num_levels_converted++;
10487 Print("converted.\n");
10491 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10492 levels_failed[level_nr] = TRUE;
10494 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10497 num_levels_handled++;
10501 PrintLine("=", 79);
10502 Print("Number of levels handled: %d\n", num_levels_handled);
10503 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10504 (num_levels_handled ?
10505 num_levels_converted * 100 / num_levels_handled : 0));
10506 PrintLine("-", 79);
10507 Print("Summary (for automatic parsing by scripts):\n");
10508 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10509 convert_leveldir->identifier, num_levels_converted,
10510 num_levels_handled,
10511 (num_levels_handled ?
10512 num_levels_converted * 100 / num_levels_handled : 0));
10514 if (num_levels_handled != num_levels_converted)
10516 Print(", FAILED:");
10517 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10518 if (levels_failed[i])
10523 PrintLine("=", 79);
10525 CloseAllAndExit(0);
10529 /* ------------------------------------------------------------------------- */
10530 /* create and save images for use in level sketches (raw BMP format) */
10531 /* ------------------------------------------------------------------------- */
10533 void CreateLevelSketchImages()
10535 #if defined(TARGET_SDL)
10540 InitElementPropertiesGfxElement();
10542 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10543 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10545 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10547 Bitmap *src_bitmap;
10549 int element = getMappedElement(i);
10550 int graphic = el2edimg(element);
10551 char basename1[16];
10552 char basename2[16];
10556 sprintf(basename1, "%03d.bmp", i);
10557 sprintf(basename2, "%03ds.bmp", i);
10559 filename1 = getPath2(global.create_images_dir, basename1);
10560 filename2 = getPath2(global.create_images_dir, basename2);
10562 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10563 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10566 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10567 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10569 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10570 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10572 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10573 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10579 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10582 FreeBitmap(bitmap1);
10583 FreeBitmap(bitmap2);
10588 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10590 CloseAllAndExit(0);
10595 /* ------------------------------------------------------------------------- */
10596 /* create and save images for custom and group elements (raw BMP format) */
10597 /* ------------------------------------------------------------------------- */
10599 void CreateCustomElementImages(char *directory)
10601 #if defined(TARGET_SDL)
10602 char *src_basename = "RocksCE-template.ilbm";
10603 char *dst_basename = "RocksCE.bmp";
10604 char *src_filename = getPath2(directory, src_basename);
10605 char *dst_filename = getPath2(directory, dst_basename);
10606 Bitmap *src_bitmap;
10608 int yoffset_ce = 0;
10609 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10612 SDLInitVideoDisplay();
10614 ReCreateBitmap(&backbuffer, video.width, video.height);
10616 src_bitmap = LoadImage(src_filename);
10618 bitmap = CreateBitmap(TILEX * 16 * 2,
10619 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10622 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10629 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10630 TILEX * x, TILEY * y + yoffset_ce);
10632 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10634 TILEX * x + TILEX * 16,
10635 TILEY * y + yoffset_ce);
10637 for (j = 2; j >= 0; j--)
10641 BlitBitmap(src_bitmap, bitmap,
10642 TILEX + c * 7, 0, 6, 10,
10643 TILEX * x + 6 + j * 7,
10644 TILEY * y + 11 + yoffset_ce);
10646 BlitBitmap(src_bitmap, bitmap,
10647 TILEX + c * 8, TILEY, 6, 10,
10648 TILEX * 16 + TILEX * x + 6 + j * 8,
10649 TILEY * y + 10 + yoffset_ce);
10655 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10662 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10663 TILEX * x, TILEY * y + yoffset_ge);
10665 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10667 TILEX * x + TILEX * 16,
10668 TILEY * y + yoffset_ge);
10670 for (j = 1; j >= 0; j--)
10674 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10675 TILEX * x + 6 + j * 10,
10676 TILEY * y + 11 + yoffset_ge);
10678 BlitBitmap(src_bitmap, bitmap,
10679 TILEX + c * 8, TILEY + 12, 6, 10,
10680 TILEX * 16 + TILEX * x + 10 + j * 8,
10681 TILEY * y + 10 + yoffset_ge);
10687 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10688 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10690 FreeBitmap(bitmap);
10692 CloseAllAndExit(0);