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" },
1326 /* ========================================================================= */
1327 /* level file functions */
1328 /* ========================================================================= */
1330 static boolean check_special_flags(char *flag)
1332 if (strEqual(options.special_flags, flag) ||
1333 strEqual(leveldir_current->special_flags, flag))
1339 static struct DateInfo getCurrentDate()
1341 time_t epoch_seconds = time(NULL);
1342 struct tm *now = localtime(&epoch_seconds);
1343 struct DateInfo date;
1345 date.year = now->tm_year + 1900;
1346 date.month = now->tm_mon + 1;
1347 date.day = now->tm_mday;
1349 date.src = DATE_SRC_CLOCK;
1354 static void resetEventFlags(struct ElementChangeInfo *change)
1358 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1359 change->has_event[i] = FALSE;
1362 static void resetEventBits()
1366 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1367 xx_event_bits[i] = 0;
1370 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1374 /* important: only change event flag if corresponding event bit is set
1375 (this is because all xx_event_bits[] values are loaded separately,
1376 and all xx_event_bits[] values are set back to zero before loading
1377 another value xx_event_bits[x] (each value representing 32 flags)) */
1379 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1380 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1381 change->has_event[i] = TRUE;
1384 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1388 /* in contrast to the above function setEventFlagsFromEventBits(), it
1389 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1390 depending on the corresponding change->has_event[i] values here, as
1391 all xx_event_bits[] values are reset in resetEventBits() before */
1393 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1394 if (change->has_event[i])
1395 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1398 static char *getDefaultElementDescription(struct ElementInfo *ei)
1400 static char description[MAX_ELEMENT_NAME_LEN + 1];
1401 char *default_description = (ei->custom_description != NULL ?
1402 ei->custom_description :
1403 ei->editor_description);
1406 /* always start with reliable default values */
1407 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1408 description[i] = '\0';
1410 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1411 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1413 return &description[0];
1416 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1418 char *default_description = getDefaultElementDescription(ei);
1421 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1422 ei->description[i] = default_description[i];
1425 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1429 for (i = 0; conf[i].data_type != -1; i++)
1431 int default_value = conf[i].default_value;
1432 int data_type = conf[i].data_type;
1433 int conf_type = conf[i].conf_type;
1434 int byte_mask = conf_type & CONF_MASK_BYTES;
1436 if (byte_mask == CONF_MASK_MULTI_BYTES)
1438 int default_num_entities = conf[i].default_num_entities;
1439 int max_num_entities = conf[i].max_num_entities;
1441 *(int *)(conf[i].num_entities) = default_num_entities;
1443 if (data_type == TYPE_STRING)
1445 char *default_string = conf[i].default_string;
1446 char *string = (char *)(conf[i].value);
1448 strncpy(string, default_string, max_num_entities);
1450 else if (data_type == TYPE_ELEMENT_LIST)
1452 int *element_array = (int *)(conf[i].value);
1455 for (j = 0; j < max_num_entities; j++)
1456 element_array[j] = default_value;
1458 else if (data_type == TYPE_CONTENT_LIST)
1460 struct Content *content = (struct Content *)(conf[i].value);
1463 for (c = 0; c < max_num_entities; c++)
1464 for (y = 0; y < 3; y++)
1465 for (x = 0; x < 3; x++)
1466 content[c].e[x][y] = default_value;
1469 else /* constant size configuration data (1, 2 or 4 bytes) */
1471 if (data_type == TYPE_BOOLEAN)
1472 *(boolean *)(conf[i].value) = default_value;
1474 *(int *) (conf[i].value) = default_value;
1479 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1483 for (i = 0; conf[i].data_type != -1; i++)
1485 int data_type = conf[i].data_type;
1486 int conf_type = conf[i].conf_type;
1487 int byte_mask = conf_type & CONF_MASK_BYTES;
1489 if (byte_mask == CONF_MASK_MULTI_BYTES)
1491 int max_num_entities = conf[i].max_num_entities;
1493 if (data_type == TYPE_STRING)
1495 char *string = (char *)(conf[i].value);
1496 char *string_copy = (char *)(conf[i].value_copy);
1498 strncpy(string_copy, string, max_num_entities);
1500 else if (data_type == TYPE_ELEMENT_LIST)
1502 int *element_array = (int *)(conf[i].value);
1503 int *element_array_copy = (int *)(conf[i].value_copy);
1506 for (j = 0; j < max_num_entities; j++)
1507 element_array_copy[j] = element_array[j];
1509 else if (data_type == TYPE_CONTENT_LIST)
1511 struct Content *content = (struct Content *)(conf[i].value);
1512 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1515 for (c = 0; c < max_num_entities; c++)
1516 for (y = 0; y < 3; y++)
1517 for (x = 0; x < 3; x++)
1518 content_copy[c].e[x][y] = content[c].e[x][y];
1521 else /* constant size configuration data (1, 2 or 4 bytes) */
1523 if (data_type == TYPE_BOOLEAN)
1524 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1526 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1531 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1535 xx_ei = *ei_from; /* copy element data into temporary buffer */
1536 yy_ei = *ei_to; /* copy element data into temporary buffer */
1538 copyConfigFromConfigList(chunk_config_CUSX_base);
1543 /* ---------- reinitialize and copy change pages ---------- */
1545 ei_to->num_change_pages = ei_from->num_change_pages;
1546 ei_to->current_change_page = ei_from->current_change_page;
1548 setElementChangePages(ei_to, ei_to->num_change_pages);
1550 for (i = 0; i < ei_to->num_change_pages; i++)
1551 ei_to->change_page[i] = ei_from->change_page[i];
1553 /* ---------- copy group element info ---------- */
1554 if (ei_from->group != NULL && ei_to->group != NULL) /* group or internal */
1555 *ei_to->group = *ei_from->group;
1557 /* mark this custom element as modified */
1558 ei_to->modified_settings = TRUE;
1561 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1563 int change_page_size = sizeof(struct ElementChangeInfo);
1565 ei->num_change_pages = MAX(1, change_pages);
1568 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1570 if (ei->current_change_page >= ei->num_change_pages)
1571 ei->current_change_page = ei->num_change_pages - 1;
1573 ei->change = &ei->change_page[ei->current_change_page];
1576 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1578 xx_change = *change; /* copy change data into temporary buffer */
1580 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1582 *change = xx_change;
1584 resetEventFlags(change);
1586 change->direct_action = 0;
1587 change->other_action = 0;
1589 change->pre_change_function = NULL;
1590 change->change_function = NULL;
1591 change->post_change_function = NULL;
1594 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1598 li = *level; /* copy level data into temporary buffer */
1599 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1600 *level = li; /* copy temporary buffer back to level data */
1602 setLevelInfoToDefaults_EM();
1603 setLevelInfoToDefaults_SP();
1605 level->native_em_level = &native_em_level;
1606 level->native_sp_level = &native_sp_level;
1607 level->native_mm_level = &native_mm_level;
1609 level->file_version = FILE_VERSION_ACTUAL;
1610 level->game_version = GAME_VERSION_ACTUAL;
1612 level->creation_date = getCurrentDate();
1614 level->encoding_16bit_field = TRUE;
1615 level->encoding_16bit_yamyam = TRUE;
1616 level->encoding_16bit_amoeba = TRUE;
1618 /* clear level name and level author string buffers */
1619 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1620 level->name[i] = '\0';
1621 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1622 level->author[i] = '\0';
1624 /* set level name and level author to default values */
1625 strcpy(level->name, NAMELESS_LEVEL_NAME);
1626 strcpy(level->author, ANONYMOUS_NAME);
1628 /* set level playfield to playable default level with player and exit */
1629 for (x = 0; x < MAX_LEV_FIELDX; x++)
1630 for (y = 0; y < MAX_LEV_FIELDY; y++)
1631 level->field[x][y] = EL_SAND;
1633 level->field[0][0] = EL_PLAYER_1;
1634 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1636 BorderElement = EL_STEELWALL;
1638 /* set all bug compatibility flags to "false" => do not emulate this bug */
1639 level->use_action_after_change_bug = FALSE;
1641 if (leveldir_current)
1643 /* try to determine better author name than 'anonymous' */
1644 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1646 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1647 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1651 switch (LEVELCLASS(leveldir_current))
1653 case LEVELCLASS_TUTORIAL:
1654 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1657 case LEVELCLASS_CONTRIB:
1658 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1659 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1662 case LEVELCLASS_PRIVATE:
1663 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1664 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1668 /* keep default value */
1675 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1677 static boolean clipboard_elements_initialized = FALSE;
1680 InitElementPropertiesStatic();
1682 li = *level; /* copy level data into temporary buffer */
1683 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1684 *level = li; /* copy temporary buffer back to level data */
1686 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1689 struct ElementInfo *ei = &element_info[element];
1691 /* never initialize clipboard elements after the very first time */
1692 /* (to be able to use clipboard elements between several levels) */
1693 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1696 if (IS_ENVELOPE(element))
1698 int envelope_nr = element - EL_ENVELOPE_1;
1700 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1702 level->envelope[envelope_nr] = xx_envelope;
1705 if (IS_CUSTOM_ELEMENT(element) ||
1706 IS_GROUP_ELEMENT(element) ||
1707 IS_INTERNAL_ELEMENT(element))
1709 xx_ei = *ei; /* copy element data into temporary buffer */
1711 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1716 setElementChangePages(ei, 1);
1717 setElementChangeInfoToDefaults(ei->change);
1719 if (IS_CUSTOM_ELEMENT(element) ||
1720 IS_GROUP_ELEMENT(element) ||
1721 IS_INTERNAL_ELEMENT(element))
1723 setElementDescriptionToDefault(ei);
1725 ei->modified_settings = FALSE;
1728 if (IS_CUSTOM_ELEMENT(element) ||
1729 IS_INTERNAL_ELEMENT(element))
1731 /* internal values used in level editor */
1733 ei->access_type = 0;
1734 ei->access_layer = 0;
1735 ei->access_protected = 0;
1736 ei->walk_to_action = 0;
1737 ei->smash_targets = 0;
1740 ei->can_explode_by_fire = FALSE;
1741 ei->can_explode_smashed = FALSE;
1742 ei->can_explode_impact = FALSE;
1744 ei->current_change_page = 0;
1747 if (IS_GROUP_ELEMENT(element) ||
1748 IS_INTERNAL_ELEMENT(element))
1750 struct ElementGroupInfo *group;
1752 /* initialize memory for list of elements in group */
1753 if (ei->group == NULL)
1754 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1758 xx_group = *group; /* copy group data into temporary buffer */
1760 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1766 clipboard_elements_initialized = TRUE;
1769 static void setLevelInfoToDefaults(struct LevelInfo *level,
1770 boolean level_info_only,
1771 boolean reset_file_status)
1773 setLevelInfoToDefaults_Level(level);
1775 if (!level_info_only)
1776 setLevelInfoToDefaults_Elements(level);
1778 if (reset_file_status)
1780 level->no_valid_file = FALSE;
1781 level->no_level_file = FALSE;
1784 level->changed = FALSE;
1787 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1789 level_file_info->nr = 0;
1790 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1791 level_file_info->packed = FALSE;
1792 level_file_info->basename = NULL;
1793 level_file_info->filename = NULL;
1796 int getMappedElement_SB(int, boolean);
1798 static void ActivateLevelTemplate()
1802 if (check_special_flags("load_xsb_to_ces"))
1804 /* fill smaller playfields with padding "beyond border wall" elements */
1805 if (level.fieldx < level_template.fieldx ||
1806 level.fieldy < level_template.fieldy)
1808 short field[level.fieldx][level.fieldy];
1809 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1810 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1811 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1812 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1814 /* copy old playfield (which is smaller than the visible area) */
1815 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1816 field[x][y] = level.field[x][y];
1818 /* fill new, larger playfield with "beyond border wall" elements */
1819 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1820 level.field[x][y] = getMappedElement_SB('_', TRUE);
1822 /* copy the old playfield to the middle of the new playfield */
1823 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1824 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1826 level.fieldx = new_fieldx;
1827 level.fieldy = new_fieldy;
1831 /* Currently there is no special action needed to activate the template
1832 data, because 'element_info' property settings overwrite the original
1833 level data, while all other variables do not change. */
1835 /* Exception: 'from_level_template' elements in the original level playfield
1836 are overwritten with the corresponding elements at the same position in
1837 playfield from the level template. */
1839 for (x = 0; x < level.fieldx; x++)
1840 for (y = 0; y < level.fieldy; y++)
1841 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1842 level.field[x][y] = level_template.field[x][y];
1844 if (check_special_flags("load_xsb_to_ces"))
1846 struct LevelInfo level_backup = level;
1848 /* overwrite all individual level settings from template level settings */
1849 level = level_template;
1851 /* restore playfield size */
1852 level.fieldx = level_backup.fieldx;
1853 level.fieldy = level_backup.fieldy;
1855 /* restore playfield content */
1856 for (x = 0; x < level.fieldx; x++)
1857 for (y = 0; y < level.fieldy; y++)
1858 level.field[x][y] = level_backup.field[x][y];
1860 /* restore name and author from individual level */
1861 strcpy(level.name, level_backup.name);
1862 strcpy(level.author, level_backup.author);
1864 /* restore flag "use_custom_template" */
1865 level.use_custom_template = level_backup.use_custom_template;
1869 static char *getLevelFilenameFromBasename(char *basename)
1871 static char *filename[2] = { NULL, NULL };
1872 int pos = (strEqual(basename, LEVELTEMPLATE_FILENAME) ? 0 : 1);
1874 checked_free(filename[pos]);
1876 filename[pos] = getPath2(getCurrentLevelDir(), basename);
1878 return filename[pos];
1881 static int getFileTypeFromBasename(char *basename)
1883 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1885 static char *filename = NULL;
1886 struct stat file_status;
1888 /* ---------- try to determine file type from filename ---------- */
1890 /* check for typical filename of a Supaplex level package file */
1891 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1892 return LEVEL_FILE_TYPE_SP;
1894 /* check for typical filename of a Diamond Caves II level package file */
1895 if (strSuffixLower(basename, ".dc") ||
1896 strSuffixLower(basename, ".dc2"))
1897 return LEVEL_FILE_TYPE_DC;
1899 /* check for typical filename of a Sokoban level package file */
1900 if (strSuffixLower(basename, ".xsb") &&
1901 strchr(basename, '%') == NULL)
1902 return LEVEL_FILE_TYPE_SB;
1904 /* ---------- try to determine file type from filesize ---------- */
1906 checked_free(filename);
1907 filename = getPath2(getCurrentLevelDir(), basename);
1909 if (stat(filename, &file_status) == 0)
1911 /* check for typical filesize of a Supaplex level package file */
1912 if (file_status.st_size == 170496)
1913 return LEVEL_FILE_TYPE_SP;
1916 return LEVEL_FILE_TYPE_UNKNOWN;
1919 static boolean checkForPackageFromBasename(char *basename)
1921 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
1922 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
1924 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
1927 static char *getSingleLevelBasenameExt(int nr, char *extension)
1929 static char basename[MAX_FILENAME_LEN];
1932 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
1934 sprintf(basename, "%03d.%s", nr, extension);
1939 static char *getSingleLevelBasename(int nr)
1941 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
1944 static char *getPackedLevelBasename(int type)
1946 static char basename[MAX_FILENAME_LEN];
1947 char *directory = getCurrentLevelDir();
1949 DirectoryEntry *dir_entry;
1951 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
1953 if ((dir = openDirectory(directory)) == NULL)
1955 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
1960 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
1962 char *entry_basename = dir_entry->basename;
1963 int entry_type = getFileTypeFromBasename(entry_basename);
1965 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
1967 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
1970 strcpy(basename, entry_basename);
1977 closeDirectory(dir);
1982 static char *getSingleLevelFilename(int nr)
1984 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
1987 #if ENABLE_UNUSED_CODE
1988 static char *getPackedLevelFilename(int type)
1990 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
1994 char *getDefaultLevelFilename(int nr)
1996 return getSingleLevelFilename(nr);
1999 #if ENABLE_UNUSED_CODE
2000 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2004 lfi->packed = FALSE;
2005 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
2006 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2010 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2011 int type, char *format, ...)
2013 static char basename[MAX_FILENAME_LEN];
2016 va_start(ap, format);
2017 vsprintf(basename, format, ap);
2021 lfi->packed = FALSE;
2022 lfi->basename = basename;
2023 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2026 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2031 lfi->basename = getPackedLevelBasename(lfi->type);
2032 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2035 static int getFiletypeFromID(char *filetype_id)
2037 char *filetype_id_lower;
2038 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2041 if (filetype_id == NULL)
2042 return LEVEL_FILE_TYPE_UNKNOWN;
2044 filetype_id_lower = getStringToLower(filetype_id);
2046 for (i = 0; filetype_id_list[i].id != NULL; i++)
2048 char *id_lower = getStringToLower(filetype_id_list[i].id);
2050 if (strEqual(filetype_id_lower, id_lower))
2051 filetype = filetype_id_list[i].filetype;
2055 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2059 free(filetype_id_lower);
2064 char *getLocalLevelTemplateFilename()
2066 return getDefaultLevelFilename(-1);
2069 char *getGlobalLevelTemplateFilename()
2071 /* global variable "leveldir_current" must be modified in the loop below */
2072 LevelDirTree *leveldir_current_last = leveldir_current;
2073 char *filename = NULL;
2075 /* check for template level in path from current to topmost tree node */
2077 while (leveldir_current != NULL)
2079 filename = getDefaultLevelFilename(-1);
2081 if (fileExists(filename))
2084 leveldir_current = leveldir_current->node_parent;
2087 /* restore global variable "leveldir_current" modified in above loop */
2088 leveldir_current = leveldir_current_last;
2093 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2097 /* special case: level number is negative => check for level template file */
2100 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2101 getSingleLevelBasename(-1));
2103 /* replace local level template filename with global template filename */
2104 lfi->filename = getGlobalLevelTemplateFilename();
2106 /* no fallback if template file not existing */
2110 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2111 if (leveldir_current->level_filename != NULL)
2113 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2115 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2116 leveldir_current->level_filename, nr);
2118 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2120 if (fileExists(lfi->filename))
2124 /* check for native Rocks'n'Diamonds level file */
2125 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2126 "%03d.%s", nr, LEVELFILE_EXTENSION);
2127 if (fileExists(lfi->filename))
2130 /* check for Emerald Mine level file (V1) */
2131 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2132 'a' + (nr / 10) % 26, '0' + nr % 10);
2133 if (fileExists(lfi->filename))
2135 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2136 'A' + (nr / 10) % 26, '0' + nr % 10);
2137 if (fileExists(lfi->filename))
2140 /* check for Emerald Mine level file (V2 to V5) */
2141 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2142 if (fileExists(lfi->filename))
2145 /* check for Emerald Mine level file (V6 / single mode) */
2146 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2147 if (fileExists(lfi->filename))
2149 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2150 if (fileExists(lfi->filename))
2153 /* check for Emerald Mine level file (V6 / teamwork mode) */
2154 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2155 if (fileExists(lfi->filename))
2157 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2158 if (fileExists(lfi->filename))
2161 /* check for various packed level file formats */
2162 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2163 if (fileExists(lfi->filename))
2166 /* no known level file found -- use default values (and fail later) */
2167 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2168 "%03d.%s", nr, LEVELFILE_EXTENSION);
2171 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2173 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2174 lfi->type = getFileTypeFromBasename(lfi->basename);
2177 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2179 /* always start with reliable default values */
2180 setFileInfoToDefaults(level_file_info);
2182 level_file_info->nr = nr; /* set requested level number */
2184 determineLevelFileInfo_Filename(level_file_info);
2185 determineLevelFileInfo_Filetype(level_file_info);
2188 /* ------------------------------------------------------------------------- */
2189 /* functions for loading R'n'D level */
2190 /* ------------------------------------------------------------------------- */
2192 int getMappedElement(int element)
2194 /* remap some (historic, now obsolete) elements */
2198 case EL_PLAYER_OBSOLETE:
2199 element = EL_PLAYER_1;
2202 case EL_KEY_OBSOLETE:
2206 case EL_EM_KEY_1_FILE_OBSOLETE:
2207 element = EL_EM_KEY_1;
2210 case EL_EM_KEY_2_FILE_OBSOLETE:
2211 element = EL_EM_KEY_2;
2214 case EL_EM_KEY_3_FILE_OBSOLETE:
2215 element = EL_EM_KEY_3;
2218 case EL_EM_KEY_4_FILE_OBSOLETE:
2219 element = EL_EM_KEY_4;
2222 case EL_ENVELOPE_OBSOLETE:
2223 element = EL_ENVELOPE_1;
2231 if (element >= NUM_FILE_ELEMENTS)
2233 Error(ERR_WARN, "invalid level element %d", element);
2235 element = EL_UNKNOWN;
2243 int getMappedElementByVersion(int element, int game_version)
2245 /* remap some elements due to certain game version */
2247 if (game_version <= VERSION_IDENT(2,2,0,0))
2249 /* map game font elements */
2250 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2251 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2252 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2253 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2256 if (game_version < VERSION_IDENT(3,0,0,0))
2258 /* map Supaplex gravity tube elements */
2259 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2260 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2261 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2262 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2269 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2271 level->file_version = getFileVersion(file);
2272 level->game_version = getFileVersion(file);
2277 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2279 level->creation_date.year = getFile16BitBE(file);
2280 level->creation_date.month = getFile8Bit(file);
2281 level->creation_date.day = getFile8Bit(file);
2283 level->creation_date.src = DATE_SRC_LEVELFILE;
2288 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2290 int initial_player_stepsize;
2291 int initial_player_gravity;
2294 level->fieldx = getFile8Bit(file);
2295 level->fieldy = getFile8Bit(file);
2297 level->time = getFile16BitBE(file);
2298 level->gems_needed = getFile16BitBE(file);
2300 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2301 level->name[i] = getFile8Bit(file);
2302 level->name[MAX_LEVEL_NAME_LEN] = 0;
2304 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2305 level->score[i] = getFile8Bit(file);
2307 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2308 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2309 for (y = 0; y < 3; y++)
2310 for (x = 0; x < 3; x++)
2311 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2313 level->amoeba_speed = getFile8Bit(file);
2314 level->time_magic_wall = getFile8Bit(file);
2315 level->time_wheel = getFile8Bit(file);
2316 level->amoeba_content = getMappedElement(getFile8Bit(file));
2318 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2321 for (i = 0; i < MAX_PLAYERS; i++)
2322 level->initial_player_stepsize[i] = initial_player_stepsize;
2324 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2326 for (i = 0; i < MAX_PLAYERS; i++)
2327 level->initial_player_gravity[i] = initial_player_gravity;
2329 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2330 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2332 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2334 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2335 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2336 level->can_move_into_acid_bits = getFile32BitBE(file);
2337 level->dont_collide_with_bits = getFile8Bit(file);
2339 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2340 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2342 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2343 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2344 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2346 level->game_engine_type = getFile8Bit(file);
2348 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2353 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2357 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2358 level->name[i] = getFile8Bit(file);
2359 level->name[MAX_LEVEL_NAME_LEN] = 0;
2364 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2368 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2369 level->author[i] = getFile8Bit(file);
2370 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2375 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2378 int chunk_size_expected = level->fieldx * level->fieldy;
2380 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2381 stored with 16-bit encoding (and should be twice as big then).
2382 Even worse, playfield data was stored 16-bit when only yamyam content
2383 contained 16-bit elements and vice versa. */
2385 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2386 chunk_size_expected *= 2;
2388 if (chunk_size_expected != chunk_size)
2390 ReadUnusedBytesFromFile(file, chunk_size);
2391 return chunk_size_expected;
2394 for (y = 0; y < level->fieldy; y++)
2395 for (x = 0; x < level->fieldx; x++)
2396 level->field[x][y] =
2397 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2402 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2405 int header_size = 4;
2406 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2407 int chunk_size_expected = header_size + content_size;
2409 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2410 stored with 16-bit encoding (and should be twice as big then).
2411 Even worse, playfield data was stored 16-bit when only yamyam content
2412 contained 16-bit elements and vice versa. */
2414 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2415 chunk_size_expected += content_size;
2417 if (chunk_size_expected != chunk_size)
2419 ReadUnusedBytesFromFile(file, chunk_size);
2420 return chunk_size_expected;
2424 level->num_yamyam_contents = getFile8Bit(file);
2428 /* correct invalid number of content fields -- should never happen */
2429 if (level->num_yamyam_contents < 1 ||
2430 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2431 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2433 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2434 for (y = 0; y < 3; y++)
2435 for (x = 0; x < 3; x++)
2436 level->yamyam_content[i].e[x][y] =
2437 getMappedElement(level->encoding_16bit_field ?
2438 getFile16BitBE(file) : getFile8Bit(file));
2442 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2447 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2449 element = getMappedElement(getFile16BitBE(file));
2450 num_contents = getFile8Bit(file);
2452 getFile8Bit(file); /* content x size (unused) */
2453 getFile8Bit(file); /* content y size (unused) */
2455 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2457 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2458 for (y = 0; y < 3; y++)
2459 for (x = 0; x < 3; x++)
2460 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2462 /* correct invalid number of content fields -- should never happen */
2463 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2464 num_contents = STD_ELEMENT_CONTENTS;
2466 if (element == EL_YAMYAM)
2468 level->num_yamyam_contents = num_contents;
2470 for (i = 0; i < num_contents; i++)
2471 for (y = 0; y < 3; y++)
2472 for (x = 0; x < 3; x++)
2473 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2475 else if (element == EL_BD_AMOEBA)
2477 level->amoeba_content = content_array[0][0][0];
2481 Error(ERR_WARN, "cannot load content for element '%d'", element);
2487 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2493 int chunk_size_expected;
2495 element = getMappedElement(getFile16BitBE(file));
2496 if (!IS_ENVELOPE(element))
2497 element = EL_ENVELOPE_1;
2499 envelope_nr = element - EL_ENVELOPE_1;
2501 envelope_len = getFile16BitBE(file);
2503 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2504 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2506 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2508 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2509 if (chunk_size_expected != chunk_size)
2511 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2512 return chunk_size_expected;
2515 for (i = 0; i < envelope_len; i++)
2516 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2521 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2523 int num_changed_custom_elements = getFile16BitBE(file);
2524 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2527 if (chunk_size_expected != chunk_size)
2529 ReadUnusedBytesFromFile(file, chunk_size - 2);
2530 return chunk_size_expected;
2533 for (i = 0; i < num_changed_custom_elements; i++)
2535 int element = getMappedElement(getFile16BitBE(file));
2536 int properties = getFile32BitBE(file);
2538 if (IS_CUSTOM_ELEMENT(element))
2539 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2541 Error(ERR_WARN, "invalid custom element number %d", element);
2543 /* older game versions that wrote level files with CUS1 chunks used
2544 different default push delay values (not yet stored in level file) */
2545 element_info[element].push_delay_fixed = 2;
2546 element_info[element].push_delay_random = 8;
2552 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2554 int num_changed_custom_elements = getFile16BitBE(file);
2555 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2558 if (chunk_size_expected != chunk_size)
2560 ReadUnusedBytesFromFile(file, chunk_size - 2);
2561 return chunk_size_expected;
2564 for (i = 0; i < num_changed_custom_elements; i++)
2566 int element = getMappedElement(getFile16BitBE(file));
2567 int custom_target_element = getMappedElement(getFile16BitBE(file));
2569 if (IS_CUSTOM_ELEMENT(element))
2570 element_info[element].change->target_element = custom_target_element;
2572 Error(ERR_WARN, "invalid custom element number %d", element);
2578 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2580 int num_changed_custom_elements = getFile16BitBE(file);
2581 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2584 if (chunk_size_expected != chunk_size)
2586 ReadUnusedBytesFromFile(file, chunk_size - 2);
2587 return chunk_size_expected;
2590 for (i = 0; i < num_changed_custom_elements; i++)
2592 int element = getMappedElement(getFile16BitBE(file));
2593 struct ElementInfo *ei = &element_info[element];
2594 unsigned int event_bits;
2596 if (!IS_CUSTOM_ELEMENT(element))
2598 Error(ERR_WARN, "invalid custom element number %d", element);
2600 element = EL_INTERNAL_DUMMY;
2603 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2604 ei->description[j] = getFile8Bit(file);
2605 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2607 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2609 /* some free bytes for future properties and padding */
2610 ReadUnusedBytesFromFile(file, 7);
2612 ei->use_gfx_element = getFile8Bit(file);
2613 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2615 ei->collect_score_initial = getFile8Bit(file);
2616 ei->collect_count_initial = getFile8Bit(file);
2618 ei->push_delay_fixed = getFile16BitBE(file);
2619 ei->push_delay_random = getFile16BitBE(file);
2620 ei->move_delay_fixed = getFile16BitBE(file);
2621 ei->move_delay_random = getFile16BitBE(file);
2623 ei->move_pattern = getFile16BitBE(file);
2624 ei->move_direction_initial = getFile8Bit(file);
2625 ei->move_stepsize = getFile8Bit(file);
2627 for (y = 0; y < 3; y++)
2628 for (x = 0; x < 3; x++)
2629 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2631 event_bits = getFile32BitBE(file);
2632 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2633 if (event_bits & (1 << j))
2634 ei->change->has_event[j] = TRUE;
2636 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2638 ei->change->delay_fixed = getFile16BitBE(file);
2639 ei->change->delay_random = getFile16BitBE(file);
2640 ei->change->delay_frames = getFile16BitBE(file);
2642 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2644 ei->change->explode = getFile8Bit(file);
2645 ei->change->use_target_content = getFile8Bit(file);
2646 ei->change->only_if_complete = getFile8Bit(file);
2647 ei->change->use_random_replace = getFile8Bit(file);
2649 ei->change->random_percentage = getFile8Bit(file);
2650 ei->change->replace_when = getFile8Bit(file);
2652 for (y = 0; y < 3; y++)
2653 for (x = 0; x < 3; x++)
2654 ei->change->target_content.e[x][y] =
2655 getMappedElement(getFile16BitBE(file));
2657 ei->slippery_type = getFile8Bit(file);
2659 /* some free bytes for future properties and padding */
2660 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2662 /* mark that this custom element has been modified */
2663 ei->modified_settings = TRUE;
2669 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2671 struct ElementInfo *ei;
2672 int chunk_size_expected;
2676 /* ---------- custom element base property values (96 bytes) ------------- */
2678 element = getMappedElement(getFile16BitBE(file));
2680 if (!IS_CUSTOM_ELEMENT(element))
2682 Error(ERR_WARN, "invalid custom element number %d", element);
2684 ReadUnusedBytesFromFile(file, chunk_size - 2);
2688 ei = &element_info[element];
2690 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2691 ei->description[i] = getFile8Bit(file);
2692 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2694 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2696 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2698 ei->num_change_pages = getFile8Bit(file);
2700 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2701 if (chunk_size_expected != chunk_size)
2703 ReadUnusedBytesFromFile(file, chunk_size - 43);
2704 return chunk_size_expected;
2707 ei->ce_value_fixed_initial = getFile16BitBE(file);
2708 ei->ce_value_random_initial = getFile16BitBE(file);
2709 ei->use_last_ce_value = getFile8Bit(file);
2711 ei->use_gfx_element = getFile8Bit(file);
2712 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2714 ei->collect_score_initial = getFile8Bit(file);
2715 ei->collect_count_initial = getFile8Bit(file);
2717 ei->drop_delay_fixed = getFile8Bit(file);
2718 ei->push_delay_fixed = getFile8Bit(file);
2719 ei->drop_delay_random = getFile8Bit(file);
2720 ei->push_delay_random = getFile8Bit(file);
2721 ei->move_delay_fixed = getFile16BitBE(file);
2722 ei->move_delay_random = getFile16BitBE(file);
2724 /* bits 0 - 15 of "move_pattern" ... */
2725 ei->move_pattern = getFile16BitBE(file);
2726 ei->move_direction_initial = getFile8Bit(file);
2727 ei->move_stepsize = getFile8Bit(file);
2729 ei->slippery_type = getFile8Bit(file);
2731 for (y = 0; y < 3; y++)
2732 for (x = 0; x < 3; x++)
2733 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2735 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2736 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2737 ei->move_leave_type = getFile8Bit(file);
2739 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2740 ei->move_pattern |= (getFile16BitBE(file) << 16);
2742 ei->access_direction = getFile8Bit(file);
2744 ei->explosion_delay = getFile8Bit(file);
2745 ei->ignition_delay = getFile8Bit(file);
2746 ei->explosion_type = getFile8Bit(file);
2748 /* some free bytes for future custom property values and padding */
2749 ReadUnusedBytesFromFile(file, 1);
2751 /* ---------- change page property values (48 bytes) --------------------- */
2753 setElementChangePages(ei, ei->num_change_pages);
2755 for (i = 0; i < ei->num_change_pages; i++)
2757 struct ElementChangeInfo *change = &ei->change_page[i];
2758 unsigned int event_bits;
2760 /* always start with reliable default values */
2761 setElementChangeInfoToDefaults(change);
2763 /* bits 0 - 31 of "has_event[]" ... */
2764 event_bits = getFile32BitBE(file);
2765 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2766 if (event_bits & (1 << j))
2767 change->has_event[j] = TRUE;
2769 change->target_element = getMappedElement(getFile16BitBE(file));
2771 change->delay_fixed = getFile16BitBE(file);
2772 change->delay_random = getFile16BitBE(file);
2773 change->delay_frames = getFile16BitBE(file);
2775 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2777 change->explode = getFile8Bit(file);
2778 change->use_target_content = getFile8Bit(file);
2779 change->only_if_complete = getFile8Bit(file);
2780 change->use_random_replace = getFile8Bit(file);
2782 change->random_percentage = getFile8Bit(file);
2783 change->replace_when = getFile8Bit(file);
2785 for (y = 0; y < 3; y++)
2786 for (x = 0; x < 3; x++)
2787 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2789 change->can_change = getFile8Bit(file);
2791 change->trigger_side = getFile8Bit(file);
2793 change->trigger_player = getFile8Bit(file);
2794 change->trigger_page = getFile8Bit(file);
2796 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2797 CH_PAGE_ANY : (1 << change->trigger_page));
2799 change->has_action = getFile8Bit(file);
2800 change->action_type = getFile8Bit(file);
2801 change->action_mode = getFile8Bit(file);
2802 change->action_arg = getFile16BitBE(file);
2804 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2805 event_bits = getFile8Bit(file);
2806 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2807 if (event_bits & (1 << (j - 32)))
2808 change->has_event[j] = TRUE;
2811 /* mark this custom element as modified */
2812 ei->modified_settings = TRUE;
2817 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2819 struct ElementInfo *ei;
2820 struct ElementGroupInfo *group;
2824 element = getMappedElement(getFile16BitBE(file));
2826 if (!IS_GROUP_ELEMENT(element))
2828 Error(ERR_WARN, "invalid group element number %d", element);
2830 ReadUnusedBytesFromFile(file, chunk_size - 2);
2834 ei = &element_info[element];
2836 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2837 ei->description[i] = getFile8Bit(file);
2838 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2840 group = element_info[element].group;
2842 group->num_elements = getFile8Bit(file);
2844 ei->use_gfx_element = getFile8Bit(file);
2845 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2847 group->choice_mode = getFile8Bit(file);
2849 /* some free bytes for future values and padding */
2850 ReadUnusedBytesFromFile(file, 3);
2852 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2853 group->element[i] = getMappedElement(getFile16BitBE(file));
2855 /* mark this group element as modified */
2856 element_info[element].modified_settings = TRUE;
2861 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2862 int element, int real_element)
2864 int micro_chunk_size = 0;
2865 int conf_type = getFile8Bit(file);
2866 int byte_mask = conf_type & CONF_MASK_BYTES;
2867 boolean element_found = FALSE;
2870 micro_chunk_size += 1;
2872 if (byte_mask == CONF_MASK_MULTI_BYTES)
2874 int num_bytes = getFile16BitBE(file);
2875 byte *buffer = checked_malloc(num_bytes);
2877 ReadBytesFromFile(file, buffer, num_bytes);
2879 for (i = 0; conf[i].data_type != -1; i++)
2881 if (conf[i].element == element &&
2882 conf[i].conf_type == conf_type)
2884 int data_type = conf[i].data_type;
2885 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2886 int max_num_entities = conf[i].max_num_entities;
2888 if (num_entities > max_num_entities)
2891 "truncating number of entities for element %d from %d to %d",
2892 element, num_entities, max_num_entities);
2894 num_entities = max_num_entities;
2897 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
2898 data_type == TYPE_CONTENT_LIST))
2900 /* for element and content lists, zero entities are not allowed */
2901 Error(ERR_WARN, "found empty list of entities for element %d",
2904 /* do not set "num_entities" here to prevent reading behind buffer */
2906 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
2910 *(int *)(conf[i].num_entities) = num_entities;
2913 element_found = TRUE;
2915 if (data_type == TYPE_STRING)
2917 char *string = (char *)(conf[i].value);
2920 for (j = 0; j < max_num_entities; j++)
2921 string[j] = (j < num_entities ? buffer[j] : '\0');
2923 else if (data_type == TYPE_ELEMENT_LIST)
2925 int *element_array = (int *)(conf[i].value);
2928 for (j = 0; j < num_entities; j++)
2930 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
2932 else if (data_type == TYPE_CONTENT_LIST)
2934 struct Content *content= (struct Content *)(conf[i].value);
2937 for (c = 0; c < num_entities; c++)
2938 for (y = 0; y < 3; y++)
2939 for (x = 0; x < 3; x++)
2940 content[c].e[x][y] =
2941 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
2944 element_found = FALSE;
2950 checked_free(buffer);
2952 micro_chunk_size += 2 + num_bytes;
2954 else /* constant size configuration data (1, 2 or 4 bytes) */
2956 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
2957 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
2958 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
2960 for (i = 0; conf[i].data_type != -1; i++)
2962 if (conf[i].element == element &&
2963 conf[i].conf_type == conf_type)
2965 int data_type = conf[i].data_type;
2967 if (data_type == TYPE_ELEMENT)
2968 value = getMappedElement(value);
2970 if (data_type == TYPE_BOOLEAN)
2971 *(boolean *)(conf[i].value) = value;
2973 *(int *) (conf[i].value) = value;
2975 element_found = TRUE;
2981 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
2986 char *error_conf_chunk_bytes =
2987 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
2988 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
2989 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
2990 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
2991 int error_element = real_element;
2993 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
2994 error_conf_chunk_bytes, error_conf_chunk_token,
2995 error_element, EL_NAME(error_element));
2998 return micro_chunk_size;
3001 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3003 int real_chunk_size = 0;
3005 li = *level; /* copy level data into temporary buffer */
3007 while (!checkEndOfFile(file))
3009 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3011 if (real_chunk_size >= chunk_size)
3015 *level = li; /* copy temporary buffer back to level data */
3017 return real_chunk_size;
3020 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3022 int real_chunk_size = 0;
3024 li = *level; /* copy level data into temporary buffer */
3026 while (!checkEndOfFile(file))
3028 int element = getMappedElement(getFile16BitBE(file));
3030 real_chunk_size += 2;
3031 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3033 if (real_chunk_size >= chunk_size)
3037 *level = li; /* copy temporary buffer back to level data */
3039 return real_chunk_size;
3042 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3044 int real_chunk_size = 0;
3046 li = *level; /* copy level data into temporary buffer */
3048 while (!checkEndOfFile(file))
3050 int element = getMappedElement(getFile16BitBE(file));
3052 real_chunk_size += 2;
3053 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3055 if (real_chunk_size >= chunk_size)
3059 *level = li; /* copy temporary buffer back to level data */
3061 return real_chunk_size;
3064 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3066 int element = getMappedElement(getFile16BitBE(file));
3067 int envelope_nr = element - EL_ENVELOPE_1;
3068 int real_chunk_size = 2;
3070 xx_envelope = level->envelope[envelope_nr]; /* copy into temporary buffer */
3072 while (!checkEndOfFile(file))
3074 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3077 if (real_chunk_size >= chunk_size)
3081 level->envelope[envelope_nr] = xx_envelope; /* copy from temporary buffer */
3083 return real_chunk_size;
3086 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3088 int element = getMappedElement(getFile16BitBE(file));
3089 int real_chunk_size = 2;
3090 struct ElementInfo *ei = &element_info[element];
3093 xx_ei = *ei; /* copy element data into temporary buffer */
3095 xx_ei.num_change_pages = -1;
3097 while (!checkEndOfFile(file))
3099 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3101 if (xx_ei.num_change_pages != -1)
3104 if (real_chunk_size >= chunk_size)
3110 if (ei->num_change_pages == -1)
3112 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3115 ei->num_change_pages = 1;
3117 setElementChangePages(ei, 1);
3118 setElementChangeInfoToDefaults(ei->change);
3120 return real_chunk_size;
3123 /* initialize number of change pages stored for this custom element */
3124 setElementChangePages(ei, ei->num_change_pages);
3125 for (i = 0; i < ei->num_change_pages; i++)
3126 setElementChangeInfoToDefaults(&ei->change_page[i]);
3128 /* start with reading properties for the first change page */
3129 xx_current_change_page = 0;
3131 while (!checkEndOfFile(file))
3133 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3135 xx_change = *change; /* copy change data into temporary buffer */
3137 resetEventBits(); /* reset bits; change page might have changed */
3139 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3142 *change = xx_change;
3144 setEventFlagsFromEventBits(change);
3146 if (real_chunk_size >= chunk_size)
3150 return real_chunk_size;
3153 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3155 int element = getMappedElement(getFile16BitBE(file));
3156 int real_chunk_size = 2;
3157 struct ElementInfo *ei = &element_info[element];
3158 struct ElementGroupInfo *group = ei->group;
3160 xx_ei = *ei; /* copy element data into temporary buffer */
3161 xx_group = *group; /* copy group data into temporary buffer */
3163 while (!checkEndOfFile(file))
3165 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3168 if (real_chunk_size >= chunk_size)
3175 return real_chunk_size;
3178 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3179 struct LevelFileInfo *level_file_info,
3180 boolean level_info_only)
3182 char *filename = level_file_info->filename;
3183 char cookie[MAX_LINE_LEN];
3184 char chunk_name[CHUNK_ID_LEN + 1];
3188 if (!(file = openFile(filename, MODE_READ)))
3190 level->no_valid_file = TRUE;
3191 level->no_level_file = TRUE;
3193 if (level_info_only)
3196 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3198 if (!setup.editor.use_template_for_new_levels)
3201 /* if level file not found, try to initialize level data from template */
3202 filename = getGlobalLevelTemplateFilename();
3204 if (!(file = openFile(filename, MODE_READ)))
3207 /* default: for empty levels, use level template for custom elements */
3208 level->use_custom_template = TRUE;
3210 level->no_valid_file = FALSE;
3213 getFileChunkBE(file, chunk_name, NULL);
3214 if (strEqual(chunk_name, "RND1"))
3216 getFile32BitBE(file); /* not used */
3218 getFileChunkBE(file, chunk_name, NULL);
3219 if (!strEqual(chunk_name, "CAVE"))
3221 level->no_valid_file = TRUE;
3223 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3230 else /* check for pre-2.0 file format with cookie string */
3232 strcpy(cookie, chunk_name);
3233 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3235 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3236 cookie[strlen(cookie) - 1] = '\0';
3238 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3240 level->no_valid_file = TRUE;
3242 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3249 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3251 level->no_valid_file = TRUE;
3253 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3260 /* pre-2.0 level files have no game version, so use file version here */
3261 level->game_version = level->file_version;
3264 if (level->file_version < FILE_VERSION_1_2)
3266 /* level files from versions before 1.2.0 without chunk structure */
3267 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3268 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3276 int (*loader)(File *, int, struct LevelInfo *);
3280 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3281 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3282 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3283 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3284 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3285 { "INFO", -1, LoadLevel_INFO },
3286 { "BODY", -1, LoadLevel_BODY },
3287 { "CONT", -1, LoadLevel_CONT },
3288 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3289 { "CNT3", -1, LoadLevel_CNT3 },
3290 { "CUS1", -1, LoadLevel_CUS1 },
3291 { "CUS2", -1, LoadLevel_CUS2 },
3292 { "CUS3", -1, LoadLevel_CUS3 },
3293 { "CUS4", -1, LoadLevel_CUS4 },
3294 { "GRP1", -1, LoadLevel_GRP1 },
3295 { "CONF", -1, LoadLevel_CONF },
3296 { "ELEM", -1, LoadLevel_ELEM },
3297 { "NOTE", -1, LoadLevel_NOTE },
3298 { "CUSX", -1, LoadLevel_CUSX },
3299 { "GRPX", -1, LoadLevel_GRPX },
3304 while (getFileChunkBE(file, chunk_name, &chunk_size))
3308 while (chunk_info[i].name != NULL &&
3309 !strEqual(chunk_name, chunk_info[i].name))
3312 if (chunk_info[i].name == NULL)
3314 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3315 chunk_name, filename);
3316 ReadUnusedBytesFromFile(file, chunk_size);
3318 else if (chunk_info[i].size != -1 &&
3319 chunk_info[i].size != chunk_size)
3321 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3322 chunk_size, chunk_name, filename);
3323 ReadUnusedBytesFromFile(file, chunk_size);
3327 /* call function to load this level chunk */
3328 int chunk_size_expected =
3329 (chunk_info[i].loader)(file, chunk_size, level);
3331 /* the size of some chunks cannot be checked before reading other
3332 chunks first (like "HEAD" and "BODY") that contain some header
3333 information, so check them here */
3334 if (chunk_size_expected != chunk_size)
3336 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3337 chunk_size, chunk_name, filename);
3347 /* ------------------------------------------------------------------------- */
3348 /* functions for loading EM level */
3349 /* ------------------------------------------------------------------------- */
3351 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3353 static int ball_xy[8][2] =
3364 struct LevelInfo_EM *level_em = level->native_em_level;
3365 struct LEVEL *lev = level_em->lev;
3366 struct PLAYER **ply = level_em->ply;
3369 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3370 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3372 lev->time_seconds = level->time;
3373 lev->required_initial = level->gems_needed;
3375 lev->emerald_score = level->score[SC_EMERALD];
3376 lev->diamond_score = level->score[SC_DIAMOND];
3377 lev->alien_score = level->score[SC_ROBOT];
3378 lev->tank_score = level->score[SC_SPACESHIP];
3379 lev->bug_score = level->score[SC_BUG];
3380 lev->eater_score = level->score[SC_YAMYAM];
3381 lev->nut_score = level->score[SC_NUT];
3382 lev->dynamite_score = level->score[SC_DYNAMITE];
3383 lev->key_score = level->score[SC_KEY];
3384 lev->exit_score = level->score[SC_TIME_BONUS];
3386 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3387 for (y = 0; y < 3; y++)
3388 for (x = 0; x < 3; x++)
3389 lev->eater_array[i][y * 3 + x] =
3390 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3392 lev->amoeba_time = level->amoeba_speed;
3393 lev->wonderwall_time_initial = level->time_magic_wall;
3394 lev->wheel_time = level->time_wheel;
3396 lev->android_move_time = level->android_move_time;
3397 lev->android_clone_time = level->android_clone_time;
3398 lev->ball_random = level->ball_random;
3399 lev->ball_state_initial = level->ball_state_initial;
3400 lev->ball_time = level->ball_time;
3401 lev->num_ball_arrays = level->num_ball_contents;
3403 lev->lenses_score = level->lenses_score;
3404 lev->magnify_score = level->magnify_score;
3405 lev->slurp_score = level->slurp_score;
3407 lev->lenses_time = level->lenses_time;
3408 lev->magnify_time = level->magnify_time;
3410 lev->wind_direction_initial =
3411 map_direction_RND_to_EM(level->wind_direction_initial);
3412 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3413 lev->wind_time : 0);
3415 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3416 for (j = 0; j < 8; j++)
3417 lev->ball_array[i][j] =
3418 map_element_RND_to_EM(level->
3419 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3421 map_android_clone_elements_RND_to_EM(level);
3423 /* first fill the complete playfield with the default border element */
3424 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3425 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3426 level_em->cave[x][y] = ZBORDER;
3428 if (BorderElement == EL_STEELWALL)
3430 for (y = 0; y < lev->height + 2; y++)
3431 for (x = 0; x < lev->width + 2; x++)
3432 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3435 /* then copy the real level contents from level file into the playfield */
3436 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3438 int new_element = map_element_RND_to_EM(level->field[x][y]);
3439 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3440 int xx = x + 1 + offset;
3441 int yy = y + 1 + offset;
3443 if (level->field[x][y] == EL_AMOEBA_DEAD)
3444 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3446 level_em->cave[xx][yy] = new_element;
3449 for (i = 0; i < MAX_PLAYERS; i++)
3451 ply[i]->x_initial = 0;
3452 ply[i]->y_initial = 0;
3455 /* initialize player positions and delete players from the playfield */
3456 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3458 if (ELEM_IS_PLAYER(level->field[x][y]))
3460 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3461 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3462 int xx = x + 1 + offset;
3463 int yy = y + 1 + offset;
3465 ply[player_nr]->x_initial = xx;
3466 ply[player_nr]->y_initial = yy;
3468 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3472 if (BorderElement == EL_STEELWALL)
3479 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3481 static int ball_xy[8][2] =
3492 struct LevelInfo_EM *level_em = level->native_em_level;
3493 struct LEVEL *lev = level_em->lev;
3494 struct PLAYER **ply = level_em->ply;
3497 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3498 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3500 level->time = lev->time_seconds;
3501 level->gems_needed = lev->required_initial;
3503 sprintf(level->name, "Level %d", level->file_info.nr);
3505 level->score[SC_EMERALD] = lev->emerald_score;
3506 level->score[SC_DIAMOND] = lev->diamond_score;
3507 level->score[SC_ROBOT] = lev->alien_score;
3508 level->score[SC_SPACESHIP] = lev->tank_score;
3509 level->score[SC_BUG] = lev->bug_score;
3510 level->score[SC_YAMYAM] = lev->eater_score;
3511 level->score[SC_NUT] = lev->nut_score;
3512 level->score[SC_DYNAMITE] = lev->dynamite_score;
3513 level->score[SC_KEY] = lev->key_score;
3514 level->score[SC_TIME_BONUS] = lev->exit_score;
3516 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3518 for (i = 0; i < level->num_yamyam_contents; i++)
3519 for (y = 0; y < 3; y++)
3520 for (x = 0; x < 3; x++)
3521 level->yamyam_content[i].e[x][y] =
3522 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3524 level->amoeba_speed = lev->amoeba_time;
3525 level->time_magic_wall = lev->wonderwall_time_initial;
3526 level->time_wheel = lev->wheel_time;
3528 level->android_move_time = lev->android_move_time;
3529 level->android_clone_time = lev->android_clone_time;
3530 level->ball_random = lev->ball_random;
3531 level->ball_state_initial = lev->ball_state_initial;
3532 level->ball_time = lev->ball_time;
3533 level->num_ball_contents = lev->num_ball_arrays;
3535 level->lenses_score = lev->lenses_score;
3536 level->magnify_score = lev->magnify_score;
3537 level->slurp_score = lev->slurp_score;
3539 level->lenses_time = lev->lenses_time;
3540 level->magnify_time = lev->magnify_time;
3542 level->wind_direction_initial =
3543 map_direction_EM_to_RND(lev->wind_direction_initial);
3545 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3546 for (j = 0; j < 8; j++)
3547 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3548 map_element_EM_to_RND(lev->ball_array[i][j]);
3550 map_android_clone_elements_EM_to_RND(level);
3552 /* convert the playfield (some elements need special treatment) */
3553 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3555 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3557 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3558 new_element = EL_AMOEBA_DEAD;
3560 level->field[x][y] = new_element;
3563 for (i = 0; i < MAX_PLAYERS; i++)
3565 /* in case of all players set to the same field, use the first player */
3566 int nr = MAX_PLAYERS - i - 1;
3567 int jx = ply[nr]->x_initial - 1;
3568 int jy = ply[nr]->y_initial - 1;
3570 if (jx != -1 && jy != -1)
3571 level->field[jx][jy] = EL_PLAYER_1 + nr;
3576 /* ------------------------------------------------------------------------- */
3577 /* functions for loading SP level */
3578 /* ------------------------------------------------------------------------- */
3580 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3582 struct LevelInfo_SP *level_sp = level->native_sp_level;
3583 LevelInfoType *header = &level_sp->header;
3586 level_sp->width = level->fieldx;
3587 level_sp->height = level->fieldy;
3589 for (x = 0; x < level->fieldx; x++)
3590 for (y = 0; y < level->fieldy; y++)
3591 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3593 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3595 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3596 header->LevelTitle[i] = level->name[i];
3597 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3599 header->InfotronsNeeded = level->gems_needed;
3601 header->SpecialPortCount = 0;
3603 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3605 boolean gravity_port_found = FALSE;
3606 boolean gravity_port_valid = FALSE;
3607 int gravity_port_flag;
3608 int gravity_port_base_element;
3609 int element = level->field[x][y];
3611 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3612 element <= EL_SP_GRAVITY_ON_PORT_UP)
3614 gravity_port_found = TRUE;
3615 gravity_port_valid = TRUE;
3616 gravity_port_flag = 1;
3617 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3619 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3620 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3622 gravity_port_found = TRUE;
3623 gravity_port_valid = TRUE;
3624 gravity_port_flag = 0;
3625 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3627 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3628 element <= EL_SP_GRAVITY_PORT_UP)
3630 /* change R'n'D style gravity inverting special port to normal port
3631 (there are no gravity inverting ports in native Supaplex engine) */
3633 gravity_port_found = TRUE;
3634 gravity_port_valid = FALSE;
3635 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3638 if (gravity_port_found)
3640 if (gravity_port_valid &&
3641 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3643 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3645 port->PortLocation = (y * level->fieldx + x) * 2;
3646 port->Gravity = gravity_port_flag;
3648 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3650 header->SpecialPortCount++;
3654 /* change special gravity port to normal port */
3656 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3659 level_sp->playfield[x][y] = element - EL_SP_START;
3664 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3666 struct LevelInfo_SP *level_sp = level->native_sp_level;
3667 LevelInfoType *header = &level_sp->header;
3668 boolean num_invalid_elements = 0;
3671 level->fieldx = level_sp->width;
3672 level->fieldy = level_sp->height;
3674 for (x = 0; x < level->fieldx; x++)
3676 for (y = 0; y < level->fieldy; y++)
3678 int element_old = level_sp->playfield[x][y];
3679 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3681 if (element_new == EL_UNKNOWN)
3683 num_invalid_elements++;
3685 Error(ERR_DEBUG, "invalid element %d at position %d, %d",
3689 level->field[x][y] = element_new;
3693 if (num_invalid_elements > 0)
3694 Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
3695 (!options.debug ? " (use '--debug' for more details)" : ""));
3697 for (i = 0; i < MAX_PLAYERS; i++)
3698 level->initial_player_gravity[i] =
3699 (header->InitialGravity == 1 ? TRUE : FALSE);
3701 /* skip leading spaces */
3702 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3703 if (header->LevelTitle[i] != ' ')
3706 /* copy level title */
3707 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3708 level->name[j] = header->LevelTitle[i];
3709 level->name[j] = '\0';
3711 /* cut trailing spaces */
3713 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3714 level->name[j - 1] = '\0';
3716 level->gems_needed = header->InfotronsNeeded;
3718 for (i = 0; i < header->SpecialPortCount; i++)
3720 SpecialPortType *port = &header->SpecialPort[i];
3721 int port_location = port->PortLocation;
3722 int gravity = port->Gravity;
3723 int port_x, port_y, port_element;
3725 port_x = (port_location / 2) % level->fieldx;
3726 port_y = (port_location / 2) / level->fieldx;
3728 if (port_x < 0 || port_x >= level->fieldx ||
3729 port_y < 0 || port_y >= level->fieldy)
3731 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3737 port_element = level->field[port_x][port_y];
3739 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3740 port_element > EL_SP_GRAVITY_PORT_UP)
3742 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3747 /* change previous (wrong) gravity inverting special port to either
3748 gravity enabling special port or gravity disabling special port */
3749 level->field[port_x][port_y] +=
3750 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3751 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3754 /* change special gravity ports without database entries to normal ports */
3755 for (x = 0; x < level->fieldx; x++)
3756 for (y = 0; y < level->fieldy; y++)
3757 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3758 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3759 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3761 level->time = 0; /* no time limit */
3762 level->amoeba_speed = 0;
3763 level->time_magic_wall = 0;
3764 level->time_wheel = 0;
3765 level->amoeba_content = EL_EMPTY;
3768 /* original Supaplex does not use score values -- use default values */
3770 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3771 level->score[i] = 0;
3774 /* there are no yamyams in supaplex levels */
3775 for (i = 0; i < level->num_yamyam_contents; i++)
3776 for (x = 0; x < 3; x++)
3777 for (y = 0; y < 3; y++)
3778 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3781 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3783 struct LevelInfo_SP *level_sp = level->native_sp_level;
3784 struct DemoInfo_SP *demo = &level_sp->demo;
3787 /* always start with reliable default values */
3788 demo->is_available = FALSE;
3791 if (TAPE_IS_EMPTY(tape))
3794 demo->level_nr = tape.level_nr; /* (currently not used) */
3796 level_sp->header.DemoRandomSeed = tape.random_seed;
3800 for (i = 0; i < tape.length; i++)
3802 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3803 int demo_repeat = tape.pos[i].delay;
3804 int demo_entries = (demo_repeat + 15) / 16;
3806 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3808 Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
3814 for (j = 0; j < demo_repeat / 16; j++)
3815 demo->data[demo->length++] = 0xf0 | demo_action;
3817 if (demo_repeat % 16)
3818 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3821 demo->is_available = TRUE;
3824 static void setTapeInfoToDefaults();
3826 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3828 struct LevelInfo_SP *level_sp = level->native_sp_level;
3829 struct DemoInfo_SP *demo = &level_sp->demo;
3830 char *filename = level->file_info.filename;
3833 /* always start with reliable default values */
3834 setTapeInfoToDefaults();
3836 if (!demo->is_available)
3839 tape.level_nr = demo->level_nr; /* (currently not used) */
3840 tape.random_seed = level_sp->header.DemoRandomSeed;
3842 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3845 tape.pos[tape.counter].delay = 0;
3847 for (i = 0; i < demo->length; i++)
3849 int demo_action = demo->data[i] & 0x0f;
3850 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3851 int tape_action = map_key_SP_to_RND(demo_action);
3852 int tape_repeat = demo_repeat + 1;
3853 byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
3854 boolean success = 0;
3857 for (j = 0; j < tape_repeat; j++)
3858 success = TapeAddAction(action);
3862 Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
3869 TapeHaltRecording();
3873 /* ------------------------------------------------------------------------- */
3874 /* functions for loading MM level */
3875 /* ------------------------------------------------------------------------- */
3877 void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
3879 struct LevelInfo_MM *level_mm = level->native_mm_level;
3882 level_mm->file_version = level->file_version;
3883 level_mm->game_version = level->game_version;
3884 level_mm->encoding_16bit_field = level->encoding_16bit_field;
3886 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
3887 level_mm->fieldy = MIN(level->fieldx, MM_MAX_PLAYFIELD_HEIGHT);
3889 level_mm->time = level->time;
3890 level_mm->kettles_needed = level->gems_needed;
3891 level_mm->auto_count_kettles = FALSE;
3892 level_mm->laser_red = FALSE;
3893 level_mm->laser_green = FALSE;
3894 level_mm->laser_blue = TRUE;
3896 strcpy(level_mm->name, level->name);
3897 strcpy(level_mm->author, level->author);
3899 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
3900 level_mm->score[SC_KEY] = level->score[SC_PACMAN];
3901 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
3903 level_mm->amoeba_speed = level->amoeba_speed;
3904 level_mm->time_fuse = 0;
3906 for (y = 0; y < level_mm->fieldx; y++)
3907 for (x = 0; x < level_mm->fieldy; x++)
3908 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
3911 void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
3913 struct LevelInfo_MM *level_mm = level->native_mm_level;
3916 level->file_version = level_mm->file_version;
3917 level->game_version = level_mm->game_version;
3918 level->encoding_16bit_field = level_mm->encoding_16bit_field;
3920 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
3921 level->fieldy = MIN(level_mm->fieldx, MAX_LEV_FIELDY);
3923 level->time = level_mm->time;
3924 level->gems_needed = level_mm->kettles_needed;
3926 strcpy(level->name, level_mm->name);
3927 strcpy(level->author, level_mm->author);
3929 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
3930 level->score[SC_KEY] = level_mm->score[SC_PACMAN];
3931 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
3933 level->amoeba_speed = level_mm->amoeba_speed;
3935 for (y = 0; y < level->fieldx; y++)
3936 for (x = 0; x < level->fieldy; x++)
3937 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
3941 /* ------------------------------------------------------------------------- */
3942 /* functions for loading DC level */
3943 /* ------------------------------------------------------------------------- */
3945 #define DC_LEVEL_HEADER_SIZE 344
3947 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
3949 static int last_data_encoded;
3953 int diff_hi, diff_lo;
3954 int data_hi, data_lo;
3955 unsigned short data_decoded;
3959 last_data_encoded = 0;
3966 diff = data_encoded - last_data_encoded;
3967 diff_hi = diff & ~0xff;
3968 diff_lo = diff & 0xff;
3972 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
3973 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
3974 data_hi = data_hi & 0xff00;
3976 data_decoded = data_hi | data_lo;
3978 last_data_encoded = data_encoded;
3980 offset1 = (offset1 + 1) % 31;
3981 offset2 = offset2 & 0xff;
3983 return data_decoded;
3986 int getMappedElement_DC(int element)
3994 /* 0x0117 - 0x036e: (?) */
3997 /* 0x042d - 0x0684: (?) */
4013 element = EL_CRYSTAL;
4016 case 0x0e77: /* quicksand (boulder) */
4017 element = EL_QUICKSAND_FAST_FULL;
4020 case 0x0e99: /* slow quicksand (boulder) */
4021 element = EL_QUICKSAND_FULL;
4025 element = EL_EM_EXIT_OPEN;
4029 element = EL_EM_EXIT_CLOSED;
4033 element = EL_EM_STEEL_EXIT_OPEN;
4037 element = EL_EM_STEEL_EXIT_CLOSED;
4040 case 0x0f4f: /* dynamite (lit 1) */
4041 element = EL_EM_DYNAMITE_ACTIVE;
4044 case 0x0f57: /* dynamite (lit 2) */
4045 element = EL_EM_DYNAMITE_ACTIVE;
4048 case 0x0f5f: /* dynamite (lit 3) */
4049 element = EL_EM_DYNAMITE_ACTIVE;
4052 case 0x0f67: /* dynamite (lit 4) */
4053 element = EL_EM_DYNAMITE_ACTIVE;
4060 element = EL_AMOEBA_WET;
4064 element = EL_AMOEBA_DROP;
4068 element = EL_DC_MAGIC_WALL;
4072 element = EL_SPACESHIP_UP;
4076 element = EL_SPACESHIP_DOWN;
4080 element = EL_SPACESHIP_LEFT;
4084 element = EL_SPACESHIP_RIGHT;
4088 element = EL_BUG_UP;
4092 element = EL_BUG_DOWN;
4096 element = EL_BUG_LEFT;
4100 element = EL_BUG_RIGHT;
4104 element = EL_MOLE_UP;
4108 element = EL_MOLE_DOWN;
4112 element = EL_MOLE_LEFT;
4116 element = EL_MOLE_RIGHT;
4124 element = EL_YAMYAM;
4128 element = EL_SWITCHGATE_OPEN;
4132 element = EL_SWITCHGATE_CLOSED;
4136 element = EL_DC_SWITCHGATE_SWITCH_UP;
4140 element = EL_TIMEGATE_CLOSED;
4143 case 0x144c: /* conveyor belt switch (green) */
4144 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4147 case 0x144f: /* conveyor belt switch (red) */
4148 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4151 case 0x1452: /* conveyor belt switch (blue) */
4152 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4156 element = EL_CONVEYOR_BELT_3_MIDDLE;
4160 element = EL_CONVEYOR_BELT_3_LEFT;
4164 element = EL_CONVEYOR_BELT_3_RIGHT;
4168 element = EL_CONVEYOR_BELT_1_MIDDLE;
4172 element = EL_CONVEYOR_BELT_1_LEFT;
4176 element = EL_CONVEYOR_BELT_1_RIGHT;
4180 element = EL_CONVEYOR_BELT_4_MIDDLE;
4184 element = EL_CONVEYOR_BELT_4_LEFT;
4188 element = EL_CONVEYOR_BELT_4_RIGHT;
4192 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4196 element = EL_EXPANDABLE_WALL_VERTICAL;
4200 element = EL_EXPANDABLE_WALL_ANY;
4203 case 0x14ce: /* growing steel wall (left/right) */
4204 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4207 case 0x14df: /* growing steel wall (up/down) */
4208 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4211 case 0x14e8: /* growing steel wall (up/down/left/right) */
4212 element = EL_EXPANDABLE_STEELWALL_ANY;
4216 element = EL_SHIELD_DEADLY;
4220 element = EL_EXTRA_TIME;
4228 element = EL_EMPTY_SPACE;
4231 case 0x1578: /* quicksand (empty) */
4232 element = EL_QUICKSAND_FAST_EMPTY;
4235 case 0x1579: /* slow quicksand (empty) */
4236 element = EL_QUICKSAND_EMPTY;
4239 /* 0x157c - 0x158b: */
4242 /* 0x1590 - 0x159f: */
4243 /* EL_DC_LANDMINE */
4246 element = EL_EM_DYNAMITE;
4249 case 0x15a1: /* key (red) */
4250 element = EL_EM_KEY_1;
4253 case 0x15a2: /* key (yellow) */
4254 element = EL_EM_KEY_2;
4257 case 0x15a3: /* key (blue) */
4258 element = EL_EM_KEY_4;
4261 case 0x15a4: /* key (green) */
4262 element = EL_EM_KEY_3;
4265 case 0x15a5: /* key (white) */
4266 element = EL_DC_KEY_WHITE;
4270 element = EL_WALL_SLIPPERY;
4277 case 0x15a8: /* wall (not round) */
4281 case 0x15a9: /* (blue) */
4282 element = EL_CHAR_A;
4285 case 0x15aa: /* (blue) */
4286 element = EL_CHAR_B;
4289 case 0x15ab: /* (blue) */
4290 element = EL_CHAR_C;
4293 case 0x15ac: /* (blue) */
4294 element = EL_CHAR_D;
4297 case 0x15ad: /* (blue) */
4298 element = EL_CHAR_E;
4301 case 0x15ae: /* (blue) */
4302 element = EL_CHAR_F;
4305 case 0x15af: /* (blue) */
4306 element = EL_CHAR_G;
4309 case 0x15b0: /* (blue) */
4310 element = EL_CHAR_H;
4313 case 0x15b1: /* (blue) */
4314 element = EL_CHAR_I;
4317 case 0x15b2: /* (blue) */
4318 element = EL_CHAR_J;
4321 case 0x15b3: /* (blue) */
4322 element = EL_CHAR_K;
4325 case 0x15b4: /* (blue) */
4326 element = EL_CHAR_L;
4329 case 0x15b5: /* (blue) */
4330 element = EL_CHAR_M;
4333 case 0x15b6: /* (blue) */
4334 element = EL_CHAR_N;
4337 case 0x15b7: /* (blue) */
4338 element = EL_CHAR_O;
4341 case 0x15b8: /* (blue) */
4342 element = EL_CHAR_P;
4345 case 0x15b9: /* (blue) */
4346 element = EL_CHAR_Q;
4349 case 0x15ba: /* (blue) */
4350 element = EL_CHAR_R;
4353 case 0x15bb: /* (blue) */
4354 element = EL_CHAR_S;
4357 case 0x15bc: /* (blue) */
4358 element = EL_CHAR_T;
4361 case 0x15bd: /* (blue) */
4362 element = EL_CHAR_U;
4365 case 0x15be: /* (blue) */
4366 element = EL_CHAR_V;
4369 case 0x15bf: /* (blue) */
4370 element = EL_CHAR_W;
4373 case 0x15c0: /* (blue) */
4374 element = EL_CHAR_X;
4377 case 0x15c1: /* (blue) */
4378 element = EL_CHAR_Y;
4381 case 0x15c2: /* (blue) */
4382 element = EL_CHAR_Z;
4385 case 0x15c3: /* (blue) */
4386 element = EL_CHAR_AUMLAUT;
4389 case 0x15c4: /* (blue) */
4390 element = EL_CHAR_OUMLAUT;
4393 case 0x15c5: /* (blue) */
4394 element = EL_CHAR_UUMLAUT;
4397 case 0x15c6: /* (blue) */
4398 element = EL_CHAR_0;
4401 case 0x15c7: /* (blue) */
4402 element = EL_CHAR_1;
4405 case 0x15c8: /* (blue) */
4406 element = EL_CHAR_2;
4409 case 0x15c9: /* (blue) */
4410 element = EL_CHAR_3;
4413 case 0x15ca: /* (blue) */
4414 element = EL_CHAR_4;
4417 case 0x15cb: /* (blue) */
4418 element = EL_CHAR_5;
4421 case 0x15cc: /* (blue) */
4422 element = EL_CHAR_6;
4425 case 0x15cd: /* (blue) */
4426 element = EL_CHAR_7;
4429 case 0x15ce: /* (blue) */
4430 element = EL_CHAR_8;
4433 case 0x15cf: /* (blue) */
4434 element = EL_CHAR_9;
4437 case 0x15d0: /* (blue) */
4438 element = EL_CHAR_PERIOD;
4441 case 0x15d1: /* (blue) */
4442 element = EL_CHAR_EXCLAM;
4445 case 0x15d2: /* (blue) */
4446 element = EL_CHAR_COLON;
4449 case 0x15d3: /* (blue) */
4450 element = EL_CHAR_LESS;
4453 case 0x15d4: /* (blue) */
4454 element = EL_CHAR_GREATER;
4457 case 0x15d5: /* (blue) */
4458 element = EL_CHAR_QUESTION;
4461 case 0x15d6: /* (blue) */
4462 element = EL_CHAR_COPYRIGHT;
4465 case 0x15d7: /* (blue) */
4466 element = EL_CHAR_UP;
4469 case 0x15d8: /* (blue) */
4470 element = EL_CHAR_DOWN;
4473 case 0x15d9: /* (blue) */
4474 element = EL_CHAR_BUTTON;
4477 case 0x15da: /* (blue) */
4478 element = EL_CHAR_PLUS;
4481 case 0x15db: /* (blue) */
4482 element = EL_CHAR_MINUS;
4485 case 0x15dc: /* (blue) */
4486 element = EL_CHAR_APOSTROPHE;
4489 case 0x15dd: /* (blue) */
4490 element = EL_CHAR_PARENLEFT;
4493 case 0x15de: /* (blue) */
4494 element = EL_CHAR_PARENRIGHT;
4497 case 0x15df: /* (green) */
4498 element = EL_CHAR_A;
4501 case 0x15e0: /* (green) */
4502 element = EL_CHAR_B;
4505 case 0x15e1: /* (green) */
4506 element = EL_CHAR_C;
4509 case 0x15e2: /* (green) */
4510 element = EL_CHAR_D;
4513 case 0x15e3: /* (green) */
4514 element = EL_CHAR_E;
4517 case 0x15e4: /* (green) */
4518 element = EL_CHAR_F;
4521 case 0x15e5: /* (green) */
4522 element = EL_CHAR_G;
4525 case 0x15e6: /* (green) */
4526 element = EL_CHAR_H;
4529 case 0x15e7: /* (green) */
4530 element = EL_CHAR_I;
4533 case 0x15e8: /* (green) */
4534 element = EL_CHAR_J;
4537 case 0x15e9: /* (green) */
4538 element = EL_CHAR_K;
4541 case 0x15ea: /* (green) */
4542 element = EL_CHAR_L;
4545 case 0x15eb: /* (green) */
4546 element = EL_CHAR_M;
4549 case 0x15ec: /* (green) */
4550 element = EL_CHAR_N;
4553 case 0x15ed: /* (green) */
4554 element = EL_CHAR_O;
4557 case 0x15ee: /* (green) */
4558 element = EL_CHAR_P;
4561 case 0x15ef: /* (green) */
4562 element = EL_CHAR_Q;
4565 case 0x15f0: /* (green) */
4566 element = EL_CHAR_R;
4569 case 0x15f1: /* (green) */
4570 element = EL_CHAR_S;
4573 case 0x15f2: /* (green) */
4574 element = EL_CHAR_T;
4577 case 0x15f3: /* (green) */
4578 element = EL_CHAR_U;
4581 case 0x15f4: /* (green) */
4582 element = EL_CHAR_V;
4585 case 0x15f5: /* (green) */
4586 element = EL_CHAR_W;
4589 case 0x15f6: /* (green) */
4590 element = EL_CHAR_X;
4593 case 0x15f7: /* (green) */
4594 element = EL_CHAR_Y;
4597 case 0x15f8: /* (green) */
4598 element = EL_CHAR_Z;
4601 case 0x15f9: /* (green) */
4602 element = EL_CHAR_AUMLAUT;
4605 case 0x15fa: /* (green) */
4606 element = EL_CHAR_OUMLAUT;
4609 case 0x15fb: /* (green) */
4610 element = EL_CHAR_UUMLAUT;
4613 case 0x15fc: /* (green) */
4614 element = EL_CHAR_0;
4617 case 0x15fd: /* (green) */
4618 element = EL_CHAR_1;
4621 case 0x15fe: /* (green) */
4622 element = EL_CHAR_2;
4625 case 0x15ff: /* (green) */
4626 element = EL_CHAR_3;
4629 case 0x1600: /* (green) */
4630 element = EL_CHAR_4;
4633 case 0x1601: /* (green) */
4634 element = EL_CHAR_5;
4637 case 0x1602: /* (green) */
4638 element = EL_CHAR_6;
4641 case 0x1603: /* (green) */
4642 element = EL_CHAR_7;
4645 case 0x1604: /* (green) */
4646 element = EL_CHAR_8;
4649 case 0x1605: /* (green) */
4650 element = EL_CHAR_9;
4653 case 0x1606: /* (green) */
4654 element = EL_CHAR_PERIOD;
4657 case 0x1607: /* (green) */
4658 element = EL_CHAR_EXCLAM;
4661 case 0x1608: /* (green) */
4662 element = EL_CHAR_COLON;
4665 case 0x1609: /* (green) */
4666 element = EL_CHAR_LESS;
4669 case 0x160a: /* (green) */
4670 element = EL_CHAR_GREATER;
4673 case 0x160b: /* (green) */
4674 element = EL_CHAR_QUESTION;
4677 case 0x160c: /* (green) */
4678 element = EL_CHAR_COPYRIGHT;
4681 case 0x160d: /* (green) */
4682 element = EL_CHAR_UP;
4685 case 0x160e: /* (green) */
4686 element = EL_CHAR_DOWN;
4689 case 0x160f: /* (green) */
4690 element = EL_CHAR_BUTTON;
4693 case 0x1610: /* (green) */
4694 element = EL_CHAR_PLUS;
4697 case 0x1611: /* (green) */
4698 element = EL_CHAR_MINUS;
4701 case 0x1612: /* (green) */
4702 element = EL_CHAR_APOSTROPHE;
4705 case 0x1613: /* (green) */
4706 element = EL_CHAR_PARENLEFT;
4709 case 0x1614: /* (green) */
4710 element = EL_CHAR_PARENRIGHT;
4713 case 0x1615: /* (blue steel) */
4714 element = EL_STEEL_CHAR_A;
4717 case 0x1616: /* (blue steel) */
4718 element = EL_STEEL_CHAR_B;
4721 case 0x1617: /* (blue steel) */
4722 element = EL_STEEL_CHAR_C;
4725 case 0x1618: /* (blue steel) */
4726 element = EL_STEEL_CHAR_D;
4729 case 0x1619: /* (blue steel) */
4730 element = EL_STEEL_CHAR_E;
4733 case 0x161a: /* (blue steel) */
4734 element = EL_STEEL_CHAR_F;
4737 case 0x161b: /* (blue steel) */
4738 element = EL_STEEL_CHAR_G;
4741 case 0x161c: /* (blue steel) */
4742 element = EL_STEEL_CHAR_H;
4745 case 0x161d: /* (blue steel) */
4746 element = EL_STEEL_CHAR_I;
4749 case 0x161e: /* (blue steel) */
4750 element = EL_STEEL_CHAR_J;
4753 case 0x161f: /* (blue steel) */
4754 element = EL_STEEL_CHAR_K;
4757 case 0x1620: /* (blue steel) */
4758 element = EL_STEEL_CHAR_L;
4761 case 0x1621: /* (blue steel) */
4762 element = EL_STEEL_CHAR_M;
4765 case 0x1622: /* (blue steel) */
4766 element = EL_STEEL_CHAR_N;
4769 case 0x1623: /* (blue steel) */
4770 element = EL_STEEL_CHAR_O;
4773 case 0x1624: /* (blue steel) */
4774 element = EL_STEEL_CHAR_P;
4777 case 0x1625: /* (blue steel) */
4778 element = EL_STEEL_CHAR_Q;
4781 case 0x1626: /* (blue steel) */
4782 element = EL_STEEL_CHAR_R;
4785 case 0x1627: /* (blue steel) */
4786 element = EL_STEEL_CHAR_S;
4789 case 0x1628: /* (blue steel) */
4790 element = EL_STEEL_CHAR_T;
4793 case 0x1629: /* (blue steel) */
4794 element = EL_STEEL_CHAR_U;
4797 case 0x162a: /* (blue steel) */
4798 element = EL_STEEL_CHAR_V;
4801 case 0x162b: /* (blue steel) */
4802 element = EL_STEEL_CHAR_W;
4805 case 0x162c: /* (blue steel) */
4806 element = EL_STEEL_CHAR_X;
4809 case 0x162d: /* (blue steel) */
4810 element = EL_STEEL_CHAR_Y;
4813 case 0x162e: /* (blue steel) */
4814 element = EL_STEEL_CHAR_Z;
4817 case 0x162f: /* (blue steel) */
4818 element = EL_STEEL_CHAR_AUMLAUT;
4821 case 0x1630: /* (blue steel) */
4822 element = EL_STEEL_CHAR_OUMLAUT;
4825 case 0x1631: /* (blue steel) */
4826 element = EL_STEEL_CHAR_UUMLAUT;
4829 case 0x1632: /* (blue steel) */
4830 element = EL_STEEL_CHAR_0;
4833 case 0x1633: /* (blue steel) */
4834 element = EL_STEEL_CHAR_1;
4837 case 0x1634: /* (blue steel) */
4838 element = EL_STEEL_CHAR_2;
4841 case 0x1635: /* (blue steel) */
4842 element = EL_STEEL_CHAR_3;
4845 case 0x1636: /* (blue steel) */
4846 element = EL_STEEL_CHAR_4;
4849 case 0x1637: /* (blue steel) */
4850 element = EL_STEEL_CHAR_5;
4853 case 0x1638: /* (blue steel) */
4854 element = EL_STEEL_CHAR_6;
4857 case 0x1639: /* (blue steel) */
4858 element = EL_STEEL_CHAR_7;
4861 case 0x163a: /* (blue steel) */
4862 element = EL_STEEL_CHAR_8;
4865 case 0x163b: /* (blue steel) */
4866 element = EL_STEEL_CHAR_9;
4869 case 0x163c: /* (blue steel) */
4870 element = EL_STEEL_CHAR_PERIOD;
4873 case 0x163d: /* (blue steel) */
4874 element = EL_STEEL_CHAR_EXCLAM;
4877 case 0x163e: /* (blue steel) */
4878 element = EL_STEEL_CHAR_COLON;
4881 case 0x163f: /* (blue steel) */
4882 element = EL_STEEL_CHAR_LESS;
4885 case 0x1640: /* (blue steel) */
4886 element = EL_STEEL_CHAR_GREATER;
4889 case 0x1641: /* (blue steel) */
4890 element = EL_STEEL_CHAR_QUESTION;
4893 case 0x1642: /* (blue steel) */
4894 element = EL_STEEL_CHAR_COPYRIGHT;
4897 case 0x1643: /* (blue steel) */
4898 element = EL_STEEL_CHAR_UP;
4901 case 0x1644: /* (blue steel) */
4902 element = EL_STEEL_CHAR_DOWN;
4905 case 0x1645: /* (blue steel) */
4906 element = EL_STEEL_CHAR_BUTTON;
4909 case 0x1646: /* (blue steel) */
4910 element = EL_STEEL_CHAR_PLUS;
4913 case 0x1647: /* (blue steel) */
4914 element = EL_STEEL_CHAR_MINUS;
4917 case 0x1648: /* (blue steel) */
4918 element = EL_STEEL_CHAR_APOSTROPHE;
4921 case 0x1649: /* (blue steel) */
4922 element = EL_STEEL_CHAR_PARENLEFT;
4925 case 0x164a: /* (blue steel) */
4926 element = EL_STEEL_CHAR_PARENRIGHT;
4929 case 0x164b: /* (green steel) */
4930 element = EL_STEEL_CHAR_A;
4933 case 0x164c: /* (green steel) */
4934 element = EL_STEEL_CHAR_B;
4937 case 0x164d: /* (green steel) */
4938 element = EL_STEEL_CHAR_C;
4941 case 0x164e: /* (green steel) */
4942 element = EL_STEEL_CHAR_D;
4945 case 0x164f: /* (green steel) */
4946 element = EL_STEEL_CHAR_E;
4949 case 0x1650: /* (green steel) */
4950 element = EL_STEEL_CHAR_F;
4953 case 0x1651: /* (green steel) */
4954 element = EL_STEEL_CHAR_G;
4957 case 0x1652: /* (green steel) */
4958 element = EL_STEEL_CHAR_H;
4961 case 0x1653: /* (green steel) */
4962 element = EL_STEEL_CHAR_I;
4965 case 0x1654: /* (green steel) */
4966 element = EL_STEEL_CHAR_J;
4969 case 0x1655: /* (green steel) */
4970 element = EL_STEEL_CHAR_K;
4973 case 0x1656: /* (green steel) */
4974 element = EL_STEEL_CHAR_L;
4977 case 0x1657: /* (green steel) */
4978 element = EL_STEEL_CHAR_M;
4981 case 0x1658: /* (green steel) */
4982 element = EL_STEEL_CHAR_N;
4985 case 0x1659: /* (green steel) */
4986 element = EL_STEEL_CHAR_O;
4989 case 0x165a: /* (green steel) */
4990 element = EL_STEEL_CHAR_P;
4993 case 0x165b: /* (green steel) */
4994 element = EL_STEEL_CHAR_Q;
4997 case 0x165c: /* (green steel) */
4998 element = EL_STEEL_CHAR_R;
5001 case 0x165d: /* (green steel) */
5002 element = EL_STEEL_CHAR_S;
5005 case 0x165e: /* (green steel) */
5006 element = EL_STEEL_CHAR_T;
5009 case 0x165f: /* (green steel) */
5010 element = EL_STEEL_CHAR_U;
5013 case 0x1660: /* (green steel) */
5014 element = EL_STEEL_CHAR_V;
5017 case 0x1661: /* (green steel) */
5018 element = EL_STEEL_CHAR_W;
5021 case 0x1662: /* (green steel) */
5022 element = EL_STEEL_CHAR_X;
5025 case 0x1663: /* (green steel) */
5026 element = EL_STEEL_CHAR_Y;
5029 case 0x1664: /* (green steel) */
5030 element = EL_STEEL_CHAR_Z;
5033 case 0x1665: /* (green steel) */
5034 element = EL_STEEL_CHAR_AUMLAUT;
5037 case 0x1666: /* (green steel) */
5038 element = EL_STEEL_CHAR_OUMLAUT;
5041 case 0x1667: /* (green steel) */
5042 element = EL_STEEL_CHAR_UUMLAUT;
5045 case 0x1668: /* (green steel) */
5046 element = EL_STEEL_CHAR_0;
5049 case 0x1669: /* (green steel) */
5050 element = EL_STEEL_CHAR_1;
5053 case 0x166a: /* (green steel) */
5054 element = EL_STEEL_CHAR_2;
5057 case 0x166b: /* (green steel) */
5058 element = EL_STEEL_CHAR_3;
5061 case 0x166c: /* (green steel) */
5062 element = EL_STEEL_CHAR_4;
5065 case 0x166d: /* (green steel) */
5066 element = EL_STEEL_CHAR_5;
5069 case 0x166e: /* (green steel) */
5070 element = EL_STEEL_CHAR_6;
5073 case 0x166f: /* (green steel) */
5074 element = EL_STEEL_CHAR_7;
5077 case 0x1670: /* (green steel) */
5078 element = EL_STEEL_CHAR_8;
5081 case 0x1671: /* (green steel) */
5082 element = EL_STEEL_CHAR_9;
5085 case 0x1672: /* (green steel) */
5086 element = EL_STEEL_CHAR_PERIOD;
5089 case 0x1673: /* (green steel) */
5090 element = EL_STEEL_CHAR_EXCLAM;
5093 case 0x1674: /* (green steel) */
5094 element = EL_STEEL_CHAR_COLON;
5097 case 0x1675: /* (green steel) */
5098 element = EL_STEEL_CHAR_LESS;
5101 case 0x1676: /* (green steel) */
5102 element = EL_STEEL_CHAR_GREATER;
5105 case 0x1677: /* (green steel) */
5106 element = EL_STEEL_CHAR_QUESTION;
5109 case 0x1678: /* (green steel) */
5110 element = EL_STEEL_CHAR_COPYRIGHT;
5113 case 0x1679: /* (green steel) */
5114 element = EL_STEEL_CHAR_UP;
5117 case 0x167a: /* (green steel) */
5118 element = EL_STEEL_CHAR_DOWN;
5121 case 0x167b: /* (green steel) */
5122 element = EL_STEEL_CHAR_BUTTON;
5125 case 0x167c: /* (green steel) */
5126 element = EL_STEEL_CHAR_PLUS;
5129 case 0x167d: /* (green steel) */
5130 element = EL_STEEL_CHAR_MINUS;
5133 case 0x167e: /* (green steel) */
5134 element = EL_STEEL_CHAR_APOSTROPHE;
5137 case 0x167f: /* (green steel) */
5138 element = EL_STEEL_CHAR_PARENLEFT;
5141 case 0x1680: /* (green steel) */
5142 element = EL_STEEL_CHAR_PARENRIGHT;
5145 case 0x1681: /* gate (red) */
5146 element = EL_EM_GATE_1;
5149 case 0x1682: /* secret gate (red) */
5150 element = EL_GATE_1_GRAY;
5153 case 0x1683: /* gate (yellow) */
5154 element = EL_EM_GATE_2;
5157 case 0x1684: /* secret gate (yellow) */
5158 element = EL_GATE_2_GRAY;
5161 case 0x1685: /* gate (blue) */
5162 element = EL_EM_GATE_4;
5165 case 0x1686: /* secret gate (blue) */
5166 element = EL_GATE_4_GRAY;
5169 case 0x1687: /* gate (green) */
5170 element = EL_EM_GATE_3;
5173 case 0x1688: /* secret gate (green) */
5174 element = EL_GATE_3_GRAY;
5177 case 0x1689: /* gate (white) */
5178 element = EL_DC_GATE_WHITE;
5181 case 0x168a: /* secret gate (white) */
5182 element = EL_DC_GATE_WHITE_GRAY;
5185 case 0x168b: /* secret gate (no key) */
5186 element = EL_DC_GATE_FAKE_GRAY;
5190 element = EL_ROBOT_WHEEL;
5194 element = EL_DC_TIMEGATE_SWITCH;
5198 element = EL_ACID_POOL_BOTTOM;
5202 element = EL_ACID_POOL_TOPLEFT;
5206 element = EL_ACID_POOL_TOPRIGHT;
5210 element = EL_ACID_POOL_BOTTOMLEFT;
5214 element = EL_ACID_POOL_BOTTOMRIGHT;
5218 element = EL_STEELWALL;
5222 element = EL_STEELWALL_SLIPPERY;
5225 case 0x1695: /* steel wall (not round) */
5226 element = EL_STEELWALL;
5229 case 0x1696: /* steel wall (left) */
5230 element = EL_DC_STEELWALL_1_LEFT;
5233 case 0x1697: /* steel wall (bottom) */
5234 element = EL_DC_STEELWALL_1_BOTTOM;
5237 case 0x1698: /* steel wall (right) */
5238 element = EL_DC_STEELWALL_1_RIGHT;
5241 case 0x1699: /* steel wall (top) */
5242 element = EL_DC_STEELWALL_1_TOP;
5245 case 0x169a: /* steel wall (left/bottom) */
5246 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5249 case 0x169b: /* steel wall (right/bottom) */
5250 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5253 case 0x169c: /* steel wall (right/top) */
5254 element = EL_DC_STEELWALL_1_TOPRIGHT;
5257 case 0x169d: /* steel wall (left/top) */
5258 element = EL_DC_STEELWALL_1_TOPLEFT;
5261 case 0x169e: /* steel wall (right/bottom small) */
5262 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5265 case 0x169f: /* steel wall (left/bottom small) */
5266 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5269 case 0x16a0: /* steel wall (right/top small) */
5270 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5273 case 0x16a1: /* steel wall (left/top small) */
5274 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5277 case 0x16a2: /* steel wall (left/right) */
5278 element = EL_DC_STEELWALL_1_VERTICAL;
5281 case 0x16a3: /* steel wall (top/bottom) */
5282 element = EL_DC_STEELWALL_1_HORIZONTAL;
5285 case 0x16a4: /* steel wall 2 (left end) */
5286 element = EL_DC_STEELWALL_2_LEFT;
5289 case 0x16a5: /* steel wall 2 (right end) */
5290 element = EL_DC_STEELWALL_2_RIGHT;
5293 case 0x16a6: /* steel wall 2 (top end) */
5294 element = EL_DC_STEELWALL_2_TOP;
5297 case 0x16a7: /* steel wall 2 (bottom end) */
5298 element = EL_DC_STEELWALL_2_BOTTOM;
5301 case 0x16a8: /* steel wall 2 (left/right) */
5302 element = EL_DC_STEELWALL_2_HORIZONTAL;
5305 case 0x16a9: /* steel wall 2 (up/down) */
5306 element = EL_DC_STEELWALL_2_VERTICAL;
5309 case 0x16aa: /* steel wall 2 (mid) */
5310 element = EL_DC_STEELWALL_2_MIDDLE;
5314 element = EL_SIGN_EXCLAMATION;
5318 element = EL_SIGN_RADIOACTIVITY;
5322 element = EL_SIGN_STOP;
5326 element = EL_SIGN_WHEELCHAIR;
5330 element = EL_SIGN_PARKING;
5334 element = EL_SIGN_NO_ENTRY;
5338 element = EL_SIGN_HEART;
5342 element = EL_SIGN_GIVE_WAY;
5346 element = EL_SIGN_ENTRY_FORBIDDEN;
5350 element = EL_SIGN_EMERGENCY_EXIT;
5354 element = EL_SIGN_YIN_YANG;
5358 element = EL_WALL_EMERALD;
5362 element = EL_WALL_DIAMOND;
5366 element = EL_WALL_PEARL;
5370 element = EL_WALL_CRYSTAL;
5374 element = EL_INVISIBLE_WALL;
5378 element = EL_INVISIBLE_STEELWALL;
5381 /* 0x16bc - 0x16cb: */
5382 /* EL_INVISIBLE_SAND */
5385 element = EL_LIGHT_SWITCH;
5389 element = EL_ENVELOPE_1;
5393 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5394 element = EL_DIAMOND;
5395 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5396 element = EL_EMERALD;
5397 else if (element >= 0x157c && element <= 0x158b)
5399 else if (element >= 0x1590 && element <= 0x159f)
5400 element = EL_DC_LANDMINE;
5401 else if (element >= 0x16bc && element <= 0x16cb)
5402 element = EL_INVISIBLE_SAND;
5405 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5406 element = EL_UNKNOWN;
5411 return getMappedElement(element);
5414 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5417 byte header[DC_LEVEL_HEADER_SIZE];
5419 int envelope_header_pos = 62;
5420 int envelope_content_pos = 94;
5421 int level_name_pos = 251;
5422 int level_author_pos = 292;
5423 int envelope_header_len;
5424 int envelope_content_len;
5426 int level_author_len;
5428 int num_yamyam_contents;
5431 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5433 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5435 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5437 header[i * 2 + 0] = header_word >> 8;
5438 header[i * 2 + 1] = header_word & 0xff;
5441 /* read some values from level header to check level decoding integrity */
5442 fieldx = header[6] | (header[7] << 8);
5443 fieldy = header[8] | (header[9] << 8);
5444 num_yamyam_contents = header[60] | (header[61] << 8);
5446 /* do some simple sanity checks to ensure that level was correctly decoded */
5447 if (fieldx < 1 || fieldx > 256 ||
5448 fieldy < 1 || fieldy > 256 ||
5449 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5451 level->no_valid_file = TRUE;
5453 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5458 /* maximum envelope header size is 31 bytes */
5459 envelope_header_len = header[envelope_header_pos];
5460 /* maximum envelope content size is 110 (156?) bytes */
5461 envelope_content_len = header[envelope_content_pos];
5463 /* maximum level title size is 40 bytes */
5464 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5465 /* maximum level author size is 30 (51?) bytes */
5466 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5470 for (i = 0; i < envelope_header_len; i++)
5471 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5472 level->envelope[0].text[envelope_size++] =
5473 header[envelope_header_pos + 1 + i];
5475 if (envelope_header_len > 0 && envelope_content_len > 0)
5477 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5478 level->envelope[0].text[envelope_size++] = '\n';
5479 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5480 level->envelope[0].text[envelope_size++] = '\n';
5483 for (i = 0; i < envelope_content_len; i++)
5484 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5485 level->envelope[0].text[envelope_size++] =
5486 header[envelope_content_pos + 1 + i];
5488 level->envelope[0].text[envelope_size] = '\0';
5490 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5491 level->envelope[0].ysize = 10;
5492 level->envelope[0].autowrap = TRUE;
5493 level->envelope[0].centered = TRUE;
5495 for (i = 0; i < level_name_len; i++)
5496 level->name[i] = header[level_name_pos + 1 + i];
5497 level->name[level_name_len] = '\0';
5499 for (i = 0; i < level_author_len; i++)
5500 level->author[i] = header[level_author_pos + 1 + i];
5501 level->author[level_author_len] = '\0';
5503 num_yamyam_contents = header[60] | (header[61] << 8);
5504 level->num_yamyam_contents =
5505 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5507 for (i = 0; i < num_yamyam_contents; i++)
5509 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5511 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5512 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5514 if (i < MAX_ELEMENT_CONTENTS)
5515 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5519 fieldx = header[6] | (header[7] << 8);
5520 fieldy = header[8] | (header[9] << 8);
5521 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5522 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5524 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5526 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5527 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5529 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5530 level->field[x][y] = getMappedElement_DC(element_dc);
5533 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5534 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5535 level->field[x][y] = EL_PLAYER_1;
5537 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5538 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5539 level->field[x][y] = EL_PLAYER_2;
5541 level->gems_needed = header[18] | (header[19] << 8);
5543 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5544 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5545 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5546 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5547 level->score[SC_NUT] = header[28] | (header[29] << 8);
5548 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5549 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5550 level->score[SC_BUG] = header[34] | (header[35] << 8);
5551 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5552 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5553 level->score[SC_KEY] = header[40] | (header[41] << 8);
5554 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5556 level->time = header[44] | (header[45] << 8);
5558 level->amoeba_speed = header[46] | (header[47] << 8);
5559 level->time_light = header[48] | (header[49] << 8);
5560 level->time_timegate = header[50] | (header[51] << 8);
5561 level->time_wheel = header[52] | (header[53] << 8);
5562 level->time_magic_wall = header[54] | (header[55] << 8);
5563 level->extra_time = header[56] | (header[57] << 8);
5564 level->shield_normal_time = header[58] | (header[59] << 8);
5566 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5567 can slip down from flat walls, like normal walls and steel walls */
5568 level->em_slippery_gems = TRUE;
5571 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5572 struct LevelFileInfo *level_file_info,
5573 boolean level_info_only)
5575 char *filename = level_file_info->filename;
5577 int num_magic_bytes = 8;
5578 char magic_bytes[num_magic_bytes + 1];
5579 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5581 if (!(file = openFile(filename, MODE_READ)))
5583 level->no_valid_file = TRUE;
5585 if (!level_info_only)
5586 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5591 // fseek(file, 0x0000, SEEK_SET);
5593 if (level_file_info->packed)
5595 /* read "magic bytes" from start of file */
5596 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5597 magic_bytes[0] = '\0';
5599 /* check "magic bytes" for correct file format */
5600 if (!strPrefix(magic_bytes, "DC2"))
5602 level->no_valid_file = TRUE;
5604 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5610 if (strPrefix(magic_bytes, "DC2Win95") ||
5611 strPrefix(magic_bytes, "DC2Win98"))
5613 int position_first_level = 0x00fa;
5614 int extra_bytes = 4;
5617 /* advance file stream to first level inside the level package */
5618 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5620 /* each block of level data is followed by block of non-level data */
5621 num_levels_to_skip *= 2;
5623 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5624 while (num_levels_to_skip >= 0)
5626 /* advance file stream to next level inside the level package */
5627 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5629 level->no_valid_file = TRUE;
5631 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5637 /* skip apparently unused extra bytes following each level */
5638 ReadUnusedBytesFromFile(file, extra_bytes);
5640 /* read size of next level in level package */
5641 skip_bytes = getFile32BitLE(file);
5643 num_levels_to_skip--;
5648 level->no_valid_file = TRUE;
5650 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5657 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5663 /* ------------------------------------------------------------------------- */
5664 /* functions for loading SB level */
5665 /* ------------------------------------------------------------------------- */
5667 int getMappedElement_SB(int element_ascii, boolean use_ces)
5675 sb_element_mapping[] =
5677 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5678 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5679 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5680 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5681 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5682 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5683 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5684 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5691 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5692 if (element_ascii == sb_element_mapping[i].ascii)
5693 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5695 return EL_UNDEFINED;
5698 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5699 struct LevelFileInfo *level_file_info,
5700 boolean level_info_only)
5702 char *filename = level_file_info->filename;
5703 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5704 char last_comment[MAX_LINE_LEN];
5705 char level_name[MAX_LINE_LEN];
5708 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5709 boolean read_continued_line = FALSE;
5710 boolean reading_playfield = FALSE;
5711 boolean got_valid_playfield_line = FALSE;
5712 boolean invalid_playfield_char = FALSE;
5713 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5714 int file_level_nr = 0;
5716 int x = 0, y = 0; /* initialized to make compilers happy */
5718 last_comment[0] = '\0';
5719 level_name[0] = '\0';
5721 if (!(file = openFile(filename, MODE_READ)))
5723 level->no_valid_file = TRUE;
5725 if (!level_info_only)
5726 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5731 while (!checkEndOfFile(file))
5733 /* level successfully read, but next level may follow here */
5734 if (!got_valid_playfield_line && reading_playfield)
5736 /* read playfield from single level file -- skip remaining file */
5737 if (!level_file_info->packed)
5740 if (file_level_nr >= num_levels_to_skip)
5745 last_comment[0] = '\0';
5746 level_name[0] = '\0';
5748 reading_playfield = FALSE;
5751 got_valid_playfield_line = FALSE;
5753 /* read next line of input file */
5754 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5757 /* check if line was completely read and is terminated by line break */
5758 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5761 /* cut trailing line break (this can be newline and/or carriage return) */
5762 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5763 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5766 /* copy raw input line for later use (mainly debugging output) */
5767 strcpy(line_raw, line);
5769 if (read_continued_line)
5771 /* append new line to existing line, if there is enough space */
5772 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5773 strcat(previous_line, line_ptr);
5775 strcpy(line, previous_line); /* copy storage buffer to line */
5777 read_continued_line = FALSE;
5780 /* if the last character is '\', continue at next line */
5781 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5783 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5784 strcpy(previous_line, line); /* copy line to storage buffer */
5786 read_continued_line = TRUE;
5791 /* skip empty lines */
5792 if (line[0] == '\0')
5795 /* extract comment text from comment line */
5798 for (line_ptr = line; *line_ptr; line_ptr++)
5799 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5802 strcpy(last_comment, line_ptr);
5807 /* extract level title text from line containing level title */
5808 if (line[0] == '\'')
5810 strcpy(level_name, &line[1]);
5812 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5813 level_name[strlen(level_name) - 1] = '\0';
5818 /* skip lines containing only spaces (or empty lines) */
5819 for (line_ptr = line; *line_ptr; line_ptr++)
5820 if (*line_ptr != ' ')
5822 if (*line_ptr == '\0')
5825 /* at this point, we have found a line containing part of a playfield */
5827 got_valid_playfield_line = TRUE;
5829 if (!reading_playfield)
5831 reading_playfield = TRUE;
5832 invalid_playfield_char = FALSE;
5834 for (x = 0; x < MAX_LEV_FIELDX; x++)
5835 for (y = 0; y < MAX_LEV_FIELDY; y++)
5836 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5841 /* start with topmost tile row */
5845 /* skip playfield line if larger row than allowed */
5846 if (y >= MAX_LEV_FIELDY)
5849 /* start with leftmost tile column */
5852 /* read playfield elements from line */
5853 for (line_ptr = line; *line_ptr; line_ptr++)
5855 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5857 /* stop parsing playfield line if larger column than allowed */
5858 if (x >= MAX_LEV_FIELDX)
5861 if (mapped_sb_element == EL_UNDEFINED)
5863 invalid_playfield_char = TRUE;
5868 level->field[x][y] = mapped_sb_element;
5870 /* continue with next tile column */
5873 level->fieldx = MAX(x, level->fieldx);
5876 if (invalid_playfield_char)
5878 /* if first playfield line, treat invalid lines as comment lines */
5880 reading_playfield = FALSE;
5885 /* continue with next tile row */
5893 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
5894 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
5896 if (!reading_playfield)
5898 level->no_valid_file = TRUE;
5900 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5905 if (*level_name != '\0')
5907 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
5908 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5910 else if (*last_comment != '\0')
5912 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
5913 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5917 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
5920 /* set all empty fields beyond the border walls to invisible steel wall */
5921 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5923 if ((x == 0 || x == level->fieldx - 1 ||
5924 y == 0 || y == level->fieldy - 1) &&
5925 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
5926 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
5927 level->field, level->fieldx, level->fieldy);
5930 /* set special level settings for Sokoban levels */
5933 level->use_step_counter = TRUE;
5935 if (load_xsb_to_ces)
5937 /* special global settings can now be set in level template */
5939 level->use_custom_template = TRUE;
5944 /* ------------------------------------------------------------------------- */
5945 /* functions for handling native levels */
5946 /* ------------------------------------------------------------------------- */
5948 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
5949 struct LevelFileInfo *level_file_info,
5950 boolean level_info_only)
5952 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
5953 level->no_valid_file = TRUE;
5956 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
5957 struct LevelFileInfo *level_file_info,
5958 boolean level_info_only)
5962 /* determine position of requested level inside level package */
5963 if (level_file_info->packed)
5964 pos = level_file_info->nr - leveldir_current->first_level;
5966 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
5967 level->no_valid_file = TRUE;
5970 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
5971 struct LevelFileInfo *level_file_info,
5972 boolean level_info_only)
5974 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
5975 level->no_valid_file = TRUE;
5978 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
5980 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5981 CopyNativeLevel_RND_to_EM(level);
5982 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5983 CopyNativeLevel_RND_to_SP(level);
5984 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
5985 CopyNativeLevel_RND_to_MM(level);
5988 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
5990 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5991 CopyNativeLevel_EM_to_RND(level);
5992 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5993 CopyNativeLevel_SP_to_RND(level);
5994 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
5995 CopyNativeLevel_MM_to_RND(level);
5998 void SaveNativeLevel(struct LevelInfo *level)
6000 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6002 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6003 char *filename = getLevelFilenameFromBasename(basename);
6005 CopyNativeLevel_RND_to_SP(level);
6006 CopyNativeTape_RND_to_SP(level);
6008 SaveNativeLevel_SP(filename);
6013 /* ------------------------------------------------------------------------- */
6014 /* functions for loading generic level */
6015 /* ------------------------------------------------------------------------- */
6017 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6018 struct LevelFileInfo *level_file_info,
6019 boolean level_info_only)
6021 /* always start with reliable default values */
6022 setLevelInfoToDefaults(level, level_info_only, TRUE);
6024 switch (level_file_info->type)
6026 case LEVEL_FILE_TYPE_RND:
6027 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6030 case LEVEL_FILE_TYPE_EM:
6031 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6032 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6035 case LEVEL_FILE_TYPE_SP:
6036 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6037 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6040 case LEVEL_FILE_TYPE_MM:
6041 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6042 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6045 case LEVEL_FILE_TYPE_DC:
6046 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6049 case LEVEL_FILE_TYPE_SB:
6050 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6054 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6058 /* if level file is invalid, restore level structure to default values */
6059 if (level->no_valid_file)
6060 setLevelInfoToDefaults(level, level_info_only, FALSE);
6062 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6063 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6065 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6066 CopyNativeLevel_Native_to_RND(level);
6069 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6071 static struct LevelFileInfo level_file_info;
6073 /* always start with reliable default values */
6074 setFileInfoToDefaults(&level_file_info);
6076 level_file_info.nr = 0; /* unknown level number */
6077 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
6078 level_file_info.filename = filename;
6080 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6083 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
6087 if (leveldir_current == NULL) /* only when dumping level */
6090 /* all engine modifications also valid for levels which use latest engine */
6091 if (level->game_version < VERSION_IDENT(3,2,0,5))
6093 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6094 level->score[SC_TIME_BONUS] /= 10;
6097 if (leveldir_current->latest_engine)
6099 /* ---------- use latest game engine ----------------------------------- */
6101 /* For all levels which are forced to use the latest game engine version
6102 (normally all but user contributed, private and undefined levels), set
6103 the game engine version to the actual version; this allows for actual
6104 corrections in the game engine to take effect for existing, converted
6105 levels (from "classic" or other existing games) to make the emulation
6106 of the corresponding game more accurate, while (hopefully) not breaking
6107 existing levels created from other players. */
6109 level->game_version = GAME_VERSION_ACTUAL;
6111 /* Set special EM style gems behaviour: EM style gems slip down from
6112 normal, steel and growing wall. As this is a more fundamental change,
6113 it seems better to set the default behaviour to "off" (as it is more
6114 natural) and make it configurable in the level editor (as a property
6115 of gem style elements). Already existing converted levels (neither
6116 private nor contributed levels) are changed to the new behaviour. */
6118 if (level->file_version < FILE_VERSION_2_0)
6119 level->em_slippery_gems = TRUE;
6124 /* ---------- use game engine the level was created with ----------------- */
6126 /* For all levels which are not forced to use the latest game engine
6127 version (normally user contributed, private and undefined levels),
6128 use the version of the game engine the levels were created for.
6130 Since 2.0.1, the game engine version is now directly stored
6131 in the level file (chunk "VERS"), so there is no need anymore
6132 to set the game version from the file version (except for old,
6133 pre-2.0 levels, where the game version is still taken from the
6134 file format version used to store the level -- see above). */
6136 /* player was faster than enemies in 1.0.0 and before */
6137 if (level->file_version == FILE_VERSION_1_0)
6138 for (i = 0; i < MAX_PLAYERS; i++)
6139 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6141 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6142 if (level->game_version == VERSION_IDENT(2,0,1,0))
6143 level->em_slippery_gems = TRUE;
6145 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6146 if (level->game_version < VERSION_IDENT(2,2,0,0))
6147 level->use_spring_bug = TRUE;
6149 if (level->game_version < VERSION_IDENT(3,2,0,5))
6151 /* time orb caused limited time in endless time levels before 3.2.0-5 */
6152 level->use_time_orb_bug = TRUE;
6154 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6155 level->block_snap_field = FALSE;
6157 /* extra time score was same value as time left score before 3.2.0-5 */
6158 level->extra_time_score = level->score[SC_TIME_BONUS];
6161 if (level->game_version < VERSION_IDENT(3,2,0,7))
6163 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6164 level->continuous_snapping = FALSE;
6167 /* only few elements were able to actively move into acid before 3.1.0 */
6168 /* trigger settings did not exist before 3.1.0; set to default "any" */
6169 if (level->game_version < VERSION_IDENT(3,1,0,0))
6171 /* correct "can move into acid" settings (all zero in old levels) */
6173 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6174 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6176 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6177 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6178 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6179 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6181 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6182 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6184 /* correct trigger settings (stored as zero == "none" in old levels) */
6186 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6188 int element = EL_CUSTOM_START + i;
6189 struct ElementInfo *ei = &element_info[element];
6191 for (j = 0; j < ei->num_change_pages; j++)
6193 struct ElementChangeInfo *change = &ei->change_page[j];
6195 change->trigger_player = CH_PLAYER_ANY;
6196 change->trigger_page = CH_PAGE_ANY;
6201 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6203 int element = EL_CUSTOM_256;
6204 struct ElementInfo *ei = &element_info[element];
6205 struct ElementChangeInfo *change = &ei->change_page[0];
6207 /* This is needed to fix a problem that was caused by a bugfix in function
6208 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6209 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6210 not replace walkable elements, but instead just placed the player on it,
6211 without placing the Sokoban field under the player). Unfortunately, this
6212 breaks "Snake Bite" style levels when the snake is halfway through a door
6213 that just closes (the snake head is still alive and can be moved in this
6214 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6215 player (without Sokoban element) which then gets killed as designed). */
6217 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6218 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6219 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6220 change->target_element = EL_PLAYER_1;
6223 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6224 if (level->game_version < VERSION_IDENT(3,2,5,0))
6226 /* This is needed to fix a problem that was caused by a bugfix in function
6227 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6228 corrects the behaviour when a custom element changes to another custom
6229 element with a higher element number that has change actions defined.
6230 Normally, only one change per frame is allowed for custom elements.
6231 Therefore, it is checked if a custom element already changed in the
6232 current frame; if it did, subsequent changes are suppressed.
6233 Unfortunately, this is only checked for element changes, but not for
6234 change actions, which are still executed. As the function above loops
6235 through all custom elements from lower to higher, an element change
6236 resulting in a lower CE number won't be checked again, while a target
6237 element with a higher number will also be checked, and potential change
6238 actions will get executed for this CE, too (which is wrong), while
6239 further changes are ignored (which is correct). As this bugfix breaks
6240 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6241 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6242 behaviour for existing levels and tapes that make use of this bug */
6244 level->use_action_after_change_bug = TRUE;
6247 /* not centering level after relocating player was default only in 3.2.3 */
6248 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6249 level->shifted_relocation = TRUE;
6251 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6252 if (level->game_version < VERSION_IDENT(3,2,6,0))
6253 level->em_explodes_by_fire = TRUE;
6256 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6260 /* map custom element change events that have changed in newer versions
6261 (these following values were accidentally changed in version 3.0.1)
6262 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6263 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6265 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6267 int element = EL_CUSTOM_START + i;
6269 /* order of checking and copying events to be mapped is important */
6270 /* (do not change the start and end value -- they are constant) */
6271 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6273 if (HAS_CHANGE_EVENT(element, j - 2))
6275 SET_CHANGE_EVENT(element, j - 2, FALSE);
6276 SET_CHANGE_EVENT(element, j, TRUE);
6280 /* order of checking and copying events to be mapped is important */
6281 /* (do not change the start and end value -- they are constant) */
6282 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6284 if (HAS_CHANGE_EVENT(element, j - 1))
6286 SET_CHANGE_EVENT(element, j - 1, FALSE);
6287 SET_CHANGE_EVENT(element, j, TRUE);
6293 /* initialize "can_change" field for old levels with only one change page */
6294 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6296 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6298 int element = EL_CUSTOM_START + i;
6300 if (CAN_CHANGE(element))
6301 element_info[element].change->can_change = TRUE;
6305 /* correct custom element values (for old levels without these options) */
6306 if (level->game_version < VERSION_IDENT(3,1,1,0))
6308 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6310 int element = EL_CUSTOM_START + i;
6311 struct ElementInfo *ei = &element_info[element];
6313 if (ei->access_direction == MV_NO_DIRECTION)
6314 ei->access_direction = MV_ALL_DIRECTIONS;
6318 /* correct custom element values (fix invalid values for all versions) */
6321 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6323 int element = EL_CUSTOM_START + i;
6324 struct ElementInfo *ei = &element_info[element];
6326 for (j = 0; j < ei->num_change_pages; j++)
6328 struct ElementChangeInfo *change = &ei->change_page[j];
6330 if (change->trigger_player == CH_PLAYER_NONE)
6331 change->trigger_player = CH_PLAYER_ANY;
6333 if (change->trigger_side == CH_SIDE_NONE)
6334 change->trigger_side = CH_SIDE_ANY;
6339 /* initialize "can_explode" field for old levels which did not store this */
6340 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6341 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6343 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6345 int element = EL_CUSTOM_START + i;
6347 if (EXPLODES_1X1_OLD(element))
6348 element_info[element].explosion_type = EXPLODES_1X1;
6350 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6351 EXPLODES_SMASHED(element) ||
6352 EXPLODES_IMPACT(element)));
6356 /* correct previously hard-coded move delay values for maze runner style */
6357 if (level->game_version < VERSION_IDENT(3,1,1,0))
6359 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6361 int element = EL_CUSTOM_START + i;
6363 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6365 /* previously hard-coded and therefore ignored */
6366 element_info[element].move_delay_fixed = 9;
6367 element_info[element].move_delay_random = 0;
6372 /* map elements that have changed in newer versions */
6373 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6374 level->game_version);
6375 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6376 for (x = 0; x < 3; x++)
6377 for (y = 0; y < 3; y++)
6378 level->yamyam_content[i].e[x][y] =
6379 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6380 level->game_version);
6382 /* initialize element properties for level editor etc. */
6383 InitElementPropertiesEngine(level->game_version);
6384 InitElementPropertiesAfterLoading(level->game_version);
6385 InitElementPropertiesGfxElement();
6388 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6392 /* map elements that have changed in newer versions */
6393 for (y = 0; y < level->fieldy; y++)
6394 for (x = 0; x < level->fieldx; x++)
6395 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6396 level->game_version);
6398 /* clear unused playfield data (nicer if level gets resized in editor) */
6399 for (x = 0; x < MAX_LEV_FIELDX; x++)
6400 for (y = 0; y < MAX_LEV_FIELDY; y++)
6401 if (x >= level->fieldx || y >= level->fieldy)
6402 level->field[x][y] = EL_EMPTY;
6404 /* copy elements to runtime playfield array */
6405 for (x = 0; x < MAX_LEV_FIELDX; x++)
6406 for (y = 0; y < MAX_LEV_FIELDY; y++)
6407 Feld[x][y] = level->field[x][y];
6409 /* initialize level size variables for faster access */
6410 lev_fieldx = level->fieldx;
6411 lev_fieldy = level->fieldy;
6413 /* determine border element for this level */
6414 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6415 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6420 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6422 struct LevelFileInfo *level_file_info = &level->file_info;
6424 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6425 CopyNativeLevel_RND_to_Native(level);
6428 void LoadLevelTemplate(int nr)
6432 setLevelFileInfo(&level_template.file_info, nr);
6433 filename = level_template.file_info.filename;
6435 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6437 LoadLevel_InitVersion(&level_template, filename);
6438 LoadLevel_InitElements(&level_template, filename);
6440 ActivateLevelTemplate();
6443 void LoadLevel(int nr)
6447 setLevelFileInfo(&level.file_info, nr);
6448 filename = level.file_info.filename;
6450 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6452 if (level.use_custom_template)
6453 LoadLevelTemplate(-1);
6455 LoadLevel_InitVersion(&level, filename);
6456 LoadLevel_InitElements(&level, filename);
6457 LoadLevel_InitPlayfield(&level, filename);
6459 LoadLevel_InitNativeEngines(&level, filename);
6462 void LoadLevelInfoOnly(int nr)
6464 setLevelFileInfo(&level.file_info, nr);
6466 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6469 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6473 chunk_size += putFileVersion(file, level->file_version);
6474 chunk_size += putFileVersion(file, level->game_version);
6479 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6483 chunk_size += putFile16BitBE(file, level->creation_date.year);
6484 chunk_size += putFile8Bit(file, level->creation_date.month);
6485 chunk_size += putFile8Bit(file, level->creation_date.day);
6490 #if ENABLE_HISTORIC_CHUNKS
6491 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6495 putFile8Bit(file, level->fieldx);
6496 putFile8Bit(file, level->fieldy);
6498 putFile16BitBE(file, level->time);
6499 putFile16BitBE(file, level->gems_needed);
6501 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6502 putFile8Bit(file, level->name[i]);
6504 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6505 putFile8Bit(file, level->score[i]);
6507 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6508 for (y = 0; y < 3; y++)
6509 for (x = 0; x < 3; x++)
6510 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6511 level->yamyam_content[i].e[x][y]));
6512 putFile8Bit(file, level->amoeba_speed);
6513 putFile8Bit(file, level->time_magic_wall);
6514 putFile8Bit(file, level->time_wheel);
6515 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6516 level->amoeba_content));
6517 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6518 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6519 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6520 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6522 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6524 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6525 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6526 putFile32BitBE(file, level->can_move_into_acid_bits);
6527 putFile8Bit(file, level->dont_collide_with_bits);
6529 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6530 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6532 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6533 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6534 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6536 putFile8Bit(file, level->game_engine_type);
6538 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6542 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6547 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6548 chunk_size += putFile8Bit(file, level->name[i]);
6553 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6558 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6559 chunk_size += putFile8Bit(file, level->author[i]);
6564 #if ENABLE_HISTORIC_CHUNKS
6565 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6570 for (y = 0; y < level->fieldy; y++)
6571 for (x = 0; x < level->fieldx; x++)
6572 if (level->encoding_16bit_field)
6573 chunk_size += putFile16BitBE(file, level->field[x][y]);
6575 chunk_size += putFile8Bit(file, level->field[x][y]);
6581 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6586 for (y = 0; y < level->fieldy; y++)
6587 for (x = 0; x < level->fieldx; x++)
6588 chunk_size += putFile16BitBE(file, level->field[x][y]);
6593 #if ENABLE_HISTORIC_CHUNKS
6594 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6598 putFile8Bit(file, EL_YAMYAM);
6599 putFile8Bit(file, level->num_yamyam_contents);
6600 putFile8Bit(file, 0);
6601 putFile8Bit(file, 0);
6603 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6604 for (y = 0; y < 3; y++)
6605 for (x = 0; x < 3; x++)
6606 if (level->encoding_16bit_field)
6607 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6609 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6613 #if ENABLE_HISTORIC_CHUNKS
6614 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6617 int num_contents, content_xsize, content_ysize;
6618 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6620 if (element == EL_YAMYAM)
6622 num_contents = level->num_yamyam_contents;
6626 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6627 for (y = 0; y < 3; y++)
6628 for (x = 0; x < 3; x++)
6629 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6631 else if (element == EL_BD_AMOEBA)
6637 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6638 for (y = 0; y < 3; y++)
6639 for (x = 0; x < 3; x++)
6640 content_array[i][x][y] = EL_EMPTY;
6641 content_array[0][0][0] = level->amoeba_content;
6645 /* chunk header already written -- write empty chunk data */
6646 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6648 Error(ERR_WARN, "cannot save content for element '%d'", element);
6652 putFile16BitBE(file, element);
6653 putFile8Bit(file, num_contents);
6654 putFile8Bit(file, content_xsize);
6655 putFile8Bit(file, content_ysize);
6657 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6659 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6660 for (y = 0; y < 3; y++)
6661 for (x = 0; x < 3; x++)
6662 putFile16BitBE(file, content_array[i][x][y]);
6666 #if ENABLE_HISTORIC_CHUNKS
6667 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6669 int envelope_nr = element - EL_ENVELOPE_1;
6670 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6674 chunk_size += putFile16BitBE(file, element);
6675 chunk_size += putFile16BitBE(file, envelope_len);
6676 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6677 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6679 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6680 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6682 for (i = 0; i < envelope_len; i++)
6683 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6689 #if ENABLE_HISTORIC_CHUNKS
6690 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6691 int num_changed_custom_elements)
6695 putFile16BitBE(file, num_changed_custom_elements);
6697 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6699 int element = EL_CUSTOM_START + i;
6701 struct ElementInfo *ei = &element_info[element];
6703 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6705 if (check < num_changed_custom_elements)
6707 putFile16BitBE(file, element);
6708 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6715 if (check != num_changed_custom_elements) /* should not happen */
6716 Error(ERR_WARN, "inconsistent number of custom element properties");
6720 #if ENABLE_HISTORIC_CHUNKS
6721 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6722 int num_changed_custom_elements)
6726 putFile16BitBE(file, num_changed_custom_elements);
6728 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6730 int element = EL_CUSTOM_START + i;
6732 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6734 if (check < num_changed_custom_elements)
6736 putFile16BitBE(file, element);
6737 putFile16BitBE(file, element_info[element].change->target_element);
6744 if (check != num_changed_custom_elements) /* should not happen */
6745 Error(ERR_WARN, "inconsistent number of custom target elements");
6749 #if ENABLE_HISTORIC_CHUNKS
6750 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6751 int num_changed_custom_elements)
6753 int i, j, x, y, check = 0;
6755 putFile16BitBE(file, num_changed_custom_elements);
6757 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6759 int element = EL_CUSTOM_START + i;
6760 struct ElementInfo *ei = &element_info[element];
6762 if (ei->modified_settings)
6764 if (check < num_changed_custom_elements)
6766 putFile16BitBE(file, element);
6768 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6769 putFile8Bit(file, ei->description[j]);
6771 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6773 /* some free bytes for future properties and padding */
6774 WriteUnusedBytesToFile(file, 7);
6776 putFile8Bit(file, ei->use_gfx_element);
6777 putFile16BitBE(file, ei->gfx_element_initial);
6779 putFile8Bit(file, ei->collect_score_initial);
6780 putFile8Bit(file, ei->collect_count_initial);
6782 putFile16BitBE(file, ei->push_delay_fixed);
6783 putFile16BitBE(file, ei->push_delay_random);
6784 putFile16BitBE(file, ei->move_delay_fixed);
6785 putFile16BitBE(file, ei->move_delay_random);
6787 putFile16BitBE(file, ei->move_pattern);
6788 putFile8Bit(file, ei->move_direction_initial);
6789 putFile8Bit(file, ei->move_stepsize);
6791 for (y = 0; y < 3; y++)
6792 for (x = 0; x < 3; x++)
6793 putFile16BitBE(file, ei->content.e[x][y]);
6795 putFile32BitBE(file, ei->change->events);
6797 putFile16BitBE(file, ei->change->target_element);
6799 putFile16BitBE(file, ei->change->delay_fixed);
6800 putFile16BitBE(file, ei->change->delay_random);
6801 putFile16BitBE(file, ei->change->delay_frames);
6803 putFile16BitBE(file, ei->change->initial_trigger_element);
6805 putFile8Bit(file, ei->change->explode);
6806 putFile8Bit(file, ei->change->use_target_content);
6807 putFile8Bit(file, ei->change->only_if_complete);
6808 putFile8Bit(file, ei->change->use_random_replace);
6810 putFile8Bit(file, ei->change->random_percentage);
6811 putFile8Bit(file, ei->change->replace_when);
6813 for (y = 0; y < 3; y++)
6814 for (x = 0; x < 3; x++)
6815 putFile16BitBE(file, ei->change->content.e[x][y]);
6817 putFile8Bit(file, ei->slippery_type);
6819 /* some free bytes for future properties and padding */
6820 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6827 if (check != num_changed_custom_elements) /* should not happen */
6828 Error(ERR_WARN, "inconsistent number of custom element properties");
6832 #if ENABLE_HISTORIC_CHUNKS
6833 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6835 struct ElementInfo *ei = &element_info[element];
6838 /* ---------- custom element base property values (96 bytes) ------------- */
6840 putFile16BitBE(file, element);
6842 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6843 putFile8Bit(file, ei->description[i]);
6845 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6847 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
6849 putFile8Bit(file, ei->num_change_pages);
6851 putFile16BitBE(file, ei->ce_value_fixed_initial);
6852 putFile16BitBE(file, ei->ce_value_random_initial);
6853 putFile8Bit(file, ei->use_last_ce_value);
6855 putFile8Bit(file, ei->use_gfx_element);
6856 putFile16BitBE(file, ei->gfx_element_initial);
6858 putFile8Bit(file, ei->collect_score_initial);
6859 putFile8Bit(file, ei->collect_count_initial);
6861 putFile8Bit(file, ei->drop_delay_fixed);
6862 putFile8Bit(file, ei->push_delay_fixed);
6863 putFile8Bit(file, ei->drop_delay_random);
6864 putFile8Bit(file, ei->push_delay_random);
6865 putFile16BitBE(file, ei->move_delay_fixed);
6866 putFile16BitBE(file, ei->move_delay_random);
6868 /* bits 0 - 15 of "move_pattern" ... */
6869 putFile16BitBE(file, ei->move_pattern & 0xffff);
6870 putFile8Bit(file, ei->move_direction_initial);
6871 putFile8Bit(file, ei->move_stepsize);
6873 putFile8Bit(file, ei->slippery_type);
6875 for (y = 0; y < 3; y++)
6876 for (x = 0; x < 3; x++)
6877 putFile16BitBE(file, ei->content.e[x][y]);
6879 putFile16BitBE(file, ei->move_enter_element);
6880 putFile16BitBE(file, ei->move_leave_element);
6881 putFile8Bit(file, ei->move_leave_type);
6883 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
6884 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
6886 putFile8Bit(file, ei->access_direction);
6888 putFile8Bit(file, ei->explosion_delay);
6889 putFile8Bit(file, ei->ignition_delay);
6890 putFile8Bit(file, ei->explosion_type);
6892 /* some free bytes for future custom property values and padding */
6893 WriteUnusedBytesToFile(file, 1);
6895 /* ---------- change page property values (48 bytes) --------------------- */
6897 for (i = 0; i < ei->num_change_pages; i++)
6899 struct ElementChangeInfo *change = &ei->change_page[i];
6900 unsigned int event_bits;
6902 /* bits 0 - 31 of "has_event[]" ... */
6904 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
6905 if (change->has_event[j])
6906 event_bits |= (1 << j);
6907 putFile32BitBE(file, event_bits);
6909 putFile16BitBE(file, change->target_element);
6911 putFile16BitBE(file, change->delay_fixed);
6912 putFile16BitBE(file, change->delay_random);
6913 putFile16BitBE(file, change->delay_frames);
6915 putFile16BitBE(file, change->initial_trigger_element);
6917 putFile8Bit(file, change->explode);
6918 putFile8Bit(file, change->use_target_content);
6919 putFile8Bit(file, change->only_if_complete);
6920 putFile8Bit(file, change->use_random_replace);
6922 putFile8Bit(file, change->random_percentage);
6923 putFile8Bit(file, change->replace_when);
6925 for (y = 0; y < 3; y++)
6926 for (x = 0; x < 3; x++)
6927 putFile16BitBE(file, change->target_content.e[x][y]);
6929 putFile8Bit(file, change->can_change);
6931 putFile8Bit(file, change->trigger_side);
6933 putFile8Bit(file, change->trigger_player);
6934 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
6935 log_2(change->trigger_page)));
6937 putFile8Bit(file, change->has_action);
6938 putFile8Bit(file, change->action_type);
6939 putFile8Bit(file, change->action_mode);
6940 putFile16BitBE(file, change->action_arg);
6942 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
6944 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
6945 if (change->has_event[j])
6946 event_bits |= (1 << (j - 32));
6947 putFile8Bit(file, event_bits);
6952 #if ENABLE_HISTORIC_CHUNKS
6953 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
6955 struct ElementInfo *ei = &element_info[element];
6956 struct ElementGroupInfo *group = ei->group;
6959 putFile16BitBE(file, element);
6961 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6962 putFile8Bit(file, ei->description[i]);
6964 putFile8Bit(file, group->num_elements);
6966 putFile8Bit(file, ei->use_gfx_element);
6967 putFile16BitBE(file, ei->gfx_element_initial);
6969 putFile8Bit(file, group->choice_mode);
6971 /* some free bytes for future values and padding */
6972 WriteUnusedBytesToFile(file, 3);
6974 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
6975 putFile16BitBE(file, group->element[i]);
6979 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
6980 boolean write_element)
6982 int save_type = entry->save_type;
6983 int data_type = entry->data_type;
6984 int conf_type = entry->conf_type;
6985 int byte_mask = conf_type & CONF_MASK_BYTES;
6986 int element = entry->element;
6987 int default_value = entry->default_value;
6989 boolean modified = FALSE;
6991 if (byte_mask != CONF_MASK_MULTI_BYTES)
6993 void *value_ptr = entry->value;
6994 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
6997 /* check if any settings have been modified before saving them */
6998 if (value != default_value)
7001 /* do not save if explicitly told or if unmodified default settings */
7002 if ((save_type == SAVE_CONF_NEVER) ||
7003 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7007 num_bytes += putFile16BitBE(file, element);
7009 num_bytes += putFile8Bit(file, conf_type);
7010 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7011 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7012 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7015 else if (data_type == TYPE_STRING)
7017 char *default_string = entry->default_string;
7018 char *string = (char *)(entry->value);
7019 int string_length = strlen(string);
7022 /* check if any settings have been modified before saving them */
7023 if (!strEqual(string, default_string))
7026 /* do not save if explicitly told or if unmodified default settings */
7027 if ((save_type == SAVE_CONF_NEVER) ||
7028 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7032 num_bytes += putFile16BitBE(file, element);
7034 num_bytes += putFile8Bit(file, conf_type);
7035 num_bytes += putFile16BitBE(file, string_length);
7037 for (i = 0; i < string_length; i++)
7038 num_bytes += putFile8Bit(file, string[i]);
7040 else if (data_type == TYPE_ELEMENT_LIST)
7042 int *element_array = (int *)(entry->value);
7043 int num_elements = *(int *)(entry->num_entities);
7046 /* check if any settings have been modified before saving them */
7047 for (i = 0; i < num_elements; i++)
7048 if (element_array[i] != default_value)
7051 /* do not save if explicitly told or if unmodified default settings */
7052 if ((save_type == SAVE_CONF_NEVER) ||
7053 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7057 num_bytes += putFile16BitBE(file, element);
7059 num_bytes += putFile8Bit(file, conf_type);
7060 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7062 for (i = 0; i < num_elements; i++)
7063 num_bytes += putFile16BitBE(file, element_array[i]);
7065 else if (data_type == TYPE_CONTENT_LIST)
7067 struct Content *content = (struct Content *)(entry->value);
7068 int num_contents = *(int *)(entry->num_entities);
7071 /* check if any settings have been modified before saving them */
7072 for (i = 0; i < num_contents; i++)
7073 for (y = 0; y < 3; y++)
7074 for (x = 0; x < 3; x++)
7075 if (content[i].e[x][y] != default_value)
7078 /* do not save if explicitly told or if unmodified default settings */
7079 if ((save_type == SAVE_CONF_NEVER) ||
7080 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7084 num_bytes += putFile16BitBE(file, element);
7086 num_bytes += putFile8Bit(file, conf_type);
7087 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7089 for (i = 0; i < num_contents; i++)
7090 for (y = 0; y < 3; y++)
7091 for (x = 0; x < 3; x++)
7092 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7098 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7103 li = *level; /* copy level data into temporary buffer */
7105 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7106 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7111 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7116 li = *level; /* copy level data into temporary buffer */
7118 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7119 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7124 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7126 int envelope_nr = element - EL_ENVELOPE_1;
7130 chunk_size += putFile16BitBE(file, element);
7132 /* copy envelope data into temporary buffer */
7133 xx_envelope = level->envelope[envelope_nr];
7135 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7136 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7141 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7143 struct ElementInfo *ei = &element_info[element];
7147 chunk_size += putFile16BitBE(file, element);
7149 xx_ei = *ei; /* copy element data into temporary buffer */
7151 /* set default description string for this specific element */
7152 strcpy(xx_default_description, getDefaultElementDescription(ei));
7154 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7155 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7157 for (i = 0; i < ei->num_change_pages; i++)
7159 struct ElementChangeInfo *change = &ei->change_page[i];
7161 xx_current_change_page = i;
7163 xx_change = *change; /* copy change data into temporary buffer */
7166 setEventBitsFromEventFlags(change);
7168 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7169 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7176 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7178 struct ElementInfo *ei = &element_info[element];
7179 struct ElementGroupInfo *group = ei->group;
7183 chunk_size += putFile16BitBE(file, element);
7185 xx_ei = *ei; /* copy element data into temporary buffer */
7186 xx_group = *group; /* copy group data into temporary buffer */
7188 /* set default description string for this specific element */
7189 strcpy(xx_default_description, getDefaultElementDescription(ei));
7191 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7192 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7197 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7198 boolean save_as_template)
7204 if (!(file = fopen(filename, MODE_WRITE)))
7206 Error(ERR_WARN, "cannot save level file '%s'", filename);
7210 level->file_version = FILE_VERSION_ACTUAL;
7211 level->game_version = GAME_VERSION_ACTUAL;
7213 level->creation_date = getCurrentDate();
7215 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7216 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7218 chunk_size = SaveLevel_VERS(NULL, level);
7219 putFileChunkBE(file, "VERS", chunk_size);
7220 SaveLevel_VERS(file, level);
7222 chunk_size = SaveLevel_DATE(NULL, level);
7223 putFileChunkBE(file, "DATE", chunk_size);
7224 SaveLevel_DATE(file, level);
7226 chunk_size = SaveLevel_NAME(NULL, level);
7227 putFileChunkBE(file, "NAME", chunk_size);
7228 SaveLevel_NAME(file, level);
7230 chunk_size = SaveLevel_AUTH(NULL, level);
7231 putFileChunkBE(file, "AUTH", chunk_size);
7232 SaveLevel_AUTH(file, level);
7234 chunk_size = SaveLevel_INFO(NULL, level);
7235 putFileChunkBE(file, "INFO", chunk_size);
7236 SaveLevel_INFO(file, level);
7238 chunk_size = SaveLevel_BODY(NULL, level);
7239 putFileChunkBE(file, "BODY", chunk_size);
7240 SaveLevel_BODY(file, level);
7242 chunk_size = SaveLevel_ELEM(NULL, level);
7243 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7245 putFileChunkBE(file, "ELEM", chunk_size);
7246 SaveLevel_ELEM(file, level);
7249 for (i = 0; i < NUM_ENVELOPES; i++)
7251 int element = EL_ENVELOPE_1 + i;
7253 chunk_size = SaveLevel_NOTE(NULL, level, element);
7254 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7256 putFileChunkBE(file, "NOTE", chunk_size);
7257 SaveLevel_NOTE(file, level, element);
7261 /* if not using template level, check for non-default custom/group elements */
7262 if (!level->use_custom_template || save_as_template)
7264 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7266 int element = EL_CUSTOM_START + i;
7268 chunk_size = SaveLevel_CUSX(NULL, level, element);
7269 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7271 putFileChunkBE(file, "CUSX", chunk_size);
7272 SaveLevel_CUSX(file, level, element);
7276 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7278 int element = EL_GROUP_START + i;
7280 chunk_size = SaveLevel_GRPX(NULL, level, element);
7281 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7283 putFileChunkBE(file, "GRPX", chunk_size);
7284 SaveLevel_GRPX(file, level, element);
7291 SetFilePermissions(filename, PERMS_PRIVATE);
7294 void SaveLevel(int nr)
7296 char *filename = getDefaultLevelFilename(nr);
7298 SaveLevelFromFilename(&level, filename, FALSE);
7301 void SaveLevelTemplate()
7303 char *filename = getLocalLevelTemplateFilename();
7305 SaveLevelFromFilename(&level, filename, TRUE);
7308 boolean SaveLevelChecked(int nr)
7310 char *filename = getDefaultLevelFilename(nr);
7311 boolean new_level = !fileExists(filename);
7312 boolean level_saved = FALSE;
7314 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7319 Request("Level saved!", REQ_CONFIRM);
7327 void DumpLevel(struct LevelInfo *level)
7329 if (level->no_level_file || level->no_valid_file)
7331 Error(ERR_WARN, "cannot dump -- no valid level file found");
7337 Print("Level xxx (file version %08d, game version %08d)\n",
7338 level->file_version, level->game_version);
7341 Print("Level author: '%s'\n", level->author);
7342 Print("Level title: '%s'\n", level->name);
7344 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7346 Print("Level time: %d seconds\n", level->time);
7347 Print("Gems needed: %d\n", level->gems_needed);
7349 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7350 Print("Time for wheel: %d seconds\n", level->time_wheel);
7351 Print("Time for light: %d seconds\n", level->time_light);
7352 Print("Time for timegate: %d seconds\n", level->time_timegate);
7354 Print("Amoeba speed: %d\n", level->amoeba_speed);
7357 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7358 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7359 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7360 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7361 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7367 /* ========================================================================= */
7368 /* tape file functions */
7369 /* ========================================================================= */
7371 static void setTapeInfoToDefaults()
7375 /* always start with reliable default values (empty tape) */
7378 /* default values (also for pre-1.2 tapes) with only the first player */
7379 tape.player_participates[0] = TRUE;
7380 for (i = 1; i < MAX_PLAYERS; i++)
7381 tape.player_participates[i] = FALSE;
7383 /* at least one (default: the first) player participates in every tape */
7384 tape.num_participating_players = 1;
7386 tape.level_nr = level_nr;
7388 tape.changed = FALSE;
7390 tape.recording = FALSE;
7391 tape.playing = FALSE;
7392 tape.pausing = FALSE;
7394 tape.no_valid_file = FALSE;
7397 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7399 tape->file_version = getFileVersion(file);
7400 tape->game_version = getFileVersion(file);
7405 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7409 tape->random_seed = getFile32BitBE(file);
7410 tape->date = getFile32BitBE(file);
7411 tape->length = getFile32BitBE(file);
7413 /* read header fields that are new since version 1.2 */
7414 if (tape->file_version >= FILE_VERSION_1_2)
7416 byte store_participating_players = getFile8Bit(file);
7419 /* since version 1.2, tapes store which players participate in the tape */
7420 tape->num_participating_players = 0;
7421 for (i = 0; i < MAX_PLAYERS; i++)
7423 tape->player_participates[i] = FALSE;
7425 if (store_participating_players & (1 << i))
7427 tape->player_participates[i] = TRUE;
7428 tape->num_participating_players++;
7432 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7434 engine_version = getFileVersion(file);
7435 if (engine_version > 0)
7436 tape->engine_version = engine_version;
7438 tape->engine_version = tape->game_version;
7444 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7446 int level_identifier_size;
7449 level_identifier_size = getFile16BitBE(file);
7451 tape->level_identifier =
7452 checked_realloc(tape->level_identifier, level_identifier_size);
7454 for (i = 0; i < level_identifier_size; i++)
7455 tape->level_identifier[i] = getFile8Bit(file);
7457 tape->level_nr = getFile16BitBE(file);
7459 chunk_size = 2 + level_identifier_size + 2;
7464 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7467 int chunk_size_expected =
7468 (tape->num_participating_players + 1) * tape->length;
7470 if (chunk_size_expected != chunk_size)
7472 ReadUnusedBytesFromFile(file, chunk_size);
7473 return chunk_size_expected;
7476 for (i = 0; i < tape->length; i++)
7478 if (i >= MAX_TAPE_LEN)
7480 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7483 // tape too large; read and ignore remaining tape data from this chunk
7484 for (;i < tape->length; i++)
7485 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7490 for (j = 0; j < MAX_PLAYERS; j++)
7492 tape->pos[i].action[j] = MV_NONE;
7494 if (tape->player_participates[j])
7495 tape->pos[i].action[j] = getFile8Bit(file);
7498 tape->pos[i].delay = getFile8Bit(file);
7500 if (tape->file_version == FILE_VERSION_1_0)
7502 /* eliminate possible diagonal moves in old tapes */
7503 /* this is only for backward compatibility */
7505 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7506 byte action = tape->pos[i].action[0];
7507 int k, num_moves = 0;
7509 for (k = 0; k<4; k++)
7511 if (action & joy_dir[k])
7513 tape->pos[i + num_moves].action[0] = joy_dir[k];
7515 tape->pos[i + num_moves].delay = 0;
7524 tape->length += num_moves;
7527 else if (tape->file_version < FILE_VERSION_2_0)
7529 /* convert pre-2.0 tapes to new tape format */
7531 if (tape->pos[i].delay > 1)
7534 tape->pos[i + 1] = tape->pos[i];
7535 tape->pos[i + 1].delay = 1;
7538 for (j = 0; j < MAX_PLAYERS; j++)
7539 tape->pos[i].action[j] = MV_NONE;
7540 tape->pos[i].delay--;
7547 if (checkEndOfFile(file))
7551 if (i != tape->length)
7552 chunk_size = (tape->num_participating_players + 1) * i;
7557 void LoadTape_SokobanSolution(char *filename)
7560 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7562 if (!(file = openFile(filename, MODE_READ)))
7564 tape.no_valid_file = TRUE;
7569 while (!checkEndOfFile(file))
7571 unsigned char c = getByteFromFile(file);
7573 if (checkEndOfFile(file))
7580 tape.pos[tape.length].action[0] = MV_UP;
7581 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7587 tape.pos[tape.length].action[0] = MV_DOWN;
7588 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7594 tape.pos[tape.length].action[0] = MV_LEFT;
7595 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7601 tape.pos[tape.length].action[0] = MV_RIGHT;
7602 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7610 /* ignore white-space characters */
7614 tape.no_valid_file = TRUE;
7616 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7624 if (tape.no_valid_file)
7627 tape.length_frames = GetTapeLengthFrames();
7628 tape.length_seconds = GetTapeLengthSeconds();
7631 void LoadTapeFromFilename(char *filename)
7633 char cookie[MAX_LINE_LEN];
7634 char chunk_name[CHUNK_ID_LEN + 1];
7638 /* always start with reliable default values */
7639 setTapeInfoToDefaults();
7641 if (strSuffix(filename, ".sln"))
7643 LoadTape_SokobanSolution(filename);
7648 if (!(file = openFile(filename, MODE_READ)))
7650 tape.no_valid_file = TRUE;
7655 getFileChunkBE(file, chunk_name, NULL);
7656 if (strEqual(chunk_name, "RND1"))
7658 getFile32BitBE(file); /* not used */
7660 getFileChunkBE(file, chunk_name, NULL);
7661 if (!strEqual(chunk_name, "TAPE"))
7663 tape.no_valid_file = TRUE;
7665 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7672 else /* check for pre-2.0 file format with cookie string */
7674 strcpy(cookie, chunk_name);
7675 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7677 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7678 cookie[strlen(cookie) - 1] = '\0';
7680 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7682 tape.no_valid_file = TRUE;
7684 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7691 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7693 tape.no_valid_file = TRUE;
7695 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7702 /* pre-2.0 tape files have no game version, so use file version here */
7703 tape.game_version = tape.file_version;
7706 if (tape.file_version < FILE_VERSION_1_2)
7708 /* tape files from versions before 1.2.0 without chunk structure */
7709 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7710 LoadTape_BODY(file, 2 * tape.length, &tape);
7718 int (*loader)(File *, int, struct TapeInfo *);
7722 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7723 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7724 { "INFO", -1, LoadTape_INFO },
7725 { "BODY", -1, LoadTape_BODY },
7729 while (getFileChunkBE(file, chunk_name, &chunk_size))
7733 while (chunk_info[i].name != NULL &&
7734 !strEqual(chunk_name, chunk_info[i].name))
7737 if (chunk_info[i].name == NULL)
7739 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7740 chunk_name, filename);
7741 ReadUnusedBytesFromFile(file, chunk_size);
7743 else if (chunk_info[i].size != -1 &&
7744 chunk_info[i].size != chunk_size)
7746 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7747 chunk_size, chunk_name, filename);
7748 ReadUnusedBytesFromFile(file, chunk_size);
7752 /* call function to load this tape chunk */
7753 int chunk_size_expected =
7754 (chunk_info[i].loader)(file, chunk_size, &tape);
7756 /* the size of some chunks cannot be checked before reading other
7757 chunks first (like "HEAD" and "BODY") that contain some header
7758 information, so check them here */
7759 if (chunk_size_expected != chunk_size)
7761 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7762 chunk_size, chunk_name, filename);
7770 tape.length_frames = GetTapeLengthFrames();
7771 tape.length_seconds = GetTapeLengthSeconds();
7774 printf("::: tape file version: %d\n", tape.file_version);
7775 printf("::: tape game version: %d\n", tape.game_version);
7776 printf("::: tape engine version: %d\n", tape.engine_version);
7780 void LoadTape(int nr)
7782 char *filename = getTapeFilename(nr);
7784 LoadTapeFromFilename(filename);
7787 void LoadSolutionTape(int nr)
7789 char *filename = getSolutionTapeFilename(nr);
7791 LoadTapeFromFilename(filename);
7793 if (TAPE_IS_EMPTY(tape) &&
7794 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7795 level.native_sp_level->demo.is_available)
7796 CopyNativeTape_SP_to_RND(&level);
7799 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7801 putFileVersion(file, tape->file_version);
7802 putFileVersion(file, tape->game_version);
7805 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7808 byte store_participating_players = 0;
7810 /* set bits for participating players for compact storage */
7811 for (i = 0; i < MAX_PLAYERS; i++)
7812 if (tape->player_participates[i])
7813 store_participating_players |= (1 << i);
7815 putFile32BitBE(file, tape->random_seed);
7816 putFile32BitBE(file, tape->date);
7817 putFile32BitBE(file, tape->length);
7819 putFile8Bit(file, store_participating_players);
7821 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7822 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7824 putFileVersion(file, tape->engine_version);
7827 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7829 int level_identifier_size = strlen(tape->level_identifier) + 1;
7832 putFile16BitBE(file, level_identifier_size);
7834 for (i = 0; i < level_identifier_size; i++)
7835 putFile8Bit(file, tape->level_identifier[i]);
7837 putFile16BitBE(file, tape->level_nr);
7840 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7844 for (i = 0; i < tape->length; i++)
7846 for (j = 0; j < MAX_PLAYERS; j++)
7847 if (tape->player_participates[j])
7848 putFile8Bit(file, tape->pos[i].action[j]);
7850 putFile8Bit(file, tape->pos[i].delay);
7854 void SaveTape(int nr)
7856 char *filename = getTapeFilename(nr);
7858 int num_participating_players = 0;
7859 int info_chunk_size;
7860 int body_chunk_size;
7863 InitTapeDirectory(leveldir_current->subdir);
7865 if (!(file = fopen(filename, MODE_WRITE)))
7867 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7871 tape.file_version = FILE_VERSION_ACTUAL;
7872 tape.game_version = GAME_VERSION_ACTUAL;
7874 /* count number of participating players */
7875 for (i = 0; i < MAX_PLAYERS; i++)
7876 if (tape.player_participates[i])
7877 num_participating_players++;
7879 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7880 body_chunk_size = (num_participating_players + 1) * tape.length;
7882 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7883 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7885 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7886 SaveTape_VERS(file, &tape);
7888 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7889 SaveTape_HEAD(file, &tape);
7891 putFileChunkBE(file, "INFO", info_chunk_size);
7892 SaveTape_INFO(file, &tape);
7894 putFileChunkBE(file, "BODY", body_chunk_size);
7895 SaveTape_BODY(file, &tape);
7899 SetFilePermissions(filename, PERMS_PRIVATE);
7901 tape.changed = FALSE;
7904 boolean SaveTapeChecked(int nr)
7906 char *filename = getTapeFilename(nr);
7907 boolean new_tape = !fileExists(filename);
7908 boolean tape_saved = FALSE;
7910 if (new_tape || Request("Replace old tape?", REQ_ASK))
7915 Request("Tape saved!", REQ_CONFIRM);
7923 void DumpTape(struct TapeInfo *tape)
7925 int tape_frame_counter;
7928 if (tape->no_valid_file)
7930 Error(ERR_WARN, "cannot dump -- no valid tape file found");
7936 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
7937 tape->level_nr, tape->file_version, tape->game_version);
7938 Print(" (effective engine version %08d)\n",
7939 tape->engine_version);
7940 Print("Level series identifier: '%s'\n", tape->level_identifier);
7943 tape_frame_counter = 0;
7945 for (i = 0; i < tape->length; i++)
7947 if (i >= MAX_TAPE_LEN)
7952 for (j = 0; j < MAX_PLAYERS; j++)
7954 if (tape->player_participates[j])
7956 int action = tape->pos[i].action[j];
7958 Print("%d:%02x ", j, action);
7959 Print("[%c%c%c%c|%c%c] - ",
7960 (action & JOY_LEFT ? '<' : ' '),
7961 (action & JOY_RIGHT ? '>' : ' '),
7962 (action & JOY_UP ? '^' : ' '),
7963 (action & JOY_DOWN ? 'v' : ' '),
7964 (action & JOY_BUTTON_1 ? '1' : ' '),
7965 (action & JOY_BUTTON_2 ? '2' : ' '));
7969 Print("(%03d) ", tape->pos[i].delay);
7970 Print("[%05d]\n", tape_frame_counter);
7972 tape_frame_counter += tape->pos[i].delay;
7979 /* ========================================================================= */
7980 /* score file functions */
7981 /* ========================================================================= */
7983 void LoadScore(int nr)
7986 char *filename = getScoreFilename(nr);
7987 char cookie[MAX_LINE_LEN];
7988 char line[MAX_LINE_LEN];
7992 /* always start with reliable default values */
7993 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7995 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
7996 highscore[i].Score = 0;
7999 if (!(file = fopen(filename, MODE_READ)))
8002 /* check file identifier */
8003 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8005 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8006 cookie[strlen(cookie) - 1] = '\0';
8008 if (!checkCookieString(cookie, SCORE_COOKIE))
8010 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8015 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8017 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8018 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8019 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8022 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8023 line[strlen(line) - 1] = '\0';
8025 for (line_ptr = line; *line_ptr; line_ptr++)
8027 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8029 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8030 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8039 void SaveScore(int nr)
8042 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8043 char *filename = getScoreFilename(nr);
8046 InitScoreDirectory(leveldir_current->subdir);
8048 if (!(file = fopen(filename, MODE_WRITE)))
8050 Error(ERR_WARN, "cannot save score for level %d", nr);
8054 fprintf(file, "%s\n\n", SCORE_COOKIE);
8056 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8057 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8061 SetFilePermissions(filename, permissions);
8065 /* ========================================================================= */
8066 /* setup file functions */
8067 /* ========================================================================= */
8069 #define TOKEN_STR_PLAYER_PREFIX "player_"
8072 #define SETUP_TOKEN_PLAYER_NAME 0
8073 #define SETUP_TOKEN_SOUND 1
8074 #define SETUP_TOKEN_SOUND_LOOPS 2
8075 #define SETUP_TOKEN_SOUND_MUSIC 3
8076 #define SETUP_TOKEN_SOUND_SIMPLE 4
8077 #define SETUP_TOKEN_TOONS 5
8078 #define SETUP_TOKEN_SCROLL_DELAY 6
8079 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
8080 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE 8
8081 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY 9
8082 #define SETUP_TOKEN_FADE_SCREENS 10
8083 #define SETUP_TOKEN_AUTORECORD 11
8084 #define SETUP_TOKEN_SHOW_TITLESCREEN 12
8085 #define SETUP_TOKEN_QUICK_DOORS 13
8086 #define SETUP_TOKEN_TEAM_MODE 14
8087 #define SETUP_TOKEN_HANDICAP 15
8088 #define SETUP_TOKEN_SKIP_LEVELS 16
8089 #define SETUP_TOKEN_INCREMENT_LEVELS 17
8090 #define SETUP_TOKEN_TIME_LIMIT 18
8091 #define SETUP_TOKEN_FULLSCREEN 19
8092 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT 20
8093 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY 21
8094 #define SETUP_TOKEN_SCREEN_RENDERING_MODE 22
8095 #define SETUP_TOKEN_ASK_ON_ESCAPE 23
8096 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 24
8097 #define SETUP_TOKEN_QUICK_SWITCH 25
8098 #define SETUP_TOKEN_INPUT_ON_FOCUS 26
8099 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 27
8100 #define SETUP_TOKEN_GAME_FRAME_DELAY 28
8101 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 29
8102 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 30
8103 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS 31
8104 #define SETUP_TOKEN_GRAPHICS_SET 32
8105 #define SETUP_TOKEN_SOUNDS_SET 33
8106 #define SETUP_TOKEN_MUSIC_SET 34
8107 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 35
8108 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 36
8109 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 37
8110 #define SETUP_TOKEN_VOLUME_SIMPLE 38
8111 #define SETUP_TOKEN_VOLUME_LOOPS 39
8112 #define SETUP_TOKEN_VOLUME_MUSIC 40
8113 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE 41
8114 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 42
8115 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE 43
8117 #define NUM_GLOBAL_SETUP_TOKENS 44
8120 #define SETUP_TOKEN_EDITOR_EL_CLASSIC 0
8121 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 1
8122 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 2
8123 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 3
8124 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 4
8125 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 5
8127 #define NUM_EDITOR_SETUP_TOKENS 6
8129 /* editor cascade setup */
8130 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
8131 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
8132 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
8133 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
8134 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
8135 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
8136 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
8137 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
8138 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
8139 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
8140 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
8141 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
8142 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
8143 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
8144 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
8146 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
8148 /* shortcut setup */
8149 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
8150 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
8151 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
8152 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
8153 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
8154 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
8155 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
8156 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
8157 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
8158 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
8159 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
8160 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
8161 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
8162 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
8163 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
8164 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
8165 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
8166 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
8167 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
8168 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
8169 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
8171 #define NUM_SHORTCUT_SETUP_TOKENS 21
8174 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
8175 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
8176 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
8177 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
8178 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
8179 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
8180 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
8181 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
8182 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
8183 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
8184 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
8185 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
8186 #define SETUP_TOKEN_PLAYER_KEY_UP 12
8187 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
8188 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8189 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8191 #define NUM_PLAYER_SETUP_TOKENS 16
8194 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8195 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8196 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8198 #define NUM_SYSTEM_SETUP_TOKENS 3
8200 /* internal setup */
8201 #define SETUP_TOKEN_INT_PROGRAM_TITLE 0
8202 #define SETUP_TOKEN_INT_PROGRAM_VERSION 1
8203 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR 2
8204 #define SETUP_TOKEN_INT_PROGRAM_EMAIL 3
8205 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE 4
8206 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 5
8207 #define SETUP_TOKEN_INT_PROGRAM_COMPANY 6
8208 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 7
8209 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 8
8210 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 9
8211 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 10
8212 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 11
8213 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 12
8214 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 13
8215 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 14
8216 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
8217 #define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE 16
8218 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH 17
8219 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT 18
8221 #define NUM_INTERNAL_SETUP_TOKENS 19
8224 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0 0
8225 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1 1
8226 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2 2
8227 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3 3
8228 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4 4
8229 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5 5
8230 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6 6
8231 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7 7
8232 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8 8
8233 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9 9
8234 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0 10
8235 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1 11
8236 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2 12
8237 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3 13
8238 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4 14
8239 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5 15
8240 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6 16
8241 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7 17
8242 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8 18
8243 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9 19
8244 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8245 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8246 #define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
8248 #define NUM_DEBUG_SETUP_TOKENS 23
8251 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8253 #define NUM_OPTIONS_SETUP_TOKENS 1
8256 static struct SetupInfo si;
8257 static struct SetupEditorInfo sei;
8258 static struct SetupEditorCascadeInfo seci;
8259 static struct SetupShortcutInfo ssi;
8260 static struct SetupInputInfo sii;
8261 static struct SetupSystemInfo syi;
8262 static struct SetupInternalInfo sxi;
8263 static struct SetupDebugInfo sdi;
8264 static struct OptionInfo soi;
8266 static struct TokenInfo global_setup_tokens[] =
8268 { TYPE_STRING, &si.player_name, "player_name" },
8269 { TYPE_SWITCH, &si.sound, "sound" },
8270 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8271 { TYPE_SWITCH, &si.sound_music, "background_music" },
8272 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8273 { TYPE_SWITCH, &si.toons, "toons" },
8274 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8275 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8276 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8277 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8278 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8279 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8280 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8281 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8282 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8283 { TYPE_SWITCH, &si.handicap, "handicap" },
8284 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8285 { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
8286 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8287 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8288 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8289 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8290 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8291 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8292 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8293 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8294 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8295 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8296 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8297 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8298 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8299 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8300 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8301 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8302 { TYPE_STRING, &si.music_set, "music_set" },
8303 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8304 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8305 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8306 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8307 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8308 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8309 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8310 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8311 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8314 static struct TokenInfo editor_setup_tokens[] =
8316 { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
8317 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8318 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8319 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8320 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8321 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8324 static struct TokenInfo editor_cascade_setup_tokens[] =
8326 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8327 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8328 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8329 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8330 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8331 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8332 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8333 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8334 { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
8335 { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
8336 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8337 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8338 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8339 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8340 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8341 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8342 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8345 static struct TokenInfo shortcut_setup_tokens[] =
8347 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8348 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8349 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8350 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8351 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8352 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8353 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8354 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8355 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8356 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8357 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8358 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8359 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8360 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8361 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8362 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8363 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8364 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8365 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8366 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8367 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8370 static struct TokenInfo player_setup_tokens[] =
8372 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8373 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8374 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8375 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8376 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8377 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8378 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8379 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8380 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8381 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8382 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8383 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8384 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8385 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8386 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8387 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8390 static struct TokenInfo system_setup_tokens[] =
8392 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8393 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8394 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8397 static struct TokenInfo internal_setup_tokens[] =
8399 { TYPE_STRING, &sxi.program_title, "program_title" },
8400 { TYPE_STRING, &sxi.program_version, "program_version" },
8401 { TYPE_STRING, &sxi.program_author, "program_author" },
8402 { TYPE_STRING, &sxi.program_email, "program_email" },
8403 { TYPE_STRING, &sxi.program_website, "program_website" },
8404 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8405 { TYPE_STRING, &sxi.program_company, "program_company" },
8406 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8407 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8408 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8409 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8410 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8411 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8412 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8413 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8414 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8415 { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" },
8416 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8417 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8420 static struct TokenInfo debug_setup_tokens[] =
8422 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8423 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8424 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8425 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8426 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8427 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8428 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8429 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8430 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8431 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8432 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8433 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8434 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8435 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8436 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8437 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8438 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8439 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8440 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8441 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8442 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8443 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8444 { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8447 static struct TokenInfo options_setup_tokens[] =
8449 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8452 static char *get_corrected_login_name(char *login_name)
8454 /* needed because player name must be a fixed length string */
8455 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8457 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8458 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8460 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8461 if (strchr(login_name_new, ' '))
8462 *strchr(login_name_new, ' ') = '\0';
8464 return login_name_new;
8467 static void setSetupInfoToDefaults(struct SetupInfo *si)
8471 si->player_name = get_corrected_login_name(getLoginName());
8474 si->sound_loops = TRUE;
8475 si->sound_music = TRUE;
8476 si->sound_simple = TRUE;
8478 si->scroll_delay = TRUE;
8479 si->scroll_delay_value = STD_SCROLL_DELAY;
8480 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8481 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8482 si->fade_screens = TRUE;
8483 si->autorecord = TRUE;
8484 si->show_titlescreen = TRUE;
8485 si->quick_doors = FALSE;
8486 si->team_mode = FALSE;
8487 si->handicap = TRUE;
8488 si->skip_levels = TRUE;
8489 si->increment_levels = TRUE;
8490 si->time_limit = TRUE;
8491 si->fullscreen = FALSE;
8492 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8493 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8494 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8495 si->ask_on_escape = TRUE;
8496 si->ask_on_escape_editor = TRUE;
8497 si->quick_switch = FALSE;
8498 si->input_on_focus = FALSE;
8499 si->prefer_aga_graphics = TRUE;
8500 si->game_frame_delay = GAME_FRAME_DELAY;
8501 si->sp_show_border_elements = FALSE;
8502 si->small_game_graphics = FALSE;
8503 si->show_snapshot_buttons = FALSE;
8505 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8506 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8507 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8509 si->override_level_graphics = FALSE;
8510 si->override_level_sounds = FALSE;
8511 si->override_level_music = FALSE;
8513 si->volume_simple = 100; /* percent */
8514 si->volume_loops = 100; /* percent */
8515 si->volume_music = 100; /* percent */
8517 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8518 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8519 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8521 si->editor.el_boulderdash = TRUE;
8522 si->editor.el_emerald_mine = TRUE;
8523 si->editor.el_emerald_mine_club = TRUE;
8524 si->editor.el_more = TRUE;
8525 si->editor.el_sokoban = TRUE;
8526 si->editor.el_supaplex = TRUE;
8527 si->editor.el_diamond_caves = TRUE;
8528 si->editor.el_dx_boulderdash = TRUE;
8530 si->editor.el_mirror_magic = TRUE;
8531 si->editor.el_deflektor = TRUE;
8533 si->editor.el_chars = TRUE;
8534 si->editor.el_steel_chars = TRUE;
8536 si->editor.el_classic = TRUE;
8537 si->editor.el_custom = TRUE;
8539 si->editor.el_user_defined = FALSE;
8540 si->editor.el_dynamic = TRUE;
8542 si->editor.el_headlines = TRUE;
8544 si->editor.show_element_token = FALSE;
8546 si->editor.use_template_for_new_levels = TRUE;
8548 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8549 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8550 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8552 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8553 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8554 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8555 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8556 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8558 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8559 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8560 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8561 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8562 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8563 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8565 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8566 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8567 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8569 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8570 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8571 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8572 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8574 for (i = 0; i < MAX_PLAYERS; i++)
8576 si->input[i].use_joystick = FALSE;
8577 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8578 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8579 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8580 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8581 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8582 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8583 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8584 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8585 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8586 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8587 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8588 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8589 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8590 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8591 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8594 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8595 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8596 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8598 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8599 si->internal.program_version = getStringCopy(getProgramRealVersionString());
8600 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8601 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8602 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8603 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8604 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8606 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8608 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8609 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8610 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8612 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8613 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8614 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8616 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8617 si->internal.choose_from_top_leveldir = FALSE;
8618 si->internal.show_scaling_in_title = TRUE;
8620 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
8621 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8623 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8624 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8625 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8626 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8627 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8628 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8629 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8630 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8631 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8632 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8634 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8635 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8636 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8637 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8638 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8639 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8640 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8641 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8642 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8643 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8645 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8646 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
8648 si->debug.show_frames_per_second = FALSE;
8650 si->options.verbose = FALSE;
8652 #if defined(PLATFORM_ANDROID)
8653 si->fullscreen = TRUE;
8657 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8659 si->editor_cascade.el_bd = TRUE;
8660 si->editor_cascade.el_em = TRUE;
8661 si->editor_cascade.el_emc = TRUE;
8662 si->editor_cascade.el_rnd = TRUE;
8663 si->editor_cascade.el_sb = TRUE;
8664 si->editor_cascade.el_sp = TRUE;
8665 si->editor_cascade.el_dc = TRUE;
8666 si->editor_cascade.el_dx = TRUE;
8668 si->editor_cascade.el_mm = TRUE;
8669 si->editor_cascade.el_df = TRUE;
8671 si->editor_cascade.el_chars = FALSE;
8672 si->editor_cascade.el_steel_chars = FALSE;
8673 si->editor_cascade.el_ce = FALSE;
8674 si->editor_cascade.el_ge = FALSE;
8675 si->editor_cascade.el_ref = FALSE;
8676 si->editor_cascade.el_user = FALSE;
8677 si->editor_cascade.el_dynamic = FALSE;
8680 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
8682 static char *getHideSetupToken(void *setup_value)
8684 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
8686 if (setup_value != NULL)
8687 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
8689 return hide_setup_token;
8692 static void setHideSetupEntry(void *setup_value_raw)
8694 /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
8695 void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
8697 char *hide_setup_token = getHideSetupToken(setup_value);
8699 if (setup_value != NULL)
8700 setHashEntry(hide_setup_hash, hide_setup_token, "");
8703 boolean hideSetupEntry(void *setup_value)
8705 char *hide_setup_token = getHideSetupToken(setup_value);
8707 return (setup_value != NULL &&
8708 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
8711 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
8712 struct TokenInfo *token_info,
8713 int token_nr, char *token_text)
8715 char *token_hide_text = getStringCat2(token_text, ".hide");
8716 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
8718 /* set the value of this setup option in the setup option structure */
8719 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
8721 /* check if this setup option should be hidden in the setup menu */
8722 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
8723 setHideSetupEntry(token_info[token_nr].value);
8726 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
8727 struct TokenInfo *token_info,
8730 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
8731 token_info[token_nr].text);
8734 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8738 if (!setup_file_hash)
8741 if (hide_setup_hash == NULL)
8742 hide_setup_hash = newSetupFileHash();
8746 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8747 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
8752 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8753 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
8756 /* shortcut setup */
8757 ssi = setup.shortcut;
8758 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8759 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
8760 setup.shortcut = ssi;
8763 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8767 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8769 sii = setup.input[pnr];
8770 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8772 char full_token[100];
8774 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8775 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
8778 setup.input[pnr] = sii;
8783 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8784 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
8787 /* internal setup */
8788 sxi = setup.internal;
8789 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8790 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
8791 setup.internal = sxi;
8795 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8796 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
8800 soi = setup.options;
8801 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8802 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
8803 setup.options = soi;
8806 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8810 if (!setup_file_hash)
8813 /* editor cascade setup */
8814 seci = setup.editor_cascade;
8815 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8816 setSetupInfo(editor_cascade_setup_tokens, i,
8817 getHashEntry(setup_file_hash,
8818 editor_cascade_setup_tokens[i].text));
8819 setup.editor_cascade = seci;
8822 void LoadSetupFromFilename(char *filename)
8824 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
8826 if (setup_file_hash)
8828 decodeSetupFileHash(setup_file_hash);
8830 freeSetupFileHash(setup_file_hash);
8834 Error(ERR_DEBUG, "using default setup values");
8838 static void LoadSetup_SpecialPostProcessing()
8840 char *player_name_new;
8842 /* needed to work around problems with fixed length strings */
8843 player_name_new = get_corrected_login_name(setup.player_name);
8844 free(setup.player_name);
8845 setup.player_name = player_name_new;
8847 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
8848 if (setup.scroll_delay == FALSE)
8850 setup.scroll_delay_value = MIN_SCROLL_DELAY;
8851 setup.scroll_delay = TRUE; /* now always "on" */
8854 /* make sure that scroll delay value stays inside valid range */
8855 setup.scroll_delay_value =
8856 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
8863 /* always start with reliable default values */
8864 setSetupInfoToDefaults(&setup);
8866 /* try to load setup values from default setup file */
8867 filename = getDefaultSetupFilename();
8869 if (fileExists(filename))
8870 LoadSetupFromFilename(filename);
8872 /* try to load setup values from user setup file */
8873 filename = getSetupFilename();
8875 LoadSetupFromFilename(filename);
8877 LoadSetup_SpecialPostProcessing();
8880 void LoadSetup_EditorCascade()
8882 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8883 SetupFileHash *setup_file_hash = NULL;
8885 /* always start with reliable default values */
8886 setSetupInfoToDefaults_EditorCascade(&setup);
8888 setup_file_hash = loadSetupFileHash(filename);
8890 if (setup_file_hash)
8892 decodeSetupFileHash_EditorCascade(setup_file_hash);
8894 freeSetupFileHash(setup_file_hash);
8900 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
8903 char mapping_guid[MAX_LINE_LEN];
8904 char *mapping_start, *mapping_end;
8906 // get GUID from game controller mapping line: copy complete line
8907 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
8908 mapping_guid[MAX_LINE_LEN - 1] = '\0';
8910 // get GUID from game controller mapping line: cut after GUID part
8911 mapping_start = strchr(mapping_guid, ',');
8912 if (mapping_start != NULL)
8913 *mapping_start = '\0';
8915 // cut newline from game controller mapping line
8916 mapping_end = strchr(mapping_line, '\n');
8917 if (mapping_end != NULL)
8918 *mapping_end = '\0';
8920 // add mapping entry to game controller mappings hash
8921 setHashEntry(mappings_hash, mapping_guid, mapping_line);
8924 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
8929 if (!(file = fopen(filename, MODE_READ)))
8931 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
8938 char line[MAX_LINE_LEN];
8940 if (!fgets(line, MAX_LINE_LEN, file))
8943 addGameControllerMappingToHash(mappings_hash, line);
8951 char *filename = getSetupFilename();
8955 InitUserDataDirectory();
8957 if (!(file = fopen(filename, MODE_WRITE)))
8959 Error(ERR_WARN, "cannot write setup file '%s'", filename);
8963 fprintFileHeader(file, SETUP_FILENAME);
8967 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8969 /* just to make things nicer :) */
8970 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
8971 i == SETUP_TOKEN_GRAPHICS_SET ||
8972 i == SETUP_TOKEN_VOLUME_SIMPLE ||
8973 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
8974 fprintf(file, "\n");
8976 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
8981 fprintf(file, "\n");
8982 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8983 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
8985 /* shortcut setup */
8986 ssi = setup.shortcut;
8987 fprintf(file, "\n");
8988 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8989 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
8992 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8996 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8997 fprintf(file, "\n");
8999 sii = setup.input[pnr];
9000 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9001 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9006 fprintf(file, "\n");
9007 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9008 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9010 /* internal setup */
9011 /* (internal setup values not saved to user setup file) */
9015 fprintf(file, "\n");
9016 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9017 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9020 soi = setup.options;
9021 fprintf(file, "\n");
9022 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9023 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9027 SetFilePermissions(filename, PERMS_PRIVATE);
9030 void SaveSetup_EditorCascade()
9032 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9036 InitUserDataDirectory();
9038 if (!(file = fopen(filename, MODE_WRITE)))
9040 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9045 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9047 seci = setup.editor_cascade;
9048 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9049 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9053 SetFilePermissions(filename, PERMS_PRIVATE);
9058 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9063 if (!(file = fopen(filename, MODE_WRITE)))
9065 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9070 BEGIN_HASH_ITERATION(mappings_hash, itr)
9072 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9074 END_HASH_ITERATION(mappings_hash, itr)
9079 void SaveSetup_AddGameControllerMapping(char *mapping)
9081 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9082 SetupFileHash *mappings_hash = newSetupFileHash();
9084 InitUserDataDirectory();
9086 // load existing personal game controller mappings
9087 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9089 // add new mapping to personal game controller mappings
9090 addGameControllerMappingToHash(mappings_hash, mapping);
9092 // save updated personal game controller mappings
9093 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9095 freeSetupFileHash(mappings_hash);
9099 void LoadCustomElementDescriptions()
9101 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9102 SetupFileHash *setup_file_hash;
9105 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9107 if (element_info[i].custom_description != NULL)
9109 free(element_info[i].custom_description);
9110 element_info[i].custom_description = NULL;
9114 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9117 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9119 char *token = getStringCat2(element_info[i].token_name, ".name");
9120 char *value = getHashEntry(setup_file_hash, token);
9123 element_info[i].custom_description = getStringCopy(value);
9128 freeSetupFileHash(setup_file_hash);
9131 static int getElementFromToken(char *token)
9133 char *value = getHashEntry(element_token_hash, token);
9138 Error(ERR_WARN, "unknown element token '%s'", token);
9140 return EL_UNDEFINED;
9143 static int get_token_parameter_value(char *token, char *value_raw)
9147 if (token == NULL || value_raw == NULL)
9148 return ARG_UNDEFINED_VALUE;
9150 suffix = strrchr(token, '.');
9154 if (strEqual(suffix, ".element"))
9155 return getElementFromToken(value_raw);
9157 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9158 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9161 void InitMenuDesignSettings_Static()
9165 /* always start with reliable default values from static default config */
9166 for (i = 0; image_config_vars[i].token != NULL; i++)
9168 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9171 *image_config_vars[i].value =
9172 get_token_parameter_value(image_config_vars[i].token, value);
9176 static void InitMenuDesignSettings_SpecialPreProcessing()
9180 /* the following initializes hierarchical values from static configuration */
9182 /* special case: initialize "ARG_DEFAULT" values in static default config */
9183 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9184 titlescreen_initial_first_default.fade_mode =
9185 title_initial_first_default.fade_mode;
9186 titlescreen_initial_first_default.fade_delay =
9187 title_initial_first_default.fade_delay;
9188 titlescreen_initial_first_default.post_delay =
9189 title_initial_first_default.post_delay;
9190 titlescreen_initial_first_default.auto_delay =
9191 title_initial_first_default.auto_delay;
9192 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
9193 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9194 titlescreen_first_default.post_delay = title_first_default.post_delay;
9195 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9196 titlemessage_initial_first_default.fade_mode =
9197 title_initial_first_default.fade_mode;
9198 titlemessage_initial_first_default.fade_delay =
9199 title_initial_first_default.fade_delay;
9200 titlemessage_initial_first_default.post_delay =
9201 title_initial_first_default.post_delay;
9202 titlemessage_initial_first_default.auto_delay =
9203 title_initial_first_default.auto_delay;
9204 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
9205 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9206 titlemessage_first_default.post_delay = title_first_default.post_delay;
9207 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9209 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
9210 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9211 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9212 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9213 titlescreen_default.fade_mode = title_default.fade_mode;
9214 titlescreen_default.fade_delay = title_default.fade_delay;
9215 titlescreen_default.post_delay = title_default.post_delay;
9216 titlescreen_default.auto_delay = title_default.auto_delay;
9217 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9218 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9219 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9220 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9221 titlemessage_default.fade_mode = title_default.fade_mode;
9222 titlemessage_default.fade_delay = title_default.fade_delay;
9223 titlemessage_default.post_delay = title_default.post_delay;
9224 titlemessage_default.auto_delay = title_default.auto_delay;
9226 /* special case: initialize "ARG_DEFAULT" values in static default config */
9227 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9228 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9230 titlescreen_initial_first[i] = titlescreen_initial_first_default;
9231 titlescreen_first[i] = titlescreen_first_default;
9232 titlemessage_initial_first[i] = titlemessage_initial_first_default;
9233 titlemessage_first[i] = titlemessage_first_default;
9235 titlescreen_initial[i] = titlescreen_initial_default;
9236 titlescreen[i] = titlescreen_default;
9237 titlemessage_initial[i] = titlemessage_initial_default;
9238 titlemessage[i] = titlemessage_default;
9241 /* special case: initialize "ARG_DEFAULT" values in static default config */
9242 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9243 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9245 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
9248 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9249 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9250 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9253 /* special case: initialize "ARG_DEFAULT" values in static default config */
9254 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9255 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9257 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9258 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9259 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9261 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
9264 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9268 static void InitMenuDesignSettings_SpecialPostProcessing()
9272 struct XY *dst, *src;
9276 { &game.button.save, &game.button.stop },
9277 { &game.button.pause2, &game.button.pause },
9278 { &game.button.load, &game.button.play },
9279 { &game.button.undo, &game.button.stop },
9280 { &game.button.redo, &game.button.play },
9286 /* special case: initialize later added SETUP list size from LEVELS value */
9287 if (menu.list_size[GAME_MODE_SETUP] == -1)
9288 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9290 /* set default position for snapshot buttons to stop/pause/play buttons */
9291 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9292 if ((*game_buttons_xy[i].dst).x == -1 &&
9293 (*game_buttons_xy[i].dst).y == -1)
9294 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9297 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9301 struct XYTileSize *dst, *src;
9304 editor_buttons_xy[] =
9307 &editor.button.element_left, &editor.palette.element_left,
9308 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9311 &editor.button.element_middle, &editor.palette.element_middle,
9312 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9315 &editor.button.element_right, &editor.palette.element_right,
9316 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9323 /* set default position for element buttons to element graphics */
9324 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9326 if ((*editor_buttons_xy[i].dst).x == -1 &&
9327 (*editor_buttons_xy[i].dst).y == -1)
9329 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9331 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9333 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9338 static void LoadMenuDesignSettingsFromFilename(char *filename)
9340 static struct TitleFadingInfo tfi;
9341 static struct TitleMessageInfo tmi;
9342 static struct TokenInfo title_tokens[] =
9344 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9345 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9346 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9347 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9351 static struct TokenInfo titlemessage_tokens[] =
9353 { TYPE_INTEGER, &tmi.x, ".x" },
9354 { TYPE_INTEGER, &tmi.y, ".y" },
9355 { TYPE_INTEGER, &tmi.width, ".width" },
9356 { TYPE_INTEGER, &tmi.height, ".height" },
9357 { TYPE_INTEGER, &tmi.chars, ".chars" },
9358 { TYPE_INTEGER, &tmi.lines, ".lines" },
9359 { TYPE_INTEGER, &tmi.align, ".align" },
9360 { TYPE_INTEGER, &tmi.valign, ".valign" },
9361 { TYPE_INTEGER, &tmi.font, ".font" },
9362 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9363 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9364 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9365 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9366 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9367 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9368 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9369 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9375 struct TitleFadingInfo *info;
9380 /* initialize first titles from "enter screen" definitions, if defined */
9381 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9382 { &title_first_default, "menu.enter_screen.TITLE" },
9384 /* initialize title screens from "next screen" definitions, if defined */
9385 { &title_initial_default, "menu.next_screen.TITLE" },
9386 { &title_default, "menu.next_screen.TITLE" },
9392 struct TitleMessageInfo *array;
9395 titlemessage_arrays[] =
9397 /* initialize first titles from "enter screen" definitions, if defined */
9398 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9399 { titlescreen_first, "menu.enter_screen.TITLE" },
9400 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9401 { titlemessage_first, "menu.enter_screen.TITLE" },
9403 /* initialize titles from "next screen" definitions, if defined */
9404 { titlescreen_initial, "menu.next_screen.TITLE" },
9405 { titlescreen, "menu.next_screen.TITLE" },
9406 { titlemessage_initial, "menu.next_screen.TITLE" },
9407 { titlemessage, "menu.next_screen.TITLE" },
9409 /* overwrite titles with title definitions, if defined */
9410 { titlescreen_initial_first, "[title_initial]" },
9411 { titlescreen_first, "[title]" },
9412 { titlemessage_initial_first, "[title_initial]" },
9413 { titlemessage_first, "[title]" },
9415 { titlescreen_initial, "[title_initial]" },
9416 { titlescreen, "[title]" },
9417 { titlemessage_initial, "[title_initial]" },
9418 { titlemessage, "[title]" },
9420 /* overwrite titles with title screen/message definitions, if defined */
9421 { titlescreen_initial_first, "[titlescreen_initial]" },
9422 { titlescreen_first, "[titlescreen]" },
9423 { titlemessage_initial_first, "[titlemessage_initial]" },
9424 { titlemessage_first, "[titlemessage]" },
9426 { titlescreen_initial, "[titlescreen_initial]" },
9427 { titlescreen, "[titlescreen]" },
9428 { titlemessage_initial, "[titlemessage_initial]" },
9429 { titlemessage, "[titlemessage]" },
9433 SetupFileHash *setup_file_hash;
9436 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9439 /* the following initializes hierarchical values from dynamic configuration */
9441 /* special case: initialize with default values that may be overwritten */
9442 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9443 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9445 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9446 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9447 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9449 if (value_1 != NULL)
9450 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9451 if (value_2 != NULL)
9452 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9453 if (value_3 != NULL)
9454 menu.list_size[i] = get_integer_from_string(value_3);
9457 /* special case: initialize with default values that may be overwritten */
9458 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9459 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9461 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9462 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9464 if (value_1 != NULL)
9465 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9466 if (value_2 != NULL)
9467 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9469 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9471 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9473 if (value_1 != NULL)
9474 menu.list_size_info[i] = get_integer_from_string(value_1);
9478 /* special case: initialize with default values that may be overwritten */
9479 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9480 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9482 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9483 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9485 if (value_1 != NULL)
9486 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9487 if (value_2 != NULL)
9488 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9491 /* special case: initialize with default values that may be overwritten */
9492 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9493 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9495 char *token_1 = "menu.enter_screen.fade_mode";
9496 char *token_2 = "menu.enter_screen.fade_delay";
9497 char *token_3 = "menu.enter_screen.post_delay";
9498 char *token_4 = "menu.leave_screen.fade_mode";
9499 char *token_5 = "menu.leave_screen.fade_delay";
9500 char *token_6 = "menu.leave_screen.post_delay";
9501 char *token_7 = "menu.next_screen.fade_mode";
9502 char *token_8 = "menu.next_screen.fade_delay";
9503 char *token_9 = "menu.next_screen.post_delay";
9504 char *value_1 = getHashEntry(setup_file_hash, token_1);
9505 char *value_2 = getHashEntry(setup_file_hash, token_2);
9506 char *value_3 = getHashEntry(setup_file_hash, token_3);
9507 char *value_4 = getHashEntry(setup_file_hash, token_4);
9508 char *value_5 = getHashEntry(setup_file_hash, token_5);
9509 char *value_6 = getHashEntry(setup_file_hash, token_6);
9510 char *value_7 = getHashEntry(setup_file_hash, token_7);
9511 char *value_8 = getHashEntry(setup_file_hash, token_8);
9512 char *value_9 = getHashEntry(setup_file_hash, token_9);
9514 if (value_1 != NULL)
9515 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9517 if (value_2 != NULL)
9518 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9520 if (value_3 != NULL)
9521 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9523 if (value_4 != NULL)
9524 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9526 if (value_5 != NULL)
9527 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9529 if (value_6 != NULL)
9530 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9532 if (value_7 != NULL)
9533 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9535 if (value_8 != NULL)
9536 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9538 if (value_9 != NULL)
9539 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9543 /* special case: initialize with default values that may be overwritten */
9544 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9545 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9547 char *token_w1 = "viewport.window.width";
9548 char *token_w2 = "viewport.window.height";
9549 char *token_01 = "viewport.playfield.x";
9550 char *token_02 = "viewport.playfield.y";
9551 char *token_03 = "viewport.playfield.width";
9552 char *token_04 = "viewport.playfield.height";
9553 char *token_05 = "viewport.playfield.border_size";
9554 char *token_06 = "viewport.door_1.x";
9555 char *token_07 = "viewport.door_1.y";
9556 char *token_08 = "viewport.door_1.width";
9557 char *token_09 = "viewport.door_1.height";
9558 char *token_10 = "viewport.door_1.border_size";
9559 char *token_11 = "viewport.door_2.x";
9560 char *token_12 = "viewport.door_2.y";
9561 char *token_13 = "viewport.door_2.width";
9562 char *token_14 = "viewport.door_2.height";
9563 char *token_15 = "viewport.door_2.border_size";
9564 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9565 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9566 char *value_01 = getHashEntry(setup_file_hash, token_01);
9567 char *value_02 = getHashEntry(setup_file_hash, token_02);
9568 char *value_03 = getHashEntry(setup_file_hash, token_03);
9569 char *value_04 = getHashEntry(setup_file_hash, token_04);
9570 char *value_05 = getHashEntry(setup_file_hash, token_05);
9571 char *value_06 = getHashEntry(setup_file_hash, token_06);
9572 char *value_07 = getHashEntry(setup_file_hash, token_07);
9573 char *value_08 = getHashEntry(setup_file_hash, token_08);
9574 char *value_09 = getHashEntry(setup_file_hash, token_09);
9575 char *value_10 = getHashEntry(setup_file_hash, token_10);
9576 char *value_11 = getHashEntry(setup_file_hash, token_11);
9577 char *value_12 = getHashEntry(setup_file_hash, token_12);
9578 char *value_13 = getHashEntry(setup_file_hash, token_13);
9579 char *value_14 = getHashEntry(setup_file_hash, token_14);
9580 char *value_15 = getHashEntry(setup_file_hash, token_15);
9582 if (value_w1 != NULL)
9583 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9584 if (value_w2 != NULL)
9585 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9586 if (value_01 != NULL)
9587 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9588 if (value_02 != NULL)
9589 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9590 if (value_03 != NULL)
9591 viewport.playfield[i].width = get_token_parameter_value(token_03,
9593 if (value_04 != NULL)
9594 viewport.playfield[i].height = get_token_parameter_value(token_04,
9596 if (value_05 != NULL)
9597 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9599 if (value_06 != NULL)
9600 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9601 if (value_07 != NULL)
9602 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9603 if (value_08 != NULL)
9604 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9605 if (value_09 != NULL)
9606 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9607 if (value_10 != NULL)
9608 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9610 if (value_11 != NULL)
9611 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9612 if (value_12 != NULL)
9613 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9614 if (value_13 != NULL)
9615 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9616 if (value_14 != NULL)
9617 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9618 if (value_15 != NULL)
9619 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9623 /* special case: initialize with default values that may be overwritten */
9624 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9625 for (i = 0; title_info[i].info != NULL; i++)
9627 struct TitleFadingInfo *info = title_info[i].info;
9628 char *base_token = title_info[i].text;
9630 for (j = 0; title_tokens[j].type != -1; j++)
9632 char *token = getStringCat2(base_token, title_tokens[j].text);
9633 char *value = getHashEntry(setup_file_hash, token);
9637 int parameter_value = get_token_parameter_value(token, value);
9641 *(int *)title_tokens[j].value = (int)parameter_value;
9650 /* special case: initialize with default values that may be overwritten */
9651 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9652 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9654 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9655 char *base_token = titlemessage_arrays[i].text;
9657 for (j = 0; titlemessage_tokens[j].type != -1; j++)
9659 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9660 char *value = getHashEntry(setup_file_hash, token);
9664 int parameter_value = get_token_parameter_value(token, value);
9666 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9670 if (titlemessage_tokens[j].type == TYPE_INTEGER)
9671 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
9673 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9683 /* read (and overwrite with) values that may be specified in config file */
9684 for (i = 0; image_config_vars[i].token != NULL; i++)
9686 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9688 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9689 if (value != NULL && !strEqual(value, ARG_DEFAULT))
9690 *image_config_vars[i].value =
9691 get_token_parameter_value(image_config_vars[i].token, value);
9694 freeSetupFileHash(setup_file_hash);
9697 void LoadMenuDesignSettings()
9699 char *filename_base = UNDEFINED_FILENAME, *filename_local;
9701 InitMenuDesignSettings_Static();
9702 InitMenuDesignSettings_SpecialPreProcessing();
9704 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9706 /* first look for special settings configured in level series config */
9707 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9709 if (fileExists(filename_base))
9710 LoadMenuDesignSettingsFromFilename(filename_base);
9713 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9715 if (filename_local != NULL && !strEqual(filename_base, filename_local))
9716 LoadMenuDesignSettingsFromFilename(filename_local);
9718 InitMenuDesignSettings_SpecialPostProcessing();
9721 void LoadMenuDesignSettings_AfterGraphics()
9723 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9726 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9728 char *filename = getEditorSetupFilename();
9729 SetupFileList *setup_file_list, *list;
9730 SetupFileHash *element_hash;
9731 int num_unknown_tokens = 0;
9734 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9737 element_hash = newSetupFileHash();
9739 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9740 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9742 /* determined size may be larger than needed (due to unknown elements) */
9744 for (list = setup_file_list; list != NULL; list = list->next)
9747 /* add space for up to 3 more elements for padding that may be needed */
9750 /* free memory for old list of elements, if needed */
9751 checked_free(*elements);
9753 /* allocate memory for new list of elements */
9754 *elements = checked_malloc(*num_elements * sizeof(int));
9757 for (list = setup_file_list; list != NULL; list = list->next)
9759 char *value = getHashEntry(element_hash, list->token);
9761 if (value == NULL) /* try to find obsolete token mapping */
9763 char *mapped_token = get_mapped_token(list->token);
9765 if (mapped_token != NULL)
9767 value = getHashEntry(element_hash, mapped_token);
9775 (*elements)[(*num_elements)++] = atoi(value);
9779 if (num_unknown_tokens == 0)
9781 Error(ERR_INFO_LINE, "-");
9782 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9783 Error(ERR_INFO, "- config file: '%s'", filename);
9785 num_unknown_tokens++;
9788 Error(ERR_INFO, "- token: '%s'", list->token);
9792 if (num_unknown_tokens > 0)
9793 Error(ERR_INFO_LINE, "-");
9795 while (*num_elements % 4) /* pad with empty elements, if needed */
9796 (*elements)[(*num_elements)++] = EL_EMPTY;
9798 freeSetupFileList(setup_file_list);
9799 freeSetupFileHash(element_hash);
9802 for (i = 0; i < *num_elements; i++)
9803 printf("editor: element '%s' [%d]\n",
9804 element_info[(*elements)[i]].token_name, (*elements)[i]);
9808 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9811 SetupFileHash *setup_file_hash = NULL;
9812 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9813 char *filename_music, *filename_prefix, *filename_info;
9819 token_to_value_ptr[] =
9821 { "title_header", &tmp_music_file_info.title_header },
9822 { "artist_header", &tmp_music_file_info.artist_header },
9823 { "album_header", &tmp_music_file_info.album_header },
9824 { "year_header", &tmp_music_file_info.year_header },
9826 { "title", &tmp_music_file_info.title },
9827 { "artist", &tmp_music_file_info.artist },
9828 { "album", &tmp_music_file_info.album },
9829 { "year", &tmp_music_file_info.year },
9835 filename_music = (is_sound ? getCustomSoundFilename(basename) :
9836 getCustomMusicFilename(basename));
9838 if (filename_music == NULL)
9841 /* ---------- try to replace file extension ---------- */
9843 filename_prefix = getStringCopy(filename_music);
9844 if (strrchr(filename_prefix, '.') != NULL)
9845 *strrchr(filename_prefix, '.') = '\0';
9846 filename_info = getStringCat2(filename_prefix, ".txt");
9848 if (fileExists(filename_info))
9849 setup_file_hash = loadSetupFileHash(filename_info);
9851 free(filename_prefix);
9852 free(filename_info);
9854 if (setup_file_hash == NULL)
9856 /* ---------- try to add file extension ---------- */
9858 filename_prefix = getStringCopy(filename_music);
9859 filename_info = getStringCat2(filename_prefix, ".txt");
9861 if (fileExists(filename_info))
9862 setup_file_hash = loadSetupFileHash(filename_info);
9864 free(filename_prefix);
9865 free(filename_info);
9868 if (setup_file_hash == NULL)
9871 /* ---------- music file info found ---------- */
9873 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
9875 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
9877 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
9879 *token_to_value_ptr[i].value_ptr =
9880 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
9883 tmp_music_file_info.basename = getStringCopy(basename);
9884 tmp_music_file_info.music = music;
9885 tmp_music_file_info.is_sound = is_sound;
9887 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
9888 *new_music_file_info = tmp_music_file_info;
9890 return new_music_file_info;
9893 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
9895 return get_music_file_info_ext(basename, music, FALSE);
9898 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
9900 return get_music_file_info_ext(basename, sound, TRUE);
9903 static boolean music_info_listed_ext(struct MusicFileInfo *list,
9904 char *basename, boolean is_sound)
9906 for (; list != NULL; list = list->next)
9907 if (list->is_sound == is_sound && strEqual(list->basename, basename))
9913 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
9915 return music_info_listed_ext(list, basename, FALSE);
9918 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
9920 return music_info_listed_ext(list, basename, TRUE);
9923 void LoadMusicInfo()
9925 char *music_directory = getCustomMusicDirectory();
9926 int num_music = getMusicListSize();
9927 int num_music_noconf = 0;
9928 int num_sounds = getSoundListSize();
9930 DirectoryEntry *dir_entry;
9931 struct FileInfo *music, *sound;
9932 struct MusicFileInfo *next, **new;
9935 while (music_file_info != NULL)
9937 next = music_file_info->next;
9939 checked_free(music_file_info->basename);
9941 checked_free(music_file_info->title_header);
9942 checked_free(music_file_info->artist_header);
9943 checked_free(music_file_info->album_header);
9944 checked_free(music_file_info->year_header);
9946 checked_free(music_file_info->title);
9947 checked_free(music_file_info->artist);
9948 checked_free(music_file_info->album);
9949 checked_free(music_file_info->year);
9951 free(music_file_info);
9953 music_file_info = next;
9956 new = &music_file_info;
9958 for (i = 0; i < num_music; i++)
9960 music = getMusicListEntry(i);
9962 if (music->filename == NULL)
9965 if (strEqual(music->filename, UNDEFINED_FILENAME))
9968 /* a configured file may be not recognized as music */
9969 if (!FileIsMusic(music->filename))
9972 if (!music_info_listed(music_file_info, music->filename))
9974 *new = get_music_file_info(music->filename, i);
9977 new = &(*new)->next;
9981 if ((dir = openDirectory(music_directory)) == NULL)
9983 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
9987 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
9989 char *basename = dir_entry->basename;
9990 boolean music_already_used = FALSE;
9993 /* skip all music files that are configured in music config file */
9994 for (i = 0; i < num_music; i++)
9996 music = getMusicListEntry(i);
9998 if (music->filename == NULL)
10001 if (strEqual(basename, music->filename))
10003 music_already_used = TRUE;
10008 if (music_already_used)
10011 if (!FileIsMusic(dir_entry->filename))
10014 if (!music_info_listed(music_file_info, basename))
10016 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10019 new = &(*new)->next;
10022 num_music_noconf++;
10025 closeDirectory(dir);
10027 for (i = 0; i < num_sounds; i++)
10029 sound = getSoundListEntry(i);
10031 if (sound->filename == NULL)
10034 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10037 /* a configured file may be not recognized as sound */
10038 if (!FileIsSound(sound->filename))
10041 if (!sound_info_listed(music_file_info, sound->filename))
10043 *new = get_sound_file_info(sound->filename, i);
10045 new = &(*new)->next;
10050 void add_helpanim_entry(int element, int action, int direction, int delay,
10051 int *num_list_entries)
10053 struct HelpAnimInfo *new_list_entry;
10054 (*num_list_entries)++;
10057 checked_realloc(helpanim_info,
10058 *num_list_entries * sizeof(struct HelpAnimInfo));
10059 new_list_entry = &helpanim_info[*num_list_entries - 1];
10061 new_list_entry->element = element;
10062 new_list_entry->action = action;
10063 new_list_entry->direction = direction;
10064 new_list_entry->delay = delay;
10067 void print_unknown_token(char *filename, char *token, int token_nr)
10071 Error(ERR_INFO_LINE, "-");
10072 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10073 Error(ERR_INFO, "- config file: '%s'", filename);
10076 Error(ERR_INFO, "- token: '%s'", token);
10079 void print_unknown_token_end(int token_nr)
10082 Error(ERR_INFO_LINE, "-");
10085 void LoadHelpAnimInfo()
10087 char *filename = getHelpAnimFilename();
10088 SetupFileList *setup_file_list = NULL, *list;
10089 SetupFileHash *element_hash, *action_hash, *direction_hash;
10090 int num_list_entries = 0;
10091 int num_unknown_tokens = 0;
10094 if (fileExists(filename))
10095 setup_file_list = loadSetupFileList(filename);
10097 if (setup_file_list == NULL)
10099 /* use reliable default values from static configuration */
10100 SetupFileList *insert_ptr;
10102 insert_ptr = setup_file_list =
10103 newSetupFileList(helpanim_config[0].token,
10104 helpanim_config[0].value);
10106 for (i = 1; helpanim_config[i].token; i++)
10107 insert_ptr = addListEntry(insert_ptr,
10108 helpanim_config[i].token,
10109 helpanim_config[i].value);
10112 element_hash = newSetupFileHash();
10113 action_hash = newSetupFileHash();
10114 direction_hash = newSetupFileHash();
10116 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10117 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10119 for (i = 0; i < NUM_ACTIONS; i++)
10120 setHashEntry(action_hash, element_action_info[i].suffix,
10121 i_to_a(element_action_info[i].value));
10123 /* do not store direction index (bit) here, but direction value! */
10124 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10125 setHashEntry(direction_hash, element_direction_info[i].suffix,
10126 i_to_a(1 << element_direction_info[i].value));
10128 for (list = setup_file_list; list != NULL; list = list->next)
10130 char *element_token, *action_token, *direction_token;
10131 char *element_value, *action_value, *direction_value;
10132 int delay = atoi(list->value);
10134 if (strEqual(list->token, "end"))
10136 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10141 /* first try to break element into element/action/direction parts;
10142 if this does not work, also accept combined "element[.act][.dir]"
10143 elements (like "dynamite.active"), which are unique elements */
10145 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10147 element_value = getHashEntry(element_hash, list->token);
10148 if (element_value != NULL) /* element found */
10149 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10150 &num_list_entries);
10153 /* no further suffixes found -- this is not an element */
10154 print_unknown_token(filename, list->token, num_unknown_tokens++);
10160 /* token has format "<prefix>.<something>" */
10162 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10163 direction_token = action_token; /* ... or direction */
10165 element_token = getStringCopy(list->token);
10166 *strchr(element_token, '.') = '\0';
10168 element_value = getHashEntry(element_hash, element_token);
10170 if (element_value == NULL) /* this is no element */
10172 element_value = getHashEntry(element_hash, list->token);
10173 if (element_value != NULL) /* combined element found */
10174 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10175 &num_list_entries);
10177 print_unknown_token(filename, list->token, num_unknown_tokens++);
10179 free(element_token);
10184 action_value = getHashEntry(action_hash, action_token);
10186 if (action_value != NULL) /* action found */
10188 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10189 &num_list_entries);
10191 free(element_token);
10196 direction_value = getHashEntry(direction_hash, direction_token);
10198 if (direction_value != NULL) /* direction found */
10200 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10201 &num_list_entries);
10203 free(element_token);
10208 if (strchr(action_token + 1, '.') == NULL)
10210 /* no further suffixes found -- this is not an action nor direction */
10212 element_value = getHashEntry(element_hash, list->token);
10213 if (element_value != NULL) /* combined element found */
10214 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10215 &num_list_entries);
10217 print_unknown_token(filename, list->token, num_unknown_tokens++);
10219 free(element_token);
10224 /* token has format "<prefix>.<suffix>.<something>" */
10226 direction_token = strchr(action_token + 1, '.');
10228 action_token = getStringCopy(action_token);
10229 *strchr(action_token + 1, '.') = '\0';
10231 action_value = getHashEntry(action_hash, action_token);
10233 if (action_value == NULL) /* this is no action */
10235 element_value = getHashEntry(element_hash, list->token);
10236 if (element_value != NULL) /* combined element found */
10237 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10238 &num_list_entries);
10240 print_unknown_token(filename, list->token, num_unknown_tokens++);
10242 free(element_token);
10243 free(action_token);
10248 direction_value = getHashEntry(direction_hash, direction_token);
10250 if (direction_value != NULL) /* direction found */
10252 add_helpanim_entry(atoi(element_value), atoi(action_value),
10253 atoi(direction_value), delay, &num_list_entries);
10255 free(element_token);
10256 free(action_token);
10261 /* this is no direction */
10263 element_value = getHashEntry(element_hash, list->token);
10264 if (element_value != NULL) /* combined element found */
10265 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10266 &num_list_entries);
10268 print_unknown_token(filename, list->token, num_unknown_tokens++);
10270 free(element_token);
10271 free(action_token);
10274 print_unknown_token_end(num_unknown_tokens);
10276 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10277 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10279 freeSetupFileList(setup_file_list);
10280 freeSetupFileHash(element_hash);
10281 freeSetupFileHash(action_hash);
10282 freeSetupFileHash(direction_hash);
10285 for (i = 0; i < num_list_entries; i++)
10286 printf("::: '%s': %d, %d, %d => %d\n",
10287 EL_NAME(helpanim_info[i].element),
10288 helpanim_info[i].element,
10289 helpanim_info[i].action,
10290 helpanim_info[i].direction,
10291 helpanim_info[i].delay);
10295 void LoadHelpTextInfo()
10297 char *filename = getHelpTextFilename();
10300 if (helptext_info != NULL)
10302 freeSetupFileHash(helptext_info);
10303 helptext_info = NULL;
10306 if (fileExists(filename))
10307 helptext_info = loadSetupFileHash(filename);
10309 if (helptext_info == NULL)
10311 /* use reliable default values from static configuration */
10312 helptext_info = newSetupFileHash();
10314 for (i = 0; helptext_config[i].token; i++)
10315 setHashEntry(helptext_info,
10316 helptext_config[i].token,
10317 helptext_config[i].value);
10321 BEGIN_HASH_ITERATION(helptext_info, itr)
10323 printf("::: '%s' => '%s'\n",
10324 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10326 END_HASH_ITERATION(hash, itr)
10331 /* ------------------------------------------------------------------------- */
10332 /* convert levels */
10333 /* ------------------------------------------------------------------------- */
10335 #define MAX_NUM_CONVERT_LEVELS 1000
10337 void ConvertLevels()
10339 static LevelDirTree *convert_leveldir = NULL;
10340 static int convert_level_nr = -1;
10341 static int num_levels_handled = 0;
10342 static int num_levels_converted = 0;
10343 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10346 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10347 global.convert_leveldir);
10349 if (convert_leveldir == NULL)
10350 Error(ERR_EXIT, "no such level identifier: '%s'",
10351 global.convert_leveldir);
10353 leveldir_current = convert_leveldir;
10355 if (global.convert_level_nr != -1)
10357 convert_leveldir->first_level = global.convert_level_nr;
10358 convert_leveldir->last_level = global.convert_level_nr;
10361 convert_level_nr = convert_leveldir->first_level;
10363 PrintLine("=", 79);
10364 Print("Converting levels\n");
10365 PrintLine("-", 79);
10366 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10367 Print("Level series name: '%s'\n", convert_leveldir->name);
10368 Print("Level series author: '%s'\n", convert_leveldir->author);
10369 Print("Number of levels: %d\n", convert_leveldir->levels);
10370 PrintLine("=", 79);
10373 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10374 levels_failed[i] = FALSE;
10376 while (convert_level_nr <= convert_leveldir->last_level)
10378 char *level_filename;
10381 level_nr = convert_level_nr++;
10383 Print("Level %03d: ", level_nr);
10385 LoadLevel(level_nr);
10386 if (level.no_level_file || level.no_valid_file)
10388 Print("(no level)\n");
10392 Print("converting level ... ");
10394 level_filename = getDefaultLevelFilename(level_nr);
10395 new_level = !fileExists(level_filename);
10399 SaveLevel(level_nr);
10401 num_levels_converted++;
10403 Print("converted.\n");
10407 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10408 levels_failed[level_nr] = TRUE;
10410 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10413 num_levels_handled++;
10417 PrintLine("=", 79);
10418 Print("Number of levels handled: %d\n", num_levels_handled);
10419 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10420 (num_levels_handled ?
10421 num_levels_converted * 100 / num_levels_handled : 0));
10422 PrintLine("-", 79);
10423 Print("Summary (for automatic parsing by scripts):\n");
10424 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10425 convert_leveldir->identifier, num_levels_converted,
10426 num_levels_handled,
10427 (num_levels_handled ?
10428 num_levels_converted * 100 / num_levels_handled : 0));
10430 if (num_levels_handled != num_levels_converted)
10432 Print(", FAILED:");
10433 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10434 if (levels_failed[i])
10439 PrintLine("=", 79);
10441 CloseAllAndExit(0);
10445 /* ------------------------------------------------------------------------- */
10446 /* create and save images for use in level sketches (raw BMP format) */
10447 /* ------------------------------------------------------------------------- */
10449 void CreateLevelSketchImages()
10451 #if defined(TARGET_SDL)
10456 InitElementPropertiesGfxElement();
10458 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10459 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10461 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10463 Bitmap *src_bitmap;
10465 int element = getMappedElement(i);
10466 int graphic = el2edimg(element);
10467 char basename1[16];
10468 char basename2[16];
10472 sprintf(basename1, "%03d.bmp", i);
10473 sprintf(basename2, "%03ds.bmp", i);
10475 filename1 = getPath2(global.create_images_dir, basename1);
10476 filename2 = getPath2(global.create_images_dir, basename2);
10478 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10479 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10482 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10483 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10485 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10486 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10488 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10489 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10495 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10498 FreeBitmap(bitmap1);
10499 FreeBitmap(bitmap2);
10504 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10506 CloseAllAndExit(0);
10511 /* ------------------------------------------------------------------------- */
10512 /* create and save images for custom and group elements (raw BMP format) */
10513 /* ------------------------------------------------------------------------- */
10515 void CreateCustomElementImages(char *directory)
10517 #if defined(TARGET_SDL)
10518 char *src_basename = "RocksCE-template.ilbm";
10519 char *dst_basename = "RocksCE.bmp";
10520 char *src_filename = getPath2(directory, src_basename);
10521 char *dst_filename = getPath2(directory, dst_basename);
10522 Bitmap *src_bitmap;
10524 int yoffset_ce = 0;
10525 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10528 SDLInitVideoDisplay();
10530 ReCreateBitmap(&backbuffer, video.width, video.height);
10532 src_bitmap = LoadImage(src_filename);
10534 bitmap = CreateBitmap(TILEX * 16 * 2,
10535 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10538 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10545 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10546 TILEX * x, TILEY * y + yoffset_ce);
10548 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10550 TILEX * x + TILEX * 16,
10551 TILEY * y + yoffset_ce);
10553 for (j = 2; j >= 0; j--)
10557 BlitBitmap(src_bitmap, bitmap,
10558 TILEX + c * 7, 0, 6, 10,
10559 TILEX * x + 6 + j * 7,
10560 TILEY * y + 11 + yoffset_ce);
10562 BlitBitmap(src_bitmap, bitmap,
10563 TILEX + c * 8, TILEY, 6, 10,
10564 TILEX * 16 + TILEX * x + 6 + j * 8,
10565 TILEY * y + 10 + yoffset_ce);
10571 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10578 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10579 TILEX * x, TILEY * y + yoffset_ge);
10581 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10583 TILEX * x + TILEX * 16,
10584 TILEY * y + yoffset_ge);
10586 for (j = 1; j >= 0; j--)
10590 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10591 TILEX * x + 6 + j * 10,
10592 TILEY * y + 11 + yoffset_ge);
10594 BlitBitmap(src_bitmap, bitmap,
10595 TILEX + c * 8, TILEY + 12, 6, 10,
10596 TILEX * 16 + TILEX * x + 10 + j * 8,
10597 TILEY * y + 10 + yoffset_ge);
10603 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10604 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10606 FreeBitmap(bitmap);
10608 CloseAllAndExit(0);