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;
6456 /* set some other uninitialized values of custom elements in older levels */
6457 if (level->game_version < VERSION_IDENT(3,1,0,0))
6459 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6461 int element = EL_CUSTOM_START + i;
6463 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6465 element_info[element].explosion_delay = 17;
6466 element_info[element].ignition_delay = 8;
6471 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6473 LoadLevel_InitStandardElements(level);
6475 if (level->file_has_custom_elements)
6476 LoadLevel_InitCustomElements(level);
6478 /* initialize element properties for level editor etc. */
6479 InitElementPropertiesEngine(level->game_version);
6480 InitElementPropertiesGfxElement();
6483 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6487 /* map elements that have changed in newer versions */
6488 for (y = 0; y < level->fieldy; y++)
6489 for (x = 0; x < level->fieldx; x++)
6490 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6491 level->game_version);
6493 /* clear unused playfield data (nicer if level gets resized in editor) */
6494 for (x = 0; x < MAX_LEV_FIELDX; x++)
6495 for (y = 0; y < MAX_LEV_FIELDY; y++)
6496 if (x >= level->fieldx || y >= level->fieldy)
6497 level->field[x][y] = EL_EMPTY;
6499 /* copy elements to runtime playfield array */
6500 for (x = 0; x < MAX_LEV_FIELDX; x++)
6501 for (y = 0; y < MAX_LEV_FIELDY; y++)
6502 Feld[x][y] = level->field[x][y];
6504 /* initialize level size variables for faster access */
6505 lev_fieldx = level->fieldx;
6506 lev_fieldy = level->fieldy;
6508 /* determine border element for this level */
6509 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6510 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6515 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6517 struct LevelFileInfo *level_file_info = &level->file_info;
6519 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6520 CopyNativeLevel_RND_to_Native(level);
6523 void LoadLevelTemplate(int nr)
6527 setLevelFileInfo(&level_template.file_info, nr);
6528 filename = level_template.file_info.filename;
6530 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6532 LoadLevel_InitVersion(&level_template, filename);
6533 LoadLevel_InitElements(&level_template, filename);
6535 ActivateLevelTemplate();
6538 void LoadLevel(int nr)
6542 setLevelFileInfo(&level.file_info, nr);
6543 filename = level.file_info.filename;
6545 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6547 if (level.use_custom_template)
6548 LoadLevelTemplate(-1);
6550 LoadLevel_InitVersion(&level, filename);
6551 LoadLevel_InitElements(&level, filename);
6552 LoadLevel_InitPlayfield(&level, filename);
6554 LoadLevel_InitNativeEngines(&level, filename);
6557 void LoadLevelInfoOnly(int nr)
6559 setLevelFileInfo(&level.file_info, nr);
6561 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6564 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6568 chunk_size += putFileVersion(file, level->file_version);
6569 chunk_size += putFileVersion(file, level->game_version);
6574 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6578 chunk_size += putFile16BitBE(file, level->creation_date.year);
6579 chunk_size += putFile8Bit(file, level->creation_date.month);
6580 chunk_size += putFile8Bit(file, level->creation_date.day);
6585 #if ENABLE_HISTORIC_CHUNKS
6586 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6590 putFile8Bit(file, level->fieldx);
6591 putFile8Bit(file, level->fieldy);
6593 putFile16BitBE(file, level->time);
6594 putFile16BitBE(file, level->gems_needed);
6596 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6597 putFile8Bit(file, level->name[i]);
6599 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6600 putFile8Bit(file, level->score[i]);
6602 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6603 for (y = 0; y < 3; y++)
6604 for (x = 0; x < 3; x++)
6605 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6606 level->yamyam_content[i].e[x][y]));
6607 putFile8Bit(file, level->amoeba_speed);
6608 putFile8Bit(file, level->time_magic_wall);
6609 putFile8Bit(file, level->time_wheel);
6610 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6611 level->amoeba_content));
6612 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6613 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6614 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6615 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6617 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6619 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6620 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6621 putFile32BitBE(file, level->can_move_into_acid_bits);
6622 putFile8Bit(file, level->dont_collide_with_bits);
6624 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6625 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6627 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6628 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6629 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6631 putFile8Bit(file, level->game_engine_type);
6633 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6637 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6642 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6643 chunk_size += putFile8Bit(file, level->name[i]);
6648 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6653 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6654 chunk_size += putFile8Bit(file, level->author[i]);
6659 #if ENABLE_HISTORIC_CHUNKS
6660 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6665 for (y = 0; y < level->fieldy; y++)
6666 for (x = 0; x < level->fieldx; x++)
6667 if (level->encoding_16bit_field)
6668 chunk_size += putFile16BitBE(file, level->field[x][y]);
6670 chunk_size += putFile8Bit(file, level->field[x][y]);
6676 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6681 for (y = 0; y < level->fieldy; y++)
6682 for (x = 0; x < level->fieldx; x++)
6683 chunk_size += putFile16BitBE(file, level->field[x][y]);
6688 #if ENABLE_HISTORIC_CHUNKS
6689 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6693 putFile8Bit(file, EL_YAMYAM);
6694 putFile8Bit(file, level->num_yamyam_contents);
6695 putFile8Bit(file, 0);
6696 putFile8Bit(file, 0);
6698 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6699 for (y = 0; y < 3; y++)
6700 for (x = 0; x < 3; x++)
6701 if (level->encoding_16bit_field)
6702 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6704 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6708 #if ENABLE_HISTORIC_CHUNKS
6709 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6712 int num_contents, content_xsize, content_ysize;
6713 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6715 if (element == EL_YAMYAM)
6717 num_contents = level->num_yamyam_contents;
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] = level->yamyam_content[i].e[x][y];
6726 else if (element == EL_BD_AMOEBA)
6732 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6733 for (y = 0; y < 3; y++)
6734 for (x = 0; x < 3; x++)
6735 content_array[i][x][y] = EL_EMPTY;
6736 content_array[0][0][0] = level->amoeba_content;
6740 /* chunk header already written -- write empty chunk data */
6741 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6743 Error(ERR_WARN, "cannot save content for element '%d'", element);
6747 putFile16BitBE(file, element);
6748 putFile8Bit(file, num_contents);
6749 putFile8Bit(file, content_xsize);
6750 putFile8Bit(file, content_ysize);
6752 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6754 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6755 for (y = 0; y < 3; y++)
6756 for (x = 0; x < 3; x++)
6757 putFile16BitBE(file, content_array[i][x][y]);
6761 #if ENABLE_HISTORIC_CHUNKS
6762 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6764 int envelope_nr = element - EL_ENVELOPE_1;
6765 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6769 chunk_size += putFile16BitBE(file, element);
6770 chunk_size += putFile16BitBE(file, envelope_len);
6771 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6772 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6774 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6775 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6777 for (i = 0; i < envelope_len; i++)
6778 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6784 #if ENABLE_HISTORIC_CHUNKS
6785 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6786 int num_changed_custom_elements)
6790 putFile16BitBE(file, num_changed_custom_elements);
6792 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6794 int element = EL_CUSTOM_START + i;
6796 struct ElementInfo *ei = &element_info[element];
6798 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6800 if (check < num_changed_custom_elements)
6802 putFile16BitBE(file, element);
6803 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6810 if (check != num_changed_custom_elements) /* should not happen */
6811 Error(ERR_WARN, "inconsistent number of custom element properties");
6815 #if ENABLE_HISTORIC_CHUNKS
6816 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6817 int num_changed_custom_elements)
6821 putFile16BitBE(file, num_changed_custom_elements);
6823 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6825 int element = EL_CUSTOM_START + i;
6827 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6829 if (check < num_changed_custom_elements)
6831 putFile16BitBE(file, element);
6832 putFile16BitBE(file, element_info[element].change->target_element);
6839 if (check != num_changed_custom_elements) /* should not happen */
6840 Error(ERR_WARN, "inconsistent number of custom target elements");
6844 #if ENABLE_HISTORIC_CHUNKS
6845 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6846 int num_changed_custom_elements)
6848 int i, j, x, y, check = 0;
6850 putFile16BitBE(file, num_changed_custom_elements);
6852 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6854 int element = EL_CUSTOM_START + i;
6855 struct ElementInfo *ei = &element_info[element];
6857 if (ei->modified_settings)
6859 if (check < num_changed_custom_elements)
6861 putFile16BitBE(file, element);
6863 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6864 putFile8Bit(file, ei->description[j]);
6866 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6868 /* some free bytes for future properties and padding */
6869 WriteUnusedBytesToFile(file, 7);
6871 putFile8Bit(file, ei->use_gfx_element);
6872 putFile16BitBE(file, ei->gfx_element_initial);
6874 putFile8Bit(file, ei->collect_score_initial);
6875 putFile8Bit(file, ei->collect_count_initial);
6877 putFile16BitBE(file, ei->push_delay_fixed);
6878 putFile16BitBE(file, ei->push_delay_random);
6879 putFile16BitBE(file, ei->move_delay_fixed);
6880 putFile16BitBE(file, ei->move_delay_random);
6882 putFile16BitBE(file, ei->move_pattern);
6883 putFile8Bit(file, ei->move_direction_initial);
6884 putFile8Bit(file, ei->move_stepsize);
6886 for (y = 0; y < 3; y++)
6887 for (x = 0; x < 3; x++)
6888 putFile16BitBE(file, ei->content.e[x][y]);
6890 putFile32BitBE(file, ei->change->events);
6892 putFile16BitBE(file, ei->change->target_element);
6894 putFile16BitBE(file, ei->change->delay_fixed);
6895 putFile16BitBE(file, ei->change->delay_random);
6896 putFile16BitBE(file, ei->change->delay_frames);
6898 putFile16BitBE(file, ei->change->initial_trigger_element);
6900 putFile8Bit(file, ei->change->explode);
6901 putFile8Bit(file, ei->change->use_target_content);
6902 putFile8Bit(file, ei->change->only_if_complete);
6903 putFile8Bit(file, ei->change->use_random_replace);
6905 putFile8Bit(file, ei->change->random_percentage);
6906 putFile8Bit(file, ei->change->replace_when);
6908 for (y = 0; y < 3; y++)
6909 for (x = 0; x < 3; x++)
6910 putFile16BitBE(file, ei->change->content.e[x][y]);
6912 putFile8Bit(file, ei->slippery_type);
6914 /* some free bytes for future properties and padding */
6915 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6922 if (check != num_changed_custom_elements) /* should not happen */
6923 Error(ERR_WARN, "inconsistent number of custom element properties");
6927 #if ENABLE_HISTORIC_CHUNKS
6928 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6930 struct ElementInfo *ei = &element_info[element];
6933 /* ---------- custom element base property values (96 bytes) ------------- */
6935 putFile16BitBE(file, element);
6937 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6938 putFile8Bit(file, ei->description[i]);
6940 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6942 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
6944 putFile8Bit(file, ei->num_change_pages);
6946 putFile16BitBE(file, ei->ce_value_fixed_initial);
6947 putFile16BitBE(file, ei->ce_value_random_initial);
6948 putFile8Bit(file, ei->use_last_ce_value);
6950 putFile8Bit(file, ei->use_gfx_element);
6951 putFile16BitBE(file, ei->gfx_element_initial);
6953 putFile8Bit(file, ei->collect_score_initial);
6954 putFile8Bit(file, ei->collect_count_initial);
6956 putFile8Bit(file, ei->drop_delay_fixed);
6957 putFile8Bit(file, ei->push_delay_fixed);
6958 putFile8Bit(file, ei->drop_delay_random);
6959 putFile8Bit(file, ei->push_delay_random);
6960 putFile16BitBE(file, ei->move_delay_fixed);
6961 putFile16BitBE(file, ei->move_delay_random);
6963 /* bits 0 - 15 of "move_pattern" ... */
6964 putFile16BitBE(file, ei->move_pattern & 0xffff);
6965 putFile8Bit(file, ei->move_direction_initial);
6966 putFile8Bit(file, ei->move_stepsize);
6968 putFile8Bit(file, ei->slippery_type);
6970 for (y = 0; y < 3; y++)
6971 for (x = 0; x < 3; x++)
6972 putFile16BitBE(file, ei->content.e[x][y]);
6974 putFile16BitBE(file, ei->move_enter_element);
6975 putFile16BitBE(file, ei->move_leave_element);
6976 putFile8Bit(file, ei->move_leave_type);
6978 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
6979 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
6981 putFile8Bit(file, ei->access_direction);
6983 putFile8Bit(file, ei->explosion_delay);
6984 putFile8Bit(file, ei->ignition_delay);
6985 putFile8Bit(file, ei->explosion_type);
6987 /* some free bytes for future custom property values and padding */
6988 WriteUnusedBytesToFile(file, 1);
6990 /* ---------- change page property values (48 bytes) --------------------- */
6992 for (i = 0; i < ei->num_change_pages; i++)
6994 struct ElementChangeInfo *change = &ei->change_page[i];
6995 unsigned int event_bits;
6997 /* bits 0 - 31 of "has_event[]" ... */
6999 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7000 if (change->has_event[j])
7001 event_bits |= (1 << j);
7002 putFile32BitBE(file, event_bits);
7004 putFile16BitBE(file, change->target_element);
7006 putFile16BitBE(file, change->delay_fixed);
7007 putFile16BitBE(file, change->delay_random);
7008 putFile16BitBE(file, change->delay_frames);
7010 putFile16BitBE(file, change->initial_trigger_element);
7012 putFile8Bit(file, change->explode);
7013 putFile8Bit(file, change->use_target_content);
7014 putFile8Bit(file, change->only_if_complete);
7015 putFile8Bit(file, change->use_random_replace);
7017 putFile8Bit(file, change->random_percentage);
7018 putFile8Bit(file, change->replace_when);
7020 for (y = 0; y < 3; y++)
7021 for (x = 0; x < 3; x++)
7022 putFile16BitBE(file, change->target_content.e[x][y]);
7024 putFile8Bit(file, change->can_change);
7026 putFile8Bit(file, change->trigger_side);
7028 putFile8Bit(file, change->trigger_player);
7029 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7030 log_2(change->trigger_page)));
7032 putFile8Bit(file, change->has_action);
7033 putFile8Bit(file, change->action_type);
7034 putFile8Bit(file, change->action_mode);
7035 putFile16BitBE(file, change->action_arg);
7037 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7039 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7040 if (change->has_event[j])
7041 event_bits |= (1 << (j - 32));
7042 putFile8Bit(file, event_bits);
7047 #if ENABLE_HISTORIC_CHUNKS
7048 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7050 struct ElementInfo *ei = &element_info[element];
7051 struct ElementGroupInfo *group = ei->group;
7054 putFile16BitBE(file, element);
7056 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7057 putFile8Bit(file, ei->description[i]);
7059 putFile8Bit(file, group->num_elements);
7061 putFile8Bit(file, ei->use_gfx_element);
7062 putFile16BitBE(file, ei->gfx_element_initial);
7064 putFile8Bit(file, group->choice_mode);
7066 /* some free bytes for future values and padding */
7067 WriteUnusedBytesToFile(file, 3);
7069 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7070 putFile16BitBE(file, group->element[i]);
7074 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7075 boolean write_element)
7077 int save_type = entry->save_type;
7078 int data_type = entry->data_type;
7079 int conf_type = entry->conf_type;
7080 int byte_mask = conf_type & CONF_MASK_BYTES;
7081 int element = entry->element;
7082 int default_value = entry->default_value;
7084 boolean modified = FALSE;
7086 if (byte_mask != CONF_MASK_MULTI_BYTES)
7088 void *value_ptr = entry->value;
7089 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7092 /* check if any settings have been modified before saving them */
7093 if (value != default_value)
7096 /* do not save if explicitly told or if unmodified default settings */
7097 if ((save_type == SAVE_CONF_NEVER) ||
7098 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7102 num_bytes += putFile16BitBE(file, element);
7104 num_bytes += putFile8Bit(file, conf_type);
7105 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7106 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7107 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7110 else if (data_type == TYPE_STRING)
7112 char *default_string = entry->default_string;
7113 char *string = (char *)(entry->value);
7114 int string_length = strlen(string);
7117 /* check if any settings have been modified before saving them */
7118 if (!strEqual(string, default_string))
7121 /* do not save if explicitly told or if unmodified default settings */
7122 if ((save_type == SAVE_CONF_NEVER) ||
7123 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7127 num_bytes += putFile16BitBE(file, element);
7129 num_bytes += putFile8Bit(file, conf_type);
7130 num_bytes += putFile16BitBE(file, string_length);
7132 for (i = 0; i < string_length; i++)
7133 num_bytes += putFile8Bit(file, string[i]);
7135 else if (data_type == TYPE_ELEMENT_LIST)
7137 int *element_array = (int *)(entry->value);
7138 int num_elements = *(int *)(entry->num_entities);
7141 /* check if any settings have been modified before saving them */
7142 for (i = 0; i < num_elements; i++)
7143 if (element_array[i] != default_value)
7146 /* do not save if explicitly told or if unmodified default settings */
7147 if ((save_type == SAVE_CONF_NEVER) ||
7148 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7152 num_bytes += putFile16BitBE(file, element);
7154 num_bytes += putFile8Bit(file, conf_type);
7155 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7157 for (i = 0; i < num_elements; i++)
7158 num_bytes += putFile16BitBE(file, element_array[i]);
7160 else if (data_type == TYPE_CONTENT_LIST)
7162 struct Content *content = (struct Content *)(entry->value);
7163 int num_contents = *(int *)(entry->num_entities);
7166 /* check if any settings have been modified before saving them */
7167 for (i = 0; i < num_contents; i++)
7168 for (y = 0; y < 3; y++)
7169 for (x = 0; x < 3; x++)
7170 if (content[i].e[x][y] != default_value)
7173 /* do not save if explicitly told or if unmodified default settings */
7174 if ((save_type == SAVE_CONF_NEVER) ||
7175 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7179 num_bytes += putFile16BitBE(file, element);
7181 num_bytes += putFile8Bit(file, conf_type);
7182 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7184 for (i = 0; i < num_contents; i++)
7185 for (y = 0; y < 3; y++)
7186 for (x = 0; x < 3; x++)
7187 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7193 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7198 li = *level; /* copy level data into temporary buffer */
7200 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7201 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7206 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7211 li = *level; /* copy level data into temporary buffer */
7213 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7214 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7219 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7221 int envelope_nr = element - EL_ENVELOPE_1;
7225 chunk_size += putFile16BitBE(file, element);
7227 /* copy envelope data into temporary buffer */
7228 xx_envelope = level->envelope[envelope_nr];
7230 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7231 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7236 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7238 struct ElementInfo *ei = &element_info[element];
7242 chunk_size += putFile16BitBE(file, element);
7244 xx_ei = *ei; /* copy element data into temporary buffer */
7246 /* set default description string for this specific element */
7247 strcpy(xx_default_description, getDefaultElementDescription(ei));
7249 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7250 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7252 for (i = 0; i < ei->num_change_pages; i++)
7254 struct ElementChangeInfo *change = &ei->change_page[i];
7256 xx_current_change_page = i;
7258 xx_change = *change; /* copy change data into temporary buffer */
7261 setEventBitsFromEventFlags(change);
7263 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7264 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7271 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7273 struct ElementInfo *ei = &element_info[element];
7274 struct ElementGroupInfo *group = ei->group;
7278 chunk_size += putFile16BitBE(file, element);
7280 xx_ei = *ei; /* copy element data into temporary buffer */
7281 xx_group = *group; /* copy group data into temporary buffer */
7283 /* set default description string for this specific element */
7284 strcpy(xx_default_description, getDefaultElementDescription(ei));
7286 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7287 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7292 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7293 boolean save_as_template)
7299 if (!(file = fopen(filename, MODE_WRITE)))
7301 Error(ERR_WARN, "cannot save level file '%s'", filename);
7305 level->file_version = FILE_VERSION_ACTUAL;
7306 level->game_version = GAME_VERSION_ACTUAL;
7308 level->creation_date = getCurrentDate();
7310 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7311 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7313 chunk_size = SaveLevel_VERS(NULL, level);
7314 putFileChunkBE(file, "VERS", chunk_size);
7315 SaveLevel_VERS(file, level);
7317 chunk_size = SaveLevel_DATE(NULL, level);
7318 putFileChunkBE(file, "DATE", chunk_size);
7319 SaveLevel_DATE(file, level);
7321 chunk_size = SaveLevel_NAME(NULL, level);
7322 putFileChunkBE(file, "NAME", chunk_size);
7323 SaveLevel_NAME(file, level);
7325 chunk_size = SaveLevel_AUTH(NULL, level);
7326 putFileChunkBE(file, "AUTH", chunk_size);
7327 SaveLevel_AUTH(file, level);
7329 chunk_size = SaveLevel_INFO(NULL, level);
7330 putFileChunkBE(file, "INFO", chunk_size);
7331 SaveLevel_INFO(file, level);
7333 chunk_size = SaveLevel_BODY(NULL, level);
7334 putFileChunkBE(file, "BODY", chunk_size);
7335 SaveLevel_BODY(file, level);
7337 chunk_size = SaveLevel_ELEM(NULL, level);
7338 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7340 putFileChunkBE(file, "ELEM", chunk_size);
7341 SaveLevel_ELEM(file, level);
7344 for (i = 0; i < NUM_ENVELOPES; i++)
7346 int element = EL_ENVELOPE_1 + i;
7348 chunk_size = SaveLevel_NOTE(NULL, level, element);
7349 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7351 putFileChunkBE(file, "NOTE", chunk_size);
7352 SaveLevel_NOTE(file, level, element);
7356 /* if not using template level, check for non-default custom/group elements */
7357 if (!level->use_custom_template || save_as_template)
7359 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7361 int element = EL_CUSTOM_START + i;
7363 chunk_size = SaveLevel_CUSX(NULL, level, element);
7364 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7366 putFileChunkBE(file, "CUSX", chunk_size);
7367 SaveLevel_CUSX(file, level, element);
7371 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7373 int element = EL_GROUP_START + i;
7375 chunk_size = SaveLevel_GRPX(NULL, level, element);
7376 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7378 putFileChunkBE(file, "GRPX", chunk_size);
7379 SaveLevel_GRPX(file, level, element);
7386 SetFilePermissions(filename, PERMS_PRIVATE);
7389 void SaveLevel(int nr)
7391 char *filename = getDefaultLevelFilename(nr);
7393 SaveLevelFromFilename(&level, filename, FALSE);
7396 void SaveLevelTemplate()
7398 char *filename = getLocalLevelTemplateFilename();
7400 SaveLevelFromFilename(&level, filename, TRUE);
7403 boolean SaveLevelChecked(int nr)
7405 char *filename = getDefaultLevelFilename(nr);
7406 boolean new_level = !fileExists(filename);
7407 boolean level_saved = FALSE;
7409 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7414 Request("Level saved!", REQ_CONFIRM);
7422 void DumpLevel(struct LevelInfo *level)
7424 if (level->no_level_file || level->no_valid_file)
7426 Error(ERR_WARN, "cannot dump -- no valid level file found");
7432 Print("Level xxx (file version %08d, game version %08d)\n",
7433 level->file_version, level->game_version);
7436 Print("Level author: '%s'\n", level->author);
7437 Print("Level title: '%s'\n", level->name);
7439 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7441 Print("Level time: %d seconds\n", level->time);
7442 Print("Gems needed: %d\n", level->gems_needed);
7444 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7445 Print("Time for wheel: %d seconds\n", level->time_wheel);
7446 Print("Time for light: %d seconds\n", level->time_light);
7447 Print("Time for timegate: %d seconds\n", level->time_timegate);
7449 Print("Amoeba speed: %d\n", level->amoeba_speed);
7452 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7453 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7454 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7455 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7456 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7462 /* ========================================================================= */
7463 /* tape file functions */
7464 /* ========================================================================= */
7466 static void setTapeInfoToDefaults()
7470 /* always start with reliable default values (empty tape) */
7473 /* default values (also for pre-1.2 tapes) with only the first player */
7474 tape.player_participates[0] = TRUE;
7475 for (i = 1; i < MAX_PLAYERS; i++)
7476 tape.player_participates[i] = FALSE;
7478 /* at least one (default: the first) player participates in every tape */
7479 tape.num_participating_players = 1;
7481 tape.level_nr = level_nr;
7483 tape.changed = FALSE;
7485 tape.recording = FALSE;
7486 tape.playing = FALSE;
7487 tape.pausing = FALSE;
7489 tape.no_valid_file = FALSE;
7492 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7494 tape->file_version = getFileVersion(file);
7495 tape->game_version = getFileVersion(file);
7500 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7504 tape->random_seed = getFile32BitBE(file);
7505 tape->date = getFile32BitBE(file);
7506 tape->length = getFile32BitBE(file);
7508 /* read header fields that are new since version 1.2 */
7509 if (tape->file_version >= FILE_VERSION_1_2)
7511 byte store_participating_players = getFile8Bit(file);
7514 /* since version 1.2, tapes store which players participate in the tape */
7515 tape->num_participating_players = 0;
7516 for (i = 0; i < MAX_PLAYERS; i++)
7518 tape->player_participates[i] = FALSE;
7520 if (store_participating_players & (1 << i))
7522 tape->player_participates[i] = TRUE;
7523 tape->num_participating_players++;
7527 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7529 engine_version = getFileVersion(file);
7530 if (engine_version > 0)
7531 tape->engine_version = engine_version;
7533 tape->engine_version = tape->game_version;
7539 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7541 int level_identifier_size;
7544 level_identifier_size = getFile16BitBE(file);
7546 tape->level_identifier =
7547 checked_realloc(tape->level_identifier, level_identifier_size);
7549 for (i = 0; i < level_identifier_size; i++)
7550 tape->level_identifier[i] = getFile8Bit(file);
7552 tape->level_nr = getFile16BitBE(file);
7554 chunk_size = 2 + level_identifier_size + 2;
7559 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7562 int chunk_size_expected =
7563 (tape->num_participating_players + 1) * tape->length;
7565 if (chunk_size_expected != chunk_size)
7567 ReadUnusedBytesFromFile(file, chunk_size);
7568 return chunk_size_expected;
7571 for (i = 0; i < tape->length; i++)
7573 if (i >= MAX_TAPE_LEN)
7575 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7578 // tape too large; read and ignore remaining tape data from this chunk
7579 for (;i < tape->length; i++)
7580 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7585 for (j = 0; j < MAX_PLAYERS; j++)
7587 tape->pos[i].action[j] = MV_NONE;
7589 if (tape->player_participates[j])
7590 tape->pos[i].action[j] = getFile8Bit(file);
7593 tape->pos[i].delay = getFile8Bit(file);
7595 if (tape->file_version == FILE_VERSION_1_0)
7597 /* eliminate possible diagonal moves in old tapes */
7598 /* this is only for backward compatibility */
7600 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7601 byte action = tape->pos[i].action[0];
7602 int k, num_moves = 0;
7604 for (k = 0; k<4; k++)
7606 if (action & joy_dir[k])
7608 tape->pos[i + num_moves].action[0] = joy_dir[k];
7610 tape->pos[i + num_moves].delay = 0;
7619 tape->length += num_moves;
7622 else if (tape->file_version < FILE_VERSION_2_0)
7624 /* convert pre-2.0 tapes to new tape format */
7626 if (tape->pos[i].delay > 1)
7629 tape->pos[i + 1] = tape->pos[i];
7630 tape->pos[i + 1].delay = 1;
7633 for (j = 0; j < MAX_PLAYERS; j++)
7634 tape->pos[i].action[j] = MV_NONE;
7635 tape->pos[i].delay--;
7642 if (checkEndOfFile(file))
7646 if (i != tape->length)
7647 chunk_size = (tape->num_participating_players + 1) * i;
7652 void LoadTape_SokobanSolution(char *filename)
7655 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7657 if (!(file = openFile(filename, MODE_READ)))
7659 tape.no_valid_file = TRUE;
7664 while (!checkEndOfFile(file))
7666 unsigned char c = getByteFromFile(file);
7668 if (checkEndOfFile(file))
7675 tape.pos[tape.length].action[0] = MV_UP;
7676 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7682 tape.pos[tape.length].action[0] = MV_DOWN;
7683 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7689 tape.pos[tape.length].action[0] = MV_LEFT;
7690 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7696 tape.pos[tape.length].action[0] = MV_RIGHT;
7697 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7705 /* ignore white-space characters */
7709 tape.no_valid_file = TRUE;
7711 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7719 if (tape.no_valid_file)
7722 tape.length_frames = GetTapeLengthFrames();
7723 tape.length_seconds = GetTapeLengthSeconds();
7726 void LoadTapeFromFilename(char *filename)
7728 char cookie[MAX_LINE_LEN];
7729 char chunk_name[CHUNK_ID_LEN + 1];
7733 /* always start with reliable default values */
7734 setTapeInfoToDefaults();
7736 if (strSuffix(filename, ".sln"))
7738 LoadTape_SokobanSolution(filename);
7743 if (!(file = openFile(filename, MODE_READ)))
7745 tape.no_valid_file = TRUE;
7750 getFileChunkBE(file, chunk_name, NULL);
7751 if (strEqual(chunk_name, "RND1"))
7753 getFile32BitBE(file); /* not used */
7755 getFileChunkBE(file, chunk_name, NULL);
7756 if (!strEqual(chunk_name, "TAPE"))
7758 tape.no_valid_file = TRUE;
7760 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7767 else /* check for pre-2.0 file format with cookie string */
7769 strcpy(cookie, chunk_name);
7770 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7772 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7773 cookie[strlen(cookie) - 1] = '\0';
7775 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7777 tape.no_valid_file = TRUE;
7779 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7786 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7788 tape.no_valid_file = TRUE;
7790 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7797 /* pre-2.0 tape files have no game version, so use file version here */
7798 tape.game_version = tape.file_version;
7801 if (tape.file_version < FILE_VERSION_1_2)
7803 /* tape files from versions before 1.2.0 without chunk structure */
7804 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7805 LoadTape_BODY(file, 2 * tape.length, &tape);
7813 int (*loader)(File *, int, struct TapeInfo *);
7817 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7818 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7819 { "INFO", -1, LoadTape_INFO },
7820 { "BODY", -1, LoadTape_BODY },
7824 while (getFileChunkBE(file, chunk_name, &chunk_size))
7828 while (chunk_info[i].name != NULL &&
7829 !strEqual(chunk_name, chunk_info[i].name))
7832 if (chunk_info[i].name == NULL)
7834 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7835 chunk_name, filename);
7836 ReadUnusedBytesFromFile(file, chunk_size);
7838 else if (chunk_info[i].size != -1 &&
7839 chunk_info[i].size != chunk_size)
7841 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7842 chunk_size, chunk_name, filename);
7843 ReadUnusedBytesFromFile(file, chunk_size);
7847 /* call function to load this tape chunk */
7848 int chunk_size_expected =
7849 (chunk_info[i].loader)(file, chunk_size, &tape);
7851 /* the size of some chunks cannot be checked before reading other
7852 chunks first (like "HEAD" and "BODY") that contain some header
7853 information, so check them here */
7854 if (chunk_size_expected != chunk_size)
7856 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7857 chunk_size, chunk_name, filename);
7865 tape.length_frames = GetTapeLengthFrames();
7866 tape.length_seconds = GetTapeLengthSeconds();
7869 printf("::: tape file version: %d\n", tape.file_version);
7870 printf("::: tape game version: %d\n", tape.game_version);
7871 printf("::: tape engine version: %d\n", tape.engine_version);
7875 void LoadTape(int nr)
7877 char *filename = getTapeFilename(nr);
7879 LoadTapeFromFilename(filename);
7882 void LoadSolutionTape(int nr)
7884 char *filename = getSolutionTapeFilename(nr);
7886 LoadTapeFromFilename(filename);
7888 if (TAPE_IS_EMPTY(tape) &&
7889 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7890 level.native_sp_level->demo.is_available)
7891 CopyNativeTape_SP_to_RND(&level);
7894 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7896 putFileVersion(file, tape->file_version);
7897 putFileVersion(file, tape->game_version);
7900 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7903 byte store_participating_players = 0;
7905 /* set bits for participating players for compact storage */
7906 for (i = 0; i < MAX_PLAYERS; i++)
7907 if (tape->player_participates[i])
7908 store_participating_players |= (1 << i);
7910 putFile32BitBE(file, tape->random_seed);
7911 putFile32BitBE(file, tape->date);
7912 putFile32BitBE(file, tape->length);
7914 putFile8Bit(file, store_participating_players);
7916 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7917 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7919 putFileVersion(file, tape->engine_version);
7922 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7924 int level_identifier_size = strlen(tape->level_identifier) + 1;
7927 putFile16BitBE(file, level_identifier_size);
7929 for (i = 0; i < level_identifier_size; i++)
7930 putFile8Bit(file, tape->level_identifier[i]);
7932 putFile16BitBE(file, tape->level_nr);
7935 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7939 for (i = 0; i < tape->length; i++)
7941 for (j = 0; j < MAX_PLAYERS; j++)
7942 if (tape->player_participates[j])
7943 putFile8Bit(file, tape->pos[i].action[j]);
7945 putFile8Bit(file, tape->pos[i].delay);
7949 void SaveTape(int nr)
7951 char *filename = getTapeFilename(nr);
7953 int num_participating_players = 0;
7954 int info_chunk_size;
7955 int body_chunk_size;
7958 InitTapeDirectory(leveldir_current->subdir);
7960 if (!(file = fopen(filename, MODE_WRITE)))
7962 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7966 tape.file_version = FILE_VERSION_ACTUAL;
7967 tape.game_version = GAME_VERSION_ACTUAL;
7969 /* count number of participating players */
7970 for (i = 0; i < MAX_PLAYERS; i++)
7971 if (tape.player_participates[i])
7972 num_participating_players++;
7974 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7975 body_chunk_size = (num_participating_players + 1) * tape.length;
7977 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7978 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7980 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7981 SaveTape_VERS(file, &tape);
7983 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7984 SaveTape_HEAD(file, &tape);
7986 putFileChunkBE(file, "INFO", info_chunk_size);
7987 SaveTape_INFO(file, &tape);
7989 putFileChunkBE(file, "BODY", body_chunk_size);
7990 SaveTape_BODY(file, &tape);
7994 SetFilePermissions(filename, PERMS_PRIVATE);
7996 tape.changed = FALSE;
7999 boolean SaveTapeChecked(int nr)
8001 char *filename = getTapeFilename(nr);
8002 boolean new_tape = !fileExists(filename);
8003 boolean tape_saved = FALSE;
8005 if (new_tape || Request("Replace old tape?", REQ_ASK))
8010 Request("Tape saved!", REQ_CONFIRM);
8018 void DumpTape(struct TapeInfo *tape)
8020 int tape_frame_counter;
8023 if (tape->no_valid_file)
8025 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8031 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8032 tape->level_nr, tape->file_version, tape->game_version);
8033 Print(" (effective engine version %08d)\n",
8034 tape->engine_version);
8035 Print("Level series identifier: '%s'\n", tape->level_identifier);
8038 tape_frame_counter = 0;
8040 for (i = 0; i < tape->length; i++)
8042 if (i >= MAX_TAPE_LEN)
8047 for (j = 0; j < MAX_PLAYERS; j++)
8049 if (tape->player_participates[j])
8051 int action = tape->pos[i].action[j];
8053 Print("%d:%02x ", j, action);
8054 Print("[%c%c%c%c|%c%c] - ",
8055 (action & JOY_LEFT ? '<' : ' '),
8056 (action & JOY_RIGHT ? '>' : ' '),
8057 (action & JOY_UP ? '^' : ' '),
8058 (action & JOY_DOWN ? 'v' : ' '),
8059 (action & JOY_BUTTON_1 ? '1' : ' '),
8060 (action & JOY_BUTTON_2 ? '2' : ' '));
8064 Print("(%03d) ", tape->pos[i].delay);
8065 Print("[%05d]\n", tape_frame_counter);
8067 tape_frame_counter += tape->pos[i].delay;
8074 /* ========================================================================= */
8075 /* score file functions */
8076 /* ========================================================================= */
8078 void LoadScore(int nr)
8081 char *filename = getScoreFilename(nr);
8082 char cookie[MAX_LINE_LEN];
8083 char line[MAX_LINE_LEN];
8087 /* always start with reliable default values */
8088 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8090 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8091 highscore[i].Score = 0;
8094 if (!(file = fopen(filename, MODE_READ)))
8097 /* check file identifier */
8098 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8100 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8101 cookie[strlen(cookie) - 1] = '\0';
8103 if (!checkCookieString(cookie, SCORE_COOKIE))
8105 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8110 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8112 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8113 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8114 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8117 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8118 line[strlen(line) - 1] = '\0';
8120 for (line_ptr = line; *line_ptr; line_ptr++)
8122 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8124 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8125 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8134 void SaveScore(int nr)
8137 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8138 char *filename = getScoreFilename(nr);
8141 InitScoreDirectory(leveldir_current->subdir);
8143 if (!(file = fopen(filename, MODE_WRITE)))
8145 Error(ERR_WARN, "cannot save score for level %d", nr);
8149 fprintf(file, "%s\n\n", SCORE_COOKIE);
8151 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8152 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8156 SetFilePermissions(filename, permissions);
8160 /* ========================================================================= */
8161 /* setup file functions */
8162 /* ========================================================================= */
8164 #define TOKEN_STR_PLAYER_PREFIX "player_"
8167 #define SETUP_TOKEN_PLAYER_NAME 0
8168 #define SETUP_TOKEN_SOUND 1
8169 #define SETUP_TOKEN_SOUND_LOOPS 2
8170 #define SETUP_TOKEN_SOUND_MUSIC 3
8171 #define SETUP_TOKEN_SOUND_SIMPLE 4
8172 #define SETUP_TOKEN_TOONS 5
8173 #define SETUP_TOKEN_SCROLL_DELAY 6
8174 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
8175 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE 8
8176 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY 9
8177 #define SETUP_TOKEN_FADE_SCREENS 10
8178 #define SETUP_TOKEN_AUTORECORD 11
8179 #define SETUP_TOKEN_SHOW_TITLESCREEN 12
8180 #define SETUP_TOKEN_QUICK_DOORS 13
8181 #define SETUP_TOKEN_TEAM_MODE 14
8182 #define SETUP_TOKEN_HANDICAP 15
8183 #define SETUP_TOKEN_SKIP_LEVELS 16
8184 #define SETUP_TOKEN_INCREMENT_LEVELS 17
8185 #define SETUP_TOKEN_TIME_LIMIT 18
8186 #define SETUP_TOKEN_FULLSCREEN 19
8187 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT 20
8188 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY 21
8189 #define SETUP_TOKEN_SCREEN_RENDERING_MODE 22
8190 #define SETUP_TOKEN_ASK_ON_ESCAPE 23
8191 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 24
8192 #define SETUP_TOKEN_QUICK_SWITCH 25
8193 #define SETUP_TOKEN_INPUT_ON_FOCUS 26
8194 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 27
8195 #define SETUP_TOKEN_GAME_FRAME_DELAY 28
8196 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 29
8197 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 30
8198 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS 31
8199 #define SETUP_TOKEN_GRAPHICS_SET 32
8200 #define SETUP_TOKEN_SOUNDS_SET 33
8201 #define SETUP_TOKEN_MUSIC_SET 34
8202 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 35
8203 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 36
8204 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 37
8205 #define SETUP_TOKEN_VOLUME_SIMPLE 38
8206 #define SETUP_TOKEN_VOLUME_LOOPS 39
8207 #define SETUP_TOKEN_VOLUME_MUSIC 40
8208 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE 41
8209 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 42
8210 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE 43
8212 #define NUM_GLOBAL_SETUP_TOKENS 44
8215 #define SETUP_TOKEN_EDITOR_EL_CLASSIC 0
8216 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 1
8217 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 2
8218 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 3
8219 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 4
8220 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 5
8222 #define NUM_EDITOR_SETUP_TOKENS 6
8224 /* editor cascade setup */
8225 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
8226 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
8227 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
8228 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
8229 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
8230 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
8231 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
8232 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
8233 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
8234 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
8235 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
8236 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
8237 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
8238 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
8239 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
8241 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
8243 /* shortcut setup */
8244 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
8245 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
8246 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
8247 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
8248 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
8249 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
8250 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
8251 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
8252 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
8253 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
8254 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
8255 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
8256 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
8257 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
8258 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
8259 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
8260 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
8261 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
8262 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
8263 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
8264 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
8266 #define NUM_SHORTCUT_SETUP_TOKENS 21
8269 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
8270 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
8271 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
8272 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
8273 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
8274 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
8275 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
8276 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
8277 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
8278 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
8279 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
8280 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
8281 #define SETUP_TOKEN_PLAYER_KEY_UP 12
8282 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
8283 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8284 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8286 #define NUM_PLAYER_SETUP_TOKENS 16
8289 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8290 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8291 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8293 #define NUM_SYSTEM_SETUP_TOKENS 3
8295 /* internal setup */
8296 #define SETUP_TOKEN_INT_PROGRAM_TITLE 0
8297 #define SETUP_TOKEN_INT_PROGRAM_VERSION 1
8298 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR 2
8299 #define SETUP_TOKEN_INT_PROGRAM_EMAIL 3
8300 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE 4
8301 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 5
8302 #define SETUP_TOKEN_INT_PROGRAM_COMPANY 6
8303 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 7
8304 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 8
8305 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 9
8306 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 10
8307 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 11
8308 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 12
8309 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 13
8310 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 14
8311 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
8312 #define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE 16
8313 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH 17
8314 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT 18
8316 #define NUM_INTERNAL_SETUP_TOKENS 19
8319 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0 0
8320 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1 1
8321 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2 2
8322 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3 3
8323 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4 4
8324 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5 5
8325 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6 6
8326 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7 7
8327 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8 8
8328 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9 9
8329 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0 10
8330 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1 11
8331 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2 12
8332 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3 13
8333 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4 14
8334 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5 15
8335 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6 16
8336 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7 17
8337 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8 18
8338 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9 19
8339 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8340 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8341 #define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
8343 #define NUM_DEBUG_SETUP_TOKENS 23
8346 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8348 #define NUM_OPTIONS_SETUP_TOKENS 1
8351 static struct SetupInfo si;
8352 static struct SetupEditorInfo sei;
8353 static struct SetupEditorCascadeInfo seci;
8354 static struct SetupShortcutInfo ssi;
8355 static struct SetupInputInfo sii;
8356 static struct SetupSystemInfo syi;
8357 static struct SetupInternalInfo sxi;
8358 static struct SetupDebugInfo sdi;
8359 static struct OptionInfo soi;
8361 static struct TokenInfo global_setup_tokens[] =
8363 { TYPE_STRING, &si.player_name, "player_name" },
8364 { TYPE_SWITCH, &si.sound, "sound" },
8365 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8366 { TYPE_SWITCH, &si.sound_music, "background_music" },
8367 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8368 { TYPE_SWITCH, &si.toons, "toons" },
8369 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8370 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8371 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8372 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8373 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8374 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8375 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8376 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8377 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8378 { TYPE_SWITCH, &si.handicap, "handicap" },
8379 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8380 { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
8381 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8382 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8383 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8384 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8385 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8386 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8387 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8388 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8389 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8390 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8391 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8392 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8393 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8394 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8395 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8396 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8397 { TYPE_STRING, &si.music_set, "music_set" },
8398 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8399 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8400 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8401 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8402 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8403 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8404 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8405 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8406 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8409 static struct TokenInfo editor_setup_tokens[] =
8411 { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
8412 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8413 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8414 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8415 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8416 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8419 static struct TokenInfo editor_cascade_setup_tokens[] =
8421 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8422 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8423 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8424 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8425 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8426 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8427 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8428 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8429 { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
8430 { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
8431 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8432 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8433 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8434 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8435 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8436 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8437 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8440 static struct TokenInfo shortcut_setup_tokens[] =
8442 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8443 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8444 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8445 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8446 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8447 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8448 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8449 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8450 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8451 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8452 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8453 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8454 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8455 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8456 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8457 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8458 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8459 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8460 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8461 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8462 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8465 static struct TokenInfo player_setup_tokens[] =
8467 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8468 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8469 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8470 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8471 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8472 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8473 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8474 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8475 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8476 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8477 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8478 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8479 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8480 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8481 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8482 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8485 static struct TokenInfo system_setup_tokens[] =
8487 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8488 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8489 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8492 static struct TokenInfo internal_setup_tokens[] =
8494 { TYPE_STRING, &sxi.program_title, "program_title" },
8495 { TYPE_STRING, &sxi.program_version, "program_version" },
8496 { TYPE_STRING, &sxi.program_author, "program_author" },
8497 { TYPE_STRING, &sxi.program_email, "program_email" },
8498 { TYPE_STRING, &sxi.program_website, "program_website" },
8499 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8500 { TYPE_STRING, &sxi.program_company, "program_company" },
8501 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8502 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8503 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8504 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8505 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8506 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8507 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8508 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8509 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8510 { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" },
8511 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8512 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8515 static struct TokenInfo debug_setup_tokens[] =
8517 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8518 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8519 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8520 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8521 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8522 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8523 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8524 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8525 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8526 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8527 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8528 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8529 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8530 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8531 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8532 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8533 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8534 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8535 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8536 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8537 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8538 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8539 { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8542 static struct TokenInfo options_setup_tokens[] =
8544 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8547 static char *get_corrected_login_name(char *login_name)
8549 /* needed because player name must be a fixed length string */
8550 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8552 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8553 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8555 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8556 if (strchr(login_name_new, ' '))
8557 *strchr(login_name_new, ' ') = '\0';
8559 return login_name_new;
8562 static void setSetupInfoToDefaults(struct SetupInfo *si)
8566 si->player_name = get_corrected_login_name(getLoginName());
8569 si->sound_loops = TRUE;
8570 si->sound_music = TRUE;
8571 si->sound_simple = TRUE;
8573 si->scroll_delay = TRUE;
8574 si->scroll_delay_value = STD_SCROLL_DELAY;
8575 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8576 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8577 si->fade_screens = TRUE;
8578 si->autorecord = TRUE;
8579 si->show_titlescreen = TRUE;
8580 si->quick_doors = FALSE;
8581 si->team_mode = FALSE;
8582 si->handicap = TRUE;
8583 si->skip_levels = TRUE;
8584 si->increment_levels = TRUE;
8585 si->time_limit = TRUE;
8586 si->fullscreen = FALSE;
8587 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8588 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8589 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8590 si->ask_on_escape = TRUE;
8591 si->ask_on_escape_editor = TRUE;
8592 si->quick_switch = FALSE;
8593 si->input_on_focus = FALSE;
8594 si->prefer_aga_graphics = TRUE;
8595 si->game_frame_delay = GAME_FRAME_DELAY;
8596 si->sp_show_border_elements = FALSE;
8597 si->small_game_graphics = FALSE;
8598 si->show_snapshot_buttons = FALSE;
8600 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8601 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8602 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8604 si->override_level_graphics = FALSE;
8605 si->override_level_sounds = FALSE;
8606 si->override_level_music = FALSE;
8608 si->volume_simple = 100; /* percent */
8609 si->volume_loops = 100; /* percent */
8610 si->volume_music = 100; /* percent */
8612 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8613 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8614 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8616 si->editor.el_boulderdash = TRUE;
8617 si->editor.el_emerald_mine = TRUE;
8618 si->editor.el_emerald_mine_club = TRUE;
8619 si->editor.el_more = TRUE;
8620 si->editor.el_sokoban = TRUE;
8621 si->editor.el_supaplex = TRUE;
8622 si->editor.el_diamond_caves = TRUE;
8623 si->editor.el_dx_boulderdash = TRUE;
8625 si->editor.el_mirror_magic = TRUE;
8626 si->editor.el_deflektor = TRUE;
8628 si->editor.el_chars = TRUE;
8629 si->editor.el_steel_chars = TRUE;
8631 si->editor.el_classic = TRUE;
8632 si->editor.el_custom = TRUE;
8634 si->editor.el_user_defined = FALSE;
8635 si->editor.el_dynamic = TRUE;
8637 si->editor.el_headlines = TRUE;
8639 si->editor.show_element_token = FALSE;
8641 si->editor.use_template_for_new_levels = TRUE;
8643 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8644 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8645 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8647 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8648 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8649 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8650 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8651 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8653 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8654 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8655 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8656 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8657 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8658 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8660 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8661 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8662 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8664 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8665 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8666 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8667 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8669 for (i = 0; i < MAX_PLAYERS; i++)
8671 si->input[i].use_joystick = FALSE;
8672 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8673 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8674 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8675 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8676 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8677 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8678 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8679 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8680 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8681 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8682 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8683 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8684 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8685 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8686 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8689 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8690 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8691 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8693 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8694 si->internal.program_version = getStringCopy(getProgramRealVersionString());
8695 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8696 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8697 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8698 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8699 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8701 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8703 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8704 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8705 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8707 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8708 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8709 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8711 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8712 si->internal.choose_from_top_leveldir = FALSE;
8713 si->internal.show_scaling_in_title = TRUE;
8715 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
8716 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8718 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8719 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8720 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8721 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8722 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8723 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8724 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8725 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8726 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8727 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8729 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8730 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8731 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8732 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8733 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8734 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8735 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8736 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8737 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8738 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8740 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8741 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
8743 si->debug.show_frames_per_second = FALSE;
8745 si->options.verbose = FALSE;
8747 #if defined(PLATFORM_ANDROID)
8748 si->fullscreen = TRUE;
8752 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8754 si->editor_cascade.el_bd = TRUE;
8755 si->editor_cascade.el_em = TRUE;
8756 si->editor_cascade.el_emc = TRUE;
8757 si->editor_cascade.el_rnd = TRUE;
8758 si->editor_cascade.el_sb = TRUE;
8759 si->editor_cascade.el_sp = TRUE;
8760 si->editor_cascade.el_dc = TRUE;
8761 si->editor_cascade.el_dx = TRUE;
8763 si->editor_cascade.el_mm = TRUE;
8764 si->editor_cascade.el_df = TRUE;
8766 si->editor_cascade.el_chars = FALSE;
8767 si->editor_cascade.el_steel_chars = FALSE;
8768 si->editor_cascade.el_ce = FALSE;
8769 si->editor_cascade.el_ge = FALSE;
8770 si->editor_cascade.el_ref = FALSE;
8771 si->editor_cascade.el_user = FALSE;
8772 si->editor_cascade.el_dynamic = FALSE;
8775 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
8777 static char *getHideSetupToken(void *setup_value)
8779 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
8781 if (setup_value != NULL)
8782 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
8784 return hide_setup_token;
8787 static void setHideSetupEntry(void *setup_value_raw)
8789 /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
8790 void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
8792 char *hide_setup_token = getHideSetupToken(setup_value);
8794 if (setup_value != NULL)
8795 setHashEntry(hide_setup_hash, hide_setup_token, "");
8798 boolean hideSetupEntry(void *setup_value)
8800 char *hide_setup_token = getHideSetupToken(setup_value);
8802 return (setup_value != NULL &&
8803 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
8806 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
8807 struct TokenInfo *token_info,
8808 int token_nr, char *token_text)
8810 char *token_hide_text = getStringCat2(token_text, ".hide");
8811 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
8813 /* set the value of this setup option in the setup option structure */
8814 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
8816 /* check if this setup option should be hidden in the setup menu */
8817 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
8818 setHideSetupEntry(token_info[token_nr].value);
8821 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
8822 struct TokenInfo *token_info,
8825 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
8826 token_info[token_nr].text);
8829 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8833 if (!setup_file_hash)
8836 if (hide_setup_hash == NULL)
8837 hide_setup_hash = newSetupFileHash();
8841 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8842 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
8847 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8848 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
8851 /* shortcut setup */
8852 ssi = setup.shortcut;
8853 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8854 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
8855 setup.shortcut = ssi;
8858 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8862 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8864 sii = setup.input[pnr];
8865 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8867 char full_token[100];
8869 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8870 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
8873 setup.input[pnr] = sii;
8878 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8879 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
8882 /* internal setup */
8883 sxi = setup.internal;
8884 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8885 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
8886 setup.internal = sxi;
8890 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8891 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
8895 soi = setup.options;
8896 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8897 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
8898 setup.options = soi;
8901 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8905 if (!setup_file_hash)
8908 /* editor cascade setup */
8909 seci = setup.editor_cascade;
8910 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8911 setSetupInfo(editor_cascade_setup_tokens, i,
8912 getHashEntry(setup_file_hash,
8913 editor_cascade_setup_tokens[i].text));
8914 setup.editor_cascade = seci;
8917 void LoadSetupFromFilename(char *filename)
8919 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
8921 if (setup_file_hash)
8923 decodeSetupFileHash(setup_file_hash);
8925 freeSetupFileHash(setup_file_hash);
8929 Error(ERR_DEBUG, "using default setup values");
8933 static void LoadSetup_SpecialPostProcessing()
8935 char *player_name_new;
8937 /* needed to work around problems with fixed length strings */
8938 player_name_new = get_corrected_login_name(setup.player_name);
8939 free(setup.player_name);
8940 setup.player_name = player_name_new;
8942 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
8943 if (setup.scroll_delay == FALSE)
8945 setup.scroll_delay_value = MIN_SCROLL_DELAY;
8946 setup.scroll_delay = TRUE; /* now always "on" */
8949 /* make sure that scroll delay value stays inside valid range */
8950 setup.scroll_delay_value =
8951 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
8958 /* always start with reliable default values */
8959 setSetupInfoToDefaults(&setup);
8961 /* try to load setup values from default setup file */
8962 filename = getDefaultSetupFilename();
8964 if (fileExists(filename))
8965 LoadSetupFromFilename(filename);
8967 /* try to load setup values from user setup file */
8968 filename = getSetupFilename();
8970 LoadSetupFromFilename(filename);
8972 LoadSetup_SpecialPostProcessing();
8975 void LoadSetup_EditorCascade()
8977 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8978 SetupFileHash *setup_file_hash = NULL;
8980 /* always start with reliable default values */
8981 setSetupInfoToDefaults_EditorCascade(&setup);
8983 setup_file_hash = loadSetupFileHash(filename);
8985 if (setup_file_hash)
8987 decodeSetupFileHash_EditorCascade(setup_file_hash);
8989 freeSetupFileHash(setup_file_hash);
8995 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
8998 char mapping_guid[MAX_LINE_LEN];
8999 char *mapping_start, *mapping_end;
9001 // get GUID from game controller mapping line: copy complete line
9002 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9003 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9005 // get GUID from game controller mapping line: cut after GUID part
9006 mapping_start = strchr(mapping_guid, ',');
9007 if (mapping_start != NULL)
9008 *mapping_start = '\0';
9010 // cut newline from game controller mapping line
9011 mapping_end = strchr(mapping_line, '\n');
9012 if (mapping_end != NULL)
9013 *mapping_end = '\0';
9015 // add mapping entry to game controller mappings hash
9016 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9019 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9024 if (!(file = fopen(filename, MODE_READ)))
9026 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9033 char line[MAX_LINE_LEN];
9035 if (!fgets(line, MAX_LINE_LEN, file))
9038 addGameControllerMappingToHash(mappings_hash, line);
9046 char *filename = getSetupFilename();
9050 InitUserDataDirectory();
9052 if (!(file = fopen(filename, MODE_WRITE)))
9054 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9058 fprintFileHeader(file, SETUP_FILENAME);
9062 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9064 /* just to make things nicer :) */
9065 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9066 i == SETUP_TOKEN_GRAPHICS_SET ||
9067 i == SETUP_TOKEN_VOLUME_SIMPLE ||
9068 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
9069 fprintf(file, "\n");
9071 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9076 fprintf(file, "\n");
9077 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9078 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9080 /* shortcut setup */
9081 ssi = setup.shortcut;
9082 fprintf(file, "\n");
9083 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9084 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9087 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9091 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9092 fprintf(file, "\n");
9094 sii = setup.input[pnr];
9095 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9096 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9101 fprintf(file, "\n");
9102 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9103 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9105 /* internal setup */
9106 /* (internal setup values not saved to user setup file) */
9110 fprintf(file, "\n");
9111 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9112 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9115 soi = setup.options;
9116 fprintf(file, "\n");
9117 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9118 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9122 SetFilePermissions(filename, PERMS_PRIVATE);
9125 void SaveSetup_EditorCascade()
9127 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9131 InitUserDataDirectory();
9133 if (!(file = fopen(filename, MODE_WRITE)))
9135 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9140 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9142 seci = setup.editor_cascade;
9143 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9144 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9148 SetFilePermissions(filename, PERMS_PRIVATE);
9153 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9158 if (!(file = fopen(filename, MODE_WRITE)))
9160 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9165 BEGIN_HASH_ITERATION(mappings_hash, itr)
9167 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9169 END_HASH_ITERATION(mappings_hash, itr)
9174 void SaveSetup_AddGameControllerMapping(char *mapping)
9176 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9177 SetupFileHash *mappings_hash = newSetupFileHash();
9179 InitUserDataDirectory();
9181 // load existing personal game controller mappings
9182 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9184 // add new mapping to personal game controller mappings
9185 addGameControllerMappingToHash(mappings_hash, mapping);
9187 // save updated personal game controller mappings
9188 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9190 freeSetupFileHash(mappings_hash);
9194 void LoadCustomElementDescriptions()
9196 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9197 SetupFileHash *setup_file_hash;
9200 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9202 if (element_info[i].custom_description != NULL)
9204 free(element_info[i].custom_description);
9205 element_info[i].custom_description = NULL;
9209 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9212 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9214 char *token = getStringCat2(element_info[i].token_name, ".name");
9215 char *value = getHashEntry(setup_file_hash, token);
9218 element_info[i].custom_description = getStringCopy(value);
9223 freeSetupFileHash(setup_file_hash);
9226 static int getElementFromToken(char *token)
9228 char *value = getHashEntry(element_token_hash, token);
9233 Error(ERR_WARN, "unknown element token '%s'", token);
9235 return EL_UNDEFINED;
9238 static int get_token_parameter_value(char *token, char *value_raw)
9242 if (token == NULL || value_raw == NULL)
9243 return ARG_UNDEFINED_VALUE;
9245 suffix = strrchr(token, '.');
9249 if (strEqual(suffix, ".element"))
9250 return getElementFromToken(value_raw);
9252 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9253 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9256 void InitMenuDesignSettings_Static()
9260 /* always start with reliable default values from static default config */
9261 for (i = 0; image_config_vars[i].token != NULL; i++)
9263 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9266 *image_config_vars[i].value =
9267 get_token_parameter_value(image_config_vars[i].token, value);
9271 static void InitMenuDesignSettings_SpecialPreProcessing()
9275 /* the following initializes hierarchical values from static configuration */
9277 /* special case: initialize "ARG_DEFAULT" values in static default config */
9278 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9279 titlescreen_initial_first_default.fade_mode =
9280 title_initial_first_default.fade_mode;
9281 titlescreen_initial_first_default.fade_delay =
9282 title_initial_first_default.fade_delay;
9283 titlescreen_initial_first_default.post_delay =
9284 title_initial_first_default.post_delay;
9285 titlescreen_initial_first_default.auto_delay =
9286 title_initial_first_default.auto_delay;
9287 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
9288 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9289 titlescreen_first_default.post_delay = title_first_default.post_delay;
9290 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9291 titlemessage_initial_first_default.fade_mode =
9292 title_initial_first_default.fade_mode;
9293 titlemessage_initial_first_default.fade_delay =
9294 title_initial_first_default.fade_delay;
9295 titlemessage_initial_first_default.post_delay =
9296 title_initial_first_default.post_delay;
9297 titlemessage_initial_first_default.auto_delay =
9298 title_initial_first_default.auto_delay;
9299 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
9300 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9301 titlemessage_first_default.post_delay = title_first_default.post_delay;
9302 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9304 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
9305 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9306 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9307 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9308 titlescreen_default.fade_mode = title_default.fade_mode;
9309 titlescreen_default.fade_delay = title_default.fade_delay;
9310 titlescreen_default.post_delay = title_default.post_delay;
9311 titlescreen_default.auto_delay = title_default.auto_delay;
9312 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9313 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9314 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9315 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9316 titlemessage_default.fade_mode = title_default.fade_mode;
9317 titlemessage_default.fade_delay = title_default.fade_delay;
9318 titlemessage_default.post_delay = title_default.post_delay;
9319 titlemessage_default.auto_delay = title_default.auto_delay;
9321 /* special case: initialize "ARG_DEFAULT" values in static default config */
9322 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9323 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9325 titlescreen_initial_first[i] = titlescreen_initial_first_default;
9326 titlescreen_first[i] = titlescreen_first_default;
9327 titlemessage_initial_first[i] = titlemessage_initial_first_default;
9328 titlemessage_first[i] = titlemessage_first_default;
9330 titlescreen_initial[i] = titlescreen_initial_default;
9331 titlescreen[i] = titlescreen_default;
9332 titlemessage_initial[i] = titlemessage_initial_default;
9333 titlemessage[i] = titlemessage_default;
9336 /* special case: initialize "ARG_DEFAULT" values in static default config */
9337 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9338 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9340 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
9343 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9344 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9345 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9348 /* special case: initialize "ARG_DEFAULT" values in static default config */
9349 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9350 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9352 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9353 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9354 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9356 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
9359 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9363 static void InitMenuDesignSettings_SpecialPostProcessing()
9367 struct XY *dst, *src;
9371 { &game.button.save, &game.button.stop },
9372 { &game.button.pause2, &game.button.pause },
9373 { &game.button.load, &game.button.play },
9374 { &game.button.undo, &game.button.stop },
9375 { &game.button.redo, &game.button.play },
9381 /* special case: initialize later added SETUP list size from LEVELS value */
9382 if (menu.list_size[GAME_MODE_SETUP] == -1)
9383 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9385 /* set default position for snapshot buttons to stop/pause/play buttons */
9386 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9387 if ((*game_buttons_xy[i].dst).x == -1 &&
9388 (*game_buttons_xy[i].dst).y == -1)
9389 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9392 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9396 struct XYTileSize *dst, *src;
9399 editor_buttons_xy[] =
9402 &editor.button.element_left, &editor.palette.element_left,
9403 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9406 &editor.button.element_middle, &editor.palette.element_middle,
9407 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9410 &editor.button.element_right, &editor.palette.element_right,
9411 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9418 /* set default position for element buttons to element graphics */
9419 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9421 if ((*editor_buttons_xy[i].dst).x == -1 &&
9422 (*editor_buttons_xy[i].dst).y == -1)
9424 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9426 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9428 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9433 static void LoadMenuDesignSettingsFromFilename(char *filename)
9435 static struct TitleFadingInfo tfi;
9436 static struct TitleMessageInfo tmi;
9437 static struct TokenInfo title_tokens[] =
9439 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9440 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9441 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9442 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9446 static struct TokenInfo titlemessage_tokens[] =
9448 { TYPE_INTEGER, &tmi.x, ".x" },
9449 { TYPE_INTEGER, &tmi.y, ".y" },
9450 { TYPE_INTEGER, &tmi.width, ".width" },
9451 { TYPE_INTEGER, &tmi.height, ".height" },
9452 { TYPE_INTEGER, &tmi.chars, ".chars" },
9453 { TYPE_INTEGER, &tmi.lines, ".lines" },
9454 { TYPE_INTEGER, &tmi.align, ".align" },
9455 { TYPE_INTEGER, &tmi.valign, ".valign" },
9456 { TYPE_INTEGER, &tmi.font, ".font" },
9457 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9458 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9459 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9460 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9461 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9462 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9463 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9464 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9470 struct TitleFadingInfo *info;
9475 /* initialize first titles from "enter screen" definitions, if defined */
9476 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9477 { &title_first_default, "menu.enter_screen.TITLE" },
9479 /* initialize title screens from "next screen" definitions, if defined */
9480 { &title_initial_default, "menu.next_screen.TITLE" },
9481 { &title_default, "menu.next_screen.TITLE" },
9487 struct TitleMessageInfo *array;
9490 titlemessage_arrays[] =
9492 /* initialize first titles from "enter screen" definitions, if defined */
9493 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9494 { titlescreen_first, "menu.enter_screen.TITLE" },
9495 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9496 { titlemessage_first, "menu.enter_screen.TITLE" },
9498 /* initialize titles from "next screen" definitions, if defined */
9499 { titlescreen_initial, "menu.next_screen.TITLE" },
9500 { titlescreen, "menu.next_screen.TITLE" },
9501 { titlemessage_initial, "menu.next_screen.TITLE" },
9502 { titlemessage, "menu.next_screen.TITLE" },
9504 /* overwrite titles with title definitions, if defined */
9505 { titlescreen_initial_first, "[title_initial]" },
9506 { titlescreen_first, "[title]" },
9507 { titlemessage_initial_first, "[title_initial]" },
9508 { titlemessage_first, "[title]" },
9510 { titlescreen_initial, "[title_initial]" },
9511 { titlescreen, "[title]" },
9512 { titlemessage_initial, "[title_initial]" },
9513 { titlemessage, "[title]" },
9515 /* overwrite titles with title screen/message definitions, if defined */
9516 { titlescreen_initial_first, "[titlescreen_initial]" },
9517 { titlescreen_first, "[titlescreen]" },
9518 { titlemessage_initial_first, "[titlemessage_initial]" },
9519 { titlemessage_first, "[titlemessage]" },
9521 { titlescreen_initial, "[titlescreen_initial]" },
9522 { titlescreen, "[titlescreen]" },
9523 { titlemessage_initial, "[titlemessage_initial]" },
9524 { titlemessage, "[titlemessage]" },
9528 SetupFileHash *setup_file_hash;
9531 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9534 /* the following initializes hierarchical values from dynamic configuration */
9536 /* special case: initialize with default values that may be overwritten */
9537 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9538 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9540 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9541 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9542 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9544 if (value_1 != NULL)
9545 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9546 if (value_2 != NULL)
9547 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9548 if (value_3 != NULL)
9549 menu.list_size[i] = get_integer_from_string(value_3);
9552 /* special case: initialize with default values that may be overwritten */
9553 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9554 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9556 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9557 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9559 if (value_1 != NULL)
9560 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9561 if (value_2 != NULL)
9562 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9564 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9566 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9568 if (value_1 != NULL)
9569 menu.list_size_info[i] = get_integer_from_string(value_1);
9573 /* special case: initialize with default values that may be overwritten */
9574 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9575 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9577 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9578 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9580 if (value_1 != NULL)
9581 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9582 if (value_2 != NULL)
9583 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9586 /* special case: initialize with default values that may be overwritten */
9587 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9588 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9590 char *token_1 = "menu.enter_screen.fade_mode";
9591 char *token_2 = "menu.enter_screen.fade_delay";
9592 char *token_3 = "menu.enter_screen.post_delay";
9593 char *token_4 = "menu.leave_screen.fade_mode";
9594 char *token_5 = "menu.leave_screen.fade_delay";
9595 char *token_6 = "menu.leave_screen.post_delay";
9596 char *token_7 = "menu.next_screen.fade_mode";
9597 char *token_8 = "menu.next_screen.fade_delay";
9598 char *token_9 = "menu.next_screen.post_delay";
9599 char *value_1 = getHashEntry(setup_file_hash, token_1);
9600 char *value_2 = getHashEntry(setup_file_hash, token_2);
9601 char *value_3 = getHashEntry(setup_file_hash, token_3);
9602 char *value_4 = getHashEntry(setup_file_hash, token_4);
9603 char *value_5 = getHashEntry(setup_file_hash, token_5);
9604 char *value_6 = getHashEntry(setup_file_hash, token_6);
9605 char *value_7 = getHashEntry(setup_file_hash, token_7);
9606 char *value_8 = getHashEntry(setup_file_hash, token_8);
9607 char *value_9 = getHashEntry(setup_file_hash, token_9);
9609 if (value_1 != NULL)
9610 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9612 if (value_2 != NULL)
9613 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9615 if (value_3 != NULL)
9616 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9618 if (value_4 != NULL)
9619 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9621 if (value_5 != NULL)
9622 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9624 if (value_6 != NULL)
9625 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9627 if (value_7 != NULL)
9628 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9630 if (value_8 != NULL)
9631 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9633 if (value_9 != NULL)
9634 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9638 /* special case: initialize with default values that may be overwritten */
9639 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9640 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9642 char *token_w1 = "viewport.window.width";
9643 char *token_w2 = "viewport.window.height";
9644 char *token_01 = "viewport.playfield.x";
9645 char *token_02 = "viewport.playfield.y";
9646 char *token_03 = "viewport.playfield.width";
9647 char *token_04 = "viewport.playfield.height";
9648 char *token_05 = "viewport.playfield.border_size";
9649 char *token_06 = "viewport.door_1.x";
9650 char *token_07 = "viewport.door_1.y";
9651 char *token_08 = "viewport.door_1.width";
9652 char *token_09 = "viewport.door_1.height";
9653 char *token_10 = "viewport.door_1.border_size";
9654 char *token_11 = "viewport.door_2.x";
9655 char *token_12 = "viewport.door_2.y";
9656 char *token_13 = "viewport.door_2.width";
9657 char *token_14 = "viewport.door_2.height";
9658 char *token_15 = "viewport.door_2.border_size";
9659 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9660 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9661 char *value_01 = getHashEntry(setup_file_hash, token_01);
9662 char *value_02 = getHashEntry(setup_file_hash, token_02);
9663 char *value_03 = getHashEntry(setup_file_hash, token_03);
9664 char *value_04 = getHashEntry(setup_file_hash, token_04);
9665 char *value_05 = getHashEntry(setup_file_hash, token_05);
9666 char *value_06 = getHashEntry(setup_file_hash, token_06);
9667 char *value_07 = getHashEntry(setup_file_hash, token_07);
9668 char *value_08 = getHashEntry(setup_file_hash, token_08);
9669 char *value_09 = getHashEntry(setup_file_hash, token_09);
9670 char *value_10 = getHashEntry(setup_file_hash, token_10);
9671 char *value_11 = getHashEntry(setup_file_hash, token_11);
9672 char *value_12 = getHashEntry(setup_file_hash, token_12);
9673 char *value_13 = getHashEntry(setup_file_hash, token_13);
9674 char *value_14 = getHashEntry(setup_file_hash, token_14);
9675 char *value_15 = getHashEntry(setup_file_hash, token_15);
9677 if (value_w1 != NULL)
9678 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9679 if (value_w2 != NULL)
9680 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9681 if (value_01 != NULL)
9682 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9683 if (value_02 != NULL)
9684 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9685 if (value_03 != NULL)
9686 viewport.playfield[i].width = get_token_parameter_value(token_03,
9688 if (value_04 != NULL)
9689 viewport.playfield[i].height = get_token_parameter_value(token_04,
9691 if (value_05 != NULL)
9692 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9694 if (value_06 != NULL)
9695 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9696 if (value_07 != NULL)
9697 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9698 if (value_08 != NULL)
9699 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9700 if (value_09 != NULL)
9701 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9702 if (value_10 != NULL)
9703 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9705 if (value_11 != NULL)
9706 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9707 if (value_12 != NULL)
9708 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9709 if (value_13 != NULL)
9710 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9711 if (value_14 != NULL)
9712 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9713 if (value_15 != NULL)
9714 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9718 /* special case: initialize with default values that may be overwritten */
9719 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9720 for (i = 0; title_info[i].info != NULL; i++)
9722 struct TitleFadingInfo *info = title_info[i].info;
9723 char *base_token = title_info[i].text;
9725 for (j = 0; title_tokens[j].type != -1; j++)
9727 char *token = getStringCat2(base_token, title_tokens[j].text);
9728 char *value = getHashEntry(setup_file_hash, token);
9732 int parameter_value = get_token_parameter_value(token, value);
9736 *(int *)title_tokens[j].value = (int)parameter_value;
9745 /* special case: initialize with default values that may be overwritten */
9746 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9747 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9749 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9750 char *base_token = titlemessage_arrays[i].text;
9752 for (j = 0; titlemessage_tokens[j].type != -1; j++)
9754 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9755 char *value = getHashEntry(setup_file_hash, token);
9759 int parameter_value = get_token_parameter_value(token, value);
9761 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9765 if (titlemessage_tokens[j].type == TYPE_INTEGER)
9766 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
9768 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9778 /* read (and overwrite with) values that may be specified in config file */
9779 for (i = 0; image_config_vars[i].token != NULL; i++)
9781 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9783 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9784 if (value != NULL && !strEqual(value, ARG_DEFAULT))
9785 *image_config_vars[i].value =
9786 get_token_parameter_value(image_config_vars[i].token, value);
9789 freeSetupFileHash(setup_file_hash);
9792 void LoadMenuDesignSettings()
9794 char *filename_base = UNDEFINED_FILENAME, *filename_local;
9796 InitMenuDesignSettings_Static();
9797 InitMenuDesignSettings_SpecialPreProcessing();
9799 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9801 /* first look for special settings configured in level series config */
9802 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9804 if (fileExists(filename_base))
9805 LoadMenuDesignSettingsFromFilename(filename_base);
9808 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9810 if (filename_local != NULL && !strEqual(filename_base, filename_local))
9811 LoadMenuDesignSettingsFromFilename(filename_local);
9813 InitMenuDesignSettings_SpecialPostProcessing();
9816 void LoadMenuDesignSettings_AfterGraphics()
9818 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9821 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9823 char *filename = getEditorSetupFilename();
9824 SetupFileList *setup_file_list, *list;
9825 SetupFileHash *element_hash;
9826 int num_unknown_tokens = 0;
9829 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9832 element_hash = newSetupFileHash();
9834 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9835 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9837 /* determined size may be larger than needed (due to unknown elements) */
9839 for (list = setup_file_list; list != NULL; list = list->next)
9842 /* add space for up to 3 more elements for padding that may be needed */
9845 /* free memory for old list of elements, if needed */
9846 checked_free(*elements);
9848 /* allocate memory for new list of elements */
9849 *elements = checked_malloc(*num_elements * sizeof(int));
9852 for (list = setup_file_list; list != NULL; list = list->next)
9854 char *value = getHashEntry(element_hash, list->token);
9856 if (value == NULL) /* try to find obsolete token mapping */
9858 char *mapped_token = get_mapped_token(list->token);
9860 if (mapped_token != NULL)
9862 value = getHashEntry(element_hash, mapped_token);
9870 (*elements)[(*num_elements)++] = atoi(value);
9874 if (num_unknown_tokens == 0)
9876 Error(ERR_INFO_LINE, "-");
9877 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9878 Error(ERR_INFO, "- config file: '%s'", filename);
9880 num_unknown_tokens++;
9883 Error(ERR_INFO, "- token: '%s'", list->token);
9887 if (num_unknown_tokens > 0)
9888 Error(ERR_INFO_LINE, "-");
9890 while (*num_elements % 4) /* pad with empty elements, if needed */
9891 (*elements)[(*num_elements)++] = EL_EMPTY;
9893 freeSetupFileList(setup_file_list);
9894 freeSetupFileHash(element_hash);
9897 for (i = 0; i < *num_elements; i++)
9898 printf("editor: element '%s' [%d]\n",
9899 element_info[(*elements)[i]].token_name, (*elements)[i]);
9903 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9906 SetupFileHash *setup_file_hash = NULL;
9907 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9908 char *filename_music, *filename_prefix, *filename_info;
9914 token_to_value_ptr[] =
9916 { "title_header", &tmp_music_file_info.title_header },
9917 { "artist_header", &tmp_music_file_info.artist_header },
9918 { "album_header", &tmp_music_file_info.album_header },
9919 { "year_header", &tmp_music_file_info.year_header },
9921 { "title", &tmp_music_file_info.title },
9922 { "artist", &tmp_music_file_info.artist },
9923 { "album", &tmp_music_file_info.album },
9924 { "year", &tmp_music_file_info.year },
9930 filename_music = (is_sound ? getCustomSoundFilename(basename) :
9931 getCustomMusicFilename(basename));
9933 if (filename_music == NULL)
9936 /* ---------- try to replace file extension ---------- */
9938 filename_prefix = getStringCopy(filename_music);
9939 if (strrchr(filename_prefix, '.') != NULL)
9940 *strrchr(filename_prefix, '.') = '\0';
9941 filename_info = getStringCat2(filename_prefix, ".txt");
9943 if (fileExists(filename_info))
9944 setup_file_hash = loadSetupFileHash(filename_info);
9946 free(filename_prefix);
9947 free(filename_info);
9949 if (setup_file_hash == NULL)
9951 /* ---------- try to add file extension ---------- */
9953 filename_prefix = getStringCopy(filename_music);
9954 filename_info = getStringCat2(filename_prefix, ".txt");
9956 if (fileExists(filename_info))
9957 setup_file_hash = loadSetupFileHash(filename_info);
9959 free(filename_prefix);
9960 free(filename_info);
9963 if (setup_file_hash == NULL)
9966 /* ---------- music file info found ---------- */
9968 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
9970 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
9972 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
9974 *token_to_value_ptr[i].value_ptr =
9975 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
9978 tmp_music_file_info.basename = getStringCopy(basename);
9979 tmp_music_file_info.music = music;
9980 tmp_music_file_info.is_sound = is_sound;
9982 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
9983 *new_music_file_info = tmp_music_file_info;
9985 return new_music_file_info;
9988 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
9990 return get_music_file_info_ext(basename, music, FALSE);
9993 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
9995 return get_music_file_info_ext(basename, sound, TRUE);
9998 static boolean music_info_listed_ext(struct MusicFileInfo *list,
9999 char *basename, boolean is_sound)
10001 for (; list != NULL; list = list->next)
10002 if (list->is_sound == is_sound && strEqual(list->basename, basename))
10008 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10010 return music_info_listed_ext(list, basename, FALSE);
10013 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10015 return music_info_listed_ext(list, basename, TRUE);
10018 void LoadMusicInfo()
10020 char *music_directory = getCustomMusicDirectory();
10021 int num_music = getMusicListSize();
10022 int num_music_noconf = 0;
10023 int num_sounds = getSoundListSize();
10025 DirectoryEntry *dir_entry;
10026 struct FileInfo *music, *sound;
10027 struct MusicFileInfo *next, **new;
10030 while (music_file_info != NULL)
10032 next = music_file_info->next;
10034 checked_free(music_file_info->basename);
10036 checked_free(music_file_info->title_header);
10037 checked_free(music_file_info->artist_header);
10038 checked_free(music_file_info->album_header);
10039 checked_free(music_file_info->year_header);
10041 checked_free(music_file_info->title);
10042 checked_free(music_file_info->artist);
10043 checked_free(music_file_info->album);
10044 checked_free(music_file_info->year);
10046 free(music_file_info);
10048 music_file_info = next;
10051 new = &music_file_info;
10053 for (i = 0; i < num_music; i++)
10055 music = getMusicListEntry(i);
10057 if (music->filename == NULL)
10060 if (strEqual(music->filename, UNDEFINED_FILENAME))
10063 /* a configured file may be not recognized as music */
10064 if (!FileIsMusic(music->filename))
10067 if (!music_info_listed(music_file_info, music->filename))
10069 *new = get_music_file_info(music->filename, i);
10072 new = &(*new)->next;
10076 if ((dir = openDirectory(music_directory)) == NULL)
10078 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10082 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
10084 char *basename = dir_entry->basename;
10085 boolean music_already_used = FALSE;
10088 /* skip all music files that are configured in music config file */
10089 for (i = 0; i < num_music; i++)
10091 music = getMusicListEntry(i);
10093 if (music->filename == NULL)
10096 if (strEqual(basename, music->filename))
10098 music_already_used = TRUE;
10103 if (music_already_used)
10106 if (!FileIsMusic(dir_entry->filename))
10109 if (!music_info_listed(music_file_info, basename))
10111 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10114 new = &(*new)->next;
10117 num_music_noconf++;
10120 closeDirectory(dir);
10122 for (i = 0; i < num_sounds; i++)
10124 sound = getSoundListEntry(i);
10126 if (sound->filename == NULL)
10129 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10132 /* a configured file may be not recognized as sound */
10133 if (!FileIsSound(sound->filename))
10136 if (!sound_info_listed(music_file_info, sound->filename))
10138 *new = get_sound_file_info(sound->filename, i);
10140 new = &(*new)->next;
10145 void add_helpanim_entry(int element, int action, int direction, int delay,
10146 int *num_list_entries)
10148 struct HelpAnimInfo *new_list_entry;
10149 (*num_list_entries)++;
10152 checked_realloc(helpanim_info,
10153 *num_list_entries * sizeof(struct HelpAnimInfo));
10154 new_list_entry = &helpanim_info[*num_list_entries - 1];
10156 new_list_entry->element = element;
10157 new_list_entry->action = action;
10158 new_list_entry->direction = direction;
10159 new_list_entry->delay = delay;
10162 void print_unknown_token(char *filename, char *token, int token_nr)
10166 Error(ERR_INFO_LINE, "-");
10167 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10168 Error(ERR_INFO, "- config file: '%s'", filename);
10171 Error(ERR_INFO, "- token: '%s'", token);
10174 void print_unknown_token_end(int token_nr)
10177 Error(ERR_INFO_LINE, "-");
10180 void LoadHelpAnimInfo()
10182 char *filename = getHelpAnimFilename();
10183 SetupFileList *setup_file_list = NULL, *list;
10184 SetupFileHash *element_hash, *action_hash, *direction_hash;
10185 int num_list_entries = 0;
10186 int num_unknown_tokens = 0;
10189 if (fileExists(filename))
10190 setup_file_list = loadSetupFileList(filename);
10192 if (setup_file_list == NULL)
10194 /* use reliable default values from static configuration */
10195 SetupFileList *insert_ptr;
10197 insert_ptr = setup_file_list =
10198 newSetupFileList(helpanim_config[0].token,
10199 helpanim_config[0].value);
10201 for (i = 1; helpanim_config[i].token; i++)
10202 insert_ptr = addListEntry(insert_ptr,
10203 helpanim_config[i].token,
10204 helpanim_config[i].value);
10207 element_hash = newSetupFileHash();
10208 action_hash = newSetupFileHash();
10209 direction_hash = newSetupFileHash();
10211 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10212 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10214 for (i = 0; i < NUM_ACTIONS; i++)
10215 setHashEntry(action_hash, element_action_info[i].suffix,
10216 i_to_a(element_action_info[i].value));
10218 /* do not store direction index (bit) here, but direction value! */
10219 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10220 setHashEntry(direction_hash, element_direction_info[i].suffix,
10221 i_to_a(1 << element_direction_info[i].value));
10223 for (list = setup_file_list; list != NULL; list = list->next)
10225 char *element_token, *action_token, *direction_token;
10226 char *element_value, *action_value, *direction_value;
10227 int delay = atoi(list->value);
10229 if (strEqual(list->token, "end"))
10231 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10236 /* first try to break element into element/action/direction parts;
10237 if this does not work, also accept combined "element[.act][.dir]"
10238 elements (like "dynamite.active"), which are unique elements */
10240 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10242 element_value = getHashEntry(element_hash, list->token);
10243 if (element_value != NULL) /* element found */
10244 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10245 &num_list_entries);
10248 /* no further suffixes found -- this is not an element */
10249 print_unknown_token(filename, list->token, num_unknown_tokens++);
10255 /* token has format "<prefix>.<something>" */
10257 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10258 direction_token = action_token; /* ... or direction */
10260 element_token = getStringCopy(list->token);
10261 *strchr(element_token, '.') = '\0';
10263 element_value = getHashEntry(element_hash, element_token);
10265 if (element_value == NULL) /* this is no element */
10267 element_value = getHashEntry(element_hash, list->token);
10268 if (element_value != NULL) /* combined element found */
10269 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10270 &num_list_entries);
10272 print_unknown_token(filename, list->token, num_unknown_tokens++);
10274 free(element_token);
10279 action_value = getHashEntry(action_hash, action_token);
10281 if (action_value != NULL) /* action found */
10283 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10284 &num_list_entries);
10286 free(element_token);
10291 direction_value = getHashEntry(direction_hash, direction_token);
10293 if (direction_value != NULL) /* direction found */
10295 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10296 &num_list_entries);
10298 free(element_token);
10303 if (strchr(action_token + 1, '.') == NULL)
10305 /* no further suffixes found -- this is not an action nor direction */
10307 element_value = getHashEntry(element_hash, list->token);
10308 if (element_value != NULL) /* combined element found */
10309 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10310 &num_list_entries);
10312 print_unknown_token(filename, list->token, num_unknown_tokens++);
10314 free(element_token);
10319 /* token has format "<prefix>.<suffix>.<something>" */
10321 direction_token = strchr(action_token + 1, '.');
10323 action_token = getStringCopy(action_token);
10324 *strchr(action_token + 1, '.') = '\0';
10326 action_value = getHashEntry(action_hash, action_token);
10328 if (action_value == NULL) /* this is no action */
10330 element_value = getHashEntry(element_hash, list->token);
10331 if (element_value != NULL) /* combined element found */
10332 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10333 &num_list_entries);
10335 print_unknown_token(filename, list->token, num_unknown_tokens++);
10337 free(element_token);
10338 free(action_token);
10343 direction_value = getHashEntry(direction_hash, direction_token);
10345 if (direction_value != NULL) /* direction found */
10347 add_helpanim_entry(atoi(element_value), atoi(action_value),
10348 atoi(direction_value), delay, &num_list_entries);
10350 free(element_token);
10351 free(action_token);
10356 /* this is no direction */
10358 element_value = getHashEntry(element_hash, list->token);
10359 if (element_value != NULL) /* combined element found */
10360 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10361 &num_list_entries);
10363 print_unknown_token(filename, list->token, num_unknown_tokens++);
10365 free(element_token);
10366 free(action_token);
10369 print_unknown_token_end(num_unknown_tokens);
10371 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10372 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10374 freeSetupFileList(setup_file_list);
10375 freeSetupFileHash(element_hash);
10376 freeSetupFileHash(action_hash);
10377 freeSetupFileHash(direction_hash);
10380 for (i = 0; i < num_list_entries; i++)
10381 printf("::: '%s': %d, %d, %d => %d\n",
10382 EL_NAME(helpanim_info[i].element),
10383 helpanim_info[i].element,
10384 helpanim_info[i].action,
10385 helpanim_info[i].direction,
10386 helpanim_info[i].delay);
10390 void LoadHelpTextInfo()
10392 char *filename = getHelpTextFilename();
10395 if (helptext_info != NULL)
10397 freeSetupFileHash(helptext_info);
10398 helptext_info = NULL;
10401 if (fileExists(filename))
10402 helptext_info = loadSetupFileHash(filename);
10404 if (helptext_info == NULL)
10406 /* use reliable default values from static configuration */
10407 helptext_info = newSetupFileHash();
10409 for (i = 0; helptext_config[i].token; i++)
10410 setHashEntry(helptext_info,
10411 helptext_config[i].token,
10412 helptext_config[i].value);
10416 BEGIN_HASH_ITERATION(helptext_info, itr)
10418 printf("::: '%s' => '%s'\n",
10419 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10421 END_HASH_ITERATION(hash, itr)
10426 /* ------------------------------------------------------------------------- */
10427 /* convert levels */
10428 /* ------------------------------------------------------------------------- */
10430 #define MAX_NUM_CONVERT_LEVELS 1000
10432 void ConvertLevels()
10434 static LevelDirTree *convert_leveldir = NULL;
10435 static int convert_level_nr = -1;
10436 static int num_levels_handled = 0;
10437 static int num_levels_converted = 0;
10438 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10441 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10442 global.convert_leveldir);
10444 if (convert_leveldir == NULL)
10445 Error(ERR_EXIT, "no such level identifier: '%s'",
10446 global.convert_leveldir);
10448 leveldir_current = convert_leveldir;
10450 if (global.convert_level_nr != -1)
10452 convert_leveldir->first_level = global.convert_level_nr;
10453 convert_leveldir->last_level = global.convert_level_nr;
10456 convert_level_nr = convert_leveldir->first_level;
10458 PrintLine("=", 79);
10459 Print("Converting levels\n");
10460 PrintLine("-", 79);
10461 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10462 Print("Level series name: '%s'\n", convert_leveldir->name);
10463 Print("Level series author: '%s'\n", convert_leveldir->author);
10464 Print("Number of levels: %d\n", convert_leveldir->levels);
10465 PrintLine("=", 79);
10468 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10469 levels_failed[i] = FALSE;
10471 while (convert_level_nr <= convert_leveldir->last_level)
10473 char *level_filename;
10476 level_nr = convert_level_nr++;
10478 Print("Level %03d: ", level_nr);
10480 LoadLevel(level_nr);
10481 if (level.no_level_file || level.no_valid_file)
10483 Print("(no level)\n");
10487 Print("converting level ... ");
10489 level_filename = getDefaultLevelFilename(level_nr);
10490 new_level = !fileExists(level_filename);
10494 SaveLevel(level_nr);
10496 num_levels_converted++;
10498 Print("converted.\n");
10502 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10503 levels_failed[level_nr] = TRUE;
10505 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10508 num_levels_handled++;
10512 PrintLine("=", 79);
10513 Print("Number of levels handled: %d\n", num_levels_handled);
10514 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10515 (num_levels_handled ?
10516 num_levels_converted * 100 / num_levels_handled : 0));
10517 PrintLine("-", 79);
10518 Print("Summary (for automatic parsing by scripts):\n");
10519 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10520 convert_leveldir->identifier, num_levels_converted,
10521 num_levels_handled,
10522 (num_levels_handled ?
10523 num_levels_converted * 100 / num_levels_handled : 0));
10525 if (num_levels_handled != num_levels_converted)
10527 Print(", FAILED:");
10528 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10529 if (levels_failed[i])
10534 PrintLine("=", 79);
10536 CloseAllAndExit(0);
10540 /* ------------------------------------------------------------------------- */
10541 /* create and save images for use in level sketches (raw BMP format) */
10542 /* ------------------------------------------------------------------------- */
10544 void CreateLevelSketchImages()
10546 #if defined(TARGET_SDL)
10551 InitElementPropertiesGfxElement();
10553 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10554 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10556 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10558 Bitmap *src_bitmap;
10560 int element = getMappedElement(i);
10561 int graphic = el2edimg(element);
10562 char basename1[16];
10563 char basename2[16];
10567 sprintf(basename1, "%03d.bmp", i);
10568 sprintf(basename2, "%03ds.bmp", i);
10570 filename1 = getPath2(global.create_images_dir, basename1);
10571 filename2 = getPath2(global.create_images_dir, basename2);
10573 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10574 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10577 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10578 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10580 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10581 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10583 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10584 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10590 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10593 FreeBitmap(bitmap1);
10594 FreeBitmap(bitmap2);
10599 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10601 CloseAllAndExit(0);
10606 /* ------------------------------------------------------------------------- */
10607 /* create and save images for custom and group elements (raw BMP format) */
10608 /* ------------------------------------------------------------------------- */
10610 void CreateCustomElementImages(char *directory)
10612 #if defined(TARGET_SDL)
10613 char *src_basename = "RocksCE-template.ilbm";
10614 char *dst_basename = "RocksCE.bmp";
10615 char *src_filename = getPath2(directory, src_basename);
10616 char *dst_filename = getPath2(directory, dst_basename);
10617 Bitmap *src_bitmap;
10619 int yoffset_ce = 0;
10620 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10623 SDLInitVideoDisplay();
10625 ReCreateBitmap(&backbuffer, video.width, video.height);
10627 src_bitmap = LoadImage(src_filename);
10629 bitmap = CreateBitmap(TILEX * 16 * 2,
10630 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10633 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10640 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10641 TILEX * x, TILEY * y + yoffset_ce);
10643 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10645 TILEX * x + TILEX * 16,
10646 TILEY * y + yoffset_ce);
10648 for (j = 2; j >= 0; j--)
10652 BlitBitmap(src_bitmap, bitmap,
10653 TILEX + c * 7, 0, 6, 10,
10654 TILEX * x + 6 + j * 7,
10655 TILEY * y + 11 + yoffset_ce);
10657 BlitBitmap(src_bitmap, bitmap,
10658 TILEX + c * 8, TILEY, 6, 10,
10659 TILEX * 16 + TILEX * x + 6 + j * 8,
10660 TILEY * y + 10 + yoffset_ce);
10666 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10673 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10674 TILEX * x, TILEY * y + yoffset_ge);
10676 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10678 TILEX * x + TILEX * 16,
10679 TILEY * y + yoffset_ge);
10681 for (j = 1; j >= 0; j--)
10685 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10686 TILEX * x + 6 + j * 10,
10687 TILEY * y + 11 + yoffset_ge);
10689 BlitBitmap(src_bitmap, bitmap,
10690 TILEX + c * 8, TILEY + 12, 6, 10,
10691 TILEX * 16 + TILEX * x + 10 + j * 8,
10692 TILEY * y + 10 + yoffset_ge);
10698 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10699 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10701 FreeBitmap(bitmap);
10703 CloseAllAndExit(0);