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"
24 #define ENABLE_UNUSED_CODE 0 /* currently unused functions */
25 #define ENABLE_HISTORIC_CHUNKS 0 /* only for historic reference */
26 #define ENABLE_RESERVED_CODE 0 /* reserved for later use */
28 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
29 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
30 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
32 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
33 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
35 #define LEVEL_CHUNK_VERS_SIZE 8 /* size of file version chunk */
36 #define LEVEL_CHUNK_DATE_SIZE 4 /* size of file date chunk */
37 #define LEVEL_CHUNK_HEAD_SIZE 80 /* size of level file header */
38 #define LEVEL_CHUNK_HEAD_UNUSED 0 /* unused level header bytes */
39 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
40 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
41 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
42 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
43 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
44 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
45 #define LEVEL_CHUNK_GRP1_SIZE 74 /* size of level GRP1 chunk */
47 /* (element number, number of change pages, change page number) */
48 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
50 /* (element number only) */
51 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
52 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
54 /* (nothing at all if unchanged) */
55 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
57 #define TAPE_CHUNK_VERS_SIZE 8 /* size of file version chunk */
58 #define TAPE_CHUNK_HEAD_SIZE 20 /* size of tape file header */
59 #define TAPE_CHUNK_HEAD_UNUSED 3 /* unused tape header bytes */
61 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
62 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
63 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
65 /* file identifier strings */
66 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
67 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
68 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
70 /* values for deciding when (not) to save configuration data */
71 #define SAVE_CONF_NEVER 0
72 #define SAVE_CONF_ALWAYS 1
73 #define SAVE_CONF_WHEN_CHANGED -1
75 /* values for chunks using micro chunks */
76 #define CONF_MASK_1_BYTE 0x00
77 #define CONF_MASK_2_BYTE 0x40
78 #define CONF_MASK_4_BYTE 0x80
79 #define CONF_MASK_MULTI_BYTES 0xc0
81 #define CONF_MASK_BYTES 0xc0
82 #define CONF_MASK_TOKEN 0x3f
84 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
85 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
86 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
87 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
89 /* these definitions are just for convenience of use and readability */
90 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
91 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
92 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
93 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
95 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
96 (x) == CONF_MASK_2_BYTE ? 2 : \
97 (x) == CONF_MASK_4_BYTE ? 4 : 0)
99 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
100 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
101 #define CONF_ELEMENT_NUM_BYTES (2)
103 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
104 (t) == TYPE_ELEMENT_LIST ? \
105 CONF_ELEMENT_NUM_BYTES : \
106 (t) == TYPE_CONTENT || \
107 (t) == TYPE_CONTENT_LIST ? \
108 CONF_CONTENT_NUM_BYTES : 1)
110 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
111 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
112 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
114 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
116 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
117 CONF_ELEMENT_NUM_BYTES)
118 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
119 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
121 /* temporary variables used to store pointers to structure members */
122 static struct LevelInfo li;
123 static struct ElementInfo xx_ei, yy_ei;
124 static struct ElementChangeInfo xx_change;
125 static struct ElementGroupInfo xx_group;
126 static struct EnvelopeInfo xx_envelope;
127 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
128 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
129 static int xx_num_contents;
130 static int xx_current_change_page;
131 static char xx_default_string_empty[1] = "";
132 static int xx_string_length_unused;
134 struct LevelFileConfigInfo
136 int element; /* element for which data is to be stored */
137 int save_type; /* save data always, never or when changed */
138 int data_type; /* data type (used internally, not stored) */
139 int conf_type; /* micro chunk identifier (stored in file) */
142 void *value; /* variable that holds the data to be stored */
143 int default_value; /* initial default value for this variable */
146 void *value_copy; /* variable that holds the data to be copied */
147 void *num_entities; /* number of entities for multi-byte data */
148 int default_num_entities; /* default number of entities for this data */
149 int max_num_entities; /* maximal number of entities for this data */
150 char *default_string; /* optional default string for string data */
153 static struct LevelFileConfigInfo chunk_config_INFO[] =
155 /* ---------- values not related to single elements ----------------------- */
158 -1, SAVE_CONF_ALWAYS,
159 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
160 &li.game_engine_type, GAME_ENGINE_TYPE_RND
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
166 &li.fieldx, STD_LEV_FIELDX
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
171 &li.fieldy, STD_LEV_FIELDY
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
181 -1, SAVE_CONF_ALWAYS,
182 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
188 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
194 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
195 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
206 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
207 &li.em_slippery_gems, FALSE
212 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
213 &li.use_custom_template, FALSE
218 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
219 &li.can_move_into_acid_bits, ~0 /* default: everything can */
224 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
225 &li.dont_collide_with_bits, ~0 /* default: always deadly */
230 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
231 &li.em_explodes_by_fire, FALSE
236 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
237 &li.score[SC_TIME_BONUS], 1
242 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
243 &li.auto_exit_sokoban, FALSE
253 static struct LevelFileConfigInfo chunk_config_ELEM[] =
255 /* (these values are the same for each player) */
258 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
259 &li.block_last_field, FALSE /* default case for EM levels */
263 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
264 &li.sp_block_last_field, TRUE /* default case for SP levels */
268 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
269 &li.instant_relocation, FALSE
273 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
274 &li.can_pass_to_walkable, FALSE
278 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
279 &li.block_snap_field, TRUE
283 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
284 &li.continuous_snapping, TRUE
288 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
289 &li.shifted_relocation, FALSE
293 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
294 &li.lazy_relocation, FALSE
297 /* (these values are different for each player) */
300 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
301 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
306 &li.initial_player_gravity[0], FALSE
310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
311 &li.use_start_element[0], FALSE
315 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
316 &li.start_element[0], EL_PLAYER_1
320 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
321 &li.use_artwork_element[0], FALSE
325 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
326 &li.artwork_element[0], EL_PLAYER_1
330 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
331 &li.use_explosion_element[0], FALSE
335 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
336 &li.explosion_element[0], EL_PLAYER_1
340 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
341 &li.use_initial_inventory[0], FALSE
345 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
346 &li.initial_inventory_size[0], 1
350 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
351 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
352 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
357 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
358 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
362 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
363 &li.initial_player_gravity[1], FALSE
367 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
368 &li.use_start_element[1], FALSE
372 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
373 &li.start_element[1], EL_PLAYER_2
377 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
378 &li.use_artwork_element[1], FALSE
382 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
383 &li.artwork_element[1], EL_PLAYER_2
387 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
388 &li.use_explosion_element[1], FALSE
392 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
393 &li.explosion_element[1], EL_PLAYER_2
397 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
398 &li.use_initial_inventory[1], FALSE
402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
403 &li.initial_inventory_size[1], 1
407 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
408 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
409 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
414 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
415 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
419 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
420 &li.initial_player_gravity[2], FALSE
424 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
425 &li.use_start_element[2], FALSE
429 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
430 &li.start_element[2], EL_PLAYER_3
434 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
435 &li.use_artwork_element[2], FALSE
439 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
440 &li.artwork_element[2], EL_PLAYER_3
444 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
445 &li.use_explosion_element[2], FALSE
449 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
450 &li.explosion_element[2], EL_PLAYER_3
454 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
455 &li.use_initial_inventory[2], FALSE
459 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
460 &li.initial_inventory_size[2], 1
464 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
465 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
466 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
471 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
472 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
476 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
477 &li.initial_player_gravity[3], FALSE
481 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
482 &li.use_start_element[3], FALSE
486 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
487 &li.start_element[3], EL_PLAYER_4
491 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
492 &li.use_artwork_element[3], FALSE
496 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
497 &li.artwork_element[3], EL_PLAYER_4
501 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
502 &li.use_explosion_element[3], FALSE
506 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
507 &li.explosion_element[3], EL_PLAYER_4
511 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
512 &li.use_initial_inventory[3], FALSE
516 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
517 &li.initial_inventory_size[3], 1
521 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
522 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
523 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
528 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
529 &li.score[SC_EMERALD], 10
534 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
535 &li.score[SC_DIAMOND], 10
540 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
541 &li.score[SC_BUG], 10
546 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
547 &li.score[SC_SPACESHIP], 10
552 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
553 &li.score[SC_PACMAN], 10
558 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
559 &li.score[SC_NUT], 10
564 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
565 &li.score[SC_DYNAMITE], 10
570 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
571 &li.score[SC_KEY], 10
576 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
577 &li.score[SC_PEARL], 10
582 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
583 &li.score[SC_CRYSTAL], 10
588 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
589 &li.amoeba_content, EL_DIAMOND
593 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
598 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
599 &li.grow_into_diggable, TRUE
604 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
605 &li.yamyam_content, EL_ROCK, NULL,
606 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
610 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
611 &li.score[SC_YAMYAM], 10
616 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
617 &li.score[SC_ROBOT], 10
621 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
627 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
633 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
634 &li.time_magic_wall, 10
639 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
640 &li.game_of_life[0], 2
644 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
645 &li.game_of_life[1], 3
649 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
650 &li.game_of_life[2], 3
654 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
655 &li.game_of_life[3], 3
660 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
665 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
670 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
675 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
680 EL_TIMEGATE_SWITCH, -1,
681 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
682 &li.time_timegate, 10
686 EL_LIGHT_SWITCH_ACTIVE, -1,
687 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
692 EL_SHIELD_NORMAL, -1,
693 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
694 &li.shield_normal_time, 10
697 EL_SHIELD_NORMAL, -1,
698 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
699 &li.score[SC_SHIELD], 10
703 EL_SHIELD_DEADLY, -1,
704 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
705 &li.shield_deadly_time, 10
708 EL_SHIELD_DEADLY, -1,
709 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
710 &li.score[SC_SHIELD], 10
715 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
720 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
721 &li.extra_time_score, 10
725 EL_TIME_ORB_FULL, -1,
726 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
727 &li.time_orb_time, 10
730 EL_TIME_ORB_FULL, -1,
731 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
732 &li.use_time_orb_bug, FALSE
737 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
738 &li.use_spring_bug, FALSE
743 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
744 &li.android_move_time, 10
748 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
749 &li.android_clone_time, 10
753 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
754 &li.android_clone_element[0], EL_EMPTY, NULL,
755 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
760 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
765 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
770 EL_EMC_MAGNIFIER, -1,
771 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
772 &li.magnify_score, 10
775 EL_EMC_MAGNIFIER, -1,
776 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
781 EL_EMC_MAGIC_BALL, -1,
782 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
786 EL_EMC_MAGIC_BALL, -1,
787 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
788 &li.ball_random, FALSE
791 EL_EMC_MAGIC_BALL, -1,
792 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
793 &li.ball_state_initial, FALSE
796 EL_EMC_MAGIC_BALL, -1,
797 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
798 &li.ball_content, EL_EMPTY, NULL,
799 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
802 /* ---------- unused values ----------------------------------------------- */
805 EL_UNKNOWN, SAVE_CONF_NEVER,
806 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
807 &li.score[SC_UNKNOWN_14], 10
810 EL_UNKNOWN, SAVE_CONF_NEVER,
811 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
812 &li.score[SC_UNKNOWN_15], 10
822 static struct LevelFileConfigInfo chunk_config_NOTE[] =
826 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
827 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
831 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
832 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
837 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
838 &xx_envelope.autowrap, FALSE
842 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
843 &xx_envelope.centered, FALSE
848 TYPE_STRING, CONF_VALUE_BYTES(1),
849 &xx_envelope.text, -1, NULL,
850 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
851 &xx_default_string_empty[0]
861 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
865 TYPE_STRING, CONF_VALUE_BYTES(1),
866 &xx_ei.description[0], -1,
867 &yy_ei.description[0],
868 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
869 &xx_default_description[0]
874 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
875 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
876 &yy_ei.properties[EP_BITFIELD_BASE_NR]
878 #if ENABLE_RESERVED_CODE
879 /* (reserved for later use) */
882 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
883 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
884 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
890 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
891 &xx_ei.use_gfx_element, FALSE,
892 &yy_ei.use_gfx_element
896 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
897 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
898 &yy_ei.gfx_element_initial
903 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
904 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
905 &yy_ei.access_direction
910 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
911 &xx_ei.collect_score_initial, 10,
912 &yy_ei.collect_score_initial
916 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
917 &xx_ei.collect_count_initial, 1,
918 &yy_ei.collect_count_initial
923 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
924 &xx_ei.ce_value_fixed_initial, 0,
925 &yy_ei.ce_value_fixed_initial
929 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
930 &xx_ei.ce_value_random_initial, 0,
931 &yy_ei.ce_value_random_initial
935 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
936 &xx_ei.use_last_ce_value, FALSE,
937 &yy_ei.use_last_ce_value
942 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
943 &xx_ei.push_delay_fixed, 8,
944 &yy_ei.push_delay_fixed
948 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
949 &xx_ei.push_delay_random, 8,
950 &yy_ei.push_delay_random
954 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
955 &xx_ei.drop_delay_fixed, 0,
956 &yy_ei.drop_delay_fixed
960 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
961 &xx_ei.drop_delay_random, 0,
962 &yy_ei.drop_delay_random
966 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
967 &xx_ei.move_delay_fixed, 0,
968 &yy_ei.move_delay_fixed
972 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
973 &xx_ei.move_delay_random, 0,
974 &yy_ei.move_delay_random
979 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
980 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
985 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
986 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
987 &yy_ei.move_direction_initial
991 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
992 &xx_ei.move_stepsize, TILEX / 8,
998 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
999 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1000 &yy_ei.move_enter_element
1004 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1005 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1006 &yy_ei.move_leave_element
1010 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1011 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1012 &yy_ei.move_leave_type
1017 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1018 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1019 &yy_ei.slippery_type
1024 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1025 &xx_ei.explosion_type, EXPLODES_3X3,
1026 &yy_ei.explosion_type
1030 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1031 &xx_ei.explosion_delay, 16,
1032 &yy_ei.explosion_delay
1036 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1037 &xx_ei.ignition_delay, 8,
1038 &yy_ei.ignition_delay
1043 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1044 &xx_ei.content, EL_EMPTY_SPACE,
1046 &xx_num_contents, 1, 1
1049 /* ---------- "num_change_pages" must be the last entry ------------------- */
1052 -1, SAVE_CONF_ALWAYS,
1053 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1054 &xx_ei.num_change_pages, 1,
1055 &yy_ei.num_change_pages
1066 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1068 /* ---------- "current_change_page" must be the first entry --------------- */
1071 -1, SAVE_CONF_ALWAYS,
1072 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1073 &xx_current_change_page, -1
1076 /* ---------- (the remaining entries can be in any order) ----------------- */
1080 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1081 &xx_change.can_change, FALSE
1086 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1087 &xx_event_bits[0], 0
1091 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1092 &xx_event_bits[1], 0
1097 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1098 &xx_change.trigger_player, CH_PLAYER_ANY
1102 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1103 &xx_change.trigger_side, CH_SIDE_ANY
1107 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1108 &xx_change.trigger_page, CH_PAGE_ANY
1113 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1114 &xx_change.target_element, EL_EMPTY_SPACE
1119 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1120 &xx_change.delay_fixed, 0
1124 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1125 &xx_change.delay_random, 0
1129 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1130 &xx_change.delay_frames, FRAMES_PER_SECOND
1135 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1136 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1141 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1142 &xx_change.explode, FALSE
1146 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1147 &xx_change.use_target_content, FALSE
1151 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1152 &xx_change.only_if_complete, FALSE
1156 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1157 &xx_change.use_random_replace, FALSE
1161 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1162 &xx_change.random_percentage, 100
1166 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1167 &xx_change.replace_when, CP_WHEN_EMPTY
1172 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1173 &xx_change.has_action, FALSE
1177 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1178 &xx_change.action_type, CA_NO_ACTION
1182 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1183 &xx_change.action_mode, CA_MODE_UNDEFINED
1187 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1188 &xx_change.action_arg, CA_ARG_UNDEFINED
1193 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1194 &xx_change.action_element, EL_EMPTY_SPACE
1199 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1200 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1201 &xx_num_contents, 1, 1
1211 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1215 TYPE_STRING, CONF_VALUE_BYTES(1),
1216 &xx_ei.description[0], -1, NULL,
1217 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1218 &xx_default_description[0]
1223 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1224 &xx_ei.use_gfx_element, FALSE
1228 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1229 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1234 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1235 &xx_group.choice_mode, ANIM_RANDOM
1240 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1241 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1242 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1252 static struct LevelFileConfigInfo chunk_config_CONF[] = /* (OBSOLETE) */
1256 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1257 &li.block_snap_field, TRUE
1261 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1262 &li.continuous_snapping, TRUE
1266 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1267 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1271 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1272 &li.use_start_element[0], FALSE
1276 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1277 &li.start_element[0], EL_PLAYER_1
1281 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1282 &li.use_artwork_element[0], FALSE
1286 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1287 &li.artwork_element[0], EL_PLAYER_1
1291 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1292 &li.use_explosion_element[0], FALSE
1296 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1297 &li.explosion_element[0], EL_PLAYER_1
1312 filetype_id_list[] =
1314 { LEVEL_FILE_TYPE_RND, "RND" },
1315 { LEVEL_FILE_TYPE_BD, "BD" },
1316 { LEVEL_FILE_TYPE_EM, "EM" },
1317 { LEVEL_FILE_TYPE_SP, "SP" },
1318 { LEVEL_FILE_TYPE_DX, "DX" },
1319 { LEVEL_FILE_TYPE_SB, "SB" },
1320 { LEVEL_FILE_TYPE_DC, "DC" },
1325 /* ========================================================================= */
1326 /* level file functions */
1327 /* ========================================================================= */
1329 static boolean check_special_flags(char *flag)
1331 if (strEqual(options.special_flags, flag) ||
1332 strEqual(leveldir_current->special_flags, flag))
1338 static struct DateInfo getCurrentDate()
1340 time_t epoch_seconds = time(NULL);
1341 struct tm *now = localtime(&epoch_seconds);
1342 struct DateInfo date;
1344 date.year = now->tm_year + 1900;
1345 date.month = now->tm_mon + 1;
1346 date.day = now->tm_mday;
1348 date.src = DATE_SRC_CLOCK;
1353 static void resetEventFlags(struct ElementChangeInfo *change)
1357 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1358 change->has_event[i] = FALSE;
1361 static void resetEventBits()
1365 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1366 xx_event_bits[i] = 0;
1369 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1373 /* important: only change event flag if corresponding event bit is set
1374 (this is because all xx_event_bits[] values are loaded separately,
1375 and all xx_event_bits[] values are set back to zero before loading
1376 another value xx_event_bits[x] (each value representing 32 flags)) */
1378 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1379 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1380 change->has_event[i] = TRUE;
1383 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1387 /* in contrast to the above function setEventFlagsFromEventBits(), it
1388 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1389 depending on the corresponding change->has_event[i] values here, as
1390 all xx_event_bits[] values are reset in resetEventBits() before */
1392 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1393 if (change->has_event[i])
1394 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1397 static char *getDefaultElementDescription(struct ElementInfo *ei)
1399 static char description[MAX_ELEMENT_NAME_LEN + 1];
1400 char *default_description = (ei->custom_description != NULL ?
1401 ei->custom_description :
1402 ei->editor_description);
1405 /* always start with reliable default values */
1406 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1407 description[i] = '\0';
1409 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1410 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1412 return &description[0];
1415 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1417 char *default_description = getDefaultElementDescription(ei);
1420 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1421 ei->description[i] = default_description[i];
1424 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1428 for (i = 0; conf[i].data_type != -1; i++)
1430 int default_value = conf[i].default_value;
1431 int data_type = conf[i].data_type;
1432 int conf_type = conf[i].conf_type;
1433 int byte_mask = conf_type & CONF_MASK_BYTES;
1435 if (byte_mask == CONF_MASK_MULTI_BYTES)
1437 int default_num_entities = conf[i].default_num_entities;
1438 int max_num_entities = conf[i].max_num_entities;
1440 *(int *)(conf[i].num_entities) = default_num_entities;
1442 if (data_type == TYPE_STRING)
1444 char *default_string = conf[i].default_string;
1445 char *string = (char *)(conf[i].value);
1447 strncpy(string, default_string, max_num_entities);
1449 else if (data_type == TYPE_ELEMENT_LIST)
1451 int *element_array = (int *)(conf[i].value);
1454 for (j = 0; j < max_num_entities; j++)
1455 element_array[j] = default_value;
1457 else if (data_type == TYPE_CONTENT_LIST)
1459 struct Content *content = (struct Content *)(conf[i].value);
1462 for (c = 0; c < max_num_entities; c++)
1463 for (y = 0; y < 3; y++)
1464 for (x = 0; x < 3; x++)
1465 content[c].e[x][y] = default_value;
1468 else /* constant size configuration data (1, 2 or 4 bytes) */
1470 if (data_type == TYPE_BOOLEAN)
1471 *(boolean *)(conf[i].value) = default_value;
1473 *(int *) (conf[i].value) = default_value;
1478 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1482 for (i = 0; conf[i].data_type != -1; i++)
1484 int data_type = conf[i].data_type;
1485 int conf_type = conf[i].conf_type;
1486 int byte_mask = conf_type & CONF_MASK_BYTES;
1488 if (byte_mask == CONF_MASK_MULTI_BYTES)
1490 int max_num_entities = conf[i].max_num_entities;
1492 if (data_type == TYPE_STRING)
1494 char *string = (char *)(conf[i].value);
1495 char *string_copy = (char *)(conf[i].value_copy);
1497 strncpy(string_copy, string, max_num_entities);
1499 else if (data_type == TYPE_ELEMENT_LIST)
1501 int *element_array = (int *)(conf[i].value);
1502 int *element_array_copy = (int *)(conf[i].value_copy);
1505 for (j = 0; j < max_num_entities; j++)
1506 element_array_copy[j] = element_array[j];
1508 else if (data_type == TYPE_CONTENT_LIST)
1510 struct Content *content = (struct Content *)(conf[i].value);
1511 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1514 for (c = 0; c < max_num_entities; c++)
1515 for (y = 0; y < 3; y++)
1516 for (x = 0; x < 3; x++)
1517 content_copy[c].e[x][y] = content[c].e[x][y];
1520 else /* constant size configuration data (1, 2 or 4 bytes) */
1522 if (data_type == TYPE_BOOLEAN)
1523 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1525 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1530 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1534 xx_ei = *ei_from; /* copy element data into temporary buffer */
1535 yy_ei = *ei_to; /* copy element data into temporary buffer */
1537 copyConfigFromConfigList(chunk_config_CUSX_base);
1542 /* ---------- reinitialize and copy change pages ---------- */
1544 ei_to->num_change_pages = ei_from->num_change_pages;
1545 ei_to->current_change_page = ei_from->current_change_page;
1547 setElementChangePages(ei_to, ei_to->num_change_pages);
1549 for (i = 0; i < ei_to->num_change_pages; i++)
1550 ei_to->change_page[i] = ei_from->change_page[i];
1552 /* ---------- copy group element info ---------- */
1553 if (ei_from->group != NULL && ei_to->group != NULL) /* group or internal */
1554 *ei_to->group = *ei_from->group;
1556 /* mark this custom element as modified */
1557 ei_to->modified_settings = TRUE;
1560 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1562 int change_page_size = sizeof(struct ElementChangeInfo);
1564 ei->num_change_pages = MAX(1, change_pages);
1567 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1569 if (ei->current_change_page >= ei->num_change_pages)
1570 ei->current_change_page = ei->num_change_pages - 1;
1572 ei->change = &ei->change_page[ei->current_change_page];
1575 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1577 xx_change = *change; /* copy change data into temporary buffer */
1579 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1581 *change = xx_change;
1583 resetEventFlags(change);
1585 change->direct_action = 0;
1586 change->other_action = 0;
1588 change->pre_change_function = NULL;
1589 change->change_function = NULL;
1590 change->post_change_function = NULL;
1593 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1597 li = *level; /* copy level data into temporary buffer */
1598 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1599 *level = li; /* copy temporary buffer back to level data */
1601 setLevelInfoToDefaults_EM();
1602 setLevelInfoToDefaults_SP();
1604 level->native_em_level = &native_em_level;
1605 level->native_sp_level = &native_sp_level;
1607 level->file_version = FILE_VERSION_ACTUAL;
1608 level->game_version = GAME_VERSION_ACTUAL;
1610 level->creation_date = getCurrentDate();
1612 level->encoding_16bit_field = TRUE;
1613 level->encoding_16bit_yamyam = TRUE;
1614 level->encoding_16bit_amoeba = TRUE;
1616 /* clear level name and level author string buffers */
1617 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1618 level->name[i] = '\0';
1619 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1620 level->author[i] = '\0';
1622 /* set level name and level author to default values */
1623 strcpy(level->name, NAMELESS_LEVEL_NAME);
1624 strcpy(level->author, ANONYMOUS_NAME);
1626 /* set level playfield to playable default level with player and exit */
1627 for (x = 0; x < MAX_LEV_FIELDX; x++)
1628 for (y = 0; y < MAX_LEV_FIELDY; y++)
1629 level->field[x][y] = EL_SAND;
1631 level->field[0][0] = EL_PLAYER_1;
1632 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1634 BorderElement = EL_STEELWALL;
1636 /* set all bug compatibility flags to "false" => do not emulate this bug */
1637 level->use_action_after_change_bug = FALSE;
1639 if (leveldir_current)
1641 /* try to determine better author name than 'anonymous' */
1642 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1644 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1645 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1649 switch (LEVELCLASS(leveldir_current))
1651 case LEVELCLASS_TUTORIAL:
1652 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1655 case LEVELCLASS_CONTRIB:
1656 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1657 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1660 case LEVELCLASS_PRIVATE:
1661 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1662 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1666 /* keep default value */
1673 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1675 static boolean clipboard_elements_initialized = FALSE;
1678 InitElementPropertiesStatic();
1680 li = *level; /* copy level data into temporary buffer */
1681 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1682 *level = li; /* copy temporary buffer back to level data */
1684 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1687 struct ElementInfo *ei = &element_info[element];
1689 /* never initialize clipboard elements after the very first time */
1690 /* (to be able to use clipboard elements between several levels) */
1691 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1694 if (IS_ENVELOPE(element))
1696 int envelope_nr = element - EL_ENVELOPE_1;
1698 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1700 level->envelope[envelope_nr] = xx_envelope;
1703 if (IS_CUSTOM_ELEMENT(element) ||
1704 IS_GROUP_ELEMENT(element) ||
1705 IS_INTERNAL_ELEMENT(element))
1707 xx_ei = *ei; /* copy element data into temporary buffer */
1709 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1714 setElementChangePages(ei, 1);
1715 setElementChangeInfoToDefaults(ei->change);
1717 if (IS_CUSTOM_ELEMENT(element) ||
1718 IS_GROUP_ELEMENT(element) ||
1719 IS_INTERNAL_ELEMENT(element))
1721 setElementDescriptionToDefault(ei);
1723 ei->modified_settings = FALSE;
1726 if (IS_CUSTOM_ELEMENT(element) ||
1727 IS_INTERNAL_ELEMENT(element))
1729 /* internal values used in level editor */
1731 ei->access_type = 0;
1732 ei->access_layer = 0;
1733 ei->access_protected = 0;
1734 ei->walk_to_action = 0;
1735 ei->smash_targets = 0;
1738 ei->can_explode_by_fire = FALSE;
1739 ei->can_explode_smashed = FALSE;
1740 ei->can_explode_impact = FALSE;
1742 ei->current_change_page = 0;
1745 if (IS_GROUP_ELEMENT(element) ||
1746 IS_INTERNAL_ELEMENT(element))
1748 struct ElementGroupInfo *group;
1750 /* initialize memory for list of elements in group */
1751 if (ei->group == NULL)
1752 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1756 xx_group = *group; /* copy group data into temporary buffer */
1758 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1764 clipboard_elements_initialized = TRUE;
1767 static void setLevelInfoToDefaults(struct LevelInfo *level,
1768 boolean level_info_only,
1769 boolean reset_file_status)
1771 setLevelInfoToDefaults_Level(level);
1773 if (!level_info_only)
1774 setLevelInfoToDefaults_Elements(level);
1776 if (reset_file_status)
1778 level->no_valid_file = FALSE;
1779 level->no_level_file = FALSE;
1782 level->changed = FALSE;
1785 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1787 level_file_info->nr = 0;
1788 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1789 level_file_info->packed = FALSE;
1790 level_file_info->basename = NULL;
1791 level_file_info->filename = NULL;
1794 int getMappedElement_SB(int, boolean);
1796 static void ActivateLevelTemplate()
1800 if (check_special_flags("load_xsb_to_ces"))
1802 /* fill smaller playfields with padding "beyond border wall" elements */
1803 if (level.fieldx < level_template.fieldx ||
1804 level.fieldy < level_template.fieldy)
1806 short field[level.fieldx][level.fieldy];
1807 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1808 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1809 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1810 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1812 /* copy old playfield (which is smaller than the visible area) */
1813 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1814 field[x][y] = level.field[x][y];
1816 /* fill new, larger playfield with "beyond border wall" elements */
1817 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1818 level.field[x][y] = getMappedElement_SB('_', TRUE);
1820 /* copy the old playfield to the middle of the new playfield */
1821 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1822 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1824 level.fieldx = new_fieldx;
1825 level.fieldy = new_fieldy;
1829 /* Currently there is no special action needed to activate the template
1830 data, because 'element_info' property settings overwrite the original
1831 level data, while all other variables do not change. */
1833 /* Exception: 'from_level_template' elements in the original level playfield
1834 are overwritten with the corresponding elements at the same position in
1835 playfield from the level template. */
1837 for (x = 0; x < level.fieldx; x++)
1838 for (y = 0; y < level.fieldy; y++)
1839 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1840 level.field[x][y] = level_template.field[x][y];
1842 if (check_special_flags("load_xsb_to_ces"))
1844 struct LevelInfo level_backup = level;
1846 /* overwrite all individual level settings from template level settings */
1847 level = level_template;
1849 /* restore playfield size */
1850 level.fieldx = level_backup.fieldx;
1851 level.fieldy = level_backup.fieldy;
1853 /* restore playfield content */
1854 for (x = 0; x < level.fieldx; x++)
1855 for (y = 0; y < level.fieldy; y++)
1856 level.field[x][y] = level_backup.field[x][y];
1858 /* restore name and author from individual level */
1859 strcpy(level.name, level_backup.name);
1860 strcpy(level.author, level_backup.author);
1862 /* restore flag "use_custom_template" */
1863 level.use_custom_template = level_backup.use_custom_template;
1867 static char *getLevelFilenameFromBasename(char *basename)
1869 static char *filename[2] = { NULL, NULL };
1870 int pos = (strEqual(basename, LEVELTEMPLATE_FILENAME) ? 0 : 1);
1872 checked_free(filename[pos]);
1874 filename[pos] = getPath2(getCurrentLevelDir(), basename);
1876 return filename[pos];
1879 static int getFileTypeFromBasename(char *basename)
1881 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1883 static char *filename = NULL;
1884 struct stat file_status;
1886 /* ---------- try to determine file type from filename ---------- */
1888 /* check for typical filename of a Supaplex level package file */
1889 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1890 return LEVEL_FILE_TYPE_SP;
1892 /* check for typical filename of a Diamond Caves II level package file */
1893 if (strSuffixLower(basename, ".dc") ||
1894 strSuffixLower(basename, ".dc2"))
1895 return LEVEL_FILE_TYPE_DC;
1897 /* check for typical filename of a Sokoban level package file */
1898 if (strSuffixLower(basename, ".xsb") &&
1899 strchr(basename, '%') == NULL)
1900 return LEVEL_FILE_TYPE_SB;
1902 /* ---------- try to determine file type from filesize ---------- */
1904 checked_free(filename);
1905 filename = getPath2(getCurrentLevelDir(), basename);
1907 if (stat(filename, &file_status) == 0)
1909 /* check for typical filesize of a Supaplex level package file */
1910 if (file_status.st_size == 170496)
1911 return LEVEL_FILE_TYPE_SP;
1914 return LEVEL_FILE_TYPE_UNKNOWN;
1917 static boolean checkForPackageFromBasename(char *basename)
1919 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
1920 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
1922 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
1925 static char *getSingleLevelBasenameExt(int nr, char *extension)
1927 static char basename[MAX_FILENAME_LEN];
1930 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
1932 sprintf(basename, "%03d.%s", nr, extension);
1937 static char *getSingleLevelBasename(int nr)
1939 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
1942 static char *getPackedLevelBasename(int type)
1944 static char basename[MAX_FILENAME_LEN];
1945 char *directory = getCurrentLevelDir();
1947 DirectoryEntry *dir_entry;
1949 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
1951 if ((dir = openDirectory(directory)) == NULL)
1953 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
1958 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
1960 char *entry_basename = dir_entry->basename;
1961 int entry_type = getFileTypeFromBasename(entry_basename);
1963 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
1965 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
1968 strcpy(basename, entry_basename);
1975 closeDirectory(dir);
1980 static char *getSingleLevelFilename(int nr)
1982 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
1985 #if ENABLE_UNUSED_CODE
1986 static char *getPackedLevelFilename(int type)
1988 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
1992 char *getDefaultLevelFilename(int nr)
1994 return getSingleLevelFilename(nr);
1997 #if ENABLE_UNUSED_CODE
1998 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2002 lfi->packed = FALSE;
2003 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
2004 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2008 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2009 int type, char *format, ...)
2011 static char basename[MAX_FILENAME_LEN];
2014 va_start(ap, format);
2015 vsprintf(basename, format, ap);
2019 lfi->packed = FALSE;
2020 lfi->basename = basename;
2021 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2024 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2029 lfi->basename = getPackedLevelBasename(lfi->type);
2030 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2033 static int getFiletypeFromID(char *filetype_id)
2035 char *filetype_id_lower;
2036 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2039 if (filetype_id == NULL)
2040 return LEVEL_FILE_TYPE_UNKNOWN;
2042 filetype_id_lower = getStringToLower(filetype_id);
2044 for (i = 0; filetype_id_list[i].id != NULL; i++)
2046 char *id_lower = getStringToLower(filetype_id_list[i].id);
2048 if (strEqual(filetype_id_lower, id_lower))
2049 filetype = filetype_id_list[i].filetype;
2053 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2057 free(filetype_id_lower);
2062 char *getLocalLevelTemplateFilename()
2064 return getDefaultLevelFilename(-1);
2067 char *getGlobalLevelTemplateFilename()
2069 /* global variable "leveldir_current" must be modified in the loop below */
2070 LevelDirTree *leveldir_current_last = leveldir_current;
2071 char *filename = NULL;
2073 /* check for template level in path from current to topmost tree node */
2075 while (leveldir_current != NULL)
2077 filename = getDefaultLevelFilename(-1);
2079 if (fileExists(filename))
2082 leveldir_current = leveldir_current->node_parent;
2085 /* restore global variable "leveldir_current" modified in above loop */
2086 leveldir_current = leveldir_current_last;
2091 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2095 /* special case: level number is negative => check for level template file */
2098 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2099 getSingleLevelBasename(-1));
2101 /* replace local level template filename with global template filename */
2102 lfi->filename = getGlobalLevelTemplateFilename();
2104 /* no fallback if template file not existing */
2108 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2109 if (leveldir_current->level_filename != NULL)
2111 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2113 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2114 leveldir_current->level_filename, nr);
2116 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2118 if (fileExists(lfi->filename))
2122 /* check for native Rocks'n'Diamonds level file */
2123 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2124 "%03d.%s", nr, LEVELFILE_EXTENSION);
2125 if (fileExists(lfi->filename))
2128 /* check for Emerald Mine level file (V1) */
2129 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2130 'a' + (nr / 10) % 26, '0' + nr % 10);
2131 if (fileExists(lfi->filename))
2133 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2134 'A' + (nr / 10) % 26, '0' + nr % 10);
2135 if (fileExists(lfi->filename))
2138 /* check for Emerald Mine level file (V2 to V5) */
2139 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2140 if (fileExists(lfi->filename))
2143 /* check for Emerald Mine level file (V6 / single mode) */
2144 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2145 if (fileExists(lfi->filename))
2147 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2148 if (fileExists(lfi->filename))
2151 /* check for Emerald Mine level file (V6 / teamwork mode) */
2152 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2153 if (fileExists(lfi->filename))
2155 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2156 if (fileExists(lfi->filename))
2159 /* check for various packed level file formats */
2160 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2161 if (fileExists(lfi->filename))
2164 /* no known level file found -- use default values (and fail later) */
2165 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2166 "%03d.%s", nr, LEVELFILE_EXTENSION);
2169 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2171 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2172 lfi->type = getFileTypeFromBasename(lfi->basename);
2175 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2177 /* always start with reliable default values */
2178 setFileInfoToDefaults(level_file_info);
2180 level_file_info->nr = nr; /* set requested level number */
2182 determineLevelFileInfo_Filename(level_file_info);
2183 determineLevelFileInfo_Filetype(level_file_info);
2186 /* ------------------------------------------------------------------------- */
2187 /* functions for loading R'n'D level */
2188 /* ------------------------------------------------------------------------- */
2190 int getMappedElement(int element)
2192 /* remap some (historic, now obsolete) elements */
2196 case EL_PLAYER_OBSOLETE:
2197 element = EL_PLAYER_1;
2200 case EL_KEY_OBSOLETE:
2204 case EL_EM_KEY_1_FILE_OBSOLETE:
2205 element = EL_EM_KEY_1;
2208 case EL_EM_KEY_2_FILE_OBSOLETE:
2209 element = EL_EM_KEY_2;
2212 case EL_EM_KEY_3_FILE_OBSOLETE:
2213 element = EL_EM_KEY_3;
2216 case EL_EM_KEY_4_FILE_OBSOLETE:
2217 element = EL_EM_KEY_4;
2220 case EL_ENVELOPE_OBSOLETE:
2221 element = EL_ENVELOPE_1;
2229 if (element >= NUM_FILE_ELEMENTS)
2231 Error(ERR_WARN, "invalid level element %d", element);
2233 element = EL_UNKNOWN;
2241 int getMappedElementByVersion(int element, int game_version)
2243 /* remap some elements due to certain game version */
2245 if (game_version <= VERSION_IDENT(2,2,0,0))
2247 /* map game font elements */
2248 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2249 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2250 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2251 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2254 if (game_version < VERSION_IDENT(3,0,0,0))
2256 /* map Supaplex gravity tube elements */
2257 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2258 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2259 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2260 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2267 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2269 level->file_version = getFileVersion(file);
2270 level->game_version = getFileVersion(file);
2275 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2277 level->creation_date.year = getFile16BitBE(file);
2278 level->creation_date.month = getFile8Bit(file);
2279 level->creation_date.day = getFile8Bit(file);
2281 level->creation_date.src = DATE_SRC_LEVELFILE;
2286 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2288 int initial_player_stepsize;
2289 int initial_player_gravity;
2292 level->fieldx = getFile8Bit(file);
2293 level->fieldy = getFile8Bit(file);
2295 level->time = getFile16BitBE(file);
2296 level->gems_needed = getFile16BitBE(file);
2298 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2299 level->name[i] = getFile8Bit(file);
2300 level->name[MAX_LEVEL_NAME_LEN] = 0;
2302 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2303 level->score[i] = getFile8Bit(file);
2305 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2306 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2307 for (y = 0; y < 3; y++)
2308 for (x = 0; x < 3; x++)
2309 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2311 level->amoeba_speed = getFile8Bit(file);
2312 level->time_magic_wall = getFile8Bit(file);
2313 level->time_wheel = getFile8Bit(file);
2314 level->amoeba_content = getMappedElement(getFile8Bit(file));
2316 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2319 for (i = 0; i < MAX_PLAYERS; i++)
2320 level->initial_player_stepsize[i] = initial_player_stepsize;
2322 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2324 for (i = 0; i < MAX_PLAYERS; i++)
2325 level->initial_player_gravity[i] = initial_player_gravity;
2327 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2328 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2330 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2332 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2333 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2334 level->can_move_into_acid_bits = getFile32BitBE(file);
2335 level->dont_collide_with_bits = getFile8Bit(file);
2337 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2338 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2340 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2341 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2342 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2344 level->game_engine_type = getFile8Bit(file);
2346 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2351 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2355 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2356 level->name[i] = getFile8Bit(file);
2357 level->name[MAX_LEVEL_NAME_LEN] = 0;
2362 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2366 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2367 level->author[i] = getFile8Bit(file);
2368 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2373 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2376 int chunk_size_expected = level->fieldx * level->fieldy;
2378 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2379 stored with 16-bit encoding (and should be twice as big then).
2380 Even worse, playfield data was stored 16-bit when only yamyam content
2381 contained 16-bit elements and vice versa. */
2383 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2384 chunk_size_expected *= 2;
2386 if (chunk_size_expected != chunk_size)
2388 ReadUnusedBytesFromFile(file, chunk_size);
2389 return chunk_size_expected;
2392 for (y = 0; y < level->fieldy; y++)
2393 for (x = 0; x < level->fieldx; x++)
2394 level->field[x][y] =
2395 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2400 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2403 int header_size = 4;
2404 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2405 int chunk_size_expected = header_size + content_size;
2407 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2408 stored with 16-bit encoding (and should be twice as big then).
2409 Even worse, playfield data was stored 16-bit when only yamyam content
2410 contained 16-bit elements and vice versa. */
2412 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2413 chunk_size_expected += content_size;
2415 if (chunk_size_expected != chunk_size)
2417 ReadUnusedBytesFromFile(file, chunk_size);
2418 return chunk_size_expected;
2422 level->num_yamyam_contents = getFile8Bit(file);
2426 /* correct invalid number of content fields -- should never happen */
2427 if (level->num_yamyam_contents < 1 ||
2428 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2429 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2431 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2432 for (y = 0; y < 3; y++)
2433 for (x = 0; x < 3; x++)
2434 level->yamyam_content[i].e[x][y] =
2435 getMappedElement(level->encoding_16bit_field ?
2436 getFile16BitBE(file) : getFile8Bit(file));
2440 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2445 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2447 element = getMappedElement(getFile16BitBE(file));
2448 num_contents = getFile8Bit(file);
2450 getFile8Bit(file); /* content x size (unused) */
2451 getFile8Bit(file); /* content y size (unused) */
2453 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2455 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2456 for (y = 0; y < 3; y++)
2457 for (x = 0; x < 3; x++)
2458 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2460 /* correct invalid number of content fields -- should never happen */
2461 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2462 num_contents = STD_ELEMENT_CONTENTS;
2464 if (element == EL_YAMYAM)
2466 level->num_yamyam_contents = num_contents;
2468 for (i = 0; i < num_contents; i++)
2469 for (y = 0; y < 3; y++)
2470 for (x = 0; x < 3; x++)
2471 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2473 else if (element == EL_BD_AMOEBA)
2475 level->amoeba_content = content_array[0][0][0];
2479 Error(ERR_WARN, "cannot load content for element '%d'", element);
2485 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2491 int chunk_size_expected;
2493 element = getMappedElement(getFile16BitBE(file));
2494 if (!IS_ENVELOPE(element))
2495 element = EL_ENVELOPE_1;
2497 envelope_nr = element - EL_ENVELOPE_1;
2499 envelope_len = getFile16BitBE(file);
2501 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2502 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2504 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2506 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2507 if (chunk_size_expected != chunk_size)
2509 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2510 return chunk_size_expected;
2513 for (i = 0; i < envelope_len; i++)
2514 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2519 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2521 int num_changed_custom_elements = getFile16BitBE(file);
2522 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2525 if (chunk_size_expected != chunk_size)
2527 ReadUnusedBytesFromFile(file, chunk_size - 2);
2528 return chunk_size_expected;
2531 for (i = 0; i < num_changed_custom_elements; i++)
2533 int element = getMappedElement(getFile16BitBE(file));
2534 int properties = getFile32BitBE(file);
2536 if (IS_CUSTOM_ELEMENT(element))
2537 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2539 Error(ERR_WARN, "invalid custom element number %d", element);
2541 /* older game versions that wrote level files with CUS1 chunks used
2542 different default push delay values (not yet stored in level file) */
2543 element_info[element].push_delay_fixed = 2;
2544 element_info[element].push_delay_random = 8;
2550 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2552 int num_changed_custom_elements = getFile16BitBE(file);
2553 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2556 if (chunk_size_expected != chunk_size)
2558 ReadUnusedBytesFromFile(file, chunk_size - 2);
2559 return chunk_size_expected;
2562 for (i = 0; i < num_changed_custom_elements; i++)
2564 int element = getMappedElement(getFile16BitBE(file));
2565 int custom_target_element = getMappedElement(getFile16BitBE(file));
2567 if (IS_CUSTOM_ELEMENT(element))
2568 element_info[element].change->target_element = custom_target_element;
2570 Error(ERR_WARN, "invalid custom element number %d", element);
2576 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2578 int num_changed_custom_elements = getFile16BitBE(file);
2579 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2582 if (chunk_size_expected != chunk_size)
2584 ReadUnusedBytesFromFile(file, chunk_size - 2);
2585 return chunk_size_expected;
2588 for (i = 0; i < num_changed_custom_elements; i++)
2590 int element = getMappedElement(getFile16BitBE(file));
2591 struct ElementInfo *ei = &element_info[element];
2592 unsigned int event_bits;
2594 if (!IS_CUSTOM_ELEMENT(element))
2596 Error(ERR_WARN, "invalid custom element number %d", element);
2598 element = EL_INTERNAL_DUMMY;
2601 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2602 ei->description[j] = getFile8Bit(file);
2603 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2605 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2607 /* some free bytes for future properties and padding */
2608 ReadUnusedBytesFromFile(file, 7);
2610 ei->use_gfx_element = getFile8Bit(file);
2611 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2613 ei->collect_score_initial = getFile8Bit(file);
2614 ei->collect_count_initial = getFile8Bit(file);
2616 ei->push_delay_fixed = getFile16BitBE(file);
2617 ei->push_delay_random = getFile16BitBE(file);
2618 ei->move_delay_fixed = getFile16BitBE(file);
2619 ei->move_delay_random = getFile16BitBE(file);
2621 ei->move_pattern = getFile16BitBE(file);
2622 ei->move_direction_initial = getFile8Bit(file);
2623 ei->move_stepsize = getFile8Bit(file);
2625 for (y = 0; y < 3; y++)
2626 for (x = 0; x < 3; x++)
2627 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2629 event_bits = getFile32BitBE(file);
2630 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2631 if (event_bits & (1 << j))
2632 ei->change->has_event[j] = TRUE;
2634 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2636 ei->change->delay_fixed = getFile16BitBE(file);
2637 ei->change->delay_random = getFile16BitBE(file);
2638 ei->change->delay_frames = getFile16BitBE(file);
2640 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2642 ei->change->explode = getFile8Bit(file);
2643 ei->change->use_target_content = getFile8Bit(file);
2644 ei->change->only_if_complete = getFile8Bit(file);
2645 ei->change->use_random_replace = getFile8Bit(file);
2647 ei->change->random_percentage = getFile8Bit(file);
2648 ei->change->replace_when = getFile8Bit(file);
2650 for (y = 0; y < 3; y++)
2651 for (x = 0; x < 3; x++)
2652 ei->change->target_content.e[x][y] =
2653 getMappedElement(getFile16BitBE(file));
2655 ei->slippery_type = getFile8Bit(file);
2657 /* some free bytes for future properties and padding */
2658 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2660 /* mark that this custom element has been modified */
2661 ei->modified_settings = TRUE;
2667 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2669 struct ElementInfo *ei;
2670 int chunk_size_expected;
2674 /* ---------- custom element base property values (96 bytes) ------------- */
2676 element = getMappedElement(getFile16BitBE(file));
2678 if (!IS_CUSTOM_ELEMENT(element))
2680 Error(ERR_WARN, "invalid custom element number %d", element);
2682 ReadUnusedBytesFromFile(file, chunk_size - 2);
2686 ei = &element_info[element];
2688 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2689 ei->description[i] = getFile8Bit(file);
2690 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2692 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2694 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2696 ei->num_change_pages = getFile8Bit(file);
2698 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2699 if (chunk_size_expected != chunk_size)
2701 ReadUnusedBytesFromFile(file, chunk_size - 43);
2702 return chunk_size_expected;
2705 ei->ce_value_fixed_initial = getFile16BitBE(file);
2706 ei->ce_value_random_initial = getFile16BitBE(file);
2707 ei->use_last_ce_value = getFile8Bit(file);
2709 ei->use_gfx_element = getFile8Bit(file);
2710 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2712 ei->collect_score_initial = getFile8Bit(file);
2713 ei->collect_count_initial = getFile8Bit(file);
2715 ei->drop_delay_fixed = getFile8Bit(file);
2716 ei->push_delay_fixed = getFile8Bit(file);
2717 ei->drop_delay_random = getFile8Bit(file);
2718 ei->push_delay_random = getFile8Bit(file);
2719 ei->move_delay_fixed = getFile16BitBE(file);
2720 ei->move_delay_random = getFile16BitBE(file);
2722 /* bits 0 - 15 of "move_pattern" ... */
2723 ei->move_pattern = getFile16BitBE(file);
2724 ei->move_direction_initial = getFile8Bit(file);
2725 ei->move_stepsize = getFile8Bit(file);
2727 ei->slippery_type = getFile8Bit(file);
2729 for (y = 0; y < 3; y++)
2730 for (x = 0; x < 3; x++)
2731 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2733 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2734 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2735 ei->move_leave_type = getFile8Bit(file);
2737 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2738 ei->move_pattern |= (getFile16BitBE(file) << 16);
2740 ei->access_direction = getFile8Bit(file);
2742 ei->explosion_delay = getFile8Bit(file);
2743 ei->ignition_delay = getFile8Bit(file);
2744 ei->explosion_type = getFile8Bit(file);
2746 /* some free bytes for future custom property values and padding */
2747 ReadUnusedBytesFromFile(file, 1);
2749 /* ---------- change page property values (48 bytes) --------------------- */
2751 setElementChangePages(ei, ei->num_change_pages);
2753 for (i = 0; i < ei->num_change_pages; i++)
2755 struct ElementChangeInfo *change = &ei->change_page[i];
2756 unsigned int event_bits;
2758 /* always start with reliable default values */
2759 setElementChangeInfoToDefaults(change);
2761 /* bits 0 - 31 of "has_event[]" ... */
2762 event_bits = getFile32BitBE(file);
2763 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2764 if (event_bits & (1 << j))
2765 change->has_event[j] = TRUE;
2767 change->target_element = getMappedElement(getFile16BitBE(file));
2769 change->delay_fixed = getFile16BitBE(file);
2770 change->delay_random = getFile16BitBE(file);
2771 change->delay_frames = getFile16BitBE(file);
2773 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2775 change->explode = getFile8Bit(file);
2776 change->use_target_content = getFile8Bit(file);
2777 change->only_if_complete = getFile8Bit(file);
2778 change->use_random_replace = getFile8Bit(file);
2780 change->random_percentage = getFile8Bit(file);
2781 change->replace_when = getFile8Bit(file);
2783 for (y = 0; y < 3; y++)
2784 for (x = 0; x < 3; x++)
2785 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2787 change->can_change = getFile8Bit(file);
2789 change->trigger_side = getFile8Bit(file);
2791 change->trigger_player = getFile8Bit(file);
2792 change->trigger_page = getFile8Bit(file);
2794 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2795 CH_PAGE_ANY : (1 << change->trigger_page));
2797 change->has_action = getFile8Bit(file);
2798 change->action_type = getFile8Bit(file);
2799 change->action_mode = getFile8Bit(file);
2800 change->action_arg = getFile16BitBE(file);
2802 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2803 event_bits = getFile8Bit(file);
2804 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2805 if (event_bits & (1 << (j - 32)))
2806 change->has_event[j] = TRUE;
2809 /* mark this custom element as modified */
2810 ei->modified_settings = TRUE;
2815 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2817 struct ElementInfo *ei;
2818 struct ElementGroupInfo *group;
2822 element = getMappedElement(getFile16BitBE(file));
2824 if (!IS_GROUP_ELEMENT(element))
2826 Error(ERR_WARN, "invalid group element number %d", element);
2828 ReadUnusedBytesFromFile(file, chunk_size - 2);
2832 ei = &element_info[element];
2834 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2835 ei->description[i] = getFile8Bit(file);
2836 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2838 group = element_info[element].group;
2840 group->num_elements = getFile8Bit(file);
2842 ei->use_gfx_element = getFile8Bit(file);
2843 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2845 group->choice_mode = getFile8Bit(file);
2847 /* some free bytes for future values and padding */
2848 ReadUnusedBytesFromFile(file, 3);
2850 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2851 group->element[i] = getMappedElement(getFile16BitBE(file));
2853 /* mark this group element as modified */
2854 element_info[element].modified_settings = TRUE;
2859 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2860 int element, int real_element)
2862 int micro_chunk_size = 0;
2863 int conf_type = getFile8Bit(file);
2864 int byte_mask = conf_type & CONF_MASK_BYTES;
2865 boolean element_found = FALSE;
2868 micro_chunk_size += 1;
2870 if (byte_mask == CONF_MASK_MULTI_BYTES)
2872 int num_bytes = getFile16BitBE(file);
2873 byte *buffer = checked_malloc(num_bytes);
2875 ReadBytesFromFile(file, buffer, num_bytes);
2877 for (i = 0; conf[i].data_type != -1; i++)
2879 if (conf[i].element == element &&
2880 conf[i].conf_type == conf_type)
2882 int data_type = conf[i].data_type;
2883 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2884 int max_num_entities = conf[i].max_num_entities;
2886 if (num_entities > max_num_entities)
2889 "truncating number of entities for element %d from %d to %d",
2890 element, num_entities, max_num_entities);
2892 num_entities = max_num_entities;
2895 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
2896 data_type == TYPE_CONTENT_LIST))
2898 /* for element and content lists, zero entities are not allowed */
2899 Error(ERR_WARN, "found empty list of entities for element %d",
2902 /* do not set "num_entities" here to prevent reading behind buffer */
2904 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
2908 *(int *)(conf[i].num_entities) = num_entities;
2911 element_found = TRUE;
2913 if (data_type == TYPE_STRING)
2915 char *string = (char *)(conf[i].value);
2918 for (j = 0; j < max_num_entities; j++)
2919 string[j] = (j < num_entities ? buffer[j] : '\0');
2921 else if (data_type == TYPE_ELEMENT_LIST)
2923 int *element_array = (int *)(conf[i].value);
2926 for (j = 0; j < num_entities; j++)
2928 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
2930 else if (data_type == TYPE_CONTENT_LIST)
2932 struct Content *content= (struct Content *)(conf[i].value);
2935 for (c = 0; c < num_entities; c++)
2936 for (y = 0; y < 3; y++)
2937 for (x = 0; x < 3; x++)
2938 content[c].e[x][y] =
2939 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
2942 element_found = FALSE;
2948 checked_free(buffer);
2950 micro_chunk_size += 2 + num_bytes;
2952 else /* constant size configuration data (1, 2 or 4 bytes) */
2954 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
2955 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
2956 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
2958 for (i = 0; conf[i].data_type != -1; i++)
2960 if (conf[i].element == element &&
2961 conf[i].conf_type == conf_type)
2963 int data_type = conf[i].data_type;
2965 if (data_type == TYPE_ELEMENT)
2966 value = getMappedElement(value);
2968 if (data_type == TYPE_BOOLEAN)
2969 *(boolean *)(conf[i].value) = value;
2971 *(int *) (conf[i].value) = value;
2973 element_found = TRUE;
2979 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
2984 char *error_conf_chunk_bytes =
2985 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
2986 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
2987 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
2988 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
2989 int error_element = real_element;
2991 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
2992 error_conf_chunk_bytes, error_conf_chunk_token,
2993 error_element, EL_NAME(error_element));
2996 return micro_chunk_size;
2999 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3001 int real_chunk_size = 0;
3003 li = *level; /* copy level data into temporary buffer */
3005 while (!checkEndOfFile(file))
3007 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3009 if (real_chunk_size >= chunk_size)
3013 *level = li; /* copy temporary buffer back to level data */
3015 return real_chunk_size;
3018 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3020 int real_chunk_size = 0;
3022 li = *level; /* copy level data into temporary buffer */
3024 while (!checkEndOfFile(file))
3026 int element = getMappedElement(getFile16BitBE(file));
3028 real_chunk_size += 2;
3029 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3031 if (real_chunk_size >= chunk_size)
3035 *level = li; /* copy temporary buffer back to level data */
3037 return real_chunk_size;
3040 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3042 int real_chunk_size = 0;
3044 li = *level; /* copy level data into temporary buffer */
3046 while (!checkEndOfFile(file))
3048 int element = getMappedElement(getFile16BitBE(file));
3050 real_chunk_size += 2;
3051 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3053 if (real_chunk_size >= chunk_size)
3057 *level = li; /* copy temporary buffer back to level data */
3059 return real_chunk_size;
3062 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3064 int element = getMappedElement(getFile16BitBE(file));
3065 int envelope_nr = element - EL_ENVELOPE_1;
3066 int real_chunk_size = 2;
3068 xx_envelope = level->envelope[envelope_nr]; /* copy into temporary buffer */
3070 while (!checkEndOfFile(file))
3072 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3075 if (real_chunk_size >= chunk_size)
3079 level->envelope[envelope_nr] = xx_envelope; /* copy from temporary buffer */
3081 return real_chunk_size;
3084 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3086 int element = getMappedElement(getFile16BitBE(file));
3087 int real_chunk_size = 2;
3088 struct ElementInfo *ei = &element_info[element];
3091 xx_ei = *ei; /* copy element data into temporary buffer */
3093 xx_ei.num_change_pages = -1;
3095 while (!checkEndOfFile(file))
3097 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3099 if (xx_ei.num_change_pages != -1)
3102 if (real_chunk_size >= chunk_size)
3108 if (ei->num_change_pages == -1)
3110 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3113 ei->num_change_pages = 1;
3115 setElementChangePages(ei, 1);
3116 setElementChangeInfoToDefaults(ei->change);
3118 return real_chunk_size;
3121 /* initialize number of change pages stored for this custom element */
3122 setElementChangePages(ei, ei->num_change_pages);
3123 for (i = 0; i < ei->num_change_pages; i++)
3124 setElementChangeInfoToDefaults(&ei->change_page[i]);
3126 /* start with reading properties for the first change page */
3127 xx_current_change_page = 0;
3129 while (!checkEndOfFile(file))
3131 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3133 xx_change = *change; /* copy change data into temporary buffer */
3135 resetEventBits(); /* reset bits; change page might have changed */
3137 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3140 *change = xx_change;
3142 setEventFlagsFromEventBits(change);
3144 if (real_chunk_size >= chunk_size)
3148 return real_chunk_size;
3151 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3153 int element = getMappedElement(getFile16BitBE(file));
3154 int real_chunk_size = 2;
3155 struct ElementInfo *ei = &element_info[element];
3156 struct ElementGroupInfo *group = ei->group;
3158 xx_ei = *ei; /* copy element data into temporary buffer */
3159 xx_group = *group; /* copy group data into temporary buffer */
3161 while (!checkEndOfFile(file))
3163 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3166 if (real_chunk_size >= chunk_size)
3173 return real_chunk_size;
3176 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3177 struct LevelFileInfo *level_file_info,
3178 boolean level_info_only)
3180 char *filename = level_file_info->filename;
3181 char cookie[MAX_LINE_LEN];
3182 char chunk_name[CHUNK_ID_LEN + 1];
3186 if (!(file = openFile(filename, MODE_READ)))
3188 level->no_valid_file = TRUE;
3189 level->no_level_file = TRUE;
3191 if (level_info_only)
3194 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3196 if (!setup.editor.use_template_for_new_levels)
3199 /* if level file not found, try to initialize level data from template */
3200 filename = getGlobalLevelTemplateFilename();
3202 if (!(file = openFile(filename, MODE_READ)))
3205 /* default: for empty levels, use level template for custom elements */
3206 level->use_custom_template = TRUE;
3208 level->no_valid_file = FALSE;
3211 getFileChunkBE(file, chunk_name, NULL);
3212 if (strEqual(chunk_name, "RND1"))
3214 getFile32BitBE(file); /* not used */
3216 getFileChunkBE(file, chunk_name, NULL);
3217 if (!strEqual(chunk_name, "CAVE"))
3219 level->no_valid_file = TRUE;
3221 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3228 else /* check for pre-2.0 file format with cookie string */
3230 strcpy(cookie, chunk_name);
3231 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3233 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3234 cookie[strlen(cookie) - 1] = '\0';
3236 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3238 level->no_valid_file = TRUE;
3240 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3247 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3249 level->no_valid_file = TRUE;
3251 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3258 /* pre-2.0 level files have no game version, so use file version here */
3259 level->game_version = level->file_version;
3262 if (level->file_version < FILE_VERSION_1_2)
3264 /* level files from versions before 1.2.0 without chunk structure */
3265 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3266 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3274 int (*loader)(File *, int, struct LevelInfo *);
3278 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3279 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3280 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3281 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3282 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3283 { "INFO", -1, LoadLevel_INFO },
3284 { "BODY", -1, LoadLevel_BODY },
3285 { "CONT", -1, LoadLevel_CONT },
3286 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3287 { "CNT3", -1, LoadLevel_CNT3 },
3288 { "CUS1", -1, LoadLevel_CUS1 },
3289 { "CUS2", -1, LoadLevel_CUS2 },
3290 { "CUS3", -1, LoadLevel_CUS3 },
3291 { "CUS4", -1, LoadLevel_CUS4 },
3292 { "GRP1", -1, LoadLevel_GRP1 },
3293 { "CONF", -1, LoadLevel_CONF },
3294 { "ELEM", -1, LoadLevel_ELEM },
3295 { "NOTE", -1, LoadLevel_NOTE },
3296 { "CUSX", -1, LoadLevel_CUSX },
3297 { "GRPX", -1, LoadLevel_GRPX },
3302 while (getFileChunkBE(file, chunk_name, &chunk_size))
3306 while (chunk_info[i].name != NULL &&
3307 !strEqual(chunk_name, chunk_info[i].name))
3310 if (chunk_info[i].name == NULL)
3312 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3313 chunk_name, filename);
3314 ReadUnusedBytesFromFile(file, chunk_size);
3316 else if (chunk_info[i].size != -1 &&
3317 chunk_info[i].size != chunk_size)
3319 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3320 chunk_size, chunk_name, filename);
3321 ReadUnusedBytesFromFile(file, chunk_size);
3325 /* call function to load this level chunk */
3326 int chunk_size_expected =
3327 (chunk_info[i].loader)(file, chunk_size, level);
3329 /* the size of some chunks cannot be checked before reading other
3330 chunks first (like "HEAD" and "BODY") that contain some header
3331 information, so check them here */
3332 if (chunk_size_expected != chunk_size)
3334 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3335 chunk_size, chunk_name, filename);
3345 /* ------------------------------------------------------------------------- */
3346 /* functions for loading EM level */
3347 /* ------------------------------------------------------------------------- */
3349 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3351 static int ball_xy[8][2] =
3362 struct LevelInfo_EM *level_em = level->native_em_level;
3363 struct LEVEL *lev = level_em->lev;
3364 struct PLAYER **ply = level_em->ply;
3367 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3368 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3370 lev->time_seconds = level->time;
3371 lev->required_initial = level->gems_needed;
3373 lev->emerald_score = level->score[SC_EMERALD];
3374 lev->diamond_score = level->score[SC_DIAMOND];
3375 lev->alien_score = level->score[SC_ROBOT];
3376 lev->tank_score = level->score[SC_SPACESHIP];
3377 lev->bug_score = level->score[SC_BUG];
3378 lev->eater_score = level->score[SC_YAMYAM];
3379 lev->nut_score = level->score[SC_NUT];
3380 lev->dynamite_score = level->score[SC_DYNAMITE];
3381 lev->key_score = level->score[SC_KEY];
3382 lev->exit_score = level->score[SC_TIME_BONUS];
3384 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3385 for (y = 0; y < 3; y++)
3386 for (x = 0; x < 3; x++)
3387 lev->eater_array[i][y * 3 + x] =
3388 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3390 lev->amoeba_time = level->amoeba_speed;
3391 lev->wonderwall_time_initial = level->time_magic_wall;
3392 lev->wheel_time = level->time_wheel;
3394 lev->android_move_time = level->android_move_time;
3395 lev->android_clone_time = level->android_clone_time;
3396 lev->ball_random = level->ball_random;
3397 lev->ball_state_initial = level->ball_state_initial;
3398 lev->ball_time = level->ball_time;
3399 lev->num_ball_arrays = level->num_ball_contents;
3401 lev->lenses_score = level->lenses_score;
3402 lev->magnify_score = level->magnify_score;
3403 lev->slurp_score = level->slurp_score;
3405 lev->lenses_time = level->lenses_time;
3406 lev->magnify_time = level->magnify_time;
3408 lev->wind_direction_initial =
3409 map_direction_RND_to_EM(level->wind_direction_initial);
3410 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3411 lev->wind_time : 0);
3413 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3414 for (j = 0; j < 8; j++)
3415 lev->ball_array[i][j] =
3416 map_element_RND_to_EM(level->
3417 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3419 map_android_clone_elements_RND_to_EM(level);
3421 /* first fill the complete playfield with the default border element */
3422 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3423 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3424 level_em->cave[x][y] = ZBORDER;
3426 if (BorderElement == EL_STEELWALL)
3428 for (y = 0; y < lev->height + 2; y++)
3429 for (x = 0; x < lev->width + 2; x++)
3430 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3433 /* then copy the real level contents from level file into the playfield */
3434 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3436 int new_element = map_element_RND_to_EM(level->field[x][y]);
3437 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3438 int xx = x + 1 + offset;
3439 int yy = y + 1 + offset;
3441 if (level->field[x][y] == EL_AMOEBA_DEAD)
3442 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3444 level_em->cave[xx][yy] = new_element;
3447 for (i = 0; i < MAX_PLAYERS; i++)
3449 ply[i]->x_initial = 0;
3450 ply[i]->y_initial = 0;
3453 /* initialize player positions and delete players from the playfield */
3454 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3456 if (ELEM_IS_PLAYER(level->field[x][y]))
3458 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3459 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3460 int xx = x + 1 + offset;
3461 int yy = y + 1 + offset;
3463 ply[player_nr]->x_initial = xx;
3464 ply[player_nr]->y_initial = yy;
3466 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3470 if (BorderElement == EL_STEELWALL)
3477 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3479 static int ball_xy[8][2] =
3490 struct LevelInfo_EM *level_em = level->native_em_level;
3491 struct LEVEL *lev = level_em->lev;
3492 struct PLAYER **ply = level_em->ply;
3495 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3496 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3498 level->time = lev->time_seconds;
3499 level->gems_needed = lev->required_initial;
3501 sprintf(level->name, "Level %d", level->file_info.nr);
3503 level->score[SC_EMERALD] = lev->emerald_score;
3504 level->score[SC_DIAMOND] = lev->diamond_score;
3505 level->score[SC_ROBOT] = lev->alien_score;
3506 level->score[SC_SPACESHIP] = lev->tank_score;
3507 level->score[SC_BUG] = lev->bug_score;
3508 level->score[SC_YAMYAM] = lev->eater_score;
3509 level->score[SC_NUT] = lev->nut_score;
3510 level->score[SC_DYNAMITE] = lev->dynamite_score;
3511 level->score[SC_KEY] = lev->key_score;
3512 level->score[SC_TIME_BONUS] = lev->exit_score;
3514 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3516 for (i = 0; i < level->num_yamyam_contents; i++)
3517 for (y = 0; y < 3; y++)
3518 for (x = 0; x < 3; x++)
3519 level->yamyam_content[i].e[x][y] =
3520 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3522 level->amoeba_speed = lev->amoeba_time;
3523 level->time_magic_wall = lev->wonderwall_time_initial;
3524 level->time_wheel = lev->wheel_time;
3526 level->android_move_time = lev->android_move_time;
3527 level->android_clone_time = lev->android_clone_time;
3528 level->ball_random = lev->ball_random;
3529 level->ball_state_initial = lev->ball_state_initial;
3530 level->ball_time = lev->ball_time;
3531 level->num_ball_contents = lev->num_ball_arrays;
3533 level->lenses_score = lev->lenses_score;
3534 level->magnify_score = lev->magnify_score;
3535 level->slurp_score = lev->slurp_score;
3537 level->lenses_time = lev->lenses_time;
3538 level->magnify_time = lev->magnify_time;
3540 level->wind_direction_initial =
3541 map_direction_EM_to_RND(lev->wind_direction_initial);
3543 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3544 for (j = 0; j < 8; j++)
3545 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3546 map_element_EM_to_RND(lev->ball_array[i][j]);
3548 map_android_clone_elements_EM_to_RND(level);
3550 /* convert the playfield (some elements need special treatment) */
3551 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3553 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3555 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3556 new_element = EL_AMOEBA_DEAD;
3558 level->field[x][y] = new_element;
3561 for (i = 0; i < MAX_PLAYERS; i++)
3563 /* in case of all players set to the same field, use the first player */
3564 int nr = MAX_PLAYERS - i - 1;
3565 int jx = ply[nr]->x_initial - 1;
3566 int jy = ply[nr]->y_initial - 1;
3568 if (jx != -1 && jy != -1)
3569 level->field[jx][jy] = EL_PLAYER_1 + nr;
3574 /* ------------------------------------------------------------------------- */
3575 /* functions for loading SP level */
3576 /* ------------------------------------------------------------------------- */
3578 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3580 struct LevelInfo_SP *level_sp = level->native_sp_level;
3581 LevelInfoType *header = &level_sp->header;
3584 level_sp->width = level->fieldx;
3585 level_sp->height = level->fieldy;
3587 for (x = 0; x < level->fieldx; x++)
3588 for (y = 0; y < level->fieldy; y++)
3589 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3591 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3593 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3594 header->LevelTitle[i] = level->name[i];
3595 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3597 header->InfotronsNeeded = level->gems_needed;
3599 header->SpecialPortCount = 0;
3601 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3603 boolean gravity_port_found = FALSE;
3604 boolean gravity_port_valid = FALSE;
3605 int gravity_port_flag;
3606 int gravity_port_base_element;
3607 int element = level->field[x][y];
3609 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3610 element <= EL_SP_GRAVITY_ON_PORT_UP)
3612 gravity_port_found = TRUE;
3613 gravity_port_valid = TRUE;
3614 gravity_port_flag = 1;
3615 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3617 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3618 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3620 gravity_port_found = TRUE;
3621 gravity_port_valid = TRUE;
3622 gravity_port_flag = 0;
3623 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3625 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3626 element <= EL_SP_GRAVITY_PORT_UP)
3628 /* change R'n'D style gravity inverting special port to normal port
3629 (there are no gravity inverting ports in native Supaplex engine) */
3631 gravity_port_found = TRUE;
3632 gravity_port_valid = FALSE;
3633 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3636 if (gravity_port_found)
3638 if (gravity_port_valid &&
3639 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3641 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3643 port->PortLocation = (y * level->fieldx + x) * 2;
3644 port->Gravity = gravity_port_flag;
3646 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3648 header->SpecialPortCount++;
3652 /* change special gravity port to normal port */
3654 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3657 level_sp->playfield[x][y] = element - EL_SP_START;
3662 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3664 struct LevelInfo_SP *level_sp = level->native_sp_level;
3665 LevelInfoType *header = &level_sp->header;
3668 level->fieldx = level_sp->width;
3669 level->fieldy = level_sp->height;
3671 for (x = 0; x < level->fieldx; x++)
3673 for (y = 0; y < level->fieldy; y++)
3675 int element_old = level_sp->playfield[x][y];
3676 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3678 if (element_new == EL_UNKNOWN)
3679 Error(ERR_WARN, "invalid element %d at position %d, %d",
3682 level->field[x][y] = element_new;
3686 for (i = 0; i < MAX_PLAYERS; i++)
3687 level->initial_player_gravity[i] =
3688 (header->InitialGravity == 1 ? TRUE : FALSE);
3690 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3691 level->name[i] = header->LevelTitle[i];
3692 level->name[SP_LEVEL_NAME_LEN] = '\0';
3694 level->gems_needed = header->InfotronsNeeded;
3696 for (i = 0; i < header->SpecialPortCount; i++)
3698 SpecialPortType *port = &header->SpecialPort[i];
3699 int port_location = port->PortLocation;
3700 int gravity = port->Gravity;
3701 int port_x, port_y, port_element;
3703 port_x = (port_location / 2) % level->fieldx;
3704 port_y = (port_location / 2) / level->fieldx;
3706 if (port_x < 0 || port_x >= level->fieldx ||
3707 port_y < 0 || port_y >= level->fieldy)
3709 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3715 port_element = level->field[port_x][port_y];
3717 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3718 port_element > EL_SP_GRAVITY_PORT_UP)
3720 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3725 /* change previous (wrong) gravity inverting special port to either
3726 gravity enabling special port or gravity disabling special port */
3727 level->field[port_x][port_y] +=
3728 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3729 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3732 /* change special gravity ports without database entries to normal ports */
3733 for (x = 0; x < level->fieldx; x++)
3734 for (y = 0; y < level->fieldy; y++)
3735 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3736 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3737 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3739 level->time = 0; /* no time limit */
3740 level->amoeba_speed = 0;
3741 level->time_magic_wall = 0;
3742 level->time_wheel = 0;
3743 level->amoeba_content = EL_EMPTY;
3746 /* original Supaplex does not use score values -- use default values */
3748 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3749 level->score[i] = 0;
3752 /* there are no yamyams in supaplex levels */
3753 for (i = 0; i < level->num_yamyam_contents; i++)
3754 for (x = 0; x < 3; x++)
3755 for (y = 0; y < 3; y++)
3756 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3759 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3761 struct LevelInfo_SP *level_sp = level->native_sp_level;
3762 struct DemoInfo_SP *demo = &level_sp->demo;
3765 /* always start with reliable default values */
3766 demo->is_available = FALSE;
3769 if (TAPE_IS_EMPTY(tape))
3772 demo->level_nr = tape.level_nr; /* (currently not used) */
3774 level_sp->header.DemoRandomSeed = tape.random_seed;
3777 for (i = 0; i < tape.length; i++)
3779 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3780 int demo_repeat = tape.pos[i].delay;
3782 for (j = 0; j < demo_repeat / 16; j++)
3783 demo->data[demo->length++] = 0xf0 | demo_action;
3785 if (demo_repeat % 16)
3786 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3789 demo->data[demo->length++] = 0xff;
3791 demo->is_available = TRUE;
3794 static void setTapeInfoToDefaults();
3796 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3798 struct LevelInfo_SP *level_sp = level->native_sp_level;
3799 struct DemoInfo_SP *demo = &level_sp->demo;
3800 char *filename = level->file_info.filename;
3803 /* always start with reliable default values */
3804 setTapeInfoToDefaults();
3806 if (!demo->is_available)
3809 tape.level_nr = demo->level_nr; /* (currently not used) */
3810 tape.length = demo->length - 1; /* without "end of demo" byte */
3811 tape.random_seed = level_sp->header.DemoRandomSeed;
3813 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3815 for (i = 0; i < demo->length - 1; i++)
3817 int demo_action = demo->data[i] & 0x0f;
3818 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3820 tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
3821 tape.pos[i].delay = demo_repeat + 1;
3824 tape.length_frames = GetTapeLengthFrames();
3825 tape.length_seconds = GetTapeLengthSeconds();
3829 /* ------------------------------------------------------------------------- */
3830 /* functions for loading DC level */
3831 /* ------------------------------------------------------------------------- */
3833 #define DC_LEVEL_HEADER_SIZE 344
3835 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
3837 static int last_data_encoded;
3841 int diff_hi, diff_lo;
3842 int data_hi, data_lo;
3843 unsigned short data_decoded;
3847 last_data_encoded = 0;
3854 diff = data_encoded - last_data_encoded;
3855 diff_hi = diff & ~0xff;
3856 diff_lo = diff & 0xff;
3860 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
3861 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
3862 data_hi = data_hi & 0xff00;
3864 data_decoded = data_hi | data_lo;
3866 last_data_encoded = data_encoded;
3868 offset1 = (offset1 + 1) % 31;
3869 offset2 = offset2 & 0xff;
3871 return data_decoded;
3874 int getMappedElement_DC(int element)
3882 /* 0x0117 - 0x036e: (?) */
3885 /* 0x042d - 0x0684: (?) */
3901 element = EL_CRYSTAL;
3904 case 0x0e77: /* quicksand (boulder) */
3905 element = EL_QUICKSAND_FAST_FULL;
3908 case 0x0e99: /* slow quicksand (boulder) */
3909 element = EL_QUICKSAND_FULL;
3913 element = EL_EM_EXIT_OPEN;
3917 element = EL_EM_EXIT_CLOSED;
3921 element = EL_EM_STEEL_EXIT_OPEN;
3925 element = EL_EM_STEEL_EXIT_CLOSED;
3928 case 0x0f4f: /* dynamite (lit 1) */
3929 element = EL_EM_DYNAMITE_ACTIVE;
3932 case 0x0f57: /* dynamite (lit 2) */
3933 element = EL_EM_DYNAMITE_ACTIVE;
3936 case 0x0f5f: /* dynamite (lit 3) */
3937 element = EL_EM_DYNAMITE_ACTIVE;
3940 case 0x0f67: /* dynamite (lit 4) */
3941 element = EL_EM_DYNAMITE_ACTIVE;
3948 element = EL_AMOEBA_WET;
3952 element = EL_AMOEBA_DROP;
3956 element = EL_DC_MAGIC_WALL;
3960 element = EL_SPACESHIP_UP;
3964 element = EL_SPACESHIP_DOWN;
3968 element = EL_SPACESHIP_LEFT;
3972 element = EL_SPACESHIP_RIGHT;
3976 element = EL_BUG_UP;
3980 element = EL_BUG_DOWN;
3984 element = EL_BUG_LEFT;
3988 element = EL_BUG_RIGHT;
3992 element = EL_MOLE_UP;
3996 element = EL_MOLE_DOWN;
4000 element = EL_MOLE_LEFT;
4004 element = EL_MOLE_RIGHT;
4012 element = EL_YAMYAM;
4016 element = EL_SWITCHGATE_OPEN;
4020 element = EL_SWITCHGATE_CLOSED;
4024 element = EL_DC_SWITCHGATE_SWITCH_UP;
4028 element = EL_TIMEGATE_CLOSED;
4031 case 0x144c: /* conveyor belt switch (green) */
4032 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4035 case 0x144f: /* conveyor belt switch (red) */
4036 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4039 case 0x1452: /* conveyor belt switch (blue) */
4040 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4044 element = EL_CONVEYOR_BELT_3_MIDDLE;
4048 element = EL_CONVEYOR_BELT_3_LEFT;
4052 element = EL_CONVEYOR_BELT_3_RIGHT;
4056 element = EL_CONVEYOR_BELT_1_MIDDLE;
4060 element = EL_CONVEYOR_BELT_1_LEFT;
4064 element = EL_CONVEYOR_BELT_1_RIGHT;
4068 element = EL_CONVEYOR_BELT_4_MIDDLE;
4072 element = EL_CONVEYOR_BELT_4_LEFT;
4076 element = EL_CONVEYOR_BELT_4_RIGHT;
4080 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4084 element = EL_EXPANDABLE_WALL_VERTICAL;
4088 element = EL_EXPANDABLE_WALL_ANY;
4091 case 0x14ce: /* growing steel wall (left/right) */
4092 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4095 case 0x14df: /* growing steel wall (up/down) */
4096 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4099 case 0x14e8: /* growing steel wall (up/down/left/right) */
4100 element = EL_EXPANDABLE_STEELWALL_ANY;
4104 element = EL_SHIELD_DEADLY;
4108 element = EL_EXTRA_TIME;
4116 element = EL_EMPTY_SPACE;
4119 case 0x1578: /* quicksand (empty) */
4120 element = EL_QUICKSAND_FAST_EMPTY;
4123 case 0x1579: /* slow quicksand (empty) */
4124 element = EL_QUICKSAND_EMPTY;
4127 /* 0x157c - 0x158b: */
4130 /* 0x1590 - 0x159f: */
4131 /* EL_DC_LANDMINE */
4134 element = EL_EM_DYNAMITE;
4137 case 0x15a1: /* key (red) */
4138 element = EL_EM_KEY_1;
4141 case 0x15a2: /* key (yellow) */
4142 element = EL_EM_KEY_2;
4145 case 0x15a3: /* key (blue) */
4146 element = EL_EM_KEY_4;
4149 case 0x15a4: /* key (green) */
4150 element = EL_EM_KEY_3;
4153 case 0x15a5: /* key (white) */
4154 element = EL_DC_KEY_WHITE;
4158 element = EL_WALL_SLIPPERY;
4165 case 0x15a8: /* wall (not round) */
4169 case 0x15a9: /* (blue) */
4170 element = EL_CHAR_A;
4173 case 0x15aa: /* (blue) */
4174 element = EL_CHAR_B;
4177 case 0x15ab: /* (blue) */
4178 element = EL_CHAR_C;
4181 case 0x15ac: /* (blue) */
4182 element = EL_CHAR_D;
4185 case 0x15ad: /* (blue) */
4186 element = EL_CHAR_E;
4189 case 0x15ae: /* (blue) */
4190 element = EL_CHAR_F;
4193 case 0x15af: /* (blue) */
4194 element = EL_CHAR_G;
4197 case 0x15b0: /* (blue) */
4198 element = EL_CHAR_H;
4201 case 0x15b1: /* (blue) */
4202 element = EL_CHAR_I;
4205 case 0x15b2: /* (blue) */
4206 element = EL_CHAR_J;
4209 case 0x15b3: /* (blue) */
4210 element = EL_CHAR_K;
4213 case 0x15b4: /* (blue) */
4214 element = EL_CHAR_L;
4217 case 0x15b5: /* (blue) */
4218 element = EL_CHAR_M;
4221 case 0x15b6: /* (blue) */
4222 element = EL_CHAR_N;
4225 case 0x15b7: /* (blue) */
4226 element = EL_CHAR_O;
4229 case 0x15b8: /* (blue) */
4230 element = EL_CHAR_P;
4233 case 0x15b9: /* (blue) */
4234 element = EL_CHAR_Q;
4237 case 0x15ba: /* (blue) */
4238 element = EL_CHAR_R;
4241 case 0x15bb: /* (blue) */
4242 element = EL_CHAR_S;
4245 case 0x15bc: /* (blue) */
4246 element = EL_CHAR_T;
4249 case 0x15bd: /* (blue) */
4250 element = EL_CHAR_U;
4253 case 0x15be: /* (blue) */
4254 element = EL_CHAR_V;
4257 case 0x15bf: /* (blue) */
4258 element = EL_CHAR_W;
4261 case 0x15c0: /* (blue) */
4262 element = EL_CHAR_X;
4265 case 0x15c1: /* (blue) */
4266 element = EL_CHAR_Y;
4269 case 0x15c2: /* (blue) */
4270 element = EL_CHAR_Z;
4273 case 0x15c3: /* (blue) */
4274 element = EL_CHAR_AUMLAUT;
4277 case 0x15c4: /* (blue) */
4278 element = EL_CHAR_OUMLAUT;
4281 case 0x15c5: /* (blue) */
4282 element = EL_CHAR_UUMLAUT;
4285 case 0x15c6: /* (blue) */
4286 element = EL_CHAR_0;
4289 case 0x15c7: /* (blue) */
4290 element = EL_CHAR_1;
4293 case 0x15c8: /* (blue) */
4294 element = EL_CHAR_2;
4297 case 0x15c9: /* (blue) */
4298 element = EL_CHAR_3;
4301 case 0x15ca: /* (blue) */
4302 element = EL_CHAR_4;
4305 case 0x15cb: /* (blue) */
4306 element = EL_CHAR_5;
4309 case 0x15cc: /* (blue) */
4310 element = EL_CHAR_6;
4313 case 0x15cd: /* (blue) */
4314 element = EL_CHAR_7;
4317 case 0x15ce: /* (blue) */
4318 element = EL_CHAR_8;
4321 case 0x15cf: /* (blue) */
4322 element = EL_CHAR_9;
4325 case 0x15d0: /* (blue) */
4326 element = EL_CHAR_PERIOD;
4329 case 0x15d1: /* (blue) */
4330 element = EL_CHAR_EXCLAM;
4333 case 0x15d2: /* (blue) */
4334 element = EL_CHAR_COLON;
4337 case 0x15d3: /* (blue) */
4338 element = EL_CHAR_LESS;
4341 case 0x15d4: /* (blue) */
4342 element = EL_CHAR_GREATER;
4345 case 0x15d5: /* (blue) */
4346 element = EL_CHAR_QUESTION;
4349 case 0x15d6: /* (blue) */
4350 element = EL_CHAR_COPYRIGHT;
4353 case 0x15d7: /* (blue) */
4354 element = EL_CHAR_UP;
4357 case 0x15d8: /* (blue) */
4358 element = EL_CHAR_DOWN;
4361 case 0x15d9: /* (blue) */
4362 element = EL_CHAR_BUTTON;
4365 case 0x15da: /* (blue) */
4366 element = EL_CHAR_PLUS;
4369 case 0x15db: /* (blue) */
4370 element = EL_CHAR_MINUS;
4373 case 0x15dc: /* (blue) */
4374 element = EL_CHAR_APOSTROPHE;
4377 case 0x15dd: /* (blue) */
4378 element = EL_CHAR_PARENLEFT;
4381 case 0x15de: /* (blue) */
4382 element = EL_CHAR_PARENRIGHT;
4385 case 0x15df: /* (green) */
4386 element = EL_CHAR_A;
4389 case 0x15e0: /* (green) */
4390 element = EL_CHAR_B;
4393 case 0x15e1: /* (green) */
4394 element = EL_CHAR_C;
4397 case 0x15e2: /* (green) */
4398 element = EL_CHAR_D;
4401 case 0x15e3: /* (green) */
4402 element = EL_CHAR_E;
4405 case 0x15e4: /* (green) */
4406 element = EL_CHAR_F;
4409 case 0x15e5: /* (green) */
4410 element = EL_CHAR_G;
4413 case 0x15e6: /* (green) */
4414 element = EL_CHAR_H;
4417 case 0x15e7: /* (green) */
4418 element = EL_CHAR_I;
4421 case 0x15e8: /* (green) */
4422 element = EL_CHAR_J;
4425 case 0x15e9: /* (green) */
4426 element = EL_CHAR_K;
4429 case 0x15ea: /* (green) */
4430 element = EL_CHAR_L;
4433 case 0x15eb: /* (green) */
4434 element = EL_CHAR_M;
4437 case 0x15ec: /* (green) */
4438 element = EL_CHAR_N;
4441 case 0x15ed: /* (green) */
4442 element = EL_CHAR_O;
4445 case 0x15ee: /* (green) */
4446 element = EL_CHAR_P;
4449 case 0x15ef: /* (green) */
4450 element = EL_CHAR_Q;
4453 case 0x15f0: /* (green) */
4454 element = EL_CHAR_R;
4457 case 0x15f1: /* (green) */
4458 element = EL_CHAR_S;
4461 case 0x15f2: /* (green) */
4462 element = EL_CHAR_T;
4465 case 0x15f3: /* (green) */
4466 element = EL_CHAR_U;
4469 case 0x15f4: /* (green) */
4470 element = EL_CHAR_V;
4473 case 0x15f5: /* (green) */
4474 element = EL_CHAR_W;
4477 case 0x15f6: /* (green) */
4478 element = EL_CHAR_X;
4481 case 0x15f7: /* (green) */
4482 element = EL_CHAR_Y;
4485 case 0x15f8: /* (green) */
4486 element = EL_CHAR_Z;
4489 case 0x15f9: /* (green) */
4490 element = EL_CHAR_AUMLAUT;
4493 case 0x15fa: /* (green) */
4494 element = EL_CHAR_OUMLAUT;
4497 case 0x15fb: /* (green) */
4498 element = EL_CHAR_UUMLAUT;
4501 case 0x15fc: /* (green) */
4502 element = EL_CHAR_0;
4505 case 0x15fd: /* (green) */
4506 element = EL_CHAR_1;
4509 case 0x15fe: /* (green) */
4510 element = EL_CHAR_2;
4513 case 0x15ff: /* (green) */
4514 element = EL_CHAR_3;
4517 case 0x1600: /* (green) */
4518 element = EL_CHAR_4;
4521 case 0x1601: /* (green) */
4522 element = EL_CHAR_5;
4525 case 0x1602: /* (green) */
4526 element = EL_CHAR_6;
4529 case 0x1603: /* (green) */
4530 element = EL_CHAR_7;
4533 case 0x1604: /* (green) */
4534 element = EL_CHAR_8;
4537 case 0x1605: /* (green) */
4538 element = EL_CHAR_9;
4541 case 0x1606: /* (green) */
4542 element = EL_CHAR_PERIOD;
4545 case 0x1607: /* (green) */
4546 element = EL_CHAR_EXCLAM;
4549 case 0x1608: /* (green) */
4550 element = EL_CHAR_COLON;
4553 case 0x1609: /* (green) */
4554 element = EL_CHAR_LESS;
4557 case 0x160a: /* (green) */
4558 element = EL_CHAR_GREATER;
4561 case 0x160b: /* (green) */
4562 element = EL_CHAR_QUESTION;
4565 case 0x160c: /* (green) */
4566 element = EL_CHAR_COPYRIGHT;
4569 case 0x160d: /* (green) */
4570 element = EL_CHAR_UP;
4573 case 0x160e: /* (green) */
4574 element = EL_CHAR_DOWN;
4577 case 0x160f: /* (green) */
4578 element = EL_CHAR_BUTTON;
4581 case 0x1610: /* (green) */
4582 element = EL_CHAR_PLUS;
4585 case 0x1611: /* (green) */
4586 element = EL_CHAR_MINUS;
4589 case 0x1612: /* (green) */
4590 element = EL_CHAR_APOSTROPHE;
4593 case 0x1613: /* (green) */
4594 element = EL_CHAR_PARENLEFT;
4597 case 0x1614: /* (green) */
4598 element = EL_CHAR_PARENRIGHT;
4601 case 0x1615: /* (blue steel) */
4602 element = EL_STEEL_CHAR_A;
4605 case 0x1616: /* (blue steel) */
4606 element = EL_STEEL_CHAR_B;
4609 case 0x1617: /* (blue steel) */
4610 element = EL_STEEL_CHAR_C;
4613 case 0x1618: /* (blue steel) */
4614 element = EL_STEEL_CHAR_D;
4617 case 0x1619: /* (blue steel) */
4618 element = EL_STEEL_CHAR_E;
4621 case 0x161a: /* (blue steel) */
4622 element = EL_STEEL_CHAR_F;
4625 case 0x161b: /* (blue steel) */
4626 element = EL_STEEL_CHAR_G;
4629 case 0x161c: /* (blue steel) */
4630 element = EL_STEEL_CHAR_H;
4633 case 0x161d: /* (blue steel) */
4634 element = EL_STEEL_CHAR_I;
4637 case 0x161e: /* (blue steel) */
4638 element = EL_STEEL_CHAR_J;
4641 case 0x161f: /* (blue steel) */
4642 element = EL_STEEL_CHAR_K;
4645 case 0x1620: /* (blue steel) */
4646 element = EL_STEEL_CHAR_L;
4649 case 0x1621: /* (blue steel) */
4650 element = EL_STEEL_CHAR_M;
4653 case 0x1622: /* (blue steel) */
4654 element = EL_STEEL_CHAR_N;
4657 case 0x1623: /* (blue steel) */
4658 element = EL_STEEL_CHAR_O;
4661 case 0x1624: /* (blue steel) */
4662 element = EL_STEEL_CHAR_P;
4665 case 0x1625: /* (blue steel) */
4666 element = EL_STEEL_CHAR_Q;
4669 case 0x1626: /* (blue steel) */
4670 element = EL_STEEL_CHAR_R;
4673 case 0x1627: /* (blue steel) */
4674 element = EL_STEEL_CHAR_S;
4677 case 0x1628: /* (blue steel) */
4678 element = EL_STEEL_CHAR_T;
4681 case 0x1629: /* (blue steel) */
4682 element = EL_STEEL_CHAR_U;
4685 case 0x162a: /* (blue steel) */
4686 element = EL_STEEL_CHAR_V;
4689 case 0x162b: /* (blue steel) */
4690 element = EL_STEEL_CHAR_W;
4693 case 0x162c: /* (blue steel) */
4694 element = EL_STEEL_CHAR_X;
4697 case 0x162d: /* (blue steel) */
4698 element = EL_STEEL_CHAR_Y;
4701 case 0x162e: /* (blue steel) */
4702 element = EL_STEEL_CHAR_Z;
4705 case 0x162f: /* (blue steel) */
4706 element = EL_STEEL_CHAR_AUMLAUT;
4709 case 0x1630: /* (blue steel) */
4710 element = EL_STEEL_CHAR_OUMLAUT;
4713 case 0x1631: /* (blue steel) */
4714 element = EL_STEEL_CHAR_UUMLAUT;
4717 case 0x1632: /* (blue steel) */
4718 element = EL_STEEL_CHAR_0;
4721 case 0x1633: /* (blue steel) */
4722 element = EL_STEEL_CHAR_1;
4725 case 0x1634: /* (blue steel) */
4726 element = EL_STEEL_CHAR_2;
4729 case 0x1635: /* (blue steel) */
4730 element = EL_STEEL_CHAR_3;
4733 case 0x1636: /* (blue steel) */
4734 element = EL_STEEL_CHAR_4;
4737 case 0x1637: /* (blue steel) */
4738 element = EL_STEEL_CHAR_5;
4741 case 0x1638: /* (blue steel) */
4742 element = EL_STEEL_CHAR_6;
4745 case 0x1639: /* (blue steel) */
4746 element = EL_STEEL_CHAR_7;
4749 case 0x163a: /* (blue steel) */
4750 element = EL_STEEL_CHAR_8;
4753 case 0x163b: /* (blue steel) */
4754 element = EL_STEEL_CHAR_9;
4757 case 0x163c: /* (blue steel) */
4758 element = EL_STEEL_CHAR_PERIOD;
4761 case 0x163d: /* (blue steel) */
4762 element = EL_STEEL_CHAR_EXCLAM;
4765 case 0x163e: /* (blue steel) */
4766 element = EL_STEEL_CHAR_COLON;
4769 case 0x163f: /* (blue steel) */
4770 element = EL_STEEL_CHAR_LESS;
4773 case 0x1640: /* (blue steel) */
4774 element = EL_STEEL_CHAR_GREATER;
4777 case 0x1641: /* (blue steel) */
4778 element = EL_STEEL_CHAR_QUESTION;
4781 case 0x1642: /* (blue steel) */
4782 element = EL_STEEL_CHAR_COPYRIGHT;
4785 case 0x1643: /* (blue steel) */
4786 element = EL_STEEL_CHAR_UP;
4789 case 0x1644: /* (blue steel) */
4790 element = EL_STEEL_CHAR_DOWN;
4793 case 0x1645: /* (blue steel) */
4794 element = EL_STEEL_CHAR_BUTTON;
4797 case 0x1646: /* (blue steel) */
4798 element = EL_STEEL_CHAR_PLUS;
4801 case 0x1647: /* (blue steel) */
4802 element = EL_STEEL_CHAR_MINUS;
4805 case 0x1648: /* (blue steel) */
4806 element = EL_STEEL_CHAR_APOSTROPHE;
4809 case 0x1649: /* (blue steel) */
4810 element = EL_STEEL_CHAR_PARENLEFT;
4813 case 0x164a: /* (blue steel) */
4814 element = EL_STEEL_CHAR_PARENRIGHT;
4817 case 0x164b: /* (green steel) */
4818 element = EL_STEEL_CHAR_A;
4821 case 0x164c: /* (green steel) */
4822 element = EL_STEEL_CHAR_B;
4825 case 0x164d: /* (green steel) */
4826 element = EL_STEEL_CHAR_C;
4829 case 0x164e: /* (green steel) */
4830 element = EL_STEEL_CHAR_D;
4833 case 0x164f: /* (green steel) */
4834 element = EL_STEEL_CHAR_E;
4837 case 0x1650: /* (green steel) */
4838 element = EL_STEEL_CHAR_F;
4841 case 0x1651: /* (green steel) */
4842 element = EL_STEEL_CHAR_G;
4845 case 0x1652: /* (green steel) */
4846 element = EL_STEEL_CHAR_H;
4849 case 0x1653: /* (green steel) */
4850 element = EL_STEEL_CHAR_I;
4853 case 0x1654: /* (green steel) */
4854 element = EL_STEEL_CHAR_J;
4857 case 0x1655: /* (green steel) */
4858 element = EL_STEEL_CHAR_K;
4861 case 0x1656: /* (green steel) */
4862 element = EL_STEEL_CHAR_L;
4865 case 0x1657: /* (green steel) */
4866 element = EL_STEEL_CHAR_M;
4869 case 0x1658: /* (green steel) */
4870 element = EL_STEEL_CHAR_N;
4873 case 0x1659: /* (green steel) */
4874 element = EL_STEEL_CHAR_O;
4877 case 0x165a: /* (green steel) */
4878 element = EL_STEEL_CHAR_P;
4881 case 0x165b: /* (green steel) */
4882 element = EL_STEEL_CHAR_Q;
4885 case 0x165c: /* (green steel) */
4886 element = EL_STEEL_CHAR_R;
4889 case 0x165d: /* (green steel) */
4890 element = EL_STEEL_CHAR_S;
4893 case 0x165e: /* (green steel) */
4894 element = EL_STEEL_CHAR_T;
4897 case 0x165f: /* (green steel) */
4898 element = EL_STEEL_CHAR_U;
4901 case 0x1660: /* (green steel) */
4902 element = EL_STEEL_CHAR_V;
4905 case 0x1661: /* (green steel) */
4906 element = EL_STEEL_CHAR_W;
4909 case 0x1662: /* (green steel) */
4910 element = EL_STEEL_CHAR_X;
4913 case 0x1663: /* (green steel) */
4914 element = EL_STEEL_CHAR_Y;
4917 case 0x1664: /* (green steel) */
4918 element = EL_STEEL_CHAR_Z;
4921 case 0x1665: /* (green steel) */
4922 element = EL_STEEL_CHAR_AUMLAUT;
4925 case 0x1666: /* (green steel) */
4926 element = EL_STEEL_CHAR_OUMLAUT;
4929 case 0x1667: /* (green steel) */
4930 element = EL_STEEL_CHAR_UUMLAUT;
4933 case 0x1668: /* (green steel) */
4934 element = EL_STEEL_CHAR_0;
4937 case 0x1669: /* (green steel) */
4938 element = EL_STEEL_CHAR_1;
4941 case 0x166a: /* (green steel) */
4942 element = EL_STEEL_CHAR_2;
4945 case 0x166b: /* (green steel) */
4946 element = EL_STEEL_CHAR_3;
4949 case 0x166c: /* (green steel) */
4950 element = EL_STEEL_CHAR_4;
4953 case 0x166d: /* (green steel) */
4954 element = EL_STEEL_CHAR_5;
4957 case 0x166e: /* (green steel) */
4958 element = EL_STEEL_CHAR_6;
4961 case 0x166f: /* (green steel) */
4962 element = EL_STEEL_CHAR_7;
4965 case 0x1670: /* (green steel) */
4966 element = EL_STEEL_CHAR_8;
4969 case 0x1671: /* (green steel) */
4970 element = EL_STEEL_CHAR_9;
4973 case 0x1672: /* (green steel) */
4974 element = EL_STEEL_CHAR_PERIOD;
4977 case 0x1673: /* (green steel) */
4978 element = EL_STEEL_CHAR_EXCLAM;
4981 case 0x1674: /* (green steel) */
4982 element = EL_STEEL_CHAR_COLON;
4985 case 0x1675: /* (green steel) */
4986 element = EL_STEEL_CHAR_LESS;
4989 case 0x1676: /* (green steel) */
4990 element = EL_STEEL_CHAR_GREATER;
4993 case 0x1677: /* (green steel) */
4994 element = EL_STEEL_CHAR_QUESTION;
4997 case 0x1678: /* (green steel) */
4998 element = EL_STEEL_CHAR_COPYRIGHT;
5001 case 0x1679: /* (green steel) */
5002 element = EL_STEEL_CHAR_UP;
5005 case 0x167a: /* (green steel) */
5006 element = EL_STEEL_CHAR_DOWN;
5009 case 0x167b: /* (green steel) */
5010 element = EL_STEEL_CHAR_BUTTON;
5013 case 0x167c: /* (green steel) */
5014 element = EL_STEEL_CHAR_PLUS;
5017 case 0x167d: /* (green steel) */
5018 element = EL_STEEL_CHAR_MINUS;
5021 case 0x167e: /* (green steel) */
5022 element = EL_STEEL_CHAR_APOSTROPHE;
5025 case 0x167f: /* (green steel) */
5026 element = EL_STEEL_CHAR_PARENLEFT;
5029 case 0x1680: /* (green steel) */
5030 element = EL_STEEL_CHAR_PARENRIGHT;
5033 case 0x1681: /* gate (red) */
5034 element = EL_EM_GATE_1;
5037 case 0x1682: /* secret gate (red) */
5038 element = EL_GATE_1_GRAY;
5041 case 0x1683: /* gate (yellow) */
5042 element = EL_EM_GATE_2;
5045 case 0x1684: /* secret gate (yellow) */
5046 element = EL_GATE_2_GRAY;
5049 case 0x1685: /* gate (blue) */
5050 element = EL_EM_GATE_4;
5053 case 0x1686: /* secret gate (blue) */
5054 element = EL_GATE_4_GRAY;
5057 case 0x1687: /* gate (green) */
5058 element = EL_EM_GATE_3;
5061 case 0x1688: /* secret gate (green) */
5062 element = EL_GATE_3_GRAY;
5065 case 0x1689: /* gate (white) */
5066 element = EL_DC_GATE_WHITE;
5069 case 0x168a: /* secret gate (white) */
5070 element = EL_DC_GATE_WHITE_GRAY;
5073 case 0x168b: /* secret gate (no key) */
5074 element = EL_DC_GATE_FAKE_GRAY;
5078 element = EL_ROBOT_WHEEL;
5082 element = EL_DC_TIMEGATE_SWITCH;
5086 element = EL_ACID_POOL_BOTTOM;
5090 element = EL_ACID_POOL_TOPLEFT;
5094 element = EL_ACID_POOL_TOPRIGHT;
5098 element = EL_ACID_POOL_BOTTOMLEFT;
5102 element = EL_ACID_POOL_BOTTOMRIGHT;
5106 element = EL_STEELWALL;
5110 element = EL_STEELWALL_SLIPPERY;
5113 case 0x1695: /* steel wall (not round) */
5114 element = EL_STEELWALL;
5117 case 0x1696: /* steel wall (left) */
5118 element = EL_DC_STEELWALL_1_LEFT;
5121 case 0x1697: /* steel wall (bottom) */
5122 element = EL_DC_STEELWALL_1_BOTTOM;
5125 case 0x1698: /* steel wall (right) */
5126 element = EL_DC_STEELWALL_1_RIGHT;
5129 case 0x1699: /* steel wall (top) */
5130 element = EL_DC_STEELWALL_1_TOP;
5133 case 0x169a: /* steel wall (left/bottom) */
5134 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5137 case 0x169b: /* steel wall (right/bottom) */
5138 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5141 case 0x169c: /* steel wall (right/top) */
5142 element = EL_DC_STEELWALL_1_TOPRIGHT;
5145 case 0x169d: /* steel wall (left/top) */
5146 element = EL_DC_STEELWALL_1_TOPLEFT;
5149 case 0x169e: /* steel wall (right/bottom small) */
5150 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5153 case 0x169f: /* steel wall (left/bottom small) */
5154 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5157 case 0x16a0: /* steel wall (right/top small) */
5158 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5161 case 0x16a1: /* steel wall (left/top small) */
5162 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5165 case 0x16a2: /* steel wall (left/right) */
5166 element = EL_DC_STEELWALL_1_VERTICAL;
5169 case 0x16a3: /* steel wall (top/bottom) */
5170 element = EL_DC_STEELWALL_1_HORIZONTAL;
5173 case 0x16a4: /* steel wall 2 (left end) */
5174 element = EL_DC_STEELWALL_2_LEFT;
5177 case 0x16a5: /* steel wall 2 (right end) */
5178 element = EL_DC_STEELWALL_2_RIGHT;
5181 case 0x16a6: /* steel wall 2 (top end) */
5182 element = EL_DC_STEELWALL_2_TOP;
5185 case 0x16a7: /* steel wall 2 (bottom end) */
5186 element = EL_DC_STEELWALL_2_BOTTOM;
5189 case 0x16a8: /* steel wall 2 (left/right) */
5190 element = EL_DC_STEELWALL_2_HORIZONTAL;
5193 case 0x16a9: /* steel wall 2 (up/down) */
5194 element = EL_DC_STEELWALL_2_VERTICAL;
5197 case 0x16aa: /* steel wall 2 (mid) */
5198 element = EL_DC_STEELWALL_2_MIDDLE;
5202 element = EL_SIGN_EXCLAMATION;
5206 element = EL_SIGN_RADIOACTIVITY;
5210 element = EL_SIGN_STOP;
5214 element = EL_SIGN_WHEELCHAIR;
5218 element = EL_SIGN_PARKING;
5222 element = EL_SIGN_NO_ENTRY;
5226 element = EL_SIGN_HEART;
5230 element = EL_SIGN_GIVE_WAY;
5234 element = EL_SIGN_ENTRY_FORBIDDEN;
5238 element = EL_SIGN_EMERGENCY_EXIT;
5242 element = EL_SIGN_YIN_YANG;
5246 element = EL_WALL_EMERALD;
5250 element = EL_WALL_DIAMOND;
5254 element = EL_WALL_PEARL;
5258 element = EL_WALL_CRYSTAL;
5262 element = EL_INVISIBLE_WALL;
5266 element = EL_INVISIBLE_STEELWALL;
5269 /* 0x16bc - 0x16cb: */
5270 /* EL_INVISIBLE_SAND */
5273 element = EL_LIGHT_SWITCH;
5277 element = EL_ENVELOPE_1;
5281 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5282 element = EL_DIAMOND;
5283 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5284 element = EL_EMERALD;
5285 else if (element >= 0x157c && element <= 0x158b)
5287 else if (element >= 0x1590 && element <= 0x159f)
5288 element = EL_DC_LANDMINE;
5289 else if (element >= 0x16bc && element <= 0x16cb)
5290 element = EL_INVISIBLE_SAND;
5293 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5294 element = EL_UNKNOWN;
5299 return getMappedElement(element);
5302 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5305 byte header[DC_LEVEL_HEADER_SIZE];
5307 int envelope_header_pos = 62;
5308 int envelope_content_pos = 94;
5309 int level_name_pos = 251;
5310 int level_author_pos = 292;
5311 int envelope_header_len;
5312 int envelope_content_len;
5314 int level_author_len;
5316 int num_yamyam_contents;
5319 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5321 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5323 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5325 header[i * 2 + 0] = header_word >> 8;
5326 header[i * 2 + 1] = header_word & 0xff;
5329 /* read some values from level header to check level decoding integrity */
5330 fieldx = header[6] | (header[7] << 8);
5331 fieldy = header[8] | (header[9] << 8);
5332 num_yamyam_contents = header[60] | (header[61] << 8);
5334 /* do some simple sanity checks to ensure that level was correctly decoded */
5335 if (fieldx < 1 || fieldx > 256 ||
5336 fieldy < 1 || fieldy > 256 ||
5337 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5339 level->no_valid_file = TRUE;
5341 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5346 /* maximum envelope header size is 31 bytes */
5347 envelope_header_len = header[envelope_header_pos];
5348 /* maximum envelope content size is 110 (156?) bytes */
5349 envelope_content_len = header[envelope_content_pos];
5351 /* maximum level title size is 40 bytes */
5352 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5353 /* maximum level author size is 30 (51?) bytes */
5354 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5358 for (i = 0; i < envelope_header_len; i++)
5359 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5360 level->envelope[0].text[envelope_size++] =
5361 header[envelope_header_pos + 1 + i];
5363 if (envelope_header_len > 0 && envelope_content_len > 0)
5365 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5366 level->envelope[0].text[envelope_size++] = '\n';
5367 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5368 level->envelope[0].text[envelope_size++] = '\n';
5371 for (i = 0; i < envelope_content_len; i++)
5372 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5373 level->envelope[0].text[envelope_size++] =
5374 header[envelope_content_pos + 1 + i];
5376 level->envelope[0].text[envelope_size] = '\0';
5378 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5379 level->envelope[0].ysize = 10;
5380 level->envelope[0].autowrap = TRUE;
5381 level->envelope[0].centered = TRUE;
5383 for (i = 0; i < level_name_len; i++)
5384 level->name[i] = header[level_name_pos + 1 + i];
5385 level->name[level_name_len] = '\0';
5387 for (i = 0; i < level_author_len; i++)
5388 level->author[i] = header[level_author_pos + 1 + i];
5389 level->author[level_author_len] = '\0';
5391 num_yamyam_contents = header[60] | (header[61] << 8);
5392 level->num_yamyam_contents =
5393 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5395 for (i = 0; i < num_yamyam_contents; i++)
5397 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5399 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5400 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5402 if (i < MAX_ELEMENT_CONTENTS)
5403 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5407 fieldx = header[6] | (header[7] << 8);
5408 fieldy = header[8] | (header[9] << 8);
5409 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5410 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5412 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5414 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5415 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5417 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5418 level->field[x][y] = getMappedElement_DC(element_dc);
5421 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5422 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5423 level->field[x][y] = EL_PLAYER_1;
5425 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5426 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5427 level->field[x][y] = EL_PLAYER_2;
5429 level->gems_needed = header[18] | (header[19] << 8);
5431 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5432 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5433 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5434 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5435 level->score[SC_NUT] = header[28] | (header[29] << 8);
5436 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5437 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5438 level->score[SC_BUG] = header[34] | (header[35] << 8);
5439 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5440 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5441 level->score[SC_KEY] = header[40] | (header[41] << 8);
5442 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5444 level->time = header[44] | (header[45] << 8);
5446 level->amoeba_speed = header[46] | (header[47] << 8);
5447 level->time_light = header[48] | (header[49] << 8);
5448 level->time_timegate = header[50] | (header[51] << 8);
5449 level->time_wheel = header[52] | (header[53] << 8);
5450 level->time_magic_wall = header[54] | (header[55] << 8);
5451 level->extra_time = header[56] | (header[57] << 8);
5452 level->shield_normal_time = header[58] | (header[59] << 8);
5454 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5455 can slip down from flat walls, like normal walls and steel walls */
5456 level->em_slippery_gems = TRUE;
5459 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5460 struct LevelFileInfo *level_file_info,
5461 boolean level_info_only)
5463 char *filename = level_file_info->filename;
5465 int num_magic_bytes = 8;
5466 char magic_bytes[num_magic_bytes + 1];
5467 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5469 if (!(file = openFile(filename, MODE_READ)))
5471 level->no_valid_file = TRUE;
5473 if (!level_info_only)
5474 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5479 // fseek(file, 0x0000, SEEK_SET);
5481 if (level_file_info->packed)
5483 /* read "magic bytes" from start of file */
5484 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5485 magic_bytes[0] = '\0';
5487 /* check "magic bytes" for correct file format */
5488 if (!strPrefix(magic_bytes, "DC2"))
5490 level->no_valid_file = TRUE;
5492 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5498 if (strPrefix(magic_bytes, "DC2Win95") ||
5499 strPrefix(magic_bytes, "DC2Win98"))
5501 int position_first_level = 0x00fa;
5502 int extra_bytes = 4;
5505 /* advance file stream to first level inside the level package */
5506 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5508 /* each block of level data is followed by block of non-level data */
5509 num_levels_to_skip *= 2;
5511 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5512 while (num_levels_to_skip >= 0)
5514 /* advance file stream to next level inside the level package */
5515 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5517 level->no_valid_file = TRUE;
5519 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5525 /* skip apparently unused extra bytes following each level */
5526 ReadUnusedBytesFromFile(file, extra_bytes);
5528 /* read size of next level in level package */
5529 skip_bytes = getFile32BitLE(file);
5531 num_levels_to_skip--;
5536 level->no_valid_file = TRUE;
5538 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5545 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5551 /* ------------------------------------------------------------------------- */
5552 /* functions for loading SB level */
5553 /* ------------------------------------------------------------------------- */
5555 int getMappedElement_SB(int element_ascii, boolean use_ces)
5563 sb_element_mapping[] =
5565 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5566 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5567 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5568 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5569 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5570 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5571 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5572 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5579 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5580 if (element_ascii == sb_element_mapping[i].ascii)
5581 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5583 return EL_UNDEFINED;
5586 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5587 struct LevelFileInfo *level_file_info,
5588 boolean level_info_only)
5590 char *filename = level_file_info->filename;
5591 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5592 char last_comment[MAX_LINE_LEN];
5593 char level_name[MAX_LINE_LEN];
5596 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5597 boolean read_continued_line = FALSE;
5598 boolean reading_playfield = FALSE;
5599 boolean got_valid_playfield_line = FALSE;
5600 boolean invalid_playfield_char = FALSE;
5601 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5602 int file_level_nr = 0;
5604 int x = 0, y = 0; /* initialized to make compilers happy */
5606 last_comment[0] = '\0';
5607 level_name[0] = '\0';
5609 if (!(file = openFile(filename, MODE_READ)))
5611 level->no_valid_file = TRUE;
5613 if (!level_info_only)
5614 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5619 while (!checkEndOfFile(file))
5621 /* level successfully read, but next level may follow here */
5622 if (!got_valid_playfield_line && reading_playfield)
5624 /* read playfield from single level file -- skip remaining file */
5625 if (!level_file_info->packed)
5628 if (file_level_nr >= num_levels_to_skip)
5633 last_comment[0] = '\0';
5634 level_name[0] = '\0';
5636 reading_playfield = FALSE;
5639 got_valid_playfield_line = FALSE;
5641 /* read next line of input file */
5642 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5645 /* check if line was completely read and is terminated by line break */
5646 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5649 /* cut trailing line break (this can be newline and/or carriage return) */
5650 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5651 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5654 /* copy raw input line for later use (mainly debugging output) */
5655 strcpy(line_raw, line);
5657 if (read_continued_line)
5659 /* append new line to existing line, if there is enough space */
5660 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5661 strcat(previous_line, line_ptr);
5663 strcpy(line, previous_line); /* copy storage buffer to line */
5665 read_continued_line = FALSE;
5668 /* if the last character is '\', continue at next line */
5669 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5671 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5672 strcpy(previous_line, line); /* copy line to storage buffer */
5674 read_continued_line = TRUE;
5679 /* skip empty lines */
5680 if (line[0] == '\0')
5683 /* extract comment text from comment line */
5686 for (line_ptr = line; *line_ptr; line_ptr++)
5687 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5690 strcpy(last_comment, line_ptr);
5695 /* extract level title text from line containing level title */
5696 if (line[0] == '\'')
5698 strcpy(level_name, &line[1]);
5700 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5701 level_name[strlen(level_name) - 1] = '\0';
5706 /* skip lines containing only spaces (or empty lines) */
5707 for (line_ptr = line; *line_ptr; line_ptr++)
5708 if (*line_ptr != ' ')
5710 if (*line_ptr == '\0')
5713 /* at this point, we have found a line containing part of a playfield */
5715 got_valid_playfield_line = TRUE;
5717 if (!reading_playfield)
5719 reading_playfield = TRUE;
5720 invalid_playfield_char = FALSE;
5722 for (x = 0; x < MAX_LEV_FIELDX; x++)
5723 for (y = 0; y < MAX_LEV_FIELDY; y++)
5724 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5729 /* start with topmost tile row */
5733 /* skip playfield line if larger row than allowed */
5734 if (y >= MAX_LEV_FIELDY)
5737 /* start with leftmost tile column */
5740 /* read playfield elements from line */
5741 for (line_ptr = line; *line_ptr; line_ptr++)
5743 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5745 /* stop parsing playfield line if larger column than allowed */
5746 if (x >= MAX_LEV_FIELDX)
5749 if (mapped_sb_element == EL_UNDEFINED)
5751 invalid_playfield_char = TRUE;
5756 level->field[x][y] = mapped_sb_element;
5758 /* continue with next tile column */
5761 level->fieldx = MAX(x, level->fieldx);
5764 if (invalid_playfield_char)
5766 /* if first playfield line, treat invalid lines as comment lines */
5768 reading_playfield = FALSE;
5773 /* continue with next tile row */
5781 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
5782 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
5784 if (!reading_playfield)
5786 level->no_valid_file = TRUE;
5788 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5793 if (*level_name != '\0')
5795 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
5796 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5798 else if (*last_comment != '\0')
5800 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
5801 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5805 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
5808 /* set all empty fields beyond the border walls to invisible steel wall */
5809 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5811 if ((x == 0 || x == level->fieldx - 1 ||
5812 y == 0 || y == level->fieldy - 1) &&
5813 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
5814 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
5815 level->field, level->fieldx, level->fieldy);
5818 /* set special level settings for Sokoban levels */
5821 level->use_step_counter = TRUE;
5823 if (load_xsb_to_ces)
5825 /* special global settings can now be set in level template */
5827 level->use_custom_template = TRUE;
5832 /* ------------------------------------------------------------------------- */
5833 /* functions for handling native levels */
5834 /* ------------------------------------------------------------------------- */
5836 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
5837 struct LevelFileInfo *level_file_info,
5838 boolean level_info_only)
5840 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
5841 level->no_valid_file = TRUE;
5844 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
5845 struct LevelFileInfo *level_file_info,
5846 boolean level_info_only)
5850 /* determine position of requested level inside level package */
5851 if (level_file_info->packed)
5852 pos = level_file_info->nr - leveldir_current->first_level;
5854 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
5855 level->no_valid_file = TRUE;
5858 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
5860 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5861 CopyNativeLevel_RND_to_EM(level);
5862 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5863 CopyNativeLevel_RND_to_SP(level);
5866 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
5868 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5869 CopyNativeLevel_EM_to_RND(level);
5870 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5871 CopyNativeLevel_SP_to_RND(level);
5874 void SaveNativeLevel(struct LevelInfo *level)
5876 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5878 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
5879 char *filename = getLevelFilenameFromBasename(basename);
5881 CopyNativeLevel_RND_to_SP(level);
5882 CopyNativeTape_RND_to_SP(level);
5884 SaveNativeLevel_SP(filename);
5889 /* ------------------------------------------------------------------------- */
5890 /* functions for loading generic level */
5891 /* ------------------------------------------------------------------------- */
5893 static void LoadLevelFromFileInfo(struct LevelInfo *level,
5894 struct LevelFileInfo *level_file_info,
5895 boolean level_info_only)
5897 /* always start with reliable default values */
5898 setLevelInfoToDefaults(level, level_info_only, TRUE);
5900 switch (level_file_info->type)
5902 case LEVEL_FILE_TYPE_RND:
5903 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5906 case LEVEL_FILE_TYPE_EM:
5907 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
5908 level->game_engine_type = GAME_ENGINE_TYPE_EM;
5911 case LEVEL_FILE_TYPE_SP:
5912 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
5913 level->game_engine_type = GAME_ENGINE_TYPE_SP;
5916 case LEVEL_FILE_TYPE_DC:
5917 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
5920 case LEVEL_FILE_TYPE_SB:
5921 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
5925 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5929 /* if level file is invalid, restore level structure to default values */
5930 if (level->no_valid_file)
5931 setLevelInfoToDefaults(level, level_info_only, FALSE);
5933 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
5934 level->game_engine_type = GAME_ENGINE_TYPE_RND;
5936 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
5937 CopyNativeLevel_Native_to_RND(level);
5940 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
5942 static struct LevelFileInfo level_file_info;
5944 /* always start with reliable default values */
5945 setFileInfoToDefaults(&level_file_info);
5947 level_file_info.nr = 0; /* unknown level number */
5948 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
5949 level_file_info.filename = filename;
5951 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
5954 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
5958 if (leveldir_current == NULL) /* only when dumping level */
5961 /* all engine modifications also valid for levels which use latest engine */
5962 if (level->game_version < VERSION_IDENT(3,2,0,5))
5964 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
5965 level->score[SC_TIME_BONUS] /= 10;
5968 if (leveldir_current->latest_engine)
5970 /* ---------- use latest game engine ----------------------------------- */
5972 /* For all levels which are forced to use the latest game engine version
5973 (normally all but user contributed, private and undefined levels), set
5974 the game engine version to the actual version; this allows for actual
5975 corrections in the game engine to take effect for existing, converted
5976 levels (from "classic" or other existing games) to make the emulation
5977 of the corresponding game more accurate, while (hopefully) not breaking
5978 existing levels created from other players. */
5980 level->game_version = GAME_VERSION_ACTUAL;
5982 /* Set special EM style gems behaviour: EM style gems slip down from
5983 normal, steel and growing wall. As this is a more fundamental change,
5984 it seems better to set the default behaviour to "off" (as it is more
5985 natural) and make it configurable in the level editor (as a property
5986 of gem style elements). Already existing converted levels (neither
5987 private nor contributed levels) are changed to the new behaviour. */
5989 if (level->file_version < FILE_VERSION_2_0)
5990 level->em_slippery_gems = TRUE;
5995 /* ---------- use game engine the level was created with ----------------- */
5997 /* For all levels which are not forced to use the latest game engine
5998 version (normally user contributed, private and undefined levels),
5999 use the version of the game engine the levels were created for.
6001 Since 2.0.1, the game engine version is now directly stored
6002 in the level file (chunk "VERS"), so there is no need anymore
6003 to set the game version from the file version (except for old,
6004 pre-2.0 levels, where the game version is still taken from the
6005 file format version used to store the level -- see above). */
6007 /* player was faster than enemies in 1.0.0 and before */
6008 if (level->file_version == FILE_VERSION_1_0)
6009 for (i = 0; i < MAX_PLAYERS; i++)
6010 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6012 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6013 if (level->game_version == VERSION_IDENT(2,0,1,0))
6014 level->em_slippery_gems = TRUE;
6016 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6017 if (level->game_version < VERSION_IDENT(2,2,0,0))
6018 level->use_spring_bug = TRUE;
6020 if (level->game_version < VERSION_IDENT(3,2,0,5))
6022 /* time orb caused limited time in endless time levels before 3.2.0-5 */
6023 level->use_time_orb_bug = TRUE;
6025 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6026 level->block_snap_field = FALSE;
6028 /* extra time score was same value as time left score before 3.2.0-5 */
6029 level->extra_time_score = level->score[SC_TIME_BONUS];
6032 if (level->game_version < VERSION_IDENT(3,2,0,7))
6034 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6035 level->continuous_snapping = FALSE;
6038 /* only few elements were able to actively move into acid before 3.1.0 */
6039 /* trigger settings did not exist before 3.1.0; set to default "any" */
6040 if (level->game_version < VERSION_IDENT(3,1,0,0))
6042 /* correct "can move into acid" settings (all zero in old levels) */
6044 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6045 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6047 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6048 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6049 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6050 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6052 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6053 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6055 /* correct trigger settings (stored as zero == "none" in old levels) */
6057 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6059 int element = EL_CUSTOM_START + i;
6060 struct ElementInfo *ei = &element_info[element];
6062 for (j = 0; j < ei->num_change_pages; j++)
6064 struct ElementChangeInfo *change = &ei->change_page[j];
6066 change->trigger_player = CH_PLAYER_ANY;
6067 change->trigger_page = CH_PAGE_ANY;
6072 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6074 int element = EL_CUSTOM_256;
6075 struct ElementInfo *ei = &element_info[element];
6076 struct ElementChangeInfo *change = &ei->change_page[0];
6078 /* This is needed to fix a problem that was caused by a bugfix in function
6079 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6080 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6081 not replace walkable elements, but instead just placed the player on it,
6082 without placing the Sokoban field under the player). Unfortunately, this
6083 breaks "Snake Bite" style levels when the snake is halfway through a door
6084 that just closes (the snake head is still alive and can be moved in this
6085 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6086 player (without Sokoban element) which then gets killed as designed). */
6088 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6089 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6090 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6091 change->target_element = EL_PLAYER_1;
6094 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6095 if (level->game_version < VERSION_IDENT(3,2,5,0))
6097 /* This is needed to fix a problem that was caused by a bugfix in function
6098 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6099 corrects the behaviour when a custom element changes to another custom
6100 element with a higher element number that has change actions defined.
6101 Normally, only one change per frame is allowed for custom elements.
6102 Therefore, it is checked if a custom element already changed in the
6103 current frame; if it did, subsequent changes are suppressed.
6104 Unfortunately, this is only checked for element changes, but not for
6105 change actions, which are still executed. As the function above loops
6106 through all custom elements from lower to higher, an element change
6107 resulting in a lower CE number won't be checked again, while a target
6108 element with a higher number will also be checked, and potential change
6109 actions will get executed for this CE, too (which is wrong), while
6110 further changes are ignored (which is correct). As this bugfix breaks
6111 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6112 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6113 behaviour for existing levels and tapes that make use of this bug */
6115 level->use_action_after_change_bug = TRUE;
6118 /* not centering level after relocating player was default only in 3.2.3 */
6119 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6120 level->shifted_relocation = TRUE;
6122 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6123 if (level->game_version < VERSION_IDENT(3,2,6,0))
6124 level->em_explodes_by_fire = TRUE;
6127 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6131 /* map custom element change events that have changed in newer versions
6132 (these following values were accidentally changed in version 3.0.1)
6133 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6134 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6136 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6138 int element = EL_CUSTOM_START + i;
6140 /* order of checking and copying events to be mapped is important */
6141 /* (do not change the start and end value -- they are constant) */
6142 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6144 if (HAS_CHANGE_EVENT(element, j - 2))
6146 SET_CHANGE_EVENT(element, j - 2, FALSE);
6147 SET_CHANGE_EVENT(element, j, TRUE);
6151 /* order of checking and copying events to be mapped is important */
6152 /* (do not change the start and end value -- they are constant) */
6153 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6155 if (HAS_CHANGE_EVENT(element, j - 1))
6157 SET_CHANGE_EVENT(element, j - 1, FALSE);
6158 SET_CHANGE_EVENT(element, j, TRUE);
6164 /* initialize "can_change" field for old levels with only one change page */
6165 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6167 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6169 int element = EL_CUSTOM_START + i;
6171 if (CAN_CHANGE(element))
6172 element_info[element].change->can_change = TRUE;
6176 /* correct custom element values (for old levels without these options) */
6177 if (level->game_version < VERSION_IDENT(3,1,1,0))
6179 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6181 int element = EL_CUSTOM_START + i;
6182 struct ElementInfo *ei = &element_info[element];
6184 if (ei->access_direction == MV_NO_DIRECTION)
6185 ei->access_direction = MV_ALL_DIRECTIONS;
6189 /* correct custom element values (fix invalid values for all versions) */
6192 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6194 int element = EL_CUSTOM_START + i;
6195 struct ElementInfo *ei = &element_info[element];
6197 for (j = 0; j < ei->num_change_pages; j++)
6199 struct ElementChangeInfo *change = &ei->change_page[j];
6201 if (change->trigger_player == CH_PLAYER_NONE)
6202 change->trigger_player = CH_PLAYER_ANY;
6204 if (change->trigger_side == CH_SIDE_NONE)
6205 change->trigger_side = CH_SIDE_ANY;
6210 /* initialize "can_explode" field for old levels which did not store this */
6211 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6212 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6214 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6216 int element = EL_CUSTOM_START + i;
6218 if (EXPLODES_1X1_OLD(element))
6219 element_info[element].explosion_type = EXPLODES_1X1;
6221 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6222 EXPLODES_SMASHED(element) ||
6223 EXPLODES_IMPACT(element)));
6227 /* correct previously hard-coded move delay values for maze runner style */
6228 if (level->game_version < VERSION_IDENT(3,1,1,0))
6230 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6232 int element = EL_CUSTOM_START + i;
6234 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6236 /* previously hard-coded and therefore ignored */
6237 element_info[element].move_delay_fixed = 9;
6238 element_info[element].move_delay_random = 0;
6243 /* map elements that have changed in newer versions */
6244 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6245 level->game_version);
6246 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6247 for (x = 0; x < 3; x++)
6248 for (y = 0; y < 3; y++)
6249 level->yamyam_content[i].e[x][y] =
6250 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6251 level->game_version);
6253 /* initialize element properties for level editor etc. */
6254 InitElementPropertiesEngine(level->game_version);
6255 InitElementPropertiesAfterLoading(level->game_version);
6256 InitElementPropertiesGfxElement();
6259 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6263 /* map elements that have changed in newer versions */
6264 for (y = 0; y < level->fieldy; y++)
6265 for (x = 0; x < level->fieldx; x++)
6266 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6267 level->game_version);
6269 /* clear unused playfield data (nicer if level gets resized in editor) */
6270 for (x = 0; x < MAX_LEV_FIELDX; x++)
6271 for (y = 0; y < MAX_LEV_FIELDY; y++)
6272 if (x >= level->fieldx || y >= level->fieldy)
6273 level->field[x][y] = EL_EMPTY;
6275 /* copy elements to runtime playfield array */
6276 for (x = 0; x < MAX_LEV_FIELDX; x++)
6277 for (y = 0; y < MAX_LEV_FIELDY; y++)
6278 Feld[x][y] = level->field[x][y];
6280 /* initialize level size variables for faster access */
6281 lev_fieldx = level->fieldx;
6282 lev_fieldy = level->fieldy;
6284 /* determine border element for this level */
6285 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6286 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6291 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6293 struct LevelFileInfo *level_file_info = &level->file_info;
6295 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6296 CopyNativeLevel_RND_to_Native(level);
6299 void LoadLevelTemplate(int nr)
6303 setLevelFileInfo(&level_template.file_info, nr);
6304 filename = level_template.file_info.filename;
6306 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6308 LoadLevel_InitVersion(&level_template, filename);
6309 LoadLevel_InitElements(&level_template, filename);
6311 ActivateLevelTemplate();
6314 void LoadLevel(int nr)
6318 setLevelFileInfo(&level.file_info, nr);
6319 filename = level.file_info.filename;
6321 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6323 if (level.use_custom_template)
6324 LoadLevelTemplate(-1);
6326 LoadLevel_InitVersion(&level, filename);
6327 LoadLevel_InitElements(&level, filename);
6328 LoadLevel_InitPlayfield(&level, filename);
6330 LoadLevel_InitNativeEngines(&level, filename);
6333 void LoadLevelInfoOnly(int nr)
6335 setLevelFileInfo(&level.file_info, nr);
6337 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6340 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6344 chunk_size += putFileVersion(file, level->file_version);
6345 chunk_size += putFileVersion(file, level->game_version);
6350 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6354 chunk_size += putFile16BitBE(file, level->creation_date.year);
6355 chunk_size += putFile8Bit(file, level->creation_date.month);
6356 chunk_size += putFile8Bit(file, level->creation_date.day);
6361 #if ENABLE_HISTORIC_CHUNKS
6362 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6366 putFile8Bit(file, level->fieldx);
6367 putFile8Bit(file, level->fieldy);
6369 putFile16BitBE(file, level->time);
6370 putFile16BitBE(file, level->gems_needed);
6372 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6373 putFile8Bit(file, level->name[i]);
6375 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6376 putFile8Bit(file, level->score[i]);
6378 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6379 for (y = 0; y < 3; y++)
6380 for (x = 0; x < 3; x++)
6381 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6382 level->yamyam_content[i].e[x][y]));
6383 putFile8Bit(file, level->amoeba_speed);
6384 putFile8Bit(file, level->time_magic_wall);
6385 putFile8Bit(file, level->time_wheel);
6386 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6387 level->amoeba_content));
6388 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6389 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6390 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6391 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6393 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6395 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6396 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6397 putFile32BitBE(file, level->can_move_into_acid_bits);
6398 putFile8Bit(file, level->dont_collide_with_bits);
6400 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6401 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6403 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6404 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6405 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6407 putFile8Bit(file, level->game_engine_type);
6409 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6413 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6418 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6419 chunk_size += putFile8Bit(file, level->name[i]);
6424 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6429 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6430 chunk_size += putFile8Bit(file, level->author[i]);
6435 #if ENABLE_HISTORIC_CHUNKS
6436 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6441 for (y = 0; y < level->fieldy; y++)
6442 for (x = 0; x < level->fieldx; x++)
6443 if (level->encoding_16bit_field)
6444 chunk_size += putFile16BitBE(file, level->field[x][y]);
6446 chunk_size += putFile8Bit(file, level->field[x][y]);
6452 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6457 for (y = 0; y < level->fieldy; y++)
6458 for (x = 0; x < level->fieldx; x++)
6459 chunk_size += putFile16BitBE(file, level->field[x][y]);
6464 #if ENABLE_HISTORIC_CHUNKS
6465 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6469 putFile8Bit(file, EL_YAMYAM);
6470 putFile8Bit(file, level->num_yamyam_contents);
6471 putFile8Bit(file, 0);
6472 putFile8Bit(file, 0);
6474 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6475 for (y = 0; y < 3; y++)
6476 for (x = 0; x < 3; x++)
6477 if (level->encoding_16bit_field)
6478 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6480 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6484 #if ENABLE_HISTORIC_CHUNKS
6485 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6488 int num_contents, content_xsize, content_ysize;
6489 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6491 if (element == EL_YAMYAM)
6493 num_contents = level->num_yamyam_contents;
6497 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6498 for (y = 0; y < 3; y++)
6499 for (x = 0; x < 3; x++)
6500 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6502 else if (element == EL_BD_AMOEBA)
6508 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6509 for (y = 0; y < 3; y++)
6510 for (x = 0; x < 3; x++)
6511 content_array[i][x][y] = EL_EMPTY;
6512 content_array[0][0][0] = level->amoeba_content;
6516 /* chunk header already written -- write empty chunk data */
6517 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6519 Error(ERR_WARN, "cannot save content for element '%d'", element);
6523 putFile16BitBE(file, element);
6524 putFile8Bit(file, num_contents);
6525 putFile8Bit(file, content_xsize);
6526 putFile8Bit(file, content_ysize);
6528 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6530 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6531 for (y = 0; y < 3; y++)
6532 for (x = 0; x < 3; x++)
6533 putFile16BitBE(file, content_array[i][x][y]);
6537 #if ENABLE_HISTORIC_CHUNKS
6538 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6540 int envelope_nr = element - EL_ENVELOPE_1;
6541 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6545 chunk_size += putFile16BitBE(file, element);
6546 chunk_size += putFile16BitBE(file, envelope_len);
6547 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6548 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6550 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6551 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6553 for (i = 0; i < envelope_len; i++)
6554 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6560 #if ENABLE_HISTORIC_CHUNKS
6561 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6562 int num_changed_custom_elements)
6566 putFile16BitBE(file, num_changed_custom_elements);
6568 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6570 int element = EL_CUSTOM_START + i;
6572 struct ElementInfo *ei = &element_info[element];
6574 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6576 if (check < num_changed_custom_elements)
6578 putFile16BitBE(file, element);
6579 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6586 if (check != num_changed_custom_elements) /* should not happen */
6587 Error(ERR_WARN, "inconsistent number of custom element properties");
6591 #if ENABLE_HISTORIC_CHUNKS
6592 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6593 int num_changed_custom_elements)
6597 putFile16BitBE(file, num_changed_custom_elements);
6599 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6601 int element = EL_CUSTOM_START + i;
6603 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6605 if (check < num_changed_custom_elements)
6607 putFile16BitBE(file, element);
6608 putFile16BitBE(file, element_info[element].change->target_element);
6615 if (check != num_changed_custom_elements) /* should not happen */
6616 Error(ERR_WARN, "inconsistent number of custom target elements");
6620 #if ENABLE_HISTORIC_CHUNKS
6621 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6622 int num_changed_custom_elements)
6624 int i, j, x, y, check = 0;
6626 putFile16BitBE(file, num_changed_custom_elements);
6628 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6630 int element = EL_CUSTOM_START + i;
6631 struct ElementInfo *ei = &element_info[element];
6633 if (ei->modified_settings)
6635 if (check < num_changed_custom_elements)
6637 putFile16BitBE(file, element);
6639 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6640 putFile8Bit(file, ei->description[j]);
6642 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6644 /* some free bytes for future properties and padding */
6645 WriteUnusedBytesToFile(file, 7);
6647 putFile8Bit(file, ei->use_gfx_element);
6648 putFile16BitBE(file, ei->gfx_element_initial);
6650 putFile8Bit(file, ei->collect_score_initial);
6651 putFile8Bit(file, ei->collect_count_initial);
6653 putFile16BitBE(file, ei->push_delay_fixed);
6654 putFile16BitBE(file, ei->push_delay_random);
6655 putFile16BitBE(file, ei->move_delay_fixed);
6656 putFile16BitBE(file, ei->move_delay_random);
6658 putFile16BitBE(file, ei->move_pattern);
6659 putFile8Bit(file, ei->move_direction_initial);
6660 putFile8Bit(file, ei->move_stepsize);
6662 for (y = 0; y < 3; y++)
6663 for (x = 0; x < 3; x++)
6664 putFile16BitBE(file, ei->content.e[x][y]);
6666 putFile32BitBE(file, ei->change->events);
6668 putFile16BitBE(file, ei->change->target_element);
6670 putFile16BitBE(file, ei->change->delay_fixed);
6671 putFile16BitBE(file, ei->change->delay_random);
6672 putFile16BitBE(file, ei->change->delay_frames);
6674 putFile16BitBE(file, ei->change->initial_trigger_element);
6676 putFile8Bit(file, ei->change->explode);
6677 putFile8Bit(file, ei->change->use_target_content);
6678 putFile8Bit(file, ei->change->only_if_complete);
6679 putFile8Bit(file, ei->change->use_random_replace);
6681 putFile8Bit(file, ei->change->random_percentage);
6682 putFile8Bit(file, ei->change->replace_when);
6684 for (y = 0; y < 3; y++)
6685 for (x = 0; x < 3; x++)
6686 putFile16BitBE(file, ei->change->content.e[x][y]);
6688 putFile8Bit(file, ei->slippery_type);
6690 /* some free bytes for future properties and padding */
6691 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6698 if (check != num_changed_custom_elements) /* should not happen */
6699 Error(ERR_WARN, "inconsistent number of custom element properties");
6703 #if ENABLE_HISTORIC_CHUNKS
6704 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6706 struct ElementInfo *ei = &element_info[element];
6709 /* ---------- custom element base property values (96 bytes) ------------- */
6711 putFile16BitBE(file, element);
6713 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6714 putFile8Bit(file, ei->description[i]);
6716 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6718 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
6720 putFile8Bit(file, ei->num_change_pages);
6722 putFile16BitBE(file, ei->ce_value_fixed_initial);
6723 putFile16BitBE(file, ei->ce_value_random_initial);
6724 putFile8Bit(file, ei->use_last_ce_value);
6726 putFile8Bit(file, ei->use_gfx_element);
6727 putFile16BitBE(file, ei->gfx_element_initial);
6729 putFile8Bit(file, ei->collect_score_initial);
6730 putFile8Bit(file, ei->collect_count_initial);
6732 putFile8Bit(file, ei->drop_delay_fixed);
6733 putFile8Bit(file, ei->push_delay_fixed);
6734 putFile8Bit(file, ei->drop_delay_random);
6735 putFile8Bit(file, ei->push_delay_random);
6736 putFile16BitBE(file, ei->move_delay_fixed);
6737 putFile16BitBE(file, ei->move_delay_random);
6739 /* bits 0 - 15 of "move_pattern" ... */
6740 putFile16BitBE(file, ei->move_pattern & 0xffff);
6741 putFile8Bit(file, ei->move_direction_initial);
6742 putFile8Bit(file, ei->move_stepsize);
6744 putFile8Bit(file, ei->slippery_type);
6746 for (y = 0; y < 3; y++)
6747 for (x = 0; x < 3; x++)
6748 putFile16BitBE(file, ei->content.e[x][y]);
6750 putFile16BitBE(file, ei->move_enter_element);
6751 putFile16BitBE(file, ei->move_leave_element);
6752 putFile8Bit(file, ei->move_leave_type);
6754 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
6755 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
6757 putFile8Bit(file, ei->access_direction);
6759 putFile8Bit(file, ei->explosion_delay);
6760 putFile8Bit(file, ei->ignition_delay);
6761 putFile8Bit(file, ei->explosion_type);
6763 /* some free bytes for future custom property values and padding */
6764 WriteUnusedBytesToFile(file, 1);
6766 /* ---------- change page property values (48 bytes) --------------------- */
6768 for (i = 0; i < ei->num_change_pages; i++)
6770 struct ElementChangeInfo *change = &ei->change_page[i];
6771 unsigned int event_bits;
6773 /* bits 0 - 31 of "has_event[]" ... */
6775 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
6776 if (change->has_event[j])
6777 event_bits |= (1 << j);
6778 putFile32BitBE(file, event_bits);
6780 putFile16BitBE(file, change->target_element);
6782 putFile16BitBE(file, change->delay_fixed);
6783 putFile16BitBE(file, change->delay_random);
6784 putFile16BitBE(file, change->delay_frames);
6786 putFile16BitBE(file, change->initial_trigger_element);
6788 putFile8Bit(file, change->explode);
6789 putFile8Bit(file, change->use_target_content);
6790 putFile8Bit(file, change->only_if_complete);
6791 putFile8Bit(file, change->use_random_replace);
6793 putFile8Bit(file, change->random_percentage);
6794 putFile8Bit(file, change->replace_when);
6796 for (y = 0; y < 3; y++)
6797 for (x = 0; x < 3; x++)
6798 putFile16BitBE(file, change->target_content.e[x][y]);
6800 putFile8Bit(file, change->can_change);
6802 putFile8Bit(file, change->trigger_side);
6804 putFile8Bit(file, change->trigger_player);
6805 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
6806 log_2(change->trigger_page)));
6808 putFile8Bit(file, change->has_action);
6809 putFile8Bit(file, change->action_type);
6810 putFile8Bit(file, change->action_mode);
6811 putFile16BitBE(file, change->action_arg);
6813 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
6815 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
6816 if (change->has_event[j])
6817 event_bits |= (1 << (j - 32));
6818 putFile8Bit(file, event_bits);
6823 #if ENABLE_HISTORIC_CHUNKS
6824 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
6826 struct ElementInfo *ei = &element_info[element];
6827 struct ElementGroupInfo *group = ei->group;
6830 putFile16BitBE(file, element);
6832 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6833 putFile8Bit(file, ei->description[i]);
6835 putFile8Bit(file, group->num_elements);
6837 putFile8Bit(file, ei->use_gfx_element);
6838 putFile16BitBE(file, ei->gfx_element_initial);
6840 putFile8Bit(file, group->choice_mode);
6842 /* some free bytes for future values and padding */
6843 WriteUnusedBytesToFile(file, 3);
6845 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
6846 putFile16BitBE(file, group->element[i]);
6850 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
6851 boolean write_element)
6853 int save_type = entry->save_type;
6854 int data_type = entry->data_type;
6855 int conf_type = entry->conf_type;
6856 int byte_mask = conf_type & CONF_MASK_BYTES;
6857 int element = entry->element;
6858 int default_value = entry->default_value;
6860 boolean modified = FALSE;
6862 if (byte_mask != CONF_MASK_MULTI_BYTES)
6864 void *value_ptr = entry->value;
6865 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
6868 /* check if any settings have been modified before saving them */
6869 if (value != default_value)
6872 /* do not save if explicitly told or if unmodified default settings */
6873 if ((save_type == SAVE_CONF_NEVER) ||
6874 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6878 num_bytes += putFile16BitBE(file, element);
6880 num_bytes += putFile8Bit(file, conf_type);
6881 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
6882 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
6883 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
6886 else if (data_type == TYPE_STRING)
6888 char *default_string = entry->default_string;
6889 char *string = (char *)(entry->value);
6890 int string_length = strlen(string);
6893 /* check if any settings have been modified before saving them */
6894 if (!strEqual(string, default_string))
6897 /* do not save if explicitly told or if unmodified default settings */
6898 if ((save_type == SAVE_CONF_NEVER) ||
6899 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6903 num_bytes += putFile16BitBE(file, element);
6905 num_bytes += putFile8Bit(file, conf_type);
6906 num_bytes += putFile16BitBE(file, string_length);
6908 for (i = 0; i < string_length; i++)
6909 num_bytes += putFile8Bit(file, string[i]);
6911 else if (data_type == TYPE_ELEMENT_LIST)
6913 int *element_array = (int *)(entry->value);
6914 int num_elements = *(int *)(entry->num_entities);
6917 /* check if any settings have been modified before saving them */
6918 for (i = 0; i < num_elements; i++)
6919 if (element_array[i] != default_value)
6922 /* do not save if explicitly told or if unmodified default settings */
6923 if ((save_type == SAVE_CONF_NEVER) ||
6924 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6928 num_bytes += putFile16BitBE(file, element);
6930 num_bytes += putFile8Bit(file, conf_type);
6931 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
6933 for (i = 0; i < num_elements; i++)
6934 num_bytes += putFile16BitBE(file, element_array[i]);
6936 else if (data_type == TYPE_CONTENT_LIST)
6938 struct Content *content = (struct Content *)(entry->value);
6939 int num_contents = *(int *)(entry->num_entities);
6942 /* check if any settings have been modified before saving them */
6943 for (i = 0; i < num_contents; i++)
6944 for (y = 0; y < 3; y++)
6945 for (x = 0; x < 3; x++)
6946 if (content[i].e[x][y] != default_value)
6949 /* do not save if explicitly told or if unmodified default settings */
6950 if ((save_type == SAVE_CONF_NEVER) ||
6951 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6955 num_bytes += putFile16BitBE(file, element);
6957 num_bytes += putFile8Bit(file, conf_type);
6958 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
6960 for (i = 0; i < num_contents; i++)
6961 for (y = 0; y < 3; y++)
6962 for (x = 0; x < 3; x++)
6963 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
6969 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
6974 li = *level; /* copy level data into temporary buffer */
6976 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
6977 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
6982 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
6987 li = *level; /* copy level data into temporary buffer */
6989 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
6990 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
6995 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
6997 int envelope_nr = element - EL_ENVELOPE_1;
7001 chunk_size += putFile16BitBE(file, element);
7003 /* copy envelope data into temporary buffer */
7004 xx_envelope = level->envelope[envelope_nr];
7006 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7007 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7012 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7014 struct ElementInfo *ei = &element_info[element];
7018 chunk_size += putFile16BitBE(file, element);
7020 xx_ei = *ei; /* copy element data into temporary buffer */
7022 /* set default description string for this specific element */
7023 strcpy(xx_default_description, getDefaultElementDescription(ei));
7025 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7026 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7028 for (i = 0; i < ei->num_change_pages; i++)
7030 struct ElementChangeInfo *change = &ei->change_page[i];
7032 xx_current_change_page = i;
7034 xx_change = *change; /* copy change data into temporary buffer */
7037 setEventBitsFromEventFlags(change);
7039 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7040 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7047 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7049 struct ElementInfo *ei = &element_info[element];
7050 struct ElementGroupInfo *group = ei->group;
7054 chunk_size += putFile16BitBE(file, element);
7056 xx_ei = *ei; /* copy element data into temporary buffer */
7057 xx_group = *group; /* copy group data into temporary buffer */
7059 /* set default description string for this specific element */
7060 strcpy(xx_default_description, getDefaultElementDescription(ei));
7062 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7063 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7068 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7069 boolean save_as_template)
7075 if (!(file = fopen(filename, MODE_WRITE)))
7077 Error(ERR_WARN, "cannot save level file '%s'", filename);
7081 level->file_version = FILE_VERSION_ACTUAL;
7082 level->game_version = GAME_VERSION_ACTUAL;
7084 level->creation_date = getCurrentDate();
7086 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7087 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7089 chunk_size = SaveLevel_VERS(NULL, level);
7090 putFileChunkBE(file, "VERS", chunk_size);
7091 SaveLevel_VERS(file, level);
7093 chunk_size = SaveLevel_DATE(NULL, level);
7094 putFileChunkBE(file, "DATE", chunk_size);
7095 SaveLevel_DATE(file, level);
7097 chunk_size = SaveLevel_NAME(NULL, level);
7098 putFileChunkBE(file, "NAME", chunk_size);
7099 SaveLevel_NAME(file, level);
7101 chunk_size = SaveLevel_AUTH(NULL, level);
7102 putFileChunkBE(file, "AUTH", chunk_size);
7103 SaveLevel_AUTH(file, level);
7105 chunk_size = SaveLevel_INFO(NULL, level);
7106 putFileChunkBE(file, "INFO", chunk_size);
7107 SaveLevel_INFO(file, level);
7109 chunk_size = SaveLevel_BODY(NULL, level);
7110 putFileChunkBE(file, "BODY", chunk_size);
7111 SaveLevel_BODY(file, level);
7113 chunk_size = SaveLevel_ELEM(NULL, level);
7114 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7116 putFileChunkBE(file, "ELEM", chunk_size);
7117 SaveLevel_ELEM(file, level);
7120 for (i = 0; i < NUM_ENVELOPES; i++)
7122 int element = EL_ENVELOPE_1 + i;
7124 chunk_size = SaveLevel_NOTE(NULL, level, element);
7125 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7127 putFileChunkBE(file, "NOTE", chunk_size);
7128 SaveLevel_NOTE(file, level, element);
7132 /* if not using template level, check for non-default custom/group elements */
7133 if (!level->use_custom_template || save_as_template)
7135 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7137 int element = EL_CUSTOM_START + i;
7139 chunk_size = SaveLevel_CUSX(NULL, level, element);
7140 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7142 putFileChunkBE(file, "CUSX", chunk_size);
7143 SaveLevel_CUSX(file, level, element);
7147 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7149 int element = EL_GROUP_START + i;
7151 chunk_size = SaveLevel_GRPX(NULL, level, element);
7152 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7154 putFileChunkBE(file, "GRPX", chunk_size);
7155 SaveLevel_GRPX(file, level, element);
7162 SetFilePermissions(filename, PERMS_PRIVATE);
7165 void SaveLevel(int nr)
7167 char *filename = getDefaultLevelFilename(nr);
7169 SaveLevelFromFilename(&level, filename, FALSE);
7172 void SaveLevelTemplate()
7174 char *filename = getLocalLevelTemplateFilename();
7176 SaveLevelFromFilename(&level, filename, TRUE);
7179 boolean SaveLevelChecked(int nr)
7181 char *filename = getDefaultLevelFilename(nr);
7182 boolean new_level = !fileExists(filename);
7183 boolean level_saved = FALSE;
7185 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7190 Request("Level saved!", REQ_CONFIRM);
7198 void DumpLevel(struct LevelInfo *level)
7200 if (level->no_level_file || level->no_valid_file)
7202 Error(ERR_WARN, "cannot dump -- no valid level file found");
7208 Print("Level xxx (file version %08d, game version %08d)\n",
7209 level->file_version, level->game_version);
7212 Print("Level author: '%s'\n", level->author);
7213 Print("Level title: '%s'\n", level->name);
7215 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7217 Print("Level time: %d seconds\n", level->time);
7218 Print("Gems needed: %d\n", level->gems_needed);
7220 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7221 Print("Time for wheel: %d seconds\n", level->time_wheel);
7222 Print("Time for light: %d seconds\n", level->time_light);
7223 Print("Time for timegate: %d seconds\n", level->time_timegate);
7225 Print("Amoeba speed: %d\n", level->amoeba_speed);
7228 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7229 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7230 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7231 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7232 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7238 /* ========================================================================= */
7239 /* tape file functions */
7240 /* ========================================================================= */
7242 static void setTapeInfoToDefaults()
7246 /* always start with reliable default values (empty tape) */
7249 /* default values (also for pre-1.2 tapes) with only the first player */
7250 tape.player_participates[0] = TRUE;
7251 for (i = 1; i < MAX_PLAYERS; i++)
7252 tape.player_participates[i] = FALSE;
7254 /* at least one (default: the first) player participates in every tape */
7255 tape.num_participating_players = 1;
7257 tape.level_nr = level_nr;
7259 tape.changed = FALSE;
7261 tape.recording = FALSE;
7262 tape.playing = FALSE;
7263 tape.pausing = FALSE;
7265 tape.no_valid_file = FALSE;
7268 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7270 tape->file_version = getFileVersion(file);
7271 tape->game_version = getFileVersion(file);
7276 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7280 tape->random_seed = getFile32BitBE(file);
7281 tape->date = getFile32BitBE(file);
7282 tape->length = getFile32BitBE(file);
7284 /* read header fields that are new since version 1.2 */
7285 if (tape->file_version >= FILE_VERSION_1_2)
7287 byte store_participating_players = getFile8Bit(file);
7290 /* since version 1.2, tapes store which players participate in the tape */
7291 tape->num_participating_players = 0;
7292 for (i = 0; i < MAX_PLAYERS; i++)
7294 tape->player_participates[i] = FALSE;
7296 if (store_participating_players & (1 << i))
7298 tape->player_participates[i] = TRUE;
7299 tape->num_participating_players++;
7303 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7305 engine_version = getFileVersion(file);
7306 if (engine_version > 0)
7307 tape->engine_version = engine_version;
7309 tape->engine_version = tape->game_version;
7315 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7317 int level_identifier_size;
7320 level_identifier_size = getFile16BitBE(file);
7322 tape->level_identifier =
7323 checked_realloc(tape->level_identifier, level_identifier_size);
7325 for (i = 0; i < level_identifier_size; i++)
7326 tape->level_identifier[i] = getFile8Bit(file);
7328 tape->level_nr = getFile16BitBE(file);
7330 chunk_size = 2 + level_identifier_size + 2;
7335 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7338 int chunk_size_expected =
7339 (tape->num_participating_players + 1) * tape->length;
7341 if (chunk_size_expected != chunk_size)
7343 ReadUnusedBytesFromFile(file, chunk_size);
7344 return chunk_size_expected;
7347 for (i = 0; i < tape->length; i++)
7349 if (i >= MAX_TAPE_LEN)
7352 for (j = 0; j < MAX_PLAYERS; j++)
7354 tape->pos[i].action[j] = MV_NONE;
7356 if (tape->player_participates[j])
7357 tape->pos[i].action[j] = getFile8Bit(file);
7360 tape->pos[i].delay = getFile8Bit(file);
7362 if (tape->file_version == FILE_VERSION_1_0)
7364 /* eliminate possible diagonal moves in old tapes */
7365 /* this is only for backward compatibility */
7367 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7368 byte action = tape->pos[i].action[0];
7369 int k, num_moves = 0;
7371 for (k = 0; k<4; k++)
7373 if (action & joy_dir[k])
7375 tape->pos[i + num_moves].action[0] = joy_dir[k];
7377 tape->pos[i + num_moves].delay = 0;
7386 tape->length += num_moves;
7389 else if (tape->file_version < FILE_VERSION_2_0)
7391 /* convert pre-2.0 tapes to new tape format */
7393 if (tape->pos[i].delay > 1)
7396 tape->pos[i + 1] = tape->pos[i];
7397 tape->pos[i + 1].delay = 1;
7400 for (j = 0; j < MAX_PLAYERS; j++)
7401 tape->pos[i].action[j] = MV_NONE;
7402 tape->pos[i].delay--;
7409 if (checkEndOfFile(file))
7413 if (i != tape->length)
7414 chunk_size = (tape->num_participating_players + 1) * i;
7419 void LoadTape_SokobanSolution(char *filename)
7422 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7424 if (!(file = openFile(filename, MODE_READ)))
7426 tape.no_valid_file = TRUE;
7431 while (!checkEndOfFile(file))
7433 unsigned char c = getByteFromFile(file);
7435 if (checkEndOfFile(file))
7442 tape.pos[tape.length].action[0] = MV_UP;
7443 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7449 tape.pos[tape.length].action[0] = MV_DOWN;
7450 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7456 tape.pos[tape.length].action[0] = MV_LEFT;
7457 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7463 tape.pos[tape.length].action[0] = MV_RIGHT;
7464 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7472 /* ignore white-space characters */
7476 tape.no_valid_file = TRUE;
7478 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7486 if (tape.no_valid_file)
7489 tape.length_frames = GetTapeLengthFrames();
7490 tape.length_seconds = GetTapeLengthSeconds();
7493 void LoadTapeFromFilename(char *filename)
7495 char cookie[MAX_LINE_LEN];
7496 char chunk_name[CHUNK_ID_LEN + 1];
7500 /* always start with reliable default values */
7501 setTapeInfoToDefaults();
7503 if (strSuffix(filename, ".sln"))
7505 LoadTape_SokobanSolution(filename);
7510 if (!(file = openFile(filename, MODE_READ)))
7512 tape.no_valid_file = TRUE;
7517 getFileChunkBE(file, chunk_name, NULL);
7518 if (strEqual(chunk_name, "RND1"))
7520 getFile32BitBE(file); /* not used */
7522 getFileChunkBE(file, chunk_name, NULL);
7523 if (!strEqual(chunk_name, "TAPE"))
7525 tape.no_valid_file = TRUE;
7527 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7534 else /* check for pre-2.0 file format with cookie string */
7536 strcpy(cookie, chunk_name);
7537 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7539 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7540 cookie[strlen(cookie) - 1] = '\0';
7542 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7544 tape.no_valid_file = TRUE;
7546 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7553 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7555 tape.no_valid_file = TRUE;
7557 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7564 /* pre-2.0 tape files have no game version, so use file version here */
7565 tape.game_version = tape.file_version;
7568 if (tape.file_version < FILE_VERSION_1_2)
7570 /* tape files from versions before 1.2.0 without chunk structure */
7571 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7572 LoadTape_BODY(file, 2 * tape.length, &tape);
7580 int (*loader)(File *, int, struct TapeInfo *);
7584 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7585 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7586 { "INFO", -1, LoadTape_INFO },
7587 { "BODY", -1, LoadTape_BODY },
7591 while (getFileChunkBE(file, chunk_name, &chunk_size))
7595 while (chunk_info[i].name != NULL &&
7596 !strEqual(chunk_name, chunk_info[i].name))
7599 if (chunk_info[i].name == NULL)
7601 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7602 chunk_name, filename);
7603 ReadUnusedBytesFromFile(file, chunk_size);
7605 else if (chunk_info[i].size != -1 &&
7606 chunk_info[i].size != chunk_size)
7608 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7609 chunk_size, chunk_name, filename);
7610 ReadUnusedBytesFromFile(file, chunk_size);
7614 /* call function to load this tape chunk */
7615 int chunk_size_expected =
7616 (chunk_info[i].loader)(file, chunk_size, &tape);
7618 /* the size of some chunks cannot be checked before reading other
7619 chunks first (like "HEAD" and "BODY") that contain some header
7620 information, so check them here */
7621 if (chunk_size_expected != chunk_size)
7623 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7624 chunk_size, chunk_name, filename);
7632 tape.length_frames = GetTapeLengthFrames();
7633 tape.length_seconds = GetTapeLengthSeconds();
7636 printf("::: tape file version: %d\n", tape.file_version);
7637 printf("::: tape game version: %d\n", tape.game_version);
7638 printf("::: tape engine version: %d\n", tape.engine_version);
7642 void LoadTape(int nr)
7644 char *filename = getTapeFilename(nr);
7646 LoadTapeFromFilename(filename);
7649 void LoadSolutionTape(int nr)
7651 char *filename = getSolutionTapeFilename(nr);
7653 LoadTapeFromFilename(filename);
7655 if (TAPE_IS_EMPTY(tape) &&
7656 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7657 level.native_sp_level->demo.is_available)
7658 CopyNativeTape_SP_to_RND(&level);
7661 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7663 putFileVersion(file, tape->file_version);
7664 putFileVersion(file, tape->game_version);
7667 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7670 byte store_participating_players = 0;
7672 /* set bits for participating players for compact storage */
7673 for (i = 0; i < MAX_PLAYERS; i++)
7674 if (tape->player_participates[i])
7675 store_participating_players |= (1 << i);
7677 putFile32BitBE(file, tape->random_seed);
7678 putFile32BitBE(file, tape->date);
7679 putFile32BitBE(file, tape->length);
7681 putFile8Bit(file, store_participating_players);
7683 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7684 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7686 putFileVersion(file, tape->engine_version);
7689 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7691 int level_identifier_size = strlen(tape->level_identifier) + 1;
7694 putFile16BitBE(file, level_identifier_size);
7696 for (i = 0; i < level_identifier_size; i++)
7697 putFile8Bit(file, tape->level_identifier[i]);
7699 putFile16BitBE(file, tape->level_nr);
7702 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7706 for (i = 0; i < tape->length; i++)
7708 for (j = 0; j < MAX_PLAYERS; j++)
7709 if (tape->player_participates[j])
7710 putFile8Bit(file, tape->pos[i].action[j]);
7712 putFile8Bit(file, tape->pos[i].delay);
7716 void SaveTape(int nr)
7718 char *filename = getTapeFilename(nr);
7720 int num_participating_players = 0;
7721 int info_chunk_size;
7722 int body_chunk_size;
7725 InitTapeDirectory(leveldir_current->subdir);
7727 if (!(file = fopen(filename, MODE_WRITE)))
7729 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7733 tape.file_version = FILE_VERSION_ACTUAL;
7734 tape.game_version = GAME_VERSION_ACTUAL;
7736 /* count number of participating players */
7737 for (i = 0; i < MAX_PLAYERS; i++)
7738 if (tape.player_participates[i])
7739 num_participating_players++;
7741 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7742 body_chunk_size = (num_participating_players + 1) * tape.length;
7744 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7745 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7747 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7748 SaveTape_VERS(file, &tape);
7750 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7751 SaveTape_HEAD(file, &tape);
7753 putFileChunkBE(file, "INFO", info_chunk_size);
7754 SaveTape_INFO(file, &tape);
7756 putFileChunkBE(file, "BODY", body_chunk_size);
7757 SaveTape_BODY(file, &tape);
7761 SetFilePermissions(filename, PERMS_PRIVATE);
7763 tape.changed = FALSE;
7766 boolean SaveTapeChecked(int nr)
7768 char *filename = getTapeFilename(nr);
7769 boolean new_tape = !fileExists(filename);
7770 boolean tape_saved = FALSE;
7772 if (new_tape || Request("Replace old tape?", REQ_ASK))
7777 Request("Tape saved!", REQ_CONFIRM);
7785 void DumpTape(struct TapeInfo *tape)
7787 int tape_frame_counter;
7790 if (tape->no_valid_file)
7792 Error(ERR_WARN, "cannot dump -- no valid tape file found");
7798 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
7799 tape->level_nr, tape->file_version, tape->game_version);
7800 Print(" (effective engine version %08d)\n",
7801 tape->engine_version);
7802 Print("Level series identifier: '%s'\n", tape->level_identifier);
7805 tape_frame_counter = 0;
7807 for (i = 0; i < tape->length; i++)
7809 if (i >= MAX_TAPE_LEN)
7814 for (j = 0; j < MAX_PLAYERS; j++)
7816 if (tape->player_participates[j])
7818 int action = tape->pos[i].action[j];
7820 Print("%d:%02x ", j, action);
7821 Print("[%c%c%c%c|%c%c] - ",
7822 (action & JOY_LEFT ? '<' : ' '),
7823 (action & JOY_RIGHT ? '>' : ' '),
7824 (action & JOY_UP ? '^' : ' '),
7825 (action & JOY_DOWN ? 'v' : ' '),
7826 (action & JOY_BUTTON_1 ? '1' : ' '),
7827 (action & JOY_BUTTON_2 ? '2' : ' '));
7831 Print("(%03d) ", tape->pos[i].delay);
7832 Print("[%05d]\n", tape_frame_counter);
7834 tape_frame_counter += tape->pos[i].delay;
7841 /* ========================================================================= */
7842 /* score file functions */
7843 /* ========================================================================= */
7845 void LoadScore(int nr)
7848 char *filename = getScoreFilename(nr);
7849 char cookie[MAX_LINE_LEN];
7850 char line[MAX_LINE_LEN];
7854 /* always start with reliable default values */
7855 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7857 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
7858 highscore[i].Score = 0;
7861 if (!(file = fopen(filename, MODE_READ)))
7864 /* check file identifier */
7865 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
7867 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7868 cookie[strlen(cookie) - 1] = '\0';
7870 if (!checkCookieString(cookie, SCORE_COOKIE))
7872 Error(ERR_WARN, "unknown format of score file '%s'", filename);
7877 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7879 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
7880 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
7881 if (fgets(line, MAX_LINE_LEN, file) == NULL)
7884 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
7885 line[strlen(line) - 1] = '\0';
7887 for (line_ptr = line; *line_ptr; line_ptr++)
7889 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
7891 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
7892 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
7901 void SaveScore(int nr)
7904 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
7905 char *filename = getScoreFilename(nr);
7908 InitScoreDirectory(leveldir_current->subdir);
7910 if (!(file = fopen(filename, MODE_WRITE)))
7912 Error(ERR_WARN, "cannot save score for level %d", nr);
7916 fprintf(file, "%s\n\n", SCORE_COOKIE);
7918 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7919 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
7923 SetFilePermissions(filename, permissions);
7927 /* ========================================================================= */
7928 /* setup file functions */
7929 /* ========================================================================= */
7931 #define TOKEN_STR_PLAYER_PREFIX "player_"
7934 #define SETUP_TOKEN_PLAYER_NAME 0
7935 #define SETUP_TOKEN_SOUND 1
7936 #define SETUP_TOKEN_SOUND_LOOPS 2
7937 #define SETUP_TOKEN_SOUND_MUSIC 3
7938 #define SETUP_TOKEN_SOUND_SIMPLE 4
7939 #define SETUP_TOKEN_TOONS 5
7940 #define SETUP_TOKEN_SCROLL_DELAY 6
7941 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
7942 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE 8
7943 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY 9
7944 #define SETUP_TOKEN_FADE_SCREENS 10
7945 #define SETUP_TOKEN_AUTORECORD 11
7946 #define SETUP_TOKEN_SHOW_TITLESCREEN 12
7947 #define SETUP_TOKEN_QUICK_DOORS 13
7948 #define SETUP_TOKEN_TEAM_MODE 14
7949 #define SETUP_TOKEN_HANDICAP 15
7950 #define SETUP_TOKEN_SKIP_LEVELS 16
7951 #define SETUP_TOKEN_INCREMENT_LEVELS 17
7952 #define SETUP_TOKEN_TIME_LIMIT 18
7953 #define SETUP_TOKEN_FULLSCREEN 19
7954 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT 20
7955 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY 21
7956 #define SETUP_TOKEN_SCREEN_RENDERING_MODE 22
7957 #define SETUP_TOKEN_ASK_ON_ESCAPE 23
7958 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 24
7959 #define SETUP_TOKEN_QUICK_SWITCH 25
7960 #define SETUP_TOKEN_INPUT_ON_FOCUS 26
7961 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 27
7962 #define SETUP_TOKEN_GAME_FRAME_DELAY 28
7963 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 29
7964 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 30
7965 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS 31
7966 #define SETUP_TOKEN_GRAPHICS_SET 32
7967 #define SETUP_TOKEN_SOUNDS_SET 33
7968 #define SETUP_TOKEN_MUSIC_SET 34
7969 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 35
7970 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 36
7971 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 37
7972 #define SETUP_TOKEN_VOLUME_SIMPLE 38
7973 #define SETUP_TOKEN_VOLUME_LOOPS 39
7974 #define SETUP_TOKEN_VOLUME_MUSIC 40
7975 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE 41
7976 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 42
7977 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE 43
7979 #define NUM_GLOBAL_SETUP_TOKENS 44
7982 #define SETUP_TOKEN_EDITOR_EL_CLASSIC 0
7983 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 1
7984 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 2
7985 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 3
7986 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 4
7987 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 5
7989 #define NUM_EDITOR_SETUP_TOKENS 6
7991 /* editor cascade setup */
7992 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
7993 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
7994 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
7995 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
7996 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
7997 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
7998 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
7999 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
8000 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
8001 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
8002 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
8003 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
8004 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
8005 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
8006 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
8008 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
8010 /* shortcut setup */
8011 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
8012 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
8013 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
8014 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
8015 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
8016 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
8017 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
8018 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
8019 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
8020 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
8021 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
8022 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
8023 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
8024 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
8025 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
8026 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
8027 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
8028 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
8029 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
8030 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
8031 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
8033 #define NUM_SHORTCUT_SETUP_TOKENS 21
8036 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
8037 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
8038 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
8039 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
8040 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
8041 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
8042 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
8043 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
8044 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
8045 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
8046 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
8047 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
8048 #define SETUP_TOKEN_PLAYER_KEY_UP 12
8049 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
8050 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8051 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8053 #define NUM_PLAYER_SETUP_TOKENS 16
8056 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8057 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8058 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8060 #define NUM_SYSTEM_SETUP_TOKENS 3
8062 /* internal setup */
8063 #define SETUP_TOKEN_INT_PROGRAM_TITLE 0
8064 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR 1
8065 #define SETUP_TOKEN_INT_PROGRAM_EMAIL 2
8066 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE 3
8067 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 4
8068 #define SETUP_TOKEN_INT_PROGRAM_COMPANY 5
8069 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 6
8070 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 7
8071 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 8
8072 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 9
8073 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 10
8074 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 11
8075 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 12
8076 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 13
8077 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 14
8078 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH 15
8079 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT 16
8081 #define NUM_INTERNAL_SETUP_TOKENS 17
8084 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0 0
8085 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1 1
8086 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2 2
8087 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3 3
8088 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4 4
8089 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5 5
8090 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6 6
8091 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7 7
8092 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8 8
8093 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9 9
8094 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0 10
8095 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1 11
8096 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2 12
8097 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3 13
8098 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4 14
8099 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5 15
8100 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6 16
8101 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7 17
8102 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8 18
8103 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9 19
8104 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8105 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8106 #define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
8108 #define NUM_DEBUG_SETUP_TOKENS 23
8111 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8113 #define NUM_OPTIONS_SETUP_TOKENS 1
8116 static struct SetupInfo si;
8117 static struct SetupEditorInfo sei;
8118 static struct SetupEditorCascadeInfo seci;
8119 static struct SetupShortcutInfo ssi;
8120 static struct SetupInputInfo sii;
8121 static struct SetupSystemInfo syi;
8122 static struct SetupInternalInfo sxi;
8123 static struct SetupDebugInfo sdi;
8124 static struct OptionInfo soi;
8126 static struct TokenInfo global_setup_tokens[] =
8128 { TYPE_STRING, &si.player_name, "player_name" },
8129 { TYPE_SWITCH, &si.sound, "sound" },
8130 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8131 { TYPE_SWITCH, &si.sound_music, "background_music" },
8132 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8133 { TYPE_SWITCH, &si.toons, "toons" },
8134 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8135 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8136 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8137 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8138 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8139 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8140 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8141 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8142 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8143 { TYPE_SWITCH, &si.handicap, "handicap" },
8144 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8145 { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
8146 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8147 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8148 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8149 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8150 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8151 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8152 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8153 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8154 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8155 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8156 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8157 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8158 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8159 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8160 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8161 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8162 { TYPE_STRING, &si.music_set, "music_set" },
8163 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8164 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8165 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8166 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8167 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8168 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8169 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8170 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8171 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8174 static struct TokenInfo editor_setup_tokens[] =
8176 { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
8177 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8178 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8179 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8180 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8181 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8184 static struct TokenInfo editor_cascade_setup_tokens[] =
8186 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8187 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8188 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8189 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8190 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8191 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8192 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8193 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8194 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8195 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8196 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8197 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8198 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8199 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8200 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8203 static struct TokenInfo shortcut_setup_tokens[] =
8205 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8206 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8207 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8208 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8209 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8210 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8211 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8212 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8213 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8214 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8215 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8216 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8217 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8218 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8219 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8220 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8221 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8222 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8223 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8224 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8225 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8228 static struct TokenInfo player_setup_tokens[] =
8230 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8231 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8232 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8233 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8234 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8235 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8236 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8237 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8238 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8239 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8240 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8241 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8242 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8243 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8244 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8245 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8248 static struct TokenInfo system_setup_tokens[] =
8250 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8251 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8252 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8255 static struct TokenInfo internal_setup_tokens[] =
8257 { TYPE_STRING, &sxi.program_title, "program_title" },
8258 { TYPE_STRING, &sxi.program_author, "program_author" },
8259 { TYPE_STRING, &sxi.program_email, "program_email" },
8260 { TYPE_STRING, &sxi.program_website, "program_website" },
8261 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8262 { TYPE_STRING, &sxi.program_company, "program_company" },
8263 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8264 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8265 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8266 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8267 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8268 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8269 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8270 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8271 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8272 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8273 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8276 static struct TokenInfo debug_setup_tokens[] =
8278 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8279 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8280 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8281 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8282 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8283 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8284 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8285 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8286 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8287 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8288 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8289 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8290 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8291 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8292 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8293 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8294 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8295 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8296 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8297 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8298 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8299 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8300 { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8303 static struct TokenInfo options_setup_tokens[] =
8305 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8308 static char *get_corrected_login_name(char *login_name)
8310 /* needed because player name must be a fixed length string */
8311 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8313 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8314 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8316 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8317 if (strchr(login_name_new, ' '))
8318 *strchr(login_name_new, ' ') = '\0';
8320 return login_name_new;
8323 static void setSetupInfoToDefaults(struct SetupInfo *si)
8327 si->player_name = get_corrected_login_name(getLoginName());
8330 si->sound_loops = TRUE;
8331 si->sound_music = TRUE;
8332 si->sound_simple = TRUE;
8334 si->scroll_delay = TRUE;
8335 si->scroll_delay_value = STD_SCROLL_DELAY;
8336 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8337 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8338 si->fade_screens = TRUE;
8339 si->autorecord = TRUE;
8340 si->show_titlescreen = TRUE;
8341 si->quick_doors = FALSE;
8342 si->team_mode = FALSE;
8343 si->handicap = TRUE;
8344 si->skip_levels = TRUE;
8345 si->increment_levels = TRUE;
8346 si->time_limit = TRUE;
8347 si->fullscreen = FALSE;
8348 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8349 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8350 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8351 si->ask_on_escape = TRUE;
8352 si->ask_on_escape_editor = TRUE;
8353 si->quick_switch = FALSE;
8354 si->input_on_focus = FALSE;
8355 si->prefer_aga_graphics = TRUE;
8356 si->game_frame_delay = GAME_FRAME_DELAY;
8357 si->sp_show_border_elements = FALSE;
8358 si->small_game_graphics = FALSE;
8359 si->show_snapshot_buttons = FALSE;
8361 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8362 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8363 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8365 si->override_level_graphics = FALSE;
8366 si->override_level_sounds = FALSE;
8367 si->override_level_music = FALSE;
8369 si->volume_simple = 100; /* percent */
8370 si->volume_loops = 100; /* percent */
8371 si->volume_music = 100; /* percent */
8373 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8374 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8375 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8377 si->editor.el_boulderdash = TRUE;
8378 si->editor.el_emerald_mine = TRUE;
8379 si->editor.el_emerald_mine_club = TRUE;
8380 si->editor.el_more = TRUE;
8381 si->editor.el_sokoban = TRUE;
8382 si->editor.el_supaplex = TRUE;
8383 si->editor.el_diamond_caves = TRUE;
8384 si->editor.el_dx_boulderdash = TRUE;
8385 si->editor.el_chars = TRUE;
8386 si->editor.el_steel_chars = TRUE;
8388 si->editor.el_classic = TRUE;
8389 si->editor.el_custom = TRUE;
8391 si->editor.el_user_defined = FALSE;
8392 si->editor.el_dynamic = TRUE;
8394 si->editor.el_headlines = TRUE;
8396 si->editor.show_element_token = FALSE;
8398 si->editor.use_template_for_new_levels = TRUE;
8400 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8401 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8402 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8404 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8405 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8406 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8407 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8408 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8410 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8411 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8412 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8413 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8414 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8415 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8417 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8418 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8419 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8421 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8422 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8423 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8424 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8426 for (i = 0; i < MAX_PLAYERS; i++)
8428 si->input[i].use_joystick = FALSE;
8429 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8430 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8431 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8432 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8433 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8434 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8435 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8436 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8437 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8438 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8439 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8440 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8441 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8442 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8443 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8446 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8447 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8448 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8450 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8451 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8452 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8453 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8454 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8455 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8457 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8459 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8460 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8461 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8463 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8464 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8465 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8467 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8468 si->internal.choose_from_top_leveldir = FALSE;
8470 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
8471 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8473 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8474 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8475 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8476 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8477 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8478 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8479 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8480 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8481 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8482 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8484 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8485 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8486 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8487 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8488 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8489 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8490 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8491 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8492 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8493 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8495 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8496 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
8498 si->debug.show_frames_per_second = FALSE;
8500 si->options.verbose = FALSE;
8502 #if defined(PLATFORM_ANDROID)
8503 si->fullscreen = TRUE;
8507 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8509 si->editor_cascade.el_bd = TRUE;
8510 si->editor_cascade.el_em = TRUE;
8511 si->editor_cascade.el_emc = TRUE;
8512 si->editor_cascade.el_rnd = TRUE;
8513 si->editor_cascade.el_sb = TRUE;
8514 si->editor_cascade.el_sp = TRUE;
8515 si->editor_cascade.el_dc = TRUE;
8516 si->editor_cascade.el_dx = TRUE;
8518 si->editor_cascade.el_chars = FALSE;
8519 si->editor_cascade.el_steel_chars = FALSE;
8520 si->editor_cascade.el_ce = FALSE;
8521 si->editor_cascade.el_ge = FALSE;
8522 si->editor_cascade.el_ref = FALSE;
8523 si->editor_cascade.el_user = FALSE;
8524 si->editor_cascade.el_dynamic = FALSE;
8527 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
8529 static char *getHideSetupToken(void *setup_value)
8531 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
8533 if (setup_value != NULL)
8534 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
8536 return hide_setup_token;
8539 static void setHideSetupEntry(void *setup_value_raw)
8541 /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
8542 void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
8544 char *hide_setup_token = getHideSetupToken(setup_value);
8546 if (setup_value != NULL)
8547 setHashEntry(hide_setup_hash, hide_setup_token, "");
8550 boolean hideSetupEntry(void *setup_value)
8552 char *hide_setup_token = getHideSetupToken(setup_value);
8554 return (setup_value != NULL &&
8555 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
8558 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
8559 struct TokenInfo *token_info,
8560 int token_nr, char *token_text)
8562 char *token_hide_text = getStringCat2(token_text, ".hide");
8563 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
8565 /* set the value of this setup option in the setup option structure */
8566 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
8568 /* check if this setup option should be hidden in the setup menu */
8569 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
8570 setHideSetupEntry(token_info[token_nr].value);
8573 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
8574 struct TokenInfo *token_info,
8577 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
8578 token_info[token_nr].text);
8581 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8585 if (!setup_file_hash)
8588 if (hide_setup_hash == NULL)
8589 hide_setup_hash = newSetupFileHash();
8593 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8594 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
8599 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8600 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
8603 /* shortcut setup */
8604 ssi = setup.shortcut;
8605 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8606 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
8607 setup.shortcut = ssi;
8610 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8614 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8616 sii = setup.input[pnr];
8617 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8619 char full_token[100];
8621 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8622 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
8625 setup.input[pnr] = sii;
8630 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8631 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
8634 /* internal setup */
8635 sxi = setup.internal;
8636 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8637 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
8638 setup.internal = sxi;
8642 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8643 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
8647 soi = setup.options;
8648 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8649 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
8650 setup.options = soi;
8653 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8657 if (!setup_file_hash)
8660 /* editor cascade setup */
8661 seci = setup.editor_cascade;
8662 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8663 setSetupInfo(editor_cascade_setup_tokens, i,
8664 getHashEntry(setup_file_hash,
8665 editor_cascade_setup_tokens[i].text));
8666 setup.editor_cascade = seci;
8669 void LoadSetupFromFilename(char *filename)
8671 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
8673 if (setup_file_hash)
8675 decodeSetupFileHash(setup_file_hash);
8677 freeSetupFileHash(setup_file_hash);
8681 Error(ERR_DEBUG, "using default setup values");
8685 static void LoadSetup_SpecialPostProcessing()
8687 char *player_name_new;
8689 /* needed to work around problems with fixed length strings */
8690 player_name_new = get_corrected_login_name(setup.player_name);
8691 free(setup.player_name);
8692 setup.player_name = player_name_new;
8694 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
8695 if (setup.scroll_delay == FALSE)
8697 setup.scroll_delay_value = MIN_SCROLL_DELAY;
8698 setup.scroll_delay = TRUE; /* now always "on" */
8701 /* make sure that scroll delay value stays inside valid range */
8702 setup.scroll_delay_value =
8703 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
8710 /* always start with reliable default values */
8711 setSetupInfoToDefaults(&setup);
8713 /* try to load setup values from default setup file */
8714 filename = getDefaultSetupFilename();
8716 if (fileExists(filename))
8717 LoadSetupFromFilename(filename);
8719 /* try to load setup values from user setup file */
8720 filename = getSetupFilename();
8722 LoadSetupFromFilename(filename);
8724 LoadSetup_SpecialPostProcessing();
8727 void LoadSetup_EditorCascade()
8729 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8730 SetupFileHash *setup_file_hash = NULL;
8732 /* always start with reliable default values */
8733 setSetupInfoToDefaults_EditorCascade(&setup);
8735 setup_file_hash = loadSetupFileHash(filename);
8737 if (setup_file_hash)
8739 decodeSetupFileHash_EditorCascade(setup_file_hash);
8741 freeSetupFileHash(setup_file_hash);
8747 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
8750 char mapping_guid[MAX_LINE_LEN];
8751 char *mapping_start, *mapping_end;
8753 // get GUID from game controller mapping line: copy complete line
8754 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
8755 mapping_guid[MAX_LINE_LEN - 1] = '\0';
8757 // get GUID from game controller mapping line: cut after GUID part
8758 mapping_start = strchr(mapping_guid, ',');
8759 if (mapping_start != NULL)
8760 *mapping_start = '\0';
8762 // cut newline from game controller mapping line
8763 mapping_end = strchr(mapping_line, '\n');
8764 if (mapping_end != NULL)
8765 *mapping_end = '\0';
8767 // add mapping entry to game controller mappings hash
8768 setHashEntry(mappings_hash, mapping_guid, mapping_line);
8771 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
8776 if (!(file = fopen(filename, MODE_READ)))
8778 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
8785 char line[MAX_LINE_LEN];
8787 if (!fgets(line, MAX_LINE_LEN, file))
8790 addGameControllerMappingToHash(mappings_hash, line);
8798 char *filename = getSetupFilename();
8802 InitUserDataDirectory();
8804 if (!(file = fopen(filename, MODE_WRITE)))
8806 Error(ERR_WARN, "cannot write setup file '%s'", filename);
8810 fprintFileHeader(file, SETUP_FILENAME);
8814 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8816 /* just to make things nicer :) */
8817 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
8818 i == SETUP_TOKEN_GRAPHICS_SET ||
8819 i == SETUP_TOKEN_VOLUME_SIMPLE ||
8820 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
8821 fprintf(file, "\n");
8823 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
8828 fprintf(file, "\n");
8829 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8830 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
8832 /* shortcut setup */
8833 ssi = setup.shortcut;
8834 fprintf(file, "\n");
8835 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8836 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
8839 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8843 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8844 fprintf(file, "\n");
8846 sii = setup.input[pnr];
8847 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8848 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
8853 fprintf(file, "\n");
8854 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8855 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
8857 /* internal setup */
8858 /* (internal setup values not saved to user setup file) */
8862 fprintf(file, "\n");
8863 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8864 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
8867 soi = setup.options;
8868 fprintf(file, "\n");
8869 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8870 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
8874 SetFilePermissions(filename, PERMS_PRIVATE);
8877 void SaveSetup_EditorCascade()
8879 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8883 InitUserDataDirectory();
8885 if (!(file = fopen(filename, MODE_WRITE)))
8887 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
8892 fprintFileHeader(file, EDITORCASCADE_FILENAME);
8894 seci = setup.editor_cascade;
8895 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8896 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
8900 SetFilePermissions(filename, PERMS_PRIVATE);
8905 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
8910 if (!(file = fopen(filename, MODE_WRITE)))
8912 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
8917 BEGIN_HASH_ITERATION(mappings_hash, itr)
8919 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
8921 END_HASH_ITERATION(mappings_hash, itr)
8926 void SaveSetup_AddGameControllerMapping(char *mapping)
8928 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
8929 SetupFileHash *mappings_hash = newSetupFileHash();
8931 InitUserDataDirectory();
8933 // load existing personal game controller mappings
8934 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
8936 // add new mapping to personal game controller mappings
8937 addGameControllerMappingToHash(mappings_hash, mapping);
8939 // save updated personal game controller mappings
8940 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
8942 freeSetupFileHash(mappings_hash);
8946 void LoadCustomElementDescriptions()
8948 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8949 SetupFileHash *setup_file_hash;
8952 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8954 if (element_info[i].custom_description != NULL)
8956 free(element_info[i].custom_description);
8957 element_info[i].custom_description = NULL;
8961 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8964 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8966 char *token = getStringCat2(element_info[i].token_name, ".name");
8967 char *value = getHashEntry(setup_file_hash, token);
8970 element_info[i].custom_description = getStringCopy(value);
8975 freeSetupFileHash(setup_file_hash);
8978 static int getElementFromToken(char *token)
8980 char *value = getHashEntry(element_token_hash, token);
8985 Error(ERR_WARN, "unknown element token '%s'", token);
8987 return EL_UNDEFINED;
8990 static int get_token_parameter_value(char *token, char *value_raw)
8994 if (token == NULL || value_raw == NULL)
8995 return ARG_UNDEFINED_VALUE;
8997 suffix = strrchr(token, '.');
9001 if (strEqual(suffix, ".element"))
9002 return getElementFromToken(value_raw);
9004 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9005 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9008 void InitMenuDesignSettings_Static()
9012 /* always start with reliable default values from static default config */
9013 for (i = 0; image_config_vars[i].token != NULL; i++)
9015 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9018 *image_config_vars[i].value =
9019 get_token_parameter_value(image_config_vars[i].token, value);
9023 static void InitMenuDesignSettings_SpecialPreProcessing()
9027 /* the following initializes hierarchical values from static configuration */
9029 /* special case: initialize "ARG_DEFAULT" values in static default config */
9030 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9031 titlescreen_initial_first_default.fade_mode =
9032 title_initial_first_default.fade_mode;
9033 titlescreen_initial_first_default.fade_delay =
9034 title_initial_first_default.fade_delay;
9035 titlescreen_initial_first_default.post_delay =
9036 title_initial_first_default.post_delay;
9037 titlescreen_initial_first_default.auto_delay =
9038 title_initial_first_default.auto_delay;
9039 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
9040 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9041 titlescreen_first_default.post_delay = title_first_default.post_delay;
9042 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9043 titlemessage_initial_first_default.fade_mode =
9044 title_initial_first_default.fade_mode;
9045 titlemessage_initial_first_default.fade_delay =
9046 title_initial_first_default.fade_delay;
9047 titlemessage_initial_first_default.post_delay =
9048 title_initial_first_default.post_delay;
9049 titlemessage_initial_first_default.auto_delay =
9050 title_initial_first_default.auto_delay;
9051 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
9052 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9053 titlemessage_first_default.post_delay = title_first_default.post_delay;
9054 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9056 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
9057 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9058 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9059 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9060 titlescreen_default.fade_mode = title_default.fade_mode;
9061 titlescreen_default.fade_delay = title_default.fade_delay;
9062 titlescreen_default.post_delay = title_default.post_delay;
9063 titlescreen_default.auto_delay = title_default.auto_delay;
9064 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9065 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9066 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9067 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9068 titlemessage_default.fade_mode = title_default.fade_mode;
9069 titlemessage_default.fade_delay = title_default.fade_delay;
9070 titlemessage_default.post_delay = title_default.post_delay;
9071 titlemessage_default.auto_delay = title_default.auto_delay;
9073 /* special case: initialize "ARG_DEFAULT" values in static default config */
9074 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9075 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9077 titlescreen_initial_first[i] = titlescreen_initial_first_default;
9078 titlescreen_first[i] = titlescreen_first_default;
9079 titlemessage_initial_first[i] = titlemessage_initial_first_default;
9080 titlemessage_first[i] = titlemessage_first_default;
9082 titlescreen_initial[i] = titlescreen_initial_default;
9083 titlescreen[i] = titlescreen_default;
9084 titlemessage_initial[i] = titlemessage_initial_default;
9085 titlemessage[i] = titlemessage_default;
9088 /* special case: initialize "ARG_DEFAULT" values in static default config */
9089 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9090 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9092 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
9095 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9096 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9097 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9100 /* special case: initialize "ARG_DEFAULT" values in static default config */
9101 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9102 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9104 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9105 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9106 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9108 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
9111 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9115 static void InitMenuDesignSettings_SpecialPostProcessing()
9119 struct XY *dst, *src;
9123 { &game.button.save, &game.button.stop },
9124 { &game.button.pause2, &game.button.pause },
9125 { &game.button.load, &game.button.play },
9126 { &game.button.undo, &game.button.stop },
9127 { &game.button.redo, &game.button.play },
9133 /* special case: initialize later added SETUP list size from LEVELS value */
9134 if (menu.list_size[GAME_MODE_SETUP] == -1)
9135 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9137 /* set default position for snapshot buttons to stop/pause/play buttons */
9138 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9139 if ((*game_buttons_xy[i].dst).x == -1 &&
9140 (*game_buttons_xy[i].dst).y == -1)
9141 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9144 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9148 struct XYTileSize *dst, *src;
9151 editor_buttons_xy[] =
9154 &editor.button.element_left, &editor.palette.element_left,
9155 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9158 &editor.button.element_middle, &editor.palette.element_middle,
9159 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9162 &editor.button.element_right, &editor.palette.element_right,
9163 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9170 /* set default position for element buttons to element graphics */
9171 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9173 if ((*editor_buttons_xy[i].dst).x == -1 &&
9174 (*editor_buttons_xy[i].dst).y == -1)
9176 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9178 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9180 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9185 static void LoadMenuDesignSettingsFromFilename(char *filename)
9187 static struct TitleFadingInfo tfi;
9188 static struct TitleMessageInfo tmi;
9189 static struct TokenInfo title_tokens[] =
9191 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9192 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9193 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9194 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9198 static struct TokenInfo titlemessage_tokens[] =
9200 { TYPE_INTEGER, &tmi.x, ".x" },
9201 { TYPE_INTEGER, &tmi.y, ".y" },
9202 { TYPE_INTEGER, &tmi.width, ".width" },
9203 { TYPE_INTEGER, &tmi.height, ".height" },
9204 { TYPE_INTEGER, &tmi.chars, ".chars" },
9205 { TYPE_INTEGER, &tmi.lines, ".lines" },
9206 { TYPE_INTEGER, &tmi.align, ".align" },
9207 { TYPE_INTEGER, &tmi.valign, ".valign" },
9208 { TYPE_INTEGER, &tmi.font, ".font" },
9209 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9210 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9211 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9212 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9213 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9214 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9215 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9216 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9222 struct TitleFadingInfo *info;
9227 /* initialize first titles from "enter screen" definitions, if defined */
9228 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9229 { &title_first_default, "menu.enter_screen.TITLE" },
9231 /* initialize title screens from "next screen" definitions, if defined */
9232 { &title_initial_default, "menu.next_screen.TITLE" },
9233 { &title_default, "menu.next_screen.TITLE" },
9239 struct TitleMessageInfo *array;
9242 titlemessage_arrays[] =
9244 /* initialize first titles from "enter screen" definitions, if defined */
9245 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9246 { titlescreen_first, "menu.enter_screen.TITLE" },
9247 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9248 { titlemessage_first, "menu.enter_screen.TITLE" },
9250 /* initialize titles from "next screen" definitions, if defined */
9251 { titlescreen_initial, "menu.next_screen.TITLE" },
9252 { titlescreen, "menu.next_screen.TITLE" },
9253 { titlemessage_initial, "menu.next_screen.TITLE" },
9254 { titlemessage, "menu.next_screen.TITLE" },
9256 /* overwrite titles with title definitions, if defined */
9257 { titlescreen_initial_first, "[title_initial]" },
9258 { titlescreen_first, "[title]" },
9259 { titlemessage_initial_first, "[title_initial]" },
9260 { titlemessage_first, "[title]" },
9262 { titlescreen_initial, "[title_initial]" },
9263 { titlescreen, "[title]" },
9264 { titlemessage_initial, "[title_initial]" },
9265 { titlemessage, "[title]" },
9267 /* overwrite titles with title screen/message definitions, if defined */
9268 { titlescreen_initial_first, "[titlescreen_initial]" },
9269 { titlescreen_first, "[titlescreen]" },
9270 { titlemessage_initial_first, "[titlemessage_initial]" },
9271 { titlemessage_first, "[titlemessage]" },
9273 { titlescreen_initial, "[titlescreen_initial]" },
9274 { titlescreen, "[titlescreen]" },
9275 { titlemessage_initial, "[titlemessage_initial]" },
9276 { titlemessage, "[titlemessage]" },
9280 SetupFileHash *setup_file_hash;
9283 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9286 /* the following initializes hierarchical values from dynamic configuration */
9288 /* special case: initialize with default values that may be overwritten */
9289 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9290 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9292 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9293 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9294 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9296 if (value_1 != NULL)
9297 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9298 if (value_2 != NULL)
9299 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9300 if (value_3 != NULL)
9301 menu.list_size[i] = get_integer_from_string(value_3);
9304 /* special case: initialize with default values that may be overwritten */
9305 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9306 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9308 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9309 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9311 if (value_1 != NULL)
9312 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9313 if (value_2 != NULL)
9314 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9316 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9318 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9320 if (value_1 != NULL)
9321 menu.list_size_info[i] = get_integer_from_string(value_1);
9325 /* special case: initialize with default values that may be overwritten */
9326 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9327 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9329 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9330 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9332 if (value_1 != NULL)
9333 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9334 if (value_2 != NULL)
9335 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9338 /* special case: initialize with default values that may be overwritten */
9339 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9340 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9342 char *token_1 = "menu.enter_screen.fade_mode";
9343 char *token_2 = "menu.enter_screen.fade_delay";
9344 char *token_3 = "menu.enter_screen.post_delay";
9345 char *token_4 = "menu.leave_screen.fade_mode";
9346 char *token_5 = "menu.leave_screen.fade_delay";
9347 char *token_6 = "menu.leave_screen.post_delay";
9348 char *token_7 = "menu.next_screen.fade_mode";
9349 char *token_8 = "menu.next_screen.fade_delay";
9350 char *token_9 = "menu.next_screen.post_delay";
9351 char *value_1 = getHashEntry(setup_file_hash, token_1);
9352 char *value_2 = getHashEntry(setup_file_hash, token_2);
9353 char *value_3 = getHashEntry(setup_file_hash, token_3);
9354 char *value_4 = getHashEntry(setup_file_hash, token_4);
9355 char *value_5 = getHashEntry(setup_file_hash, token_5);
9356 char *value_6 = getHashEntry(setup_file_hash, token_6);
9357 char *value_7 = getHashEntry(setup_file_hash, token_7);
9358 char *value_8 = getHashEntry(setup_file_hash, token_8);
9359 char *value_9 = getHashEntry(setup_file_hash, token_9);
9361 if (value_1 != NULL)
9362 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9364 if (value_2 != NULL)
9365 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9367 if (value_3 != NULL)
9368 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9370 if (value_4 != NULL)
9371 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9373 if (value_5 != NULL)
9374 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9376 if (value_6 != NULL)
9377 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9379 if (value_7 != NULL)
9380 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9382 if (value_8 != NULL)
9383 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9385 if (value_9 != NULL)
9386 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9390 /* special case: initialize with default values that may be overwritten */
9391 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9392 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9394 char *token_w1 = "viewport.window.width";
9395 char *token_w2 = "viewport.window.height";
9396 char *token_01 = "viewport.playfield.x";
9397 char *token_02 = "viewport.playfield.y";
9398 char *token_03 = "viewport.playfield.width";
9399 char *token_04 = "viewport.playfield.height";
9400 char *token_05 = "viewport.playfield.border_size";
9401 char *token_06 = "viewport.door_1.x";
9402 char *token_07 = "viewport.door_1.y";
9403 char *token_08 = "viewport.door_1.width";
9404 char *token_09 = "viewport.door_1.height";
9405 char *token_10 = "viewport.door_1.border_size";
9406 char *token_11 = "viewport.door_2.x";
9407 char *token_12 = "viewport.door_2.y";
9408 char *token_13 = "viewport.door_2.width";
9409 char *token_14 = "viewport.door_2.height";
9410 char *token_15 = "viewport.door_2.border_size";
9411 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9412 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9413 char *value_01 = getHashEntry(setup_file_hash, token_01);
9414 char *value_02 = getHashEntry(setup_file_hash, token_02);
9415 char *value_03 = getHashEntry(setup_file_hash, token_03);
9416 char *value_04 = getHashEntry(setup_file_hash, token_04);
9417 char *value_05 = getHashEntry(setup_file_hash, token_05);
9418 char *value_06 = getHashEntry(setup_file_hash, token_06);
9419 char *value_07 = getHashEntry(setup_file_hash, token_07);
9420 char *value_08 = getHashEntry(setup_file_hash, token_08);
9421 char *value_09 = getHashEntry(setup_file_hash, token_09);
9422 char *value_10 = getHashEntry(setup_file_hash, token_10);
9423 char *value_11 = getHashEntry(setup_file_hash, token_11);
9424 char *value_12 = getHashEntry(setup_file_hash, token_12);
9425 char *value_13 = getHashEntry(setup_file_hash, token_13);
9426 char *value_14 = getHashEntry(setup_file_hash, token_14);
9427 char *value_15 = getHashEntry(setup_file_hash, token_15);
9429 if (value_w1 != NULL)
9430 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9431 if (value_w2 != NULL)
9432 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9433 if (value_01 != NULL)
9434 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9435 if (value_02 != NULL)
9436 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9437 if (value_03 != NULL)
9438 viewport.playfield[i].width = get_token_parameter_value(token_03,
9440 if (value_04 != NULL)
9441 viewport.playfield[i].height = get_token_parameter_value(token_04,
9443 if (value_05 != NULL)
9444 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9446 if (value_06 != NULL)
9447 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9448 if (value_07 != NULL)
9449 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9450 if (value_08 != NULL)
9451 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9452 if (value_09 != NULL)
9453 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9454 if (value_10 != NULL)
9455 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9457 if (value_11 != NULL)
9458 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9459 if (value_12 != NULL)
9460 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9461 if (value_13 != NULL)
9462 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9463 if (value_14 != NULL)
9464 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9465 if (value_15 != NULL)
9466 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9470 /* special case: initialize with default values that may be overwritten */
9471 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9472 for (i = 0; title_info[i].info != NULL; i++)
9474 struct TitleFadingInfo *info = title_info[i].info;
9475 char *base_token = title_info[i].text;
9477 for (j = 0; title_tokens[j].type != -1; j++)
9479 char *token = getStringCat2(base_token, title_tokens[j].text);
9480 char *value = getHashEntry(setup_file_hash, token);
9484 int parameter_value = get_token_parameter_value(token, value);
9488 *(int *)title_tokens[j].value = (int)parameter_value;
9497 /* special case: initialize with default values that may be overwritten */
9498 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9499 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9501 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9502 char *base_token = titlemessage_arrays[i].text;
9504 for (j = 0; titlemessage_tokens[j].type != -1; j++)
9506 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9507 char *value = getHashEntry(setup_file_hash, token);
9511 int parameter_value = get_token_parameter_value(token, value);
9513 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9517 if (titlemessage_tokens[j].type == TYPE_INTEGER)
9518 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
9520 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9530 /* read (and overwrite with) values that may be specified in config file */
9531 for (i = 0; image_config_vars[i].token != NULL; i++)
9533 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9535 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9536 if (value != NULL && !strEqual(value, ARG_DEFAULT))
9537 *image_config_vars[i].value =
9538 get_token_parameter_value(image_config_vars[i].token, value);
9541 freeSetupFileHash(setup_file_hash);
9544 void LoadMenuDesignSettings()
9546 char *filename_base = UNDEFINED_FILENAME, *filename_local;
9548 InitMenuDesignSettings_Static();
9549 InitMenuDesignSettings_SpecialPreProcessing();
9551 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9553 /* first look for special settings configured in level series config */
9554 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9556 if (fileExists(filename_base))
9557 LoadMenuDesignSettingsFromFilename(filename_base);
9560 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9562 if (filename_local != NULL && !strEqual(filename_base, filename_local))
9563 LoadMenuDesignSettingsFromFilename(filename_local);
9565 InitMenuDesignSettings_SpecialPostProcessing();
9568 void LoadMenuDesignSettings_AfterGraphics()
9570 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9573 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9575 char *filename = getEditorSetupFilename();
9576 SetupFileList *setup_file_list, *list;
9577 SetupFileHash *element_hash;
9578 int num_unknown_tokens = 0;
9581 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9584 element_hash = newSetupFileHash();
9586 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9587 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9589 /* determined size may be larger than needed (due to unknown elements) */
9591 for (list = setup_file_list; list != NULL; list = list->next)
9594 /* add space for up to 3 more elements for padding that may be needed */
9597 /* free memory for old list of elements, if needed */
9598 checked_free(*elements);
9600 /* allocate memory for new list of elements */
9601 *elements = checked_malloc(*num_elements * sizeof(int));
9604 for (list = setup_file_list; list != NULL; list = list->next)
9606 char *value = getHashEntry(element_hash, list->token);
9608 if (value == NULL) /* try to find obsolete token mapping */
9610 char *mapped_token = get_mapped_token(list->token);
9612 if (mapped_token != NULL)
9614 value = getHashEntry(element_hash, mapped_token);
9622 (*elements)[(*num_elements)++] = atoi(value);
9626 if (num_unknown_tokens == 0)
9628 Error(ERR_INFO_LINE, "-");
9629 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9630 Error(ERR_INFO, "- config file: '%s'", filename);
9632 num_unknown_tokens++;
9635 Error(ERR_INFO, "- token: '%s'", list->token);
9639 if (num_unknown_tokens > 0)
9640 Error(ERR_INFO_LINE, "-");
9642 while (*num_elements % 4) /* pad with empty elements, if needed */
9643 (*elements)[(*num_elements)++] = EL_EMPTY;
9645 freeSetupFileList(setup_file_list);
9646 freeSetupFileHash(element_hash);
9649 for (i = 0; i < *num_elements; i++)
9650 printf("editor: element '%s' [%d]\n",
9651 element_info[(*elements)[i]].token_name, (*elements)[i]);
9655 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9658 SetupFileHash *setup_file_hash = NULL;
9659 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9660 char *filename_music, *filename_prefix, *filename_info;
9666 token_to_value_ptr[] =
9668 { "title_header", &tmp_music_file_info.title_header },
9669 { "artist_header", &tmp_music_file_info.artist_header },
9670 { "album_header", &tmp_music_file_info.album_header },
9671 { "year_header", &tmp_music_file_info.year_header },
9673 { "title", &tmp_music_file_info.title },
9674 { "artist", &tmp_music_file_info.artist },
9675 { "album", &tmp_music_file_info.album },
9676 { "year", &tmp_music_file_info.year },
9682 filename_music = (is_sound ? getCustomSoundFilename(basename) :
9683 getCustomMusicFilename(basename));
9685 if (filename_music == NULL)
9688 /* ---------- try to replace file extension ---------- */
9690 filename_prefix = getStringCopy(filename_music);
9691 if (strrchr(filename_prefix, '.') != NULL)
9692 *strrchr(filename_prefix, '.') = '\0';
9693 filename_info = getStringCat2(filename_prefix, ".txt");
9695 if (fileExists(filename_info))
9696 setup_file_hash = loadSetupFileHash(filename_info);
9698 free(filename_prefix);
9699 free(filename_info);
9701 if (setup_file_hash == NULL)
9703 /* ---------- try to add file extension ---------- */
9705 filename_prefix = getStringCopy(filename_music);
9706 filename_info = getStringCat2(filename_prefix, ".txt");
9708 if (fileExists(filename_info))
9709 setup_file_hash = loadSetupFileHash(filename_info);
9711 free(filename_prefix);
9712 free(filename_info);
9715 if (setup_file_hash == NULL)
9718 /* ---------- music file info found ---------- */
9720 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
9722 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
9724 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
9726 *token_to_value_ptr[i].value_ptr =
9727 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
9730 tmp_music_file_info.basename = getStringCopy(basename);
9731 tmp_music_file_info.music = music;
9732 tmp_music_file_info.is_sound = is_sound;
9734 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
9735 *new_music_file_info = tmp_music_file_info;
9737 return new_music_file_info;
9740 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
9742 return get_music_file_info_ext(basename, music, FALSE);
9745 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
9747 return get_music_file_info_ext(basename, sound, TRUE);
9750 static boolean music_info_listed_ext(struct MusicFileInfo *list,
9751 char *basename, boolean is_sound)
9753 for (; list != NULL; list = list->next)
9754 if (list->is_sound == is_sound && strEqual(list->basename, basename))
9760 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
9762 return music_info_listed_ext(list, basename, FALSE);
9765 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
9767 return music_info_listed_ext(list, basename, TRUE);
9770 void LoadMusicInfo()
9772 char *music_directory = getCustomMusicDirectory();
9773 int num_music = getMusicListSize();
9774 int num_music_noconf = 0;
9775 int num_sounds = getSoundListSize();
9777 DirectoryEntry *dir_entry;
9778 struct FileInfo *music, *sound;
9779 struct MusicFileInfo *next, **new;
9782 while (music_file_info != NULL)
9784 next = music_file_info->next;
9786 checked_free(music_file_info->basename);
9788 checked_free(music_file_info->title_header);
9789 checked_free(music_file_info->artist_header);
9790 checked_free(music_file_info->album_header);
9791 checked_free(music_file_info->year_header);
9793 checked_free(music_file_info->title);
9794 checked_free(music_file_info->artist);
9795 checked_free(music_file_info->album);
9796 checked_free(music_file_info->year);
9798 free(music_file_info);
9800 music_file_info = next;
9803 new = &music_file_info;
9805 for (i = 0; i < num_music; i++)
9807 music = getMusicListEntry(i);
9809 if (music->filename == NULL)
9812 if (strEqual(music->filename, UNDEFINED_FILENAME))
9815 /* a configured file may be not recognized as music */
9816 if (!FileIsMusic(music->filename))
9819 if (!music_info_listed(music_file_info, music->filename))
9821 *new = get_music_file_info(music->filename, i);
9824 new = &(*new)->next;
9828 if ((dir = openDirectory(music_directory)) == NULL)
9830 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
9834 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
9836 char *basename = dir_entry->basename;
9837 boolean music_already_used = FALSE;
9840 /* skip all music files that are configured in music config file */
9841 for (i = 0; i < num_music; i++)
9843 music = getMusicListEntry(i);
9845 if (music->filename == NULL)
9848 if (strEqual(basename, music->filename))
9850 music_already_used = TRUE;
9855 if (music_already_used)
9858 if (!FileIsMusic(dir_entry->filename))
9861 if (!music_info_listed(music_file_info, basename))
9863 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
9866 new = &(*new)->next;
9872 closeDirectory(dir);
9874 for (i = 0; i < num_sounds; i++)
9876 sound = getSoundListEntry(i);
9878 if (sound->filename == NULL)
9881 if (strEqual(sound->filename, UNDEFINED_FILENAME))
9884 /* a configured file may be not recognized as sound */
9885 if (!FileIsSound(sound->filename))
9888 if (!sound_info_listed(music_file_info, sound->filename))
9890 *new = get_sound_file_info(sound->filename, i);
9892 new = &(*new)->next;
9897 void add_helpanim_entry(int element, int action, int direction, int delay,
9898 int *num_list_entries)
9900 struct HelpAnimInfo *new_list_entry;
9901 (*num_list_entries)++;
9904 checked_realloc(helpanim_info,
9905 *num_list_entries * sizeof(struct HelpAnimInfo));
9906 new_list_entry = &helpanim_info[*num_list_entries - 1];
9908 new_list_entry->element = element;
9909 new_list_entry->action = action;
9910 new_list_entry->direction = direction;
9911 new_list_entry->delay = delay;
9914 void print_unknown_token(char *filename, char *token, int token_nr)
9918 Error(ERR_INFO_LINE, "-");
9919 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9920 Error(ERR_INFO, "- config file: '%s'", filename);
9923 Error(ERR_INFO, "- token: '%s'", token);
9926 void print_unknown_token_end(int token_nr)
9929 Error(ERR_INFO_LINE, "-");
9932 void LoadHelpAnimInfo()
9934 char *filename = getHelpAnimFilename();
9935 SetupFileList *setup_file_list = NULL, *list;
9936 SetupFileHash *element_hash, *action_hash, *direction_hash;
9937 int num_list_entries = 0;
9938 int num_unknown_tokens = 0;
9941 if (fileExists(filename))
9942 setup_file_list = loadSetupFileList(filename);
9944 if (setup_file_list == NULL)
9946 /* use reliable default values from static configuration */
9947 SetupFileList *insert_ptr;
9949 insert_ptr = setup_file_list =
9950 newSetupFileList(helpanim_config[0].token,
9951 helpanim_config[0].value);
9953 for (i = 1; helpanim_config[i].token; i++)
9954 insert_ptr = addListEntry(insert_ptr,
9955 helpanim_config[i].token,
9956 helpanim_config[i].value);
9959 element_hash = newSetupFileHash();
9960 action_hash = newSetupFileHash();
9961 direction_hash = newSetupFileHash();
9963 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
9964 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9966 for (i = 0; i < NUM_ACTIONS; i++)
9967 setHashEntry(action_hash, element_action_info[i].suffix,
9968 i_to_a(element_action_info[i].value));
9970 /* do not store direction index (bit) here, but direction value! */
9971 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
9972 setHashEntry(direction_hash, element_direction_info[i].suffix,
9973 i_to_a(1 << element_direction_info[i].value));
9975 for (list = setup_file_list; list != NULL; list = list->next)
9977 char *element_token, *action_token, *direction_token;
9978 char *element_value, *action_value, *direction_value;
9979 int delay = atoi(list->value);
9981 if (strEqual(list->token, "end"))
9983 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
9988 /* first try to break element into element/action/direction parts;
9989 if this does not work, also accept combined "element[.act][.dir]"
9990 elements (like "dynamite.active"), which are unique elements */
9992 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
9994 element_value = getHashEntry(element_hash, list->token);
9995 if (element_value != NULL) /* element found */
9996 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10000 /* no further suffixes found -- this is not an element */
10001 print_unknown_token(filename, list->token, num_unknown_tokens++);
10007 /* token has format "<prefix>.<something>" */
10009 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10010 direction_token = action_token; /* ... or direction */
10012 element_token = getStringCopy(list->token);
10013 *strchr(element_token, '.') = '\0';
10015 element_value = getHashEntry(element_hash, element_token);
10017 if (element_value == NULL) /* this is no element */
10019 element_value = getHashEntry(element_hash, list->token);
10020 if (element_value != NULL) /* combined element found */
10021 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10022 &num_list_entries);
10024 print_unknown_token(filename, list->token, num_unknown_tokens++);
10026 free(element_token);
10031 action_value = getHashEntry(action_hash, action_token);
10033 if (action_value != NULL) /* action found */
10035 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10036 &num_list_entries);
10038 free(element_token);
10043 direction_value = getHashEntry(direction_hash, direction_token);
10045 if (direction_value != NULL) /* direction found */
10047 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10048 &num_list_entries);
10050 free(element_token);
10055 if (strchr(action_token + 1, '.') == NULL)
10057 /* no further suffixes found -- this is not an action nor direction */
10059 element_value = getHashEntry(element_hash, list->token);
10060 if (element_value != NULL) /* combined element found */
10061 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10062 &num_list_entries);
10064 print_unknown_token(filename, list->token, num_unknown_tokens++);
10066 free(element_token);
10071 /* token has format "<prefix>.<suffix>.<something>" */
10073 direction_token = strchr(action_token + 1, '.');
10075 action_token = getStringCopy(action_token);
10076 *strchr(action_token + 1, '.') = '\0';
10078 action_value = getHashEntry(action_hash, action_token);
10080 if (action_value == NULL) /* this is no action */
10082 element_value = getHashEntry(element_hash, list->token);
10083 if (element_value != NULL) /* combined element found */
10084 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10085 &num_list_entries);
10087 print_unknown_token(filename, list->token, num_unknown_tokens++);
10089 free(element_token);
10090 free(action_token);
10095 direction_value = getHashEntry(direction_hash, direction_token);
10097 if (direction_value != NULL) /* direction found */
10099 add_helpanim_entry(atoi(element_value), atoi(action_value),
10100 atoi(direction_value), delay, &num_list_entries);
10102 free(element_token);
10103 free(action_token);
10108 /* this is no direction */
10110 element_value = getHashEntry(element_hash, list->token);
10111 if (element_value != NULL) /* combined element found */
10112 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10113 &num_list_entries);
10115 print_unknown_token(filename, list->token, num_unknown_tokens++);
10117 free(element_token);
10118 free(action_token);
10121 print_unknown_token_end(num_unknown_tokens);
10123 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10124 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10126 freeSetupFileList(setup_file_list);
10127 freeSetupFileHash(element_hash);
10128 freeSetupFileHash(action_hash);
10129 freeSetupFileHash(direction_hash);
10132 for (i = 0; i < num_list_entries; i++)
10133 printf("::: '%s': %d, %d, %d => %d\n",
10134 EL_NAME(helpanim_info[i].element),
10135 helpanim_info[i].element,
10136 helpanim_info[i].action,
10137 helpanim_info[i].direction,
10138 helpanim_info[i].delay);
10142 void LoadHelpTextInfo()
10144 char *filename = getHelpTextFilename();
10147 if (helptext_info != NULL)
10149 freeSetupFileHash(helptext_info);
10150 helptext_info = NULL;
10153 if (fileExists(filename))
10154 helptext_info = loadSetupFileHash(filename);
10156 if (helptext_info == NULL)
10158 /* use reliable default values from static configuration */
10159 helptext_info = newSetupFileHash();
10161 for (i = 0; helptext_config[i].token; i++)
10162 setHashEntry(helptext_info,
10163 helptext_config[i].token,
10164 helptext_config[i].value);
10168 BEGIN_HASH_ITERATION(helptext_info, itr)
10170 printf("::: '%s' => '%s'\n",
10171 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10173 END_HASH_ITERATION(hash, itr)
10178 /* ------------------------------------------------------------------------- */
10179 /* convert levels */
10180 /* ------------------------------------------------------------------------- */
10182 #define MAX_NUM_CONVERT_LEVELS 1000
10184 void ConvertLevels()
10186 static LevelDirTree *convert_leveldir = NULL;
10187 static int convert_level_nr = -1;
10188 static int num_levels_handled = 0;
10189 static int num_levels_converted = 0;
10190 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10193 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10194 global.convert_leveldir);
10196 if (convert_leveldir == NULL)
10197 Error(ERR_EXIT, "no such level identifier: '%s'",
10198 global.convert_leveldir);
10200 leveldir_current = convert_leveldir;
10202 if (global.convert_level_nr != -1)
10204 convert_leveldir->first_level = global.convert_level_nr;
10205 convert_leveldir->last_level = global.convert_level_nr;
10208 convert_level_nr = convert_leveldir->first_level;
10210 PrintLine("=", 79);
10211 Print("Converting levels\n");
10212 PrintLine("-", 79);
10213 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10214 Print("Level series name: '%s'\n", convert_leveldir->name);
10215 Print("Level series author: '%s'\n", convert_leveldir->author);
10216 Print("Number of levels: %d\n", convert_leveldir->levels);
10217 PrintLine("=", 79);
10220 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10221 levels_failed[i] = FALSE;
10223 while (convert_level_nr <= convert_leveldir->last_level)
10225 char *level_filename;
10228 level_nr = convert_level_nr++;
10230 Print("Level %03d: ", level_nr);
10232 LoadLevel(level_nr);
10233 if (level.no_level_file || level.no_valid_file)
10235 Print("(no level)\n");
10239 Print("converting level ... ");
10241 level_filename = getDefaultLevelFilename(level_nr);
10242 new_level = !fileExists(level_filename);
10246 SaveLevel(level_nr);
10248 num_levels_converted++;
10250 Print("converted.\n");
10254 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10255 levels_failed[level_nr] = TRUE;
10257 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10260 num_levels_handled++;
10264 PrintLine("=", 79);
10265 Print("Number of levels handled: %d\n", num_levels_handled);
10266 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10267 (num_levels_handled ?
10268 num_levels_converted * 100 / num_levels_handled : 0));
10269 PrintLine("-", 79);
10270 Print("Summary (for automatic parsing by scripts):\n");
10271 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10272 convert_leveldir->identifier, num_levels_converted,
10273 num_levels_handled,
10274 (num_levels_handled ?
10275 num_levels_converted * 100 / num_levels_handled : 0));
10277 if (num_levels_handled != num_levels_converted)
10279 Print(", FAILED:");
10280 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10281 if (levels_failed[i])
10286 PrintLine("=", 79);
10288 CloseAllAndExit(0);
10292 /* ------------------------------------------------------------------------- */
10293 /* create and save images for use in level sketches (raw BMP format) */
10294 /* ------------------------------------------------------------------------- */
10296 void CreateLevelSketchImages()
10298 #if defined(TARGET_SDL)
10303 InitElementPropertiesGfxElement();
10305 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10306 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10308 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10310 Bitmap *src_bitmap;
10312 int element = getMappedElement(i);
10313 int graphic = el2edimg(element);
10314 char basename1[16];
10315 char basename2[16];
10319 sprintf(basename1, "%03d.bmp", i);
10320 sprintf(basename2, "%03ds.bmp", i);
10322 filename1 = getPath2(global.create_images_dir, basename1);
10323 filename2 = getPath2(global.create_images_dir, basename2);
10325 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10326 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10329 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10330 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10332 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10333 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10335 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10336 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10342 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10345 FreeBitmap(bitmap1);
10346 FreeBitmap(bitmap2);
10351 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10353 CloseAllAndExit(0);
10358 /* ------------------------------------------------------------------------- */
10359 /* create and save images for custom and group elements (raw BMP format) */
10360 /* ------------------------------------------------------------------------- */
10362 void CreateCustomElementImages(char *directory)
10364 #if defined(TARGET_SDL)
10365 char *src_basename = "RocksCE-template.ilbm";
10366 char *dst_basename = "RocksCE.bmp";
10367 char *src_filename = getPath2(directory, src_basename);
10368 char *dst_filename = getPath2(directory, dst_basename);
10369 Bitmap *src_bitmap;
10371 int yoffset_ce = 0;
10372 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10375 SDLInitVideoDisplay();
10377 ReCreateBitmap(&backbuffer, video.width, video.height);
10379 src_bitmap = LoadImage(src_filename);
10381 bitmap = CreateBitmap(TILEX * 16 * 2,
10382 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10385 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10392 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10393 TILEX * x, TILEY * y + yoffset_ce);
10395 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10397 TILEX * x + TILEX * 16,
10398 TILEY * y + yoffset_ce);
10400 for (j = 2; j >= 0; j--)
10404 BlitBitmap(src_bitmap, bitmap,
10405 TILEX + c * 7, 0, 6, 10,
10406 TILEX * x + 6 + j * 7,
10407 TILEY * y + 11 + yoffset_ce);
10409 BlitBitmap(src_bitmap, bitmap,
10410 TILEX + c * 8, TILEY, 6, 10,
10411 TILEX * 16 + TILEX * x + 6 + j * 8,
10412 TILEY * y + 10 + yoffset_ce);
10418 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10425 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10426 TILEX * x, TILEY * y + yoffset_ge);
10428 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10430 TILEX * x + TILEX * 16,
10431 TILEY * y + yoffset_ge);
10433 for (j = 1; j >= 0; j--)
10437 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10438 TILEX * x + 6 + j * 10,
10439 TILEY * y + 11 + yoffset_ge);
10441 BlitBitmap(src_bitmap, bitmap,
10442 TILEX + c * 8, TILEY + 12, 6, 10,
10443 TILEX * 16 + TILEX * x + 10 + j * 8,
10444 TILEY * y + 10 + yoffset_ge);
10450 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10451 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10453 FreeBitmap(bitmap);
10455 CloseAllAndExit(0);