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 level file not found, try to initialize level data from template */
3197 filename = getGlobalLevelTemplateFilename();
3199 if (!(file = openFile(filename, MODE_READ)))
3202 /* default: for empty levels, use level template for custom elements */
3203 level->use_custom_template = TRUE;
3205 level->no_valid_file = FALSE;
3208 getFileChunkBE(file, chunk_name, NULL);
3209 if (strEqual(chunk_name, "RND1"))
3211 getFile32BitBE(file); /* not used */
3213 getFileChunkBE(file, chunk_name, NULL);
3214 if (!strEqual(chunk_name, "CAVE"))
3216 level->no_valid_file = TRUE;
3218 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3225 else /* check for pre-2.0 file format with cookie string */
3227 strcpy(cookie, chunk_name);
3228 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3230 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3231 cookie[strlen(cookie) - 1] = '\0';
3233 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3235 level->no_valid_file = TRUE;
3237 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3244 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3246 level->no_valid_file = TRUE;
3248 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3255 /* pre-2.0 level files have no game version, so use file version here */
3256 level->game_version = level->file_version;
3259 if (level->file_version < FILE_VERSION_1_2)
3261 /* level files from versions before 1.2.0 without chunk structure */
3262 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3263 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3271 int (*loader)(File *, int, struct LevelInfo *);
3275 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3276 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3277 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3278 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3279 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3280 { "INFO", -1, LoadLevel_INFO },
3281 { "BODY", -1, LoadLevel_BODY },
3282 { "CONT", -1, LoadLevel_CONT },
3283 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3284 { "CNT3", -1, LoadLevel_CNT3 },
3285 { "CUS1", -1, LoadLevel_CUS1 },
3286 { "CUS2", -1, LoadLevel_CUS2 },
3287 { "CUS3", -1, LoadLevel_CUS3 },
3288 { "CUS4", -1, LoadLevel_CUS4 },
3289 { "GRP1", -1, LoadLevel_GRP1 },
3290 { "CONF", -1, LoadLevel_CONF },
3291 { "ELEM", -1, LoadLevel_ELEM },
3292 { "NOTE", -1, LoadLevel_NOTE },
3293 { "CUSX", -1, LoadLevel_CUSX },
3294 { "GRPX", -1, LoadLevel_GRPX },
3299 while (getFileChunkBE(file, chunk_name, &chunk_size))
3303 while (chunk_info[i].name != NULL &&
3304 !strEqual(chunk_name, chunk_info[i].name))
3307 if (chunk_info[i].name == NULL)
3309 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3310 chunk_name, filename);
3311 ReadUnusedBytesFromFile(file, chunk_size);
3313 else if (chunk_info[i].size != -1 &&
3314 chunk_info[i].size != chunk_size)
3316 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3317 chunk_size, chunk_name, filename);
3318 ReadUnusedBytesFromFile(file, chunk_size);
3322 /* call function to load this level chunk */
3323 int chunk_size_expected =
3324 (chunk_info[i].loader)(file, chunk_size, level);
3326 /* the size of some chunks cannot be checked before reading other
3327 chunks first (like "HEAD" and "BODY") that contain some header
3328 information, so check them here */
3329 if (chunk_size_expected != chunk_size)
3331 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3332 chunk_size, chunk_name, filename);
3342 /* ------------------------------------------------------------------------- */
3343 /* functions for loading EM level */
3344 /* ------------------------------------------------------------------------- */
3346 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3348 static int ball_xy[8][2] =
3359 struct LevelInfo_EM *level_em = level->native_em_level;
3360 struct LEVEL *lev = level_em->lev;
3361 struct PLAYER **ply = level_em->ply;
3364 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3365 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3367 lev->time_seconds = level->time;
3368 lev->required_initial = level->gems_needed;
3370 lev->emerald_score = level->score[SC_EMERALD];
3371 lev->diamond_score = level->score[SC_DIAMOND];
3372 lev->alien_score = level->score[SC_ROBOT];
3373 lev->tank_score = level->score[SC_SPACESHIP];
3374 lev->bug_score = level->score[SC_BUG];
3375 lev->eater_score = level->score[SC_YAMYAM];
3376 lev->nut_score = level->score[SC_NUT];
3377 lev->dynamite_score = level->score[SC_DYNAMITE];
3378 lev->key_score = level->score[SC_KEY];
3379 lev->exit_score = level->score[SC_TIME_BONUS];
3381 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3382 for (y = 0; y < 3; y++)
3383 for (x = 0; x < 3; x++)
3384 lev->eater_array[i][y * 3 + x] =
3385 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3387 lev->amoeba_time = level->amoeba_speed;
3388 lev->wonderwall_time_initial = level->time_magic_wall;
3389 lev->wheel_time = level->time_wheel;
3391 lev->android_move_time = level->android_move_time;
3392 lev->android_clone_time = level->android_clone_time;
3393 lev->ball_random = level->ball_random;
3394 lev->ball_state_initial = level->ball_state_initial;
3395 lev->ball_time = level->ball_time;
3396 lev->num_ball_arrays = level->num_ball_contents;
3398 lev->lenses_score = level->lenses_score;
3399 lev->magnify_score = level->magnify_score;
3400 lev->slurp_score = level->slurp_score;
3402 lev->lenses_time = level->lenses_time;
3403 lev->magnify_time = level->magnify_time;
3405 lev->wind_direction_initial =
3406 map_direction_RND_to_EM(level->wind_direction_initial);
3407 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3408 lev->wind_time : 0);
3410 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3411 for (j = 0; j < 8; j++)
3412 lev->ball_array[i][j] =
3413 map_element_RND_to_EM(level->
3414 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3416 map_android_clone_elements_RND_to_EM(level);
3418 /* first fill the complete playfield with the default border element */
3419 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3420 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3421 level_em->cave[x][y] = ZBORDER;
3423 if (BorderElement == EL_STEELWALL)
3425 for (y = 0; y < lev->height + 2; y++)
3426 for (x = 0; x < lev->width + 2; x++)
3427 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3430 /* then copy the real level contents from level file into the playfield */
3431 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3433 int new_element = map_element_RND_to_EM(level->field[x][y]);
3434 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3435 int xx = x + 1 + offset;
3436 int yy = y + 1 + offset;
3438 if (level->field[x][y] == EL_AMOEBA_DEAD)
3439 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3441 level_em->cave[xx][yy] = new_element;
3444 for (i = 0; i < MAX_PLAYERS; i++)
3446 ply[i]->x_initial = 0;
3447 ply[i]->y_initial = 0;
3450 /* initialize player positions and delete players from the playfield */
3451 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3453 if (ELEM_IS_PLAYER(level->field[x][y]))
3455 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3456 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3457 int xx = x + 1 + offset;
3458 int yy = y + 1 + offset;
3460 ply[player_nr]->x_initial = xx;
3461 ply[player_nr]->y_initial = yy;
3463 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3467 if (BorderElement == EL_STEELWALL)
3474 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3476 static int ball_xy[8][2] =
3487 struct LevelInfo_EM *level_em = level->native_em_level;
3488 struct LEVEL *lev = level_em->lev;
3489 struct PLAYER **ply = level_em->ply;
3492 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3493 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3495 level->time = lev->time_seconds;
3496 level->gems_needed = lev->required_initial;
3498 sprintf(level->name, "Level %d", level->file_info.nr);
3500 level->score[SC_EMERALD] = lev->emerald_score;
3501 level->score[SC_DIAMOND] = lev->diamond_score;
3502 level->score[SC_ROBOT] = lev->alien_score;
3503 level->score[SC_SPACESHIP] = lev->tank_score;
3504 level->score[SC_BUG] = lev->bug_score;
3505 level->score[SC_YAMYAM] = lev->eater_score;
3506 level->score[SC_NUT] = lev->nut_score;
3507 level->score[SC_DYNAMITE] = lev->dynamite_score;
3508 level->score[SC_KEY] = lev->key_score;
3509 level->score[SC_TIME_BONUS] = lev->exit_score;
3511 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3513 for (i = 0; i < level->num_yamyam_contents; i++)
3514 for (y = 0; y < 3; y++)
3515 for (x = 0; x < 3; x++)
3516 level->yamyam_content[i].e[x][y] =
3517 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3519 level->amoeba_speed = lev->amoeba_time;
3520 level->time_magic_wall = lev->wonderwall_time_initial;
3521 level->time_wheel = lev->wheel_time;
3523 level->android_move_time = lev->android_move_time;
3524 level->android_clone_time = lev->android_clone_time;
3525 level->ball_random = lev->ball_random;
3526 level->ball_state_initial = lev->ball_state_initial;
3527 level->ball_time = lev->ball_time;
3528 level->num_ball_contents = lev->num_ball_arrays;
3530 level->lenses_score = lev->lenses_score;
3531 level->magnify_score = lev->magnify_score;
3532 level->slurp_score = lev->slurp_score;
3534 level->lenses_time = lev->lenses_time;
3535 level->magnify_time = lev->magnify_time;
3537 level->wind_direction_initial =
3538 map_direction_EM_to_RND(lev->wind_direction_initial);
3540 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3541 for (j = 0; j < 8; j++)
3542 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3543 map_element_EM_to_RND(lev->ball_array[i][j]);
3545 map_android_clone_elements_EM_to_RND(level);
3547 /* convert the playfield (some elements need special treatment) */
3548 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3550 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3552 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3553 new_element = EL_AMOEBA_DEAD;
3555 level->field[x][y] = new_element;
3558 for (i = 0; i < MAX_PLAYERS; i++)
3560 /* in case of all players set to the same field, use the first player */
3561 int nr = MAX_PLAYERS - i - 1;
3562 int jx = ply[nr]->x_initial - 1;
3563 int jy = ply[nr]->y_initial - 1;
3565 if (jx != -1 && jy != -1)
3566 level->field[jx][jy] = EL_PLAYER_1 + nr;
3571 /* ------------------------------------------------------------------------- */
3572 /* functions for loading SP level */
3573 /* ------------------------------------------------------------------------- */
3575 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3577 struct LevelInfo_SP *level_sp = level->native_sp_level;
3578 LevelInfoType *header = &level_sp->header;
3581 level_sp->width = level->fieldx;
3582 level_sp->height = level->fieldy;
3584 for (x = 0; x < level->fieldx; x++)
3585 for (y = 0; y < level->fieldy; y++)
3586 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3588 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3590 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3591 header->LevelTitle[i] = level->name[i];
3592 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3594 header->InfotronsNeeded = level->gems_needed;
3596 header->SpecialPortCount = 0;
3598 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3600 boolean gravity_port_found = FALSE;
3601 boolean gravity_port_valid = FALSE;
3602 int gravity_port_flag;
3603 int gravity_port_base_element;
3604 int element = level->field[x][y];
3606 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3607 element <= EL_SP_GRAVITY_ON_PORT_UP)
3609 gravity_port_found = TRUE;
3610 gravity_port_valid = TRUE;
3611 gravity_port_flag = 1;
3612 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3614 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3615 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3617 gravity_port_found = TRUE;
3618 gravity_port_valid = TRUE;
3619 gravity_port_flag = 0;
3620 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3622 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3623 element <= EL_SP_GRAVITY_PORT_UP)
3625 /* change R'n'D style gravity inverting special port to normal port
3626 (there are no gravity inverting ports in native Supaplex engine) */
3628 gravity_port_found = TRUE;
3629 gravity_port_valid = FALSE;
3630 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3633 if (gravity_port_found)
3635 if (gravity_port_valid &&
3636 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3638 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3640 port->PortLocation = (y * level->fieldx + x) * 2;
3641 port->Gravity = gravity_port_flag;
3643 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3645 header->SpecialPortCount++;
3649 /* change special gravity port to normal port */
3651 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3654 level_sp->playfield[x][y] = element - EL_SP_START;
3659 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3661 struct LevelInfo_SP *level_sp = level->native_sp_level;
3662 LevelInfoType *header = &level_sp->header;
3665 level->fieldx = level_sp->width;
3666 level->fieldy = level_sp->height;
3668 for (x = 0; x < level->fieldx; x++)
3670 for (y = 0; y < level->fieldy; y++)
3672 int element_old = level_sp->playfield[x][y];
3673 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3675 if (element_new == EL_UNKNOWN)
3676 Error(ERR_WARN, "invalid element %d at position %d, %d",
3679 level->field[x][y] = element_new;
3683 for (i = 0; i < MAX_PLAYERS; i++)
3684 level->initial_player_gravity[i] =
3685 (header->InitialGravity == 1 ? TRUE : FALSE);
3687 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3688 level->name[i] = header->LevelTitle[i];
3689 level->name[SP_LEVEL_NAME_LEN] = '\0';
3691 level->gems_needed = header->InfotronsNeeded;
3693 for (i = 0; i < header->SpecialPortCount; i++)
3695 SpecialPortType *port = &header->SpecialPort[i];
3696 int port_location = port->PortLocation;
3697 int gravity = port->Gravity;
3698 int port_x, port_y, port_element;
3700 port_x = (port_location / 2) % level->fieldx;
3701 port_y = (port_location / 2) / level->fieldx;
3703 if (port_x < 0 || port_x >= level->fieldx ||
3704 port_y < 0 || port_y >= level->fieldy)
3706 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3712 port_element = level->field[port_x][port_y];
3714 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3715 port_element > EL_SP_GRAVITY_PORT_UP)
3717 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3722 /* change previous (wrong) gravity inverting special port to either
3723 gravity enabling special port or gravity disabling special port */
3724 level->field[port_x][port_y] +=
3725 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3726 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3729 /* change special gravity ports without database entries to normal ports */
3730 for (x = 0; x < level->fieldx; x++)
3731 for (y = 0; y < level->fieldy; y++)
3732 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3733 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3734 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3736 level->time = 0; /* no time limit */
3737 level->amoeba_speed = 0;
3738 level->time_magic_wall = 0;
3739 level->time_wheel = 0;
3740 level->amoeba_content = EL_EMPTY;
3743 /* original Supaplex does not use score values -- use default values */
3745 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3746 level->score[i] = 0;
3749 /* there are no yamyams in supaplex levels */
3750 for (i = 0; i < level->num_yamyam_contents; i++)
3751 for (x = 0; x < 3; x++)
3752 for (y = 0; y < 3; y++)
3753 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3756 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3758 struct LevelInfo_SP *level_sp = level->native_sp_level;
3759 struct DemoInfo_SP *demo = &level_sp->demo;
3762 /* always start with reliable default values */
3763 demo->is_available = FALSE;
3766 if (TAPE_IS_EMPTY(tape))
3769 demo->level_nr = tape.level_nr; /* (currently not used) */
3771 level_sp->header.DemoRandomSeed = tape.random_seed;
3774 for (i = 0; i < tape.length; i++)
3776 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3777 int demo_repeat = tape.pos[i].delay;
3779 for (j = 0; j < demo_repeat / 16; j++)
3780 demo->data[demo->length++] = 0xf0 | demo_action;
3782 if (demo_repeat % 16)
3783 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3786 demo->data[demo->length++] = 0xff;
3788 demo->is_available = TRUE;
3791 static void setTapeInfoToDefaults();
3793 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3795 struct LevelInfo_SP *level_sp = level->native_sp_level;
3796 struct DemoInfo_SP *demo = &level_sp->demo;
3797 char *filename = level->file_info.filename;
3800 /* always start with reliable default values */
3801 setTapeInfoToDefaults();
3803 if (!demo->is_available)
3806 tape.level_nr = demo->level_nr; /* (currently not used) */
3807 tape.length = demo->length - 1; /* without "end of demo" byte */
3808 tape.random_seed = level_sp->header.DemoRandomSeed;
3810 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3812 for (i = 0; i < demo->length - 1; i++)
3814 int demo_action = demo->data[i] & 0x0f;
3815 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3817 tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
3818 tape.pos[i].delay = demo_repeat + 1;
3821 tape.length_frames = GetTapeLengthFrames();
3822 tape.length_seconds = GetTapeLengthSeconds();
3826 /* ------------------------------------------------------------------------- */
3827 /* functions for loading DC level */
3828 /* ------------------------------------------------------------------------- */
3830 #define DC_LEVEL_HEADER_SIZE 344
3832 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
3834 static int last_data_encoded;
3838 int diff_hi, diff_lo;
3839 int data_hi, data_lo;
3840 unsigned short data_decoded;
3844 last_data_encoded = 0;
3851 diff = data_encoded - last_data_encoded;
3852 diff_hi = diff & ~0xff;
3853 diff_lo = diff & 0xff;
3857 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
3858 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
3859 data_hi = data_hi & 0xff00;
3861 data_decoded = data_hi | data_lo;
3863 last_data_encoded = data_encoded;
3865 offset1 = (offset1 + 1) % 31;
3866 offset2 = offset2 & 0xff;
3868 return data_decoded;
3871 int getMappedElement_DC(int element)
3879 /* 0x0117 - 0x036e: (?) */
3882 /* 0x042d - 0x0684: (?) */
3898 element = EL_CRYSTAL;
3901 case 0x0e77: /* quicksand (boulder) */
3902 element = EL_QUICKSAND_FAST_FULL;
3905 case 0x0e99: /* slow quicksand (boulder) */
3906 element = EL_QUICKSAND_FULL;
3910 element = EL_EM_EXIT_OPEN;
3914 element = EL_EM_EXIT_CLOSED;
3918 element = EL_EM_STEEL_EXIT_OPEN;
3922 element = EL_EM_STEEL_EXIT_CLOSED;
3925 case 0x0f4f: /* dynamite (lit 1) */
3926 element = EL_EM_DYNAMITE_ACTIVE;
3929 case 0x0f57: /* dynamite (lit 2) */
3930 element = EL_EM_DYNAMITE_ACTIVE;
3933 case 0x0f5f: /* dynamite (lit 3) */
3934 element = EL_EM_DYNAMITE_ACTIVE;
3937 case 0x0f67: /* dynamite (lit 4) */
3938 element = EL_EM_DYNAMITE_ACTIVE;
3945 element = EL_AMOEBA_WET;
3949 element = EL_AMOEBA_DROP;
3953 element = EL_DC_MAGIC_WALL;
3957 element = EL_SPACESHIP_UP;
3961 element = EL_SPACESHIP_DOWN;
3965 element = EL_SPACESHIP_LEFT;
3969 element = EL_SPACESHIP_RIGHT;
3973 element = EL_BUG_UP;
3977 element = EL_BUG_DOWN;
3981 element = EL_BUG_LEFT;
3985 element = EL_BUG_RIGHT;
3989 element = EL_MOLE_UP;
3993 element = EL_MOLE_DOWN;
3997 element = EL_MOLE_LEFT;
4001 element = EL_MOLE_RIGHT;
4009 element = EL_YAMYAM;
4013 element = EL_SWITCHGATE_OPEN;
4017 element = EL_SWITCHGATE_CLOSED;
4021 element = EL_DC_SWITCHGATE_SWITCH_UP;
4025 element = EL_TIMEGATE_CLOSED;
4028 case 0x144c: /* conveyor belt switch (green) */
4029 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4032 case 0x144f: /* conveyor belt switch (red) */
4033 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4036 case 0x1452: /* conveyor belt switch (blue) */
4037 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4041 element = EL_CONVEYOR_BELT_3_MIDDLE;
4045 element = EL_CONVEYOR_BELT_3_LEFT;
4049 element = EL_CONVEYOR_BELT_3_RIGHT;
4053 element = EL_CONVEYOR_BELT_1_MIDDLE;
4057 element = EL_CONVEYOR_BELT_1_LEFT;
4061 element = EL_CONVEYOR_BELT_1_RIGHT;
4065 element = EL_CONVEYOR_BELT_4_MIDDLE;
4069 element = EL_CONVEYOR_BELT_4_LEFT;
4073 element = EL_CONVEYOR_BELT_4_RIGHT;
4077 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4081 element = EL_EXPANDABLE_WALL_VERTICAL;
4085 element = EL_EXPANDABLE_WALL_ANY;
4088 case 0x14ce: /* growing steel wall (left/right) */
4089 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4092 case 0x14df: /* growing steel wall (up/down) */
4093 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4096 case 0x14e8: /* growing steel wall (up/down/left/right) */
4097 element = EL_EXPANDABLE_STEELWALL_ANY;
4101 element = EL_SHIELD_DEADLY;
4105 element = EL_EXTRA_TIME;
4113 element = EL_EMPTY_SPACE;
4116 case 0x1578: /* quicksand (empty) */
4117 element = EL_QUICKSAND_FAST_EMPTY;
4120 case 0x1579: /* slow quicksand (empty) */
4121 element = EL_QUICKSAND_EMPTY;
4124 /* 0x157c - 0x158b: */
4127 /* 0x1590 - 0x159f: */
4128 /* EL_DC_LANDMINE */
4131 element = EL_EM_DYNAMITE;
4134 case 0x15a1: /* key (red) */
4135 element = EL_EM_KEY_1;
4138 case 0x15a2: /* key (yellow) */
4139 element = EL_EM_KEY_2;
4142 case 0x15a3: /* key (blue) */
4143 element = EL_EM_KEY_4;
4146 case 0x15a4: /* key (green) */
4147 element = EL_EM_KEY_3;
4150 case 0x15a5: /* key (white) */
4151 element = EL_DC_KEY_WHITE;
4155 element = EL_WALL_SLIPPERY;
4162 case 0x15a8: /* wall (not round) */
4166 case 0x15a9: /* (blue) */
4167 element = EL_CHAR_A;
4170 case 0x15aa: /* (blue) */
4171 element = EL_CHAR_B;
4174 case 0x15ab: /* (blue) */
4175 element = EL_CHAR_C;
4178 case 0x15ac: /* (blue) */
4179 element = EL_CHAR_D;
4182 case 0x15ad: /* (blue) */
4183 element = EL_CHAR_E;
4186 case 0x15ae: /* (blue) */
4187 element = EL_CHAR_F;
4190 case 0x15af: /* (blue) */
4191 element = EL_CHAR_G;
4194 case 0x15b0: /* (blue) */
4195 element = EL_CHAR_H;
4198 case 0x15b1: /* (blue) */
4199 element = EL_CHAR_I;
4202 case 0x15b2: /* (blue) */
4203 element = EL_CHAR_J;
4206 case 0x15b3: /* (blue) */
4207 element = EL_CHAR_K;
4210 case 0x15b4: /* (blue) */
4211 element = EL_CHAR_L;
4214 case 0x15b5: /* (blue) */
4215 element = EL_CHAR_M;
4218 case 0x15b6: /* (blue) */
4219 element = EL_CHAR_N;
4222 case 0x15b7: /* (blue) */
4223 element = EL_CHAR_O;
4226 case 0x15b8: /* (blue) */
4227 element = EL_CHAR_P;
4230 case 0x15b9: /* (blue) */
4231 element = EL_CHAR_Q;
4234 case 0x15ba: /* (blue) */
4235 element = EL_CHAR_R;
4238 case 0x15bb: /* (blue) */
4239 element = EL_CHAR_S;
4242 case 0x15bc: /* (blue) */
4243 element = EL_CHAR_T;
4246 case 0x15bd: /* (blue) */
4247 element = EL_CHAR_U;
4250 case 0x15be: /* (blue) */
4251 element = EL_CHAR_V;
4254 case 0x15bf: /* (blue) */
4255 element = EL_CHAR_W;
4258 case 0x15c0: /* (blue) */
4259 element = EL_CHAR_X;
4262 case 0x15c1: /* (blue) */
4263 element = EL_CHAR_Y;
4266 case 0x15c2: /* (blue) */
4267 element = EL_CHAR_Z;
4270 case 0x15c3: /* (blue) */
4271 element = EL_CHAR_AUMLAUT;
4274 case 0x15c4: /* (blue) */
4275 element = EL_CHAR_OUMLAUT;
4278 case 0x15c5: /* (blue) */
4279 element = EL_CHAR_UUMLAUT;
4282 case 0x15c6: /* (blue) */
4283 element = EL_CHAR_0;
4286 case 0x15c7: /* (blue) */
4287 element = EL_CHAR_1;
4290 case 0x15c8: /* (blue) */
4291 element = EL_CHAR_2;
4294 case 0x15c9: /* (blue) */
4295 element = EL_CHAR_3;
4298 case 0x15ca: /* (blue) */
4299 element = EL_CHAR_4;
4302 case 0x15cb: /* (blue) */
4303 element = EL_CHAR_5;
4306 case 0x15cc: /* (blue) */
4307 element = EL_CHAR_6;
4310 case 0x15cd: /* (blue) */
4311 element = EL_CHAR_7;
4314 case 0x15ce: /* (blue) */
4315 element = EL_CHAR_8;
4318 case 0x15cf: /* (blue) */
4319 element = EL_CHAR_9;
4322 case 0x15d0: /* (blue) */
4323 element = EL_CHAR_PERIOD;
4326 case 0x15d1: /* (blue) */
4327 element = EL_CHAR_EXCLAM;
4330 case 0x15d2: /* (blue) */
4331 element = EL_CHAR_COLON;
4334 case 0x15d3: /* (blue) */
4335 element = EL_CHAR_LESS;
4338 case 0x15d4: /* (blue) */
4339 element = EL_CHAR_GREATER;
4342 case 0x15d5: /* (blue) */
4343 element = EL_CHAR_QUESTION;
4346 case 0x15d6: /* (blue) */
4347 element = EL_CHAR_COPYRIGHT;
4350 case 0x15d7: /* (blue) */
4351 element = EL_CHAR_UP;
4354 case 0x15d8: /* (blue) */
4355 element = EL_CHAR_DOWN;
4358 case 0x15d9: /* (blue) */
4359 element = EL_CHAR_BUTTON;
4362 case 0x15da: /* (blue) */
4363 element = EL_CHAR_PLUS;
4366 case 0x15db: /* (blue) */
4367 element = EL_CHAR_MINUS;
4370 case 0x15dc: /* (blue) */
4371 element = EL_CHAR_APOSTROPHE;
4374 case 0x15dd: /* (blue) */
4375 element = EL_CHAR_PARENLEFT;
4378 case 0x15de: /* (blue) */
4379 element = EL_CHAR_PARENRIGHT;
4382 case 0x15df: /* (green) */
4383 element = EL_CHAR_A;
4386 case 0x15e0: /* (green) */
4387 element = EL_CHAR_B;
4390 case 0x15e1: /* (green) */
4391 element = EL_CHAR_C;
4394 case 0x15e2: /* (green) */
4395 element = EL_CHAR_D;
4398 case 0x15e3: /* (green) */
4399 element = EL_CHAR_E;
4402 case 0x15e4: /* (green) */
4403 element = EL_CHAR_F;
4406 case 0x15e5: /* (green) */
4407 element = EL_CHAR_G;
4410 case 0x15e6: /* (green) */
4411 element = EL_CHAR_H;
4414 case 0x15e7: /* (green) */
4415 element = EL_CHAR_I;
4418 case 0x15e8: /* (green) */
4419 element = EL_CHAR_J;
4422 case 0x15e9: /* (green) */
4423 element = EL_CHAR_K;
4426 case 0x15ea: /* (green) */
4427 element = EL_CHAR_L;
4430 case 0x15eb: /* (green) */
4431 element = EL_CHAR_M;
4434 case 0x15ec: /* (green) */
4435 element = EL_CHAR_N;
4438 case 0x15ed: /* (green) */
4439 element = EL_CHAR_O;
4442 case 0x15ee: /* (green) */
4443 element = EL_CHAR_P;
4446 case 0x15ef: /* (green) */
4447 element = EL_CHAR_Q;
4450 case 0x15f0: /* (green) */
4451 element = EL_CHAR_R;
4454 case 0x15f1: /* (green) */
4455 element = EL_CHAR_S;
4458 case 0x15f2: /* (green) */
4459 element = EL_CHAR_T;
4462 case 0x15f3: /* (green) */
4463 element = EL_CHAR_U;
4466 case 0x15f4: /* (green) */
4467 element = EL_CHAR_V;
4470 case 0x15f5: /* (green) */
4471 element = EL_CHAR_W;
4474 case 0x15f6: /* (green) */
4475 element = EL_CHAR_X;
4478 case 0x15f7: /* (green) */
4479 element = EL_CHAR_Y;
4482 case 0x15f8: /* (green) */
4483 element = EL_CHAR_Z;
4486 case 0x15f9: /* (green) */
4487 element = EL_CHAR_AUMLAUT;
4490 case 0x15fa: /* (green) */
4491 element = EL_CHAR_OUMLAUT;
4494 case 0x15fb: /* (green) */
4495 element = EL_CHAR_UUMLAUT;
4498 case 0x15fc: /* (green) */
4499 element = EL_CHAR_0;
4502 case 0x15fd: /* (green) */
4503 element = EL_CHAR_1;
4506 case 0x15fe: /* (green) */
4507 element = EL_CHAR_2;
4510 case 0x15ff: /* (green) */
4511 element = EL_CHAR_3;
4514 case 0x1600: /* (green) */
4515 element = EL_CHAR_4;
4518 case 0x1601: /* (green) */
4519 element = EL_CHAR_5;
4522 case 0x1602: /* (green) */
4523 element = EL_CHAR_6;
4526 case 0x1603: /* (green) */
4527 element = EL_CHAR_7;
4530 case 0x1604: /* (green) */
4531 element = EL_CHAR_8;
4534 case 0x1605: /* (green) */
4535 element = EL_CHAR_9;
4538 case 0x1606: /* (green) */
4539 element = EL_CHAR_PERIOD;
4542 case 0x1607: /* (green) */
4543 element = EL_CHAR_EXCLAM;
4546 case 0x1608: /* (green) */
4547 element = EL_CHAR_COLON;
4550 case 0x1609: /* (green) */
4551 element = EL_CHAR_LESS;
4554 case 0x160a: /* (green) */
4555 element = EL_CHAR_GREATER;
4558 case 0x160b: /* (green) */
4559 element = EL_CHAR_QUESTION;
4562 case 0x160c: /* (green) */
4563 element = EL_CHAR_COPYRIGHT;
4566 case 0x160d: /* (green) */
4567 element = EL_CHAR_UP;
4570 case 0x160e: /* (green) */
4571 element = EL_CHAR_DOWN;
4574 case 0x160f: /* (green) */
4575 element = EL_CHAR_BUTTON;
4578 case 0x1610: /* (green) */
4579 element = EL_CHAR_PLUS;
4582 case 0x1611: /* (green) */
4583 element = EL_CHAR_MINUS;
4586 case 0x1612: /* (green) */
4587 element = EL_CHAR_APOSTROPHE;
4590 case 0x1613: /* (green) */
4591 element = EL_CHAR_PARENLEFT;
4594 case 0x1614: /* (green) */
4595 element = EL_CHAR_PARENRIGHT;
4598 case 0x1615: /* (blue steel) */
4599 element = EL_STEEL_CHAR_A;
4602 case 0x1616: /* (blue steel) */
4603 element = EL_STEEL_CHAR_B;
4606 case 0x1617: /* (blue steel) */
4607 element = EL_STEEL_CHAR_C;
4610 case 0x1618: /* (blue steel) */
4611 element = EL_STEEL_CHAR_D;
4614 case 0x1619: /* (blue steel) */
4615 element = EL_STEEL_CHAR_E;
4618 case 0x161a: /* (blue steel) */
4619 element = EL_STEEL_CHAR_F;
4622 case 0x161b: /* (blue steel) */
4623 element = EL_STEEL_CHAR_G;
4626 case 0x161c: /* (blue steel) */
4627 element = EL_STEEL_CHAR_H;
4630 case 0x161d: /* (blue steel) */
4631 element = EL_STEEL_CHAR_I;
4634 case 0x161e: /* (blue steel) */
4635 element = EL_STEEL_CHAR_J;
4638 case 0x161f: /* (blue steel) */
4639 element = EL_STEEL_CHAR_K;
4642 case 0x1620: /* (blue steel) */
4643 element = EL_STEEL_CHAR_L;
4646 case 0x1621: /* (blue steel) */
4647 element = EL_STEEL_CHAR_M;
4650 case 0x1622: /* (blue steel) */
4651 element = EL_STEEL_CHAR_N;
4654 case 0x1623: /* (blue steel) */
4655 element = EL_STEEL_CHAR_O;
4658 case 0x1624: /* (blue steel) */
4659 element = EL_STEEL_CHAR_P;
4662 case 0x1625: /* (blue steel) */
4663 element = EL_STEEL_CHAR_Q;
4666 case 0x1626: /* (blue steel) */
4667 element = EL_STEEL_CHAR_R;
4670 case 0x1627: /* (blue steel) */
4671 element = EL_STEEL_CHAR_S;
4674 case 0x1628: /* (blue steel) */
4675 element = EL_STEEL_CHAR_T;
4678 case 0x1629: /* (blue steel) */
4679 element = EL_STEEL_CHAR_U;
4682 case 0x162a: /* (blue steel) */
4683 element = EL_STEEL_CHAR_V;
4686 case 0x162b: /* (blue steel) */
4687 element = EL_STEEL_CHAR_W;
4690 case 0x162c: /* (blue steel) */
4691 element = EL_STEEL_CHAR_X;
4694 case 0x162d: /* (blue steel) */
4695 element = EL_STEEL_CHAR_Y;
4698 case 0x162e: /* (blue steel) */
4699 element = EL_STEEL_CHAR_Z;
4702 case 0x162f: /* (blue steel) */
4703 element = EL_STEEL_CHAR_AUMLAUT;
4706 case 0x1630: /* (blue steel) */
4707 element = EL_STEEL_CHAR_OUMLAUT;
4710 case 0x1631: /* (blue steel) */
4711 element = EL_STEEL_CHAR_UUMLAUT;
4714 case 0x1632: /* (blue steel) */
4715 element = EL_STEEL_CHAR_0;
4718 case 0x1633: /* (blue steel) */
4719 element = EL_STEEL_CHAR_1;
4722 case 0x1634: /* (blue steel) */
4723 element = EL_STEEL_CHAR_2;
4726 case 0x1635: /* (blue steel) */
4727 element = EL_STEEL_CHAR_3;
4730 case 0x1636: /* (blue steel) */
4731 element = EL_STEEL_CHAR_4;
4734 case 0x1637: /* (blue steel) */
4735 element = EL_STEEL_CHAR_5;
4738 case 0x1638: /* (blue steel) */
4739 element = EL_STEEL_CHAR_6;
4742 case 0x1639: /* (blue steel) */
4743 element = EL_STEEL_CHAR_7;
4746 case 0x163a: /* (blue steel) */
4747 element = EL_STEEL_CHAR_8;
4750 case 0x163b: /* (blue steel) */
4751 element = EL_STEEL_CHAR_9;
4754 case 0x163c: /* (blue steel) */
4755 element = EL_STEEL_CHAR_PERIOD;
4758 case 0x163d: /* (blue steel) */
4759 element = EL_STEEL_CHAR_EXCLAM;
4762 case 0x163e: /* (blue steel) */
4763 element = EL_STEEL_CHAR_COLON;
4766 case 0x163f: /* (blue steel) */
4767 element = EL_STEEL_CHAR_LESS;
4770 case 0x1640: /* (blue steel) */
4771 element = EL_STEEL_CHAR_GREATER;
4774 case 0x1641: /* (blue steel) */
4775 element = EL_STEEL_CHAR_QUESTION;
4778 case 0x1642: /* (blue steel) */
4779 element = EL_STEEL_CHAR_COPYRIGHT;
4782 case 0x1643: /* (blue steel) */
4783 element = EL_STEEL_CHAR_UP;
4786 case 0x1644: /* (blue steel) */
4787 element = EL_STEEL_CHAR_DOWN;
4790 case 0x1645: /* (blue steel) */
4791 element = EL_STEEL_CHAR_BUTTON;
4794 case 0x1646: /* (blue steel) */
4795 element = EL_STEEL_CHAR_PLUS;
4798 case 0x1647: /* (blue steel) */
4799 element = EL_STEEL_CHAR_MINUS;
4802 case 0x1648: /* (blue steel) */
4803 element = EL_STEEL_CHAR_APOSTROPHE;
4806 case 0x1649: /* (blue steel) */
4807 element = EL_STEEL_CHAR_PARENLEFT;
4810 case 0x164a: /* (blue steel) */
4811 element = EL_STEEL_CHAR_PARENRIGHT;
4814 case 0x164b: /* (green steel) */
4815 element = EL_STEEL_CHAR_A;
4818 case 0x164c: /* (green steel) */
4819 element = EL_STEEL_CHAR_B;
4822 case 0x164d: /* (green steel) */
4823 element = EL_STEEL_CHAR_C;
4826 case 0x164e: /* (green steel) */
4827 element = EL_STEEL_CHAR_D;
4830 case 0x164f: /* (green steel) */
4831 element = EL_STEEL_CHAR_E;
4834 case 0x1650: /* (green steel) */
4835 element = EL_STEEL_CHAR_F;
4838 case 0x1651: /* (green steel) */
4839 element = EL_STEEL_CHAR_G;
4842 case 0x1652: /* (green steel) */
4843 element = EL_STEEL_CHAR_H;
4846 case 0x1653: /* (green steel) */
4847 element = EL_STEEL_CHAR_I;
4850 case 0x1654: /* (green steel) */
4851 element = EL_STEEL_CHAR_J;
4854 case 0x1655: /* (green steel) */
4855 element = EL_STEEL_CHAR_K;
4858 case 0x1656: /* (green steel) */
4859 element = EL_STEEL_CHAR_L;
4862 case 0x1657: /* (green steel) */
4863 element = EL_STEEL_CHAR_M;
4866 case 0x1658: /* (green steel) */
4867 element = EL_STEEL_CHAR_N;
4870 case 0x1659: /* (green steel) */
4871 element = EL_STEEL_CHAR_O;
4874 case 0x165a: /* (green steel) */
4875 element = EL_STEEL_CHAR_P;
4878 case 0x165b: /* (green steel) */
4879 element = EL_STEEL_CHAR_Q;
4882 case 0x165c: /* (green steel) */
4883 element = EL_STEEL_CHAR_R;
4886 case 0x165d: /* (green steel) */
4887 element = EL_STEEL_CHAR_S;
4890 case 0x165e: /* (green steel) */
4891 element = EL_STEEL_CHAR_T;
4894 case 0x165f: /* (green steel) */
4895 element = EL_STEEL_CHAR_U;
4898 case 0x1660: /* (green steel) */
4899 element = EL_STEEL_CHAR_V;
4902 case 0x1661: /* (green steel) */
4903 element = EL_STEEL_CHAR_W;
4906 case 0x1662: /* (green steel) */
4907 element = EL_STEEL_CHAR_X;
4910 case 0x1663: /* (green steel) */
4911 element = EL_STEEL_CHAR_Y;
4914 case 0x1664: /* (green steel) */
4915 element = EL_STEEL_CHAR_Z;
4918 case 0x1665: /* (green steel) */
4919 element = EL_STEEL_CHAR_AUMLAUT;
4922 case 0x1666: /* (green steel) */
4923 element = EL_STEEL_CHAR_OUMLAUT;
4926 case 0x1667: /* (green steel) */
4927 element = EL_STEEL_CHAR_UUMLAUT;
4930 case 0x1668: /* (green steel) */
4931 element = EL_STEEL_CHAR_0;
4934 case 0x1669: /* (green steel) */
4935 element = EL_STEEL_CHAR_1;
4938 case 0x166a: /* (green steel) */
4939 element = EL_STEEL_CHAR_2;
4942 case 0x166b: /* (green steel) */
4943 element = EL_STEEL_CHAR_3;
4946 case 0x166c: /* (green steel) */
4947 element = EL_STEEL_CHAR_4;
4950 case 0x166d: /* (green steel) */
4951 element = EL_STEEL_CHAR_5;
4954 case 0x166e: /* (green steel) */
4955 element = EL_STEEL_CHAR_6;
4958 case 0x166f: /* (green steel) */
4959 element = EL_STEEL_CHAR_7;
4962 case 0x1670: /* (green steel) */
4963 element = EL_STEEL_CHAR_8;
4966 case 0x1671: /* (green steel) */
4967 element = EL_STEEL_CHAR_9;
4970 case 0x1672: /* (green steel) */
4971 element = EL_STEEL_CHAR_PERIOD;
4974 case 0x1673: /* (green steel) */
4975 element = EL_STEEL_CHAR_EXCLAM;
4978 case 0x1674: /* (green steel) */
4979 element = EL_STEEL_CHAR_COLON;
4982 case 0x1675: /* (green steel) */
4983 element = EL_STEEL_CHAR_LESS;
4986 case 0x1676: /* (green steel) */
4987 element = EL_STEEL_CHAR_GREATER;
4990 case 0x1677: /* (green steel) */
4991 element = EL_STEEL_CHAR_QUESTION;
4994 case 0x1678: /* (green steel) */
4995 element = EL_STEEL_CHAR_COPYRIGHT;
4998 case 0x1679: /* (green steel) */
4999 element = EL_STEEL_CHAR_UP;
5002 case 0x167a: /* (green steel) */
5003 element = EL_STEEL_CHAR_DOWN;
5006 case 0x167b: /* (green steel) */
5007 element = EL_STEEL_CHAR_BUTTON;
5010 case 0x167c: /* (green steel) */
5011 element = EL_STEEL_CHAR_PLUS;
5014 case 0x167d: /* (green steel) */
5015 element = EL_STEEL_CHAR_MINUS;
5018 case 0x167e: /* (green steel) */
5019 element = EL_STEEL_CHAR_APOSTROPHE;
5022 case 0x167f: /* (green steel) */
5023 element = EL_STEEL_CHAR_PARENLEFT;
5026 case 0x1680: /* (green steel) */
5027 element = EL_STEEL_CHAR_PARENRIGHT;
5030 case 0x1681: /* gate (red) */
5031 element = EL_EM_GATE_1;
5034 case 0x1682: /* secret gate (red) */
5035 element = EL_GATE_1_GRAY;
5038 case 0x1683: /* gate (yellow) */
5039 element = EL_EM_GATE_2;
5042 case 0x1684: /* secret gate (yellow) */
5043 element = EL_GATE_2_GRAY;
5046 case 0x1685: /* gate (blue) */
5047 element = EL_EM_GATE_4;
5050 case 0x1686: /* secret gate (blue) */
5051 element = EL_GATE_4_GRAY;
5054 case 0x1687: /* gate (green) */
5055 element = EL_EM_GATE_3;
5058 case 0x1688: /* secret gate (green) */
5059 element = EL_GATE_3_GRAY;
5062 case 0x1689: /* gate (white) */
5063 element = EL_DC_GATE_WHITE;
5066 case 0x168a: /* secret gate (white) */
5067 element = EL_DC_GATE_WHITE_GRAY;
5070 case 0x168b: /* secret gate (no key) */
5071 element = EL_DC_GATE_FAKE_GRAY;
5075 element = EL_ROBOT_WHEEL;
5079 element = EL_DC_TIMEGATE_SWITCH;
5083 element = EL_ACID_POOL_BOTTOM;
5087 element = EL_ACID_POOL_TOPLEFT;
5091 element = EL_ACID_POOL_TOPRIGHT;
5095 element = EL_ACID_POOL_BOTTOMLEFT;
5099 element = EL_ACID_POOL_BOTTOMRIGHT;
5103 element = EL_STEELWALL;
5107 element = EL_STEELWALL_SLIPPERY;
5110 case 0x1695: /* steel wall (not round) */
5111 element = EL_STEELWALL;
5114 case 0x1696: /* steel wall (left) */
5115 element = EL_DC_STEELWALL_1_LEFT;
5118 case 0x1697: /* steel wall (bottom) */
5119 element = EL_DC_STEELWALL_1_BOTTOM;
5122 case 0x1698: /* steel wall (right) */
5123 element = EL_DC_STEELWALL_1_RIGHT;
5126 case 0x1699: /* steel wall (top) */
5127 element = EL_DC_STEELWALL_1_TOP;
5130 case 0x169a: /* steel wall (left/bottom) */
5131 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5134 case 0x169b: /* steel wall (right/bottom) */
5135 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5138 case 0x169c: /* steel wall (right/top) */
5139 element = EL_DC_STEELWALL_1_TOPRIGHT;
5142 case 0x169d: /* steel wall (left/top) */
5143 element = EL_DC_STEELWALL_1_TOPLEFT;
5146 case 0x169e: /* steel wall (right/bottom small) */
5147 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5150 case 0x169f: /* steel wall (left/bottom small) */
5151 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5154 case 0x16a0: /* steel wall (right/top small) */
5155 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5158 case 0x16a1: /* steel wall (left/top small) */
5159 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5162 case 0x16a2: /* steel wall (left/right) */
5163 element = EL_DC_STEELWALL_1_VERTICAL;
5166 case 0x16a3: /* steel wall (top/bottom) */
5167 element = EL_DC_STEELWALL_1_HORIZONTAL;
5170 case 0x16a4: /* steel wall 2 (left end) */
5171 element = EL_DC_STEELWALL_2_LEFT;
5174 case 0x16a5: /* steel wall 2 (right end) */
5175 element = EL_DC_STEELWALL_2_RIGHT;
5178 case 0x16a6: /* steel wall 2 (top end) */
5179 element = EL_DC_STEELWALL_2_TOP;
5182 case 0x16a7: /* steel wall 2 (bottom end) */
5183 element = EL_DC_STEELWALL_2_BOTTOM;
5186 case 0x16a8: /* steel wall 2 (left/right) */
5187 element = EL_DC_STEELWALL_2_HORIZONTAL;
5190 case 0x16a9: /* steel wall 2 (up/down) */
5191 element = EL_DC_STEELWALL_2_VERTICAL;
5194 case 0x16aa: /* steel wall 2 (mid) */
5195 element = EL_DC_STEELWALL_2_MIDDLE;
5199 element = EL_SIGN_EXCLAMATION;
5203 element = EL_SIGN_RADIOACTIVITY;
5207 element = EL_SIGN_STOP;
5211 element = EL_SIGN_WHEELCHAIR;
5215 element = EL_SIGN_PARKING;
5219 element = EL_SIGN_NO_ENTRY;
5223 element = EL_SIGN_HEART;
5227 element = EL_SIGN_GIVE_WAY;
5231 element = EL_SIGN_ENTRY_FORBIDDEN;
5235 element = EL_SIGN_EMERGENCY_EXIT;
5239 element = EL_SIGN_YIN_YANG;
5243 element = EL_WALL_EMERALD;
5247 element = EL_WALL_DIAMOND;
5251 element = EL_WALL_PEARL;
5255 element = EL_WALL_CRYSTAL;
5259 element = EL_INVISIBLE_WALL;
5263 element = EL_INVISIBLE_STEELWALL;
5266 /* 0x16bc - 0x16cb: */
5267 /* EL_INVISIBLE_SAND */
5270 element = EL_LIGHT_SWITCH;
5274 element = EL_ENVELOPE_1;
5278 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5279 element = EL_DIAMOND;
5280 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5281 element = EL_EMERALD;
5282 else if (element >= 0x157c && element <= 0x158b)
5284 else if (element >= 0x1590 && element <= 0x159f)
5285 element = EL_DC_LANDMINE;
5286 else if (element >= 0x16bc && element <= 0x16cb)
5287 element = EL_INVISIBLE_SAND;
5290 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5291 element = EL_UNKNOWN;
5296 return getMappedElement(element);
5299 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5302 byte header[DC_LEVEL_HEADER_SIZE];
5304 int envelope_header_pos = 62;
5305 int envelope_content_pos = 94;
5306 int level_name_pos = 251;
5307 int level_author_pos = 292;
5308 int envelope_header_len;
5309 int envelope_content_len;
5311 int level_author_len;
5313 int num_yamyam_contents;
5316 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5318 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5320 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5322 header[i * 2 + 0] = header_word >> 8;
5323 header[i * 2 + 1] = header_word & 0xff;
5326 /* read some values from level header to check level decoding integrity */
5327 fieldx = header[6] | (header[7] << 8);
5328 fieldy = header[8] | (header[9] << 8);
5329 num_yamyam_contents = header[60] | (header[61] << 8);
5331 /* do some simple sanity checks to ensure that level was correctly decoded */
5332 if (fieldx < 1 || fieldx > 256 ||
5333 fieldy < 1 || fieldy > 256 ||
5334 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5336 level->no_valid_file = TRUE;
5338 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5343 /* maximum envelope header size is 31 bytes */
5344 envelope_header_len = header[envelope_header_pos];
5345 /* maximum envelope content size is 110 (156?) bytes */
5346 envelope_content_len = header[envelope_content_pos];
5348 /* maximum level title size is 40 bytes */
5349 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5350 /* maximum level author size is 30 (51?) bytes */
5351 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5355 for (i = 0; i < envelope_header_len; i++)
5356 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5357 level->envelope[0].text[envelope_size++] =
5358 header[envelope_header_pos + 1 + i];
5360 if (envelope_header_len > 0 && envelope_content_len > 0)
5362 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5363 level->envelope[0].text[envelope_size++] = '\n';
5364 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5365 level->envelope[0].text[envelope_size++] = '\n';
5368 for (i = 0; i < envelope_content_len; i++)
5369 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5370 level->envelope[0].text[envelope_size++] =
5371 header[envelope_content_pos + 1 + i];
5373 level->envelope[0].text[envelope_size] = '\0';
5375 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5376 level->envelope[0].ysize = 10;
5377 level->envelope[0].autowrap = TRUE;
5378 level->envelope[0].centered = TRUE;
5380 for (i = 0; i < level_name_len; i++)
5381 level->name[i] = header[level_name_pos + 1 + i];
5382 level->name[level_name_len] = '\0';
5384 for (i = 0; i < level_author_len; i++)
5385 level->author[i] = header[level_author_pos + 1 + i];
5386 level->author[level_author_len] = '\0';
5388 num_yamyam_contents = header[60] | (header[61] << 8);
5389 level->num_yamyam_contents =
5390 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5392 for (i = 0; i < num_yamyam_contents; i++)
5394 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5396 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5397 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5399 if (i < MAX_ELEMENT_CONTENTS)
5400 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5404 fieldx = header[6] | (header[7] << 8);
5405 fieldy = header[8] | (header[9] << 8);
5406 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5407 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5409 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5411 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5412 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5414 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5415 level->field[x][y] = getMappedElement_DC(element_dc);
5418 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5419 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5420 level->field[x][y] = EL_PLAYER_1;
5422 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5423 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5424 level->field[x][y] = EL_PLAYER_2;
5426 level->gems_needed = header[18] | (header[19] << 8);
5428 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5429 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5430 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5431 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5432 level->score[SC_NUT] = header[28] | (header[29] << 8);
5433 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5434 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5435 level->score[SC_BUG] = header[34] | (header[35] << 8);
5436 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5437 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5438 level->score[SC_KEY] = header[40] | (header[41] << 8);
5439 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5441 level->time = header[44] | (header[45] << 8);
5443 level->amoeba_speed = header[46] | (header[47] << 8);
5444 level->time_light = header[48] | (header[49] << 8);
5445 level->time_timegate = header[50] | (header[51] << 8);
5446 level->time_wheel = header[52] | (header[53] << 8);
5447 level->time_magic_wall = header[54] | (header[55] << 8);
5448 level->extra_time = header[56] | (header[57] << 8);
5449 level->shield_normal_time = header[58] | (header[59] << 8);
5451 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5452 can slip down from flat walls, like normal walls and steel walls */
5453 level->em_slippery_gems = TRUE;
5456 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5457 struct LevelFileInfo *level_file_info,
5458 boolean level_info_only)
5460 char *filename = level_file_info->filename;
5462 int num_magic_bytes = 8;
5463 char magic_bytes[num_magic_bytes + 1];
5464 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5466 if (!(file = openFile(filename, MODE_READ)))
5468 level->no_valid_file = TRUE;
5470 if (!level_info_only)
5471 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5476 // fseek(file, 0x0000, SEEK_SET);
5478 if (level_file_info->packed)
5480 /* read "magic bytes" from start of file */
5481 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5482 magic_bytes[0] = '\0';
5484 /* check "magic bytes" for correct file format */
5485 if (!strPrefix(magic_bytes, "DC2"))
5487 level->no_valid_file = TRUE;
5489 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5495 if (strPrefix(magic_bytes, "DC2Win95") ||
5496 strPrefix(magic_bytes, "DC2Win98"))
5498 int position_first_level = 0x00fa;
5499 int extra_bytes = 4;
5502 /* advance file stream to first level inside the level package */
5503 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5505 /* each block of level data is followed by block of non-level data */
5506 num_levels_to_skip *= 2;
5508 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5509 while (num_levels_to_skip >= 0)
5511 /* advance file stream to next level inside the level package */
5512 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5514 level->no_valid_file = TRUE;
5516 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5522 /* skip apparently unused extra bytes following each level */
5523 ReadUnusedBytesFromFile(file, extra_bytes);
5525 /* read size of next level in level package */
5526 skip_bytes = getFile32BitLE(file);
5528 num_levels_to_skip--;
5533 level->no_valid_file = TRUE;
5535 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5542 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5548 /* ------------------------------------------------------------------------- */
5549 /* functions for loading SB level */
5550 /* ------------------------------------------------------------------------- */
5552 int getMappedElement_SB(int element_ascii, boolean use_ces)
5560 sb_element_mapping[] =
5562 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5563 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5564 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5565 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5566 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5567 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5568 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5569 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5576 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5577 if (element_ascii == sb_element_mapping[i].ascii)
5578 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5580 return EL_UNDEFINED;
5583 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5584 struct LevelFileInfo *level_file_info,
5585 boolean level_info_only)
5587 char *filename = level_file_info->filename;
5588 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5589 char last_comment[MAX_LINE_LEN];
5590 char level_name[MAX_LINE_LEN];
5593 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5594 boolean read_continued_line = FALSE;
5595 boolean reading_playfield = FALSE;
5596 boolean got_valid_playfield_line = FALSE;
5597 boolean invalid_playfield_char = FALSE;
5598 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5599 int file_level_nr = 0;
5601 int x = 0, y = 0; /* initialized to make compilers happy */
5603 last_comment[0] = '\0';
5604 level_name[0] = '\0';
5606 if (!(file = openFile(filename, MODE_READ)))
5608 level->no_valid_file = TRUE;
5610 if (!level_info_only)
5611 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5616 while (!checkEndOfFile(file))
5618 /* level successfully read, but next level may follow here */
5619 if (!got_valid_playfield_line && reading_playfield)
5621 /* read playfield from single level file -- skip remaining file */
5622 if (!level_file_info->packed)
5625 if (file_level_nr >= num_levels_to_skip)
5630 last_comment[0] = '\0';
5631 level_name[0] = '\0';
5633 reading_playfield = FALSE;
5636 got_valid_playfield_line = FALSE;
5638 /* read next line of input file */
5639 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5642 /* check if line was completely read and is terminated by line break */
5643 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5646 /* cut trailing line break (this can be newline and/or carriage return) */
5647 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5648 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5651 /* copy raw input line for later use (mainly debugging output) */
5652 strcpy(line_raw, line);
5654 if (read_continued_line)
5656 /* append new line to existing line, if there is enough space */
5657 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5658 strcat(previous_line, line_ptr);
5660 strcpy(line, previous_line); /* copy storage buffer to line */
5662 read_continued_line = FALSE;
5665 /* if the last character is '\', continue at next line */
5666 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5668 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5669 strcpy(previous_line, line); /* copy line to storage buffer */
5671 read_continued_line = TRUE;
5676 /* skip empty lines */
5677 if (line[0] == '\0')
5680 /* extract comment text from comment line */
5683 for (line_ptr = line; *line_ptr; line_ptr++)
5684 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5687 strcpy(last_comment, line_ptr);
5692 /* extract level title text from line containing level title */
5693 if (line[0] == '\'')
5695 strcpy(level_name, &line[1]);
5697 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5698 level_name[strlen(level_name) - 1] = '\0';
5703 /* skip lines containing only spaces (or empty lines) */
5704 for (line_ptr = line; *line_ptr; line_ptr++)
5705 if (*line_ptr != ' ')
5707 if (*line_ptr == '\0')
5710 /* at this point, we have found a line containing part of a playfield */
5712 got_valid_playfield_line = TRUE;
5714 if (!reading_playfield)
5716 reading_playfield = TRUE;
5717 invalid_playfield_char = FALSE;
5719 for (x = 0; x < MAX_LEV_FIELDX; x++)
5720 for (y = 0; y < MAX_LEV_FIELDY; y++)
5721 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5726 /* start with topmost tile row */
5730 /* skip playfield line if larger row than allowed */
5731 if (y >= MAX_LEV_FIELDY)
5734 /* start with leftmost tile column */
5737 /* read playfield elements from line */
5738 for (line_ptr = line; *line_ptr; line_ptr++)
5740 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5742 /* stop parsing playfield line if larger column than allowed */
5743 if (x >= MAX_LEV_FIELDX)
5746 if (mapped_sb_element == EL_UNDEFINED)
5748 invalid_playfield_char = TRUE;
5753 level->field[x][y] = mapped_sb_element;
5755 /* continue with next tile column */
5758 level->fieldx = MAX(x, level->fieldx);
5761 if (invalid_playfield_char)
5763 /* if first playfield line, treat invalid lines as comment lines */
5765 reading_playfield = FALSE;
5770 /* continue with next tile row */
5778 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
5779 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
5781 if (!reading_playfield)
5783 level->no_valid_file = TRUE;
5785 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5790 if (*level_name != '\0')
5792 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
5793 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5795 else if (*last_comment != '\0')
5797 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
5798 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5802 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
5805 /* set all empty fields beyond the border walls to invisible steel wall */
5806 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5808 if ((x == 0 || x == level->fieldx - 1 ||
5809 y == 0 || y == level->fieldy - 1) &&
5810 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
5811 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
5812 level->field, level->fieldx, level->fieldy);
5815 /* set special level settings for Sokoban levels */
5818 level->use_step_counter = TRUE;
5820 if (load_xsb_to_ces)
5822 /* special global settings can now be set in level template */
5824 level->use_custom_template = TRUE;
5829 /* ------------------------------------------------------------------------- */
5830 /* functions for handling native levels */
5831 /* ------------------------------------------------------------------------- */
5833 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
5834 struct LevelFileInfo *level_file_info,
5835 boolean level_info_only)
5837 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
5838 level->no_valid_file = TRUE;
5841 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
5842 struct LevelFileInfo *level_file_info,
5843 boolean level_info_only)
5847 /* determine position of requested level inside level package */
5848 if (level_file_info->packed)
5849 pos = level_file_info->nr - leveldir_current->first_level;
5851 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
5852 level->no_valid_file = TRUE;
5855 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
5857 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5858 CopyNativeLevel_RND_to_EM(level);
5859 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5860 CopyNativeLevel_RND_to_SP(level);
5863 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
5865 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5866 CopyNativeLevel_EM_to_RND(level);
5867 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5868 CopyNativeLevel_SP_to_RND(level);
5871 void SaveNativeLevel(struct LevelInfo *level)
5873 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5875 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
5876 char *filename = getLevelFilenameFromBasename(basename);
5878 CopyNativeLevel_RND_to_SP(level);
5879 CopyNativeTape_RND_to_SP(level);
5881 SaveNativeLevel_SP(filename);
5886 /* ------------------------------------------------------------------------- */
5887 /* functions for loading generic level */
5888 /* ------------------------------------------------------------------------- */
5890 static void LoadLevelFromFileInfo(struct LevelInfo *level,
5891 struct LevelFileInfo *level_file_info,
5892 boolean level_info_only)
5894 /* always start with reliable default values */
5895 setLevelInfoToDefaults(level, level_info_only, TRUE);
5897 switch (level_file_info->type)
5899 case LEVEL_FILE_TYPE_RND:
5900 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5903 case LEVEL_FILE_TYPE_EM:
5904 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
5905 level->game_engine_type = GAME_ENGINE_TYPE_EM;
5908 case LEVEL_FILE_TYPE_SP:
5909 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
5910 level->game_engine_type = GAME_ENGINE_TYPE_SP;
5913 case LEVEL_FILE_TYPE_DC:
5914 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
5917 case LEVEL_FILE_TYPE_SB:
5918 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
5922 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5926 /* if level file is invalid, restore level structure to default values */
5927 if (level->no_valid_file)
5928 setLevelInfoToDefaults(level, level_info_only, FALSE);
5930 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
5931 level->game_engine_type = GAME_ENGINE_TYPE_RND;
5933 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
5934 CopyNativeLevel_Native_to_RND(level);
5937 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
5939 static struct LevelFileInfo level_file_info;
5941 /* always start with reliable default values */
5942 setFileInfoToDefaults(&level_file_info);
5944 level_file_info.nr = 0; /* unknown level number */
5945 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
5946 level_file_info.filename = filename;
5948 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
5951 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
5955 if (leveldir_current == NULL) /* only when dumping level */
5958 /* all engine modifications also valid for levels which use latest engine */
5959 if (level->game_version < VERSION_IDENT(3,2,0,5))
5961 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
5962 level->score[SC_TIME_BONUS] /= 10;
5965 if (leveldir_current->latest_engine)
5967 /* ---------- use latest game engine ----------------------------------- */
5969 /* For all levels which are forced to use the latest game engine version
5970 (normally all but user contributed, private and undefined levels), set
5971 the game engine version to the actual version; this allows for actual
5972 corrections in the game engine to take effect for existing, converted
5973 levels (from "classic" or other existing games) to make the emulation
5974 of the corresponding game more accurate, while (hopefully) not breaking
5975 existing levels created from other players. */
5977 level->game_version = GAME_VERSION_ACTUAL;
5979 /* Set special EM style gems behaviour: EM style gems slip down from
5980 normal, steel and growing wall. As this is a more fundamental change,
5981 it seems better to set the default behaviour to "off" (as it is more
5982 natural) and make it configurable in the level editor (as a property
5983 of gem style elements). Already existing converted levels (neither
5984 private nor contributed levels) are changed to the new behaviour. */
5986 if (level->file_version < FILE_VERSION_2_0)
5987 level->em_slippery_gems = TRUE;
5992 /* ---------- use game engine the level was created with ----------------- */
5994 /* For all levels which are not forced to use the latest game engine
5995 version (normally user contributed, private and undefined levels),
5996 use the version of the game engine the levels were created for.
5998 Since 2.0.1, the game engine version is now directly stored
5999 in the level file (chunk "VERS"), so there is no need anymore
6000 to set the game version from the file version (except for old,
6001 pre-2.0 levels, where the game version is still taken from the
6002 file format version used to store the level -- see above). */
6004 /* player was faster than enemies in 1.0.0 and before */
6005 if (level->file_version == FILE_VERSION_1_0)
6006 for (i = 0; i < MAX_PLAYERS; i++)
6007 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6009 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6010 if (level->game_version == VERSION_IDENT(2,0,1,0))
6011 level->em_slippery_gems = TRUE;
6013 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6014 if (level->game_version < VERSION_IDENT(2,2,0,0))
6015 level->use_spring_bug = TRUE;
6017 if (level->game_version < VERSION_IDENT(3,2,0,5))
6019 /* time orb caused limited time in endless time levels before 3.2.0-5 */
6020 level->use_time_orb_bug = TRUE;
6022 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6023 level->block_snap_field = FALSE;
6025 /* extra time score was same value as time left score before 3.2.0-5 */
6026 level->extra_time_score = level->score[SC_TIME_BONUS];
6029 if (level->game_version < VERSION_IDENT(3,2,0,7))
6031 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6032 level->continuous_snapping = FALSE;
6035 /* only few elements were able to actively move into acid before 3.1.0 */
6036 /* trigger settings did not exist before 3.1.0; set to default "any" */
6037 if (level->game_version < VERSION_IDENT(3,1,0,0))
6039 /* correct "can move into acid" settings (all zero in old levels) */
6041 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6042 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6044 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6045 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6046 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6047 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6049 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6050 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6052 /* correct trigger settings (stored as zero == "none" in old levels) */
6054 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6056 int element = EL_CUSTOM_START + i;
6057 struct ElementInfo *ei = &element_info[element];
6059 for (j = 0; j < ei->num_change_pages; j++)
6061 struct ElementChangeInfo *change = &ei->change_page[j];
6063 change->trigger_player = CH_PLAYER_ANY;
6064 change->trigger_page = CH_PAGE_ANY;
6069 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6071 int element = EL_CUSTOM_256;
6072 struct ElementInfo *ei = &element_info[element];
6073 struct ElementChangeInfo *change = &ei->change_page[0];
6075 /* This is needed to fix a problem that was caused by a bugfix in function
6076 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6077 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6078 not replace walkable elements, but instead just placed the player on it,
6079 without placing the Sokoban field under the player). Unfortunately, this
6080 breaks "Snake Bite" style levels when the snake is halfway through a door
6081 that just closes (the snake head is still alive and can be moved in this
6082 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6083 player (without Sokoban element) which then gets killed as designed). */
6085 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6086 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6087 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6088 change->target_element = EL_PLAYER_1;
6091 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6092 if (level->game_version < VERSION_IDENT(3,2,5,0))
6094 /* This is needed to fix a problem that was caused by a bugfix in function
6095 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6096 corrects the behaviour when a custom element changes to another custom
6097 element with a higher element number that has change actions defined.
6098 Normally, only one change per frame is allowed for custom elements.
6099 Therefore, it is checked if a custom element already changed in the
6100 current frame; if it did, subsequent changes are suppressed.
6101 Unfortunately, this is only checked for element changes, but not for
6102 change actions, which are still executed. As the function above loops
6103 through all custom elements from lower to higher, an element change
6104 resulting in a lower CE number won't be checked again, while a target
6105 element with a higher number will also be checked, and potential change
6106 actions will get executed for this CE, too (which is wrong), while
6107 further changes are ignored (which is correct). As this bugfix breaks
6108 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6109 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6110 behaviour for existing levels and tapes that make use of this bug */
6112 level->use_action_after_change_bug = TRUE;
6115 /* not centering level after relocating player was default only in 3.2.3 */
6116 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6117 level->shifted_relocation = TRUE;
6119 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6120 if (level->game_version < VERSION_IDENT(3,2,6,0))
6121 level->em_explodes_by_fire = TRUE;
6124 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6128 /* map custom element change events that have changed in newer versions
6129 (these following values were accidentally changed in version 3.0.1)
6130 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6131 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6133 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6135 int element = EL_CUSTOM_START + i;
6137 /* order of checking and copying events to be mapped is important */
6138 /* (do not change the start and end value -- they are constant) */
6139 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6141 if (HAS_CHANGE_EVENT(element, j - 2))
6143 SET_CHANGE_EVENT(element, j - 2, FALSE);
6144 SET_CHANGE_EVENT(element, j, TRUE);
6148 /* order of checking and copying events to be mapped is important */
6149 /* (do not change the start and end value -- they are constant) */
6150 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6152 if (HAS_CHANGE_EVENT(element, j - 1))
6154 SET_CHANGE_EVENT(element, j - 1, FALSE);
6155 SET_CHANGE_EVENT(element, j, TRUE);
6161 /* initialize "can_change" field for old levels with only one change page */
6162 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6164 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6166 int element = EL_CUSTOM_START + i;
6168 if (CAN_CHANGE(element))
6169 element_info[element].change->can_change = TRUE;
6173 /* correct custom element values (for old levels without these options) */
6174 if (level->game_version < VERSION_IDENT(3,1,1,0))
6176 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6178 int element = EL_CUSTOM_START + i;
6179 struct ElementInfo *ei = &element_info[element];
6181 if (ei->access_direction == MV_NO_DIRECTION)
6182 ei->access_direction = MV_ALL_DIRECTIONS;
6186 /* correct custom element values (fix invalid values for all versions) */
6189 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6191 int element = EL_CUSTOM_START + i;
6192 struct ElementInfo *ei = &element_info[element];
6194 for (j = 0; j < ei->num_change_pages; j++)
6196 struct ElementChangeInfo *change = &ei->change_page[j];
6198 if (change->trigger_player == CH_PLAYER_NONE)
6199 change->trigger_player = CH_PLAYER_ANY;
6201 if (change->trigger_side == CH_SIDE_NONE)
6202 change->trigger_side = CH_SIDE_ANY;
6207 /* initialize "can_explode" field for old levels which did not store this */
6208 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6209 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6211 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6213 int element = EL_CUSTOM_START + i;
6215 if (EXPLODES_1X1_OLD(element))
6216 element_info[element].explosion_type = EXPLODES_1X1;
6218 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6219 EXPLODES_SMASHED(element) ||
6220 EXPLODES_IMPACT(element)));
6224 /* correct previously hard-coded move delay values for maze runner style */
6225 if (level->game_version < VERSION_IDENT(3,1,1,0))
6227 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6229 int element = EL_CUSTOM_START + i;
6231 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6233 /* previously hard-coded and therefore ignored */
6234 element_info[element].move_delay_fixed = 9;
6235 element_info[element].move_delay_random = 0;
6240 /* map elements that have changed in newer versions */
6241 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6242 level->game_version);
6243 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6244 for (x = 0; x < 3; x++)
6245 for (y = 0; y < 3; y++)
6246 level->yamyam_content[i].e[x][y] =
6247 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6248 level->game_version);
6250 /* initialize element properties for level editor etc. */
6251 InitElementPropertiesEngine(level->game_version);
6252 InitElementPropertiesAfterLoading(level->game_version);
6253 InitElementPropertiesGfxElement();
6256 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6260 /* map elements that have changed in newer versions */
6261 for (y = 0; y < level->fieldy; y++)
6262 for (x = 0; x < level->fieldx; x++)
6263 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6264 level->game_version);
6266 /* clear unused playfield data (nicer if level gets resized in editor) */
6267 for (x = 0; x < MAX_LEV_FIELDX; x++)
6268 for (y = 0; y < MAX_LEV_FIELDY; y++)
6269 if (x >= level->fieldx || y >= level->fieldy)
6270 level->field[x][y] = EL_EMPTY;
6272 /* copy elements to runtime playfield array */
6273 for (x = 0; x < MAX_LEV_FIELDX; x++)
6274 for (y = 0; y < MAX_LEV_FIELDY; y++)
6275 Feld[x][y] = level->field[x][y];
6277 /* initialize level size variables for faster access */
6278 lev_fieldx = level->fieldx;
6279 lev_fieldy = level->fieldy;
6281 /* determine border element for this level */
6282 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6283 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6288 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6290 struct LevelFileInfo *level_file_info = &level->file_info;
6292 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6293 CopyNativeLevel_RND_to_Native(level);
6296 void LoadLevelTemplate(int nr)
6300 setLevelFileInfo(&level_template.file_info, nr);
6301 filename = level_template.file_info.filename;
6303 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6305 LoadLevel_InitVersion(&level_template, filename);
6306 LoadLevel_InitElements(&level_template, filename);
6308 ActivateLevelTemplate();
6311 void LoadLevel(int nr)
6315 setLevelFileInfo(&level.file_info, nr);
6316 filename = level.file_info.filename;
6318 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6320 if (level.use_custom_template)
6321 LoadLevelTemplate(-1);
6323 LoadLevel_InitVersion(&level, filename);
6324 LoadLevel_InitElements(&level, filename);
6325 LoadLevel_InitPlayfield(&level, filename);
6327 LoadLevel_InitNativeEngines(&level, filename);
6330 void LoadLevelInfoOnly(int nr)
6332 setLevelFileInfo(&level.file_info, nr);
6334 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6337 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6341 chunk_size += putFileVersion(file, level->file_version);
6342 chunk_size += putFileVersion(file, level->game_version);
6347 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6351 chunk_size += putFile16BitBE(file, level->creation_date.year);
6352 chunk_size += putFile8Bit(file, level->creation_date.month);
6353 chunk_size += putFile8Bit(file, level->creation_date.day);
6358 #if ENABLE_HISTORIC_CHUNKS
6359 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6363 putFile8Bit(file, level->fieldx);
6364 putFile8Bit(file, level->fieldy);
6366 putFile16BitBE(file, level->time);
6367 putFile16BitBE(file, level->gems_needed);
6369 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6370 putFile8Bit(file, level->name[i]);
6372 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6373 putFile8Bit(file, level->score[i]);
6375 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6376 for (y = 0; y < 3; y++)
6377 for (x = 0; x < 3; x++)
6378 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6379 level->yamyam_content[i].e[x][y]));
6380 putFile8Bit(file, level->amoeba_speed);
6381 putFile8Bit(file, level->time_magic_wall);
6382 putFile8Bit(file, level->time_wheel);
6383 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6384 level->amoeba_content));
6385 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6386 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6387 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6388 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6390 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6392 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6393 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6394 putFile32BitBE(file, level->can_move_into_acid_bits);
6395 putFile8Bit(file, level->dont_collide_with_bits);
6397 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6398 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6400 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6401 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6402 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6404 putFile8Bit(file, level->game_engine_type);
6406 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6410 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6415 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6416 chunk_size += putFile8Bit(file, level->name[i]);
6421 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6426 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6427 chunk_size += putFile8Bit(file, level->author[i]);
6432 #if ENABLE_HISTORIC_CHUNKS
6433 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6438 for (y = 0; y < level->fieldy; y++)
6439 for (x = 0; x < level->fieldx; x++)
6440 if (level->encoding_16bit_field)
6441 chunk_size += putFile16BitBE(file, level->field[x][y]);
6443 chunk_size += putFile8Bit(file, level->field[x][y]);
6449 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6454 for (y = 0; y < level->fieldy; y++)
6455 for (x = 0; x < level->fieldx; x++)
6456 chunk_size += putFile16BitBE(file, level->field[x][y]);
6461 #if ENABLE_HISTORIC_CHUNKS
6462 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6466 putFile8Bit(file, EL_YAMYAM);
6467 putFile8Bit(file, level->num_yamyam_contents);
6468 putFile8Bit(file, 0);
6469 putFile8Bit(file, 0);
6471 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6472 for (y = 0; y < 3; y++)
6473 for (x = 0; x < 3; x++)
6474 if (level->encoding_16bit_field)
6475 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6477 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6481 #if ENABLE_HISTORIC_CHUNKS
6482 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6485 int num_contents, content_xsize, content_ysize;
6486 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6488 if (element == EL_YAMYAM)
6490 num_contents = level->num_yamyam_contents;
6494 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6495 for (y = 0; y < 3; y++)
6496 for (x = 0; x < 3; x++)
6497 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6499 else if (element == EL_BD_AMOEBA)
6505 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6506 for (y = 0; y < 3; y++)
6507 for (x = 0; x < 3; x++)
6508 content_array[i][x][y] = EL_EMPTY;
6509 content_array[0][0][0] = level->amoeba_content;
6513 /* chunk header already written -- write empty chunk data */
6514 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6516 Error(ERR_WARN, "cannot save content for element '%d'", element);
6520 putFile16BitBE(file, element);
6521 putFile8Bit(file, num_contents);
6522 putFile8Bit(file, content_xsize);
6523 putFile8Bit(file, content_ysize);
6525 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6527 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6528 for (y = 0; y < 3; y++)
6529 for (x = 0; x < 3; x++)
6530 putFile16BitBE(file, content_array[i][x][y]);
6534 #if ENABLE_HISTORIC_CHUNKS
6535 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6537 int envelope_nr = element - EL_ENVELOPE_1;
6538 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6542 chunk_size += putFile16BitBE(file, element);
6543 chunk_size += putFile16BitBE(file, envelope_len);
6544 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6545 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6547 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6548 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6550 for (i = 0; i < envelope_len; i++)
6551 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6557 #if ENABLE_HISTORIC_CHUNKS
6558 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6559 int num_changed_custom_elements)
6563 putFile16BitBE(file, num_changed_custom_elements);
6565 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6567 int element = EL_CUSTOM_START + i;
6569 struct ElementInfo *ei = &element_info[element];
6571 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6573 if (check < num_changed_custom_elements)
6575 putFile16BitBE(file, element);
6576 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6583 if (check != num_changed_custom_elements) /* should not happen */
6584 Error(ERR_WARN, "inconsistent number of custom element properties");
6588 #if ENABLE_HISTORIC_CHUNKS
6589 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6590 int num_changed_custom_elements)
6594 putFile16BitBE(file, num_changed_custom_elements);
6596 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6598 int element = EL_CUSTOM_START + i;
6600 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6602 if (check < num_changed_custom_elements)
6604 putFile16BitBE(file, element);
6605 putFile16BitBE(file, element_info[element].change->target_element);
6612 if (check != num_changed_custom_elements) /* should not happen */
6613 Error(ERR_WARN, "inconsistent number of custom target elements");
6617 #if ENABLE_HISTORIC_CHUNKS
6618 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6619 int num_changed_custom_elements)
6621 int i, j, x, y, check = 0;
6623 putFile16BitBE(file, num_changed_custom_elements);
6625 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6627 int element = EL_CUSTOM_START + i;
6628 struct ElementInfo *ei = &element_info[element];
6630 if (ei->modified_settings)
6632 if (check < num_changed_custom_elements)
6634 putFile16BitBE(file, element);
6636 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6637 putFile8Bit(file, ei->description[j]);
6639 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6641 /* some free bytes for future properties and padding */
6642 WriteUnusedBytesToFile(file, 7);
6644 putFile8Bit(file, ei->use_gfx_element);
6645 putFile16BitBE(file, ei->gfx_element_initial);
6647 putFile8Bit(file, ei->collect_score_initial);
6648 putFile8Bit(file, ei->collect_count_initial);
6650 putFile16BitBE(file, ei->push_delay_fixed);
6651 putFile16BitBE(file, ei->push_delay_random);
6652 putFile16BitBE(file, ei->move_delay_fixed);
6653 putFile16BitBE(file, ei->move_delay_random);
6655 putFile16BitBE(file, ei->move_pattern);
6656 putFile8Bit(file, ei->move_direction_initial);
6657 putFile8Bit(file, ei->move_stepsize);
6659 for (y = 0; y < 3; y++)
6660 for (x = 0; x < 3; x++)
6661 putFile16BitBE(file, ei->content.e[x][y]);
6663 putFile32BitBE(file, ei->change->events);
6665 putFile16BitBE(file, ei->change->target_element);
6667 putFile16BitBE(file, ei->change->delay_fixed);
6668 putFile16BitBE(file, ei->change->delay_random);
6669 putFile16BitBE(file, ei->change->delay_frames);
6671 putFile16BitBE(file, ei->change->initial_trigger_element);
6673 putFile8Bit(file, ei->change->explode);
6674 putFile8Bit(file, ei->change->use_target_content);
6675 putFile8Bit(file, ei->change->only_if_complete);
6676 putFile8Bit(file, ei->change->use_random_replace);
6678 putFile8Bit(file, ei->change->random_percentage);
6679 putFile8Bit(file, ei->change->replace_when);
6681 for (y = 0; y < 3; y++)
6682 for (x = 0; x < 3; x++)
6683 putFile16BitBE(file, ei->change->content.e[x][y]);
6685 putFile8Bit(file, ei->slippery_type);
6687 /* some free bytes for future properties and padding */
6688 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6695 if (check != num_changed_custom_elements) /* should not happen */
6696 Error(ERR_WARN, "inconsistent number of custom element properties");
6700 #if ENABLE_HISTORIC_CHUNKS
6701 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6703 struct ElementInfo *ei = &element_info[element];
6706 /* ---------- custom element base property values (96 bytes) ------------- */
6708 putFile16BitBE(file, element);
6710 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6711 putFile8Bit(file, ei->description[i]);
6713 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6715 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
6717 putFile8Bit(file, ei->num_change_pages);
6719 putFile16BitBE(file, ei->ce_value_fixed_initial);
6720 putFile16BitBE(file, ei->ce_value_random_initial);
6721 putFile8Bit(file, ei->use_last_ce_value);
6723 putFile8Bit(file, ei->use_gfx_element);
6724 putFile16BitBE(file, ei->gfx_element_initial);
6726 putFile8Bit(file, ei->collect_score_initial);
6727 putFile8Bit(file, ei->collect_count_initial);
6729 putFile8Bit(file, ei->drop_delay_fixed);
6730 putFile8Bit(file, ei->push_delay_fixed);
6731 putFile8Bit(file, ei->drop_delay_random);
6732 putFile8Bit(file, ei->push_delay_random);
6733 putFile16BitBE(file, ei->move_delay_fixed);
6734 putFile16BitBE(file, ei->move_delay_random);
6736 /* bits 0 - 15 of "move_pattern" ... */
6737 putFile16BitBE(file, ei->move_pattern & 0xffff);
6738 putFile8Bit(file, ei->move_direction_initial);
6739 putFile8Bit(file, ei->move_stepsize);
6741 putFile8Bit(file, ei->slippery_type);
6743 for (y = 0; y < 3; y++)
6744 for (x = 0; x < 3; x++)
6745 putFile16BitBE(file, ei->content.e[x][y]);
6747 putFile16BitBE(file, ei->move_enter_element);
6748 putFile16BitBE(file, ei->move_leave_element);
6749 putFile8Bit(file, ei->move_leave_type);
6751 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
6752 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
6754 putFile8Bit(file, ei->access_direction);
6756 putFile8Bit(file, ei->explosion_delay);
6757 putFile8Bit(file, ei->ignition_delay);
6758 putFile8Bit(file, ei->explosion_type);
6760 /* some free bytes for future custom property values and padding */
6761 WriteUnusedBytesToFile(file, 1);
6763 /* ---------- change page property values (48 bytes) --------------------- */
6765 for (i = 0; i < ei->num_change_pages; i++)
6767 struct ElementChangeInfo *change = &ei->change_page[i];
6768 unsigned int event_bits;
6770 /* bits 0 - 31 of "has_event[]" ... */
6772 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
6773 if (change->has_event[j])
6774 event_bits |= (1 << j);
6775 putFile32BitBE(file, event_bits);
6777 putFile16BitBE(file, change->target_element);
6779 putFile16BitBE(file, change->delay_fixed);
6780 putFile16BitBE(file, change->delay_random);
6781 putFile16BitBE(file, change->delay_frames);
6783 putFile16BitBE(file, change->initial_trigger_element);
6785 putFile8Bit(file, change->explode);
6786 putFile8Bit(file, change->use_target_content);
6787 putFile8Bit(file, change->only_if_complete);
6788 putFile8Bit(file, change->use_random_replace);
6790 putFile8Bit(file, change->random_percentage);
6791 putFile8Bit(file, change->replace_when);
6793 for (y = 0; y < 3; y++)
6794 for (x = 0; x < 3; x++)
6795 putFile16BitBE(file, change->target_content.e[x][y]);
6797 putFile8Bit(file, change->can_change);
6799 putFile8Bit(file, change->trigger_side);
6801 putFile8Bit(file, change->trigger_player);
6802 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
6803 log_2(change->trigger_page)));
6805 putFile8Bit(file, change->has_action);
6806 putFile8Bit(file, change->action_type);
6807 putFile8Bit(file, change->action_mode);
6808 putFile16BitBE(file, change->action_arg);
6810 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
6812 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
6813 if (change->has_event[j])
6814 event_bits |= (1 << (j - 32));
6815 putFile8Bit(file, event_bits);
6820 #if ENABLE_HISTORIC_CHUNKS
6821 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
6823 struct ElementInfo *ei = &element_info[element];
6824 struct ElementGroupInfo *group = ei->group;
6827 putFile16BitBE(file, element);
6829 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6830 putFile8Bit(file, ei->description[i]);
6832 putFile8Bit(file, group->num_elements);
6834 putFile8Bit(file, ei->use_gfx_element);
6835 putFile16BitBE(file, ei->gfx_element_initial);
6837 putFile8Bit(file, group->choice_mode);
6839 /* some free bytes for future values and padding */
6840 WriteUnusedBytesToFile(file, 3);
6842 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
6843 putFile16BitBE(file, group->element[i]);
6847 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
6848 boolean write_element)
6850 int save_type = entry->save_type;
6851 int data_type = entry->data_type;
6852 int conf_type = entry->conf_type;
6853 int byte_mask = conf_type & CONF_MASK_BYTES;
6854 int element = entry->element;
6855 int default_value = entry->default_value;
6857 boolean modified = FALSE;
6859 if (byte_mask != CONF_MASK_MULTI_BYTES)
6861 void *value_ptr = entry->value;
6862 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
6865 /* check if any settings have been modified before saving them */
6866 if (value != default_value)
6869 /* do not save if explicitly told or if unmodified default settings */
6870 if ((save_type == SAVE_CONF_NEVER) ||
6871 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6875 num_bytes += putFile16BitBE(file, element);
6877 num_bytes += putFile8Bit(file, conf_type);
6878 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
6879 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
6880 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
6883 else if (data_type == TYPE_STRING)
6885 char *default_string = entry->default_string;
6886 char *string = (char *)(entry->value);
6887 int string_length = strlen(string);
6890 /* check if any settings have been modified before saving them */
6891 if (!strEqual(string, default_string))
6894 /* do not save if explicitly told or if unmodified default settings */
6895 if ((save_type == SAVE_CONF_NEVER) ||
6896 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6900 num_bytes += putFile16BitBE(file, element);
6902 num_bytes += putFile8Bit(file, conf_type);
6903 num_bytes += putFile16BitBE(file, string_length);
6905 for (i = 0; i < string_length; i++)
6906 num_bytes += putFile8Bit(file, string[i]);
6908 else if (data_type == TYPE_ELEMENT_LIST)
6910 int *element_array = (int *)(entry->value);
6911 int num_elements = *(int *)(entry->num_entities);
6914 /* check if any settings have been modified before saving them */
6915 for (i = 0; i < num_elements; i++)
6916 if (element_array[i] != default_value)
6919 /* do not save if explicitly told or if unmodified default settings */
6920 if ((save_type == SAVE_CONF_NEVER) ||
6921 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6925 num_bytes += putFile16BitBE(file, element);
6927 num_bytes += putFile8Bit(file, conf_type);
6928 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
6930 for (i = 0; i < num_elements; i++)
6931 num_bytes += putFile16BitBE(file, element_array[i]);
6933 else if (data_type == TYPE_CONTENT_LIST)
6935 struct Content *content = (struct Content *)(entry->value);
6936 int num_contents = *(int *)(entry->num_entities);
6939 /* check if any settings have been modified before saving them */
6940 for (i = 0; i < num_contents; i++)
6941 for (y = 0; y < 3; y++)
6942 for (x = 0; x < 3; x++)
6943 if (content[i].e[x][y] != default_value)
6946 /* do not save if explicitly told or if unmodified default settings */
6947 if ((save_type == SAVE_CONF_NEVER) ||
6948 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6952 num_bytes += putFile16BitBE(file, element);
6954 num_bytes += putFile8Bit(file, conf_type);
6955 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
6957 for (i = 0; i < num_contents; i++)
6958 for (y = 0; y < 3; y++)
6959 for (x = 0; x < 3; x++)
6960 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
6966 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
6971 li = *level; /* copy level data into temporary buffer */
6973 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
6974 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
6979 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
6984 li = *level; /* copy level data into temporary buffer */
6986 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
6987 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
6992 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
6994 int envelope_nr = element - EL_ENVELOPE_1;
6998 chunk_size += putFile16BitBE(file, element);
7000 /* copy envelope data into temporary buffer */
7001 xx_envelope = level->envelope[envelope_nr];
7003 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7004 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7009 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7011 struct ElementInfo *ei = &element_info[element];
7015 chunk_size += putFile16BitBE(file, element);
7017 xx_ei = *ei; /* copy element data into temporary buffer */
7019 /* set default description string for this specific element */
7020 strcpy(xx_default_description, getDefaultElementDescription(ei));
7022 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7023 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7025 for (i = 0; i < ei->num_change_pages; i++)
7027 struct ElementChangeInfo *change = &ei->change_page[i];
7029 xx_current_change_page = i;
7031 xx_change = *change; /* copy change data into temporary buffer */
7034 setEventBitsFromEventFlags(change);
7036 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7037 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7044 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7046 struct ElementInfo *ei = &element_info[element];
7047 struct ElementGroupInfo *group = ei->group;
7051 chunk_size += putFile16BitBE(file, element);
7053 xx_ei = *ei; /* copy element data into temporary buffer */
7054 xx_group = *group; /* copy group data into temporary buffer */
7056 /* set default description string for this specific element */
7057 strcpy(xx_default_description, getDefaultElementDescription(ei));
7059 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7060 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7065 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
7071 if (!(file = fopen(filename, MODE_WRITE)))
7073 Error(ERR_WARN, "cannot save level file '%s'", filename);
7077 level->file_version = FILE_VERSION_ACTUAL;
7078 level->game_version = GAME_VERSION_ACTUAL;
7080 level->creation_date = getCurrentDate();
7082 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7083 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7085 chunk_size = SaveLevel_VERS(NULL, level);
7086 putFileChunkBE(file, "VERS", chunk_size);
7087 SaveLevel_VERS(file, level);
7089 chunk_size = SaveLevel_DATE(NULL, level);
7090 putFileChunkBE(file, "DATE", chunk_size);
7091 SaveLevel_DATE(file, level);
7093 chunk_size = SaveLevel_NAME(NULL, level);
7094 putFileChunkBE(file, "NAME", chunk_size);
7095 SaveLevel_NAME(file, level);
7097 chunk_size = SaveLevel_AUTH(NULL, level);
7098 putFileChunkBE(file, "AUTH", chunk_size);
7099 SaveLevel_AUTH(file, level);
7101 chunk_size = SaveLevel_INFO(NULL, level);
7102 putFileChunkBE(file, "INFO", chunk_size);
7103 SaveLevel_INFO(file, level);
7105 chunk_size = SaveLevel_BODY(NULL, level);
7106 putFileChunkBE(file, "BODY", chunk_size);
7107 SaveLevel_BODY(file, level);
7109 chunk_size = SaveLevel_ELEM(NULL, level);
7110 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7112 putFileChunkBE(file, "ELEM", chunk_size);
7113 SaveLevel_ELEM(file, level);
7116 for (i = 0; i < NUM_ENVELOPES; i++)
7118 int element = EL_ENVELOPE_1 + i;
7120 chunk_size = SaveLevel_NOTE(NULL, level, element);
7121 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7123 putFileChunkBE(file, "NOTE", chunk_size);
7124 SaveLevel_NOTE(file, level, element);
7128 /* if not using template level, check for non-default custom/group elements */
7129 if (!level->use_custom_template)
7131 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7133 int element = EL_CUSTOM_START + i;
7135 chunk_size = SaveLevel_CUSX(NULL, level, element);
7136 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7138 putFileChunkBE(file, "CUSX", chunk_size);
7139 SaveLevel_CUSX(file, level, element);
7143 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7145 int element = EL_GROUP_START + i;
7147 chunk_size = SaveLevel_GRPX(NULL, level, element);
7148 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7150 putFileChunkBE(file, "GRPX", chunk_size);
7151 SaveLevel_GRPX(file, level, element);
7158 SetFilePermissions(filename, PERMS_PRIVATE);
7161 void SaveLevel(int nr)
7163 char *filename = getDefaultLevelFilename(nr);
7165 SaveLevelFromFilename(&level, filename);
7168 void SaveLevelTemplate()
7170 char *filename = getDefaultLevelFilename(-1);
7172 SaveLevelFromFilename(&level, filename);
7175 boolean SaveLevelChecked(int nr)
7177 char *filename = getDefaultLevelFilename(nr);
7178 boolean new_level = !fileExists(filename);
7179 boolean level_saved = FALSE;
7181 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7186 Request("Level saved!", REQ_CONFIRM);
7194 void DumpLevel(struct LevelInfo *level)
7196 if (level->no_level_file || level->no_valid_file)
7198 Error(ERR_WARN, "cannot dump -- no valid level file found");
7204 Print("Level xxx (file version %08d, game version %08d)\n",
7205 level->file_version, level->game_version);
7208 Print("Level author: '%s'\n", level->author);
7209 Print("Level title: '%s'\n", level->name);
7211 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7213 Print("Level time: %d seconds\n", level->time);
7214 Print("Gems needed: %d\n", level->gems_needed);
7216 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7217 Print("Time for wheel: %d seconds\n", level->time_wheel);
7218 Print("Time for light: %d seconds\n", level->time_light);
7219 Print("Time for timegate: %d seconds\n", level->time_timegate);
7221 Print("Amoeba speed: %d\n", level->amoeba_speed);
7224 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7225 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7226 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7227 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7228 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7234 /* ========================================================================= */
7235 /* tape file functions */
7236 /* ========================================================================= */
7238 static void setTapeInfoToDefaults()
7242 /* always start with reliable default values (empty tape) */
7245 /* default values (also for pre-1.2 tapes) with only the first player */
7246 tape.player_participates[0] = TRUE;
7247 for (i = 1; i < MAX_PLAYERS; i++)
7248 tape.player_participates[i] = FALSE;
7250 /* at least one (default: the first) player participates in every tape */
7251 tape.num_participating_players = 1;
7253 tape.level_nr = level_nr;
7255 tape.changed = FALSE;
7257 tape.recording = FALSE;
7258 tape.playing = FALSE;
7259 tape.pausing = FALSE;
7261 tape.no_valid_file = FALSE;
7264 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7266 tape->file_version = getFileVersion(file);
7267 tape->game_version = getFileVersion(file);
7272 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7276 tape->random_seed = getFile32BitBE(file);
7277 tape->date = getFile32BitBE(file);
7278 tape->length = getFile32BitBE(file);
7280 /* read header fields that are new since version 1.2 */
7281 if (tape->file_version >= FILE_VERSION_1_2)
7283 byte store_participating_players = getFile8Bit(file);
7286 /* since version 1.2, tapes store which players participate in the tape */
7287 tape->num_participating_players = 0;
7288 for (i = 0; i < MAX_PLAYERS; i++)
7290 tape->player_participates[i] = FALSE;
7292 if (store_participating_players & (1 << i))
7294 tape->player_participates[i] = TRUE;
7295 tape->num_participating_players++;
7299 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7301 engine_version = getFileVersion(file);
7302 if (engine_version > 0)
7303 tape->engine_version = engine_version;
7305 tape->engine_version = tape->game_version;
7311 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7313 int level_identifier_size;
7316 level_identifier_size = getFile16BitBE(file);
7318 tape->level_identifier =
7319 checked_realloc(tape->level_identifier, level_identifier_size);
7321 for (i = 0; i < level_identifier_size; i++)
7322 tape->level_identifier[i] = getFile8Bit(file);
7324 tape->level_nr = getFile16BitBE(file);
7326 chunk_size = 2 + level_identifier_size + 2;
7331 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7334 int chunk_size_expected =
7335 (tape->num_participating_players + 1) * tape->length;
7337 if (chunk_size_expected != chunk_size)
7339 ReadUnusedBytesFromFile(file, chunk_size);
7340 return chunk_size_expected;
7343 for (i = 0; i < tape->length; i++)
7345 if (i >= MAX_TAPE_LEN)
7348 for (j = 0; j < MAX_PLAYERS; j++)
7350 tape->pos[i].action[j] = MV_NONE;
7352 if (tape->player_participates[j])
7353 tape->pos[i].action[j] = getFile8Bit(file);
7356 tape->pos[i].delay = getFile8Bit(file);
7358 if (tape->file_version == FILE_VERSION_1_0)
7360 /* eliminate possible diagonal moves in old tapes */
7361 /* this is only for backward compatibility */
7363 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7364 byte action = tape->pos[i].action[0];
7365 int k, num_moves = 0;
7367 for (k = 0; k<4; k++)
7369 if (action & joy_dir[k])
7371 tape->pos[i + num_moves].action[0] = joy_dir[k];
7373 tape->pos[i + num_moves].delay = 0;
7382 tape->length += num_moves;
7385 else if (tape->file_version < FILE_VERSION_2_0)
7387 /* convert pre-2.0 tapes to new tape format */
7389 if (tape->pos[i].delay > 1)
7392 tape->pos[i + 1] = tape->pos[i];
7393 tape->pos[i + 1].delay = 1;
7396 for (j = 0; j < MAX_PLAYERS; j++)
7397 tape->pos[i].action[j] = MV_NONE;
7398 tape->pos[i].delay--;
7405 if (checkEndOfFile(file))
7409 if (i != tape->length)
7410 chunk_size = (tape->num_participating_players + 1) * i;
7415 void LoadTape_SokobanSolution(char *filename)
7418 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7420 if (!(file = openFile(filename, MODE_READ)))
7422 tape.no_valid_file = TRUE;
7427 while (!checkEndOfFile(file))
7429 unsigned char c = getByteFromFile(file);
7431 if (checkEndOfFile(file))
7438 tape.pos[tape.length].action[0] = MV_UP;
7439 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7445 tape.pos[tape.length].action[0] = MV_DOWN;
7446 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7452 tape.pos[tape.length].action[0] = MV_LEFT;
7453 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7459 tape.pos[tape.length].action[0] = MV_RIGHT;
7460 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7468 /* ignore white-space characters */
7472 tape.no_valid_file = TRUE;
7474 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7482 if (tape.no_valid_file)
7485 tape.length_frames = GetTapeLengthFrames();
7486 tape.length_seconds = GetTapeLengthSeconds();
7489 void LoadTapeFromFilename(char *filename)
7491 char cookie[MAX_LINE_LEN];
7492 char chunk_name[CHUNK_ID_LEN + 1];
7496 /* always start with reliable default values */
7497 setTapeInfoToDefaults();
7499 if (strSuffix(filename, ".sln"))
7501 LoadTape_SokobanSolution(filename);
7506 if (!(file = openFile(filename, MODE_READ)))
7508 tape.no_valid_file = TRUE;
7513 getFileChunkBE(file, chunk_name, NULL);
7514 if (strEqual(chunk_name, "RND1"))
7516 getFile32BitBE(file); /* not used */
7518 getFileChunkBE(file, chunk_name, NULL);
7519 if (!strEqual(chunk_name, "TAPE"))
7521 tape.no_valid_file = TRUE;
7523 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7530 else /* check for pre-2.0 file format with cookie string */
7532 strcpy(cookie, chunk_name);
7533 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7535 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7536 cookie[strlen(cookie) - 1] = '\0';
7538 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7540 tape.no_valid_file = TRUE;
7542 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7549 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7551 tape.no_valid_file = TRUE;
7553 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7560 /* pre-2.0 tape files have no game version, so use file version here */
7561 tape.game_version = tape.file_version;
7564 if (tape.file_version < FILE_VERSION_1_2)
7566 /* tape files from versions before 1.2.0 without chunk structure */
7567 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7568 LoadTape_BODY(file, 2 * tape.length, &tape);
7576 int (*loader)(File *, int, struct TapeInfo *);
7580 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7581 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7582 { "INFO", -1, LoadTape_INFO },
7583 { "BODY", -1, LoadTape_BODY },
7587 while (getFileChunkBE(file, chunk_name, &chunk_size))
7591 while (chunk_info[i].name != NULL &&
7592 !strEqual(chunk_name, chunk_info[i].name))
7595 if (chunk_info[i].name == NULL)
7597 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7598 chunk_name, filename);
7599 ReadUnusedBytesFromFile(file, chunk_size);
7601 else if (chunk_info[i].size != -1 &&
7602 chunk_info[i].size != chunk_size)
7604 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7605 chunk_size, chunk_name, filename);
7606 ReadUnusedBytesFromFile(file, chunk_size);
7610 /* call function to load this tape chunk */
7611 int chunk_size_expected =
7612 (chunk_info[i].loader)(file, chunk_size, &tape);
7614 /* the size of some chunks cannot be checked before reading other
7615 chunks first (like "HEAD" and "BODY") that contain some header
7616 information, so check them here */
7617 if (chunk_size_expected != chunk_size)
7619 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7620 chunk_size, chunk_name, filename);
7628 tape.length_frames = GetTapeLengthFrames();
7629 tape.length_seconds = GetTapeLengthSeconds();
7632 printf("::: tape file version: %d\n", tape.file_version);
7633 printf("::: tape game version: %d\n", tape.game_version);
7634 printf("::: tape engine version: %d\n", tape.engine_version);
7638 void LoadTape(int nr)
7640 char *filename = getTapeFilename(nr);
7642 LoadTapeFromFilename(filename);
7645 void LoadSolutionTape(int nr)
7647 char *filename = getSolutionTapeFilename(nr);
7649 LoadTapeFromFilename(filename);
7651 if (TAPE_IS_EMPTY(tape) &&
7652 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7653 level.native_sp_level->demo.is_available)
7654 CopyNativeTape_SP_to_RND(&level);
7657 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7659 putFileVersion(file, tape->file_version);
7660 putFileVersion(file, tape->game_version);
7663 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7666 byte store_participating_players = 0;
7668 /* set bits for participating players for compact storage */
7669 for (i = 0; i < MAX_PLAYERS; i++)
7670 if (tape->player_participates[i])
7671 store_participating_players |= (1 << i);
7673 putFile32BitBE(file, tape->random_seed);
7674 putFile32BitBE(file, tape->date);
7675 putFile32BitBE(file, tape->length);
7677 putFile8Bit(file, store_participating_players);
7679 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7680 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7682 putFileVersion(file, tape->engine_version);
7685 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7687 int level_identifier_size = strlen(tape->level_identifier) + 1;
7690 putFile16BitBE(file, level_identifier_size);
7692 for (i = 0; i < level_identifier_size; i++)
7693 putFile8Bit(file, tape->level_identifier[i]);
7695 putFile16BitBE(file, tape->level_nr);
7698 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7702 for (i = 0; i < tape->length; i++)
7704 for (j = 0; j < MAX_PLAYERS; j++)
7705 if (tape->player_participates[j])
7706 putFile8Bit(file, tape->pos[i].action[j]);
7708 putFile8Bit(file, tape->pos[i].delay);
7712 void SaveTape(int nr)
7714 char *filename = getTapeFilename(nr);
7716 int num_participating_players = 0;
7717 int info_chunk_size;
7718 int body_chunk_size;
7721 InitTapeDirectory(leveldir_current->subdir);
7723 if (!(file = fopen(filename, MODE_WRITE)))
7725 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7729 tape.file_version = FILE_VERSION_ACTUAL;
7730 tape.game_version = GAME_VERSION_ACTUAL;
7732 /* count number of participating players */
7733 for (i = 0; i < MAX_PLAYERS; i++)
7734 if (tape.player_participates[i])
7735 num_participating_players++;
7737 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7738 body_chunk_size = (num_participating_players + 1) * tape.length;
7740 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7741 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7743 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7744 SaveTape_VERS(file, &tape);
7746 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7747 SaveTape_HEAD(file, &tape);
7749 putFileChunkBE(file, "INFO", info_chunk_size);
7750 SaveTape_INFO(file, &tape);
7752 putFileChunkBE(file, "BODY", body_chunk_size);
7753 SaveTape_BODY(file, &tape);
7757 SetFilePermissions(filename, PERMS_PRIVATE);
7759 tape.changed = FALSE;
7762 boolean SaveTapeChecked(int nr)
7764 char *filename = getTapeFilename(nr);
7765 boolean new_tape = !fileExists(filename);
7766 boolean tape_saved = FALSE;
7768 if (new_tape || Request("Replace old tape?", REQ_ASK))
7773 Request("Tape saved!", REQ_CONFIRM);
7781 void DumpTape(struct TapeInfo *tape)
7783 int tape_frame_counter;
7786 if (tape->no_valid_file)
7788 Error(ERR_WARN, "cannot dump -- no valid tape file found");
7794 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
7795 tape->level_nr, tape->file_version, tape->game_version);
7796 Print(" (effective engine version %08d)\n",
7797 tape->engine_version);
7798 Print("Level series identifier: '%s'\n", tape->level_identifier);
7801 tape_frame_counter = 0;
7803 for (i = 0; i < tape->length; i++)
7805 if (i >= MAX_TAPE_LEN)
7810 for (j = 0; j < MAX_PLAYERS; j++)
7812 if (tape->player_participates[j])
7814 int action = tape->pos[i].action[j];
7816 Print("%d:%02x ", j, action);
7817 Print("[%c%c%c%c|%c%c] - ",
7818 (action & JOY_LEFT ? '<' : ' '),
7819 (action & JOY_RIGHT ? '>' : ' '),
7820 (action & JOY_UP ? '^' : ' '),
7821 (action & JOY_DOWN ? 'v' : ' '),
7822 (action & JOY_BUTTON_1 ? '1' : ' '),
7823 (action & JOY_BUTTON_2 ? '2' : ' '));
7827 Print("(%03d) ", tape->pos[i].delay);
7828 Print("[%05d]\n", tape_frame_counter);
7830 tape_frame_counter += tape->pos[i].delay;
7837 /* ========================================================================= */
7838 /* score file functions */
7839 /* ========================================================================= */
7841 void LoadScore(int nr)
7844 char *filename = getScoreFilename(nr);
7845 char cookie[MAX_LINE_LEN];
7846 char line[MAX_LINE_LEN];
7850 /* always start with reliable default values */
7851 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7853 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
7854 highscore[i].Score = 0;
7857 if (!(file = fopen(filename, MODE_READ)))
7860 /* check file identifier */
7861 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
7863 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7864 cookie[strlen(cookie) - 1] = '\0';
7866 if (!checkCookieString(cookie, SCORE_COOKIE))
7868 Error(ERR_WARN, "unknown format of score file '%s'", filename);
7873 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7875 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
7876 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
7877 if (fgets(line, MAX_LINE_LEN, file) == NULL)
7880 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
7881 line[strlen(line) - 1] = '\0';
7883 for (line_ptr = line; *line_ptr; line_ptr++)
7885 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
7887 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
7888 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
7897 void SaveScore(int nr)
7900 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
7901 char *filename = getScoreFilename(nr);
7904 InitScoreDirectory(leveldir_current->subdir);
7906 if (!(file = fopen(filename, MODE_WRITE)))
7908 Error(ERR_WARN, "cannot save score for level %d", nr);
7912 fprintf(file, "%s\n\n", SCORE_COOKIE);
7914 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7915 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
7919 SetFilePermissions(filename, permissions);
7923 /* ========================================================================= */
7924 /* setup file functions */
7925 /* ========================================================================= */
7927 #define TOKEN_STR_PLAYER_PREFIX "player_"
7930 #define SETUP_TOKEN_PLAYER_NAME 0
7931 #define SETUP_TOKEN_SOUND 1
7932 #define SETUP_TOKEN_SOUND_LOOPS 2
7933 #define SETUP_TOKEN_SOUND_MUSIC 3
7934 #define SETUP_TOKEN_SOUND_SIMPLE 4
7935 #define SETUP_TOKEN_TOONS 5
7936 #define SETUP_TOKEN_SCROLL_DELAY 6
7937 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
7938 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE 8
7939 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY 9
7940 #define SETUP_TOKEN_FADE_SCREENS 10
7941 #define SETUP_TOKEN_AUTORECORD 11
7942 #define SETUP_TOKEN_SHOW_TITLESCREEN 12
7943 #define SETUP_TOKEN_QUICK_DOORS 13
7944 #define SETUP_TOKEN_TEAM_MODE 14
7945 #define SETUP_TOKEN_HANDICAP 15
7946 #define SETUP_TOKEN_SKIP_LEVELS 16
7947 #define SETUP_TOKEN_TIME_LIMIT 17
7948 #define SETUP_TOKEN_FULLSCREEN 18
7949 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT 19
7950 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY 20
7951 #define SETUP_TOKEN_SCREEN_RENDERING_MODE 21
7952 #define SETUP_TOKEN_ASK_ON_ESCAPE 22
7953 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 23
7954 #define SETUP_TOKEN_QUICK_SWITCH 24
7955 #define SETUP_TOKEN_INPUT_ON_FOCUS 25
7956 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 26
7957 #define SETUP_TOKEN_GAME_FRAME_DELAY 27
7958 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 28
7959 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 29
7960 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS 30
7961 #define SETUP_TOKEN_GRAPHICS_SET 31
7962 #define SETUP_TOKEN_SOUNDS_SET 32
7963 #define SETUP_TOKEN_MUSIC_SET 33
7964 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 34
7965 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 35
7966 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 36
7967 #define SETUP_TOKEN_VOLUME_SIMPLE 37
7968 #define SETUP_TOKEN_VOLUME_LOOPS 38
7969 #define SETUP_TOKEN_VOLUME_MUSIC 39
7970 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE 40
7971 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 41
7972 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE 42
7974 #define NUM_GLOBAL_SETUP_TOKENS 43
7977 #define SETUP_TOKEN_EDITOR_EL_CHARS 0
7978 #define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS 1
7979 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 2
7980 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 3
7981 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 4
7982 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 5
7984 #define NUM_EDITOR_SETUP_TOKENS 6
7986 /* editor cascade setup */
7987 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
7988 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
7989 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
7990 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
7991 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
7992 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
7993 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
7994 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
7995 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
7996 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
7997 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
7998 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
7999 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
8000 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
8001 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
8003 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
8005 /* shortcut setup */
8006 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
8007 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
8008 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
8009 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
8010 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
8011 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
8012 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
8013 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
8014 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
8015 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
8016 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
8017 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
8018 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
8019 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
8020 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
8021 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
8022 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
8023 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
8024 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
8025 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
8026 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
8028 #define NUM_SHORTCUT_SETUP_TOKENS 21
8031 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
8032 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
8033 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
8034 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
8035 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
8036 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
8037 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
8038 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
8039 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
8040 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
8041 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
8042 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
8043 #define SETUP_TOKEN_PLAYER_KEY_UP 12
8044 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
8045 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8046 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8048 #define NUM_PLAYER_SETUP_TOKENS 16
8051 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8052 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8053 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8055 #define NUM_SYSTEM_SETUP_TOKENS 3
8057 /* internal setup */
8058 #define SETUP_TOKEN_INT_PROGRAM_TITLE 0
8059 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR 1
8060 #define SETUP_TOKEN_INT_PROGRAM_EMAIL 2
8061 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE 3
8062 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 4
8063 #define SETUP_TOKEN_INT_PROGRAM_COMPANY 5
8064 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 6
8065 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 7
8066 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 8
8067 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 9
8068 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 10
8069 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 11
8070 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 12
8071 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 13
8072 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 14
8073 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH 15
8074 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT 16
8076 #define NUM_INTERNAL_SETUP_TOKENS 17
8079 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0 0
8080 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1 1
8081 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2 2
8082 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3 3
8083 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4 4
8084 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5 5
8085 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6 6
8086 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7 7
8087 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8 8
8088 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9 9
8089 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0 10
8090 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1 11
8091 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2 12
8092 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3 13
8093 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4 14
8094 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5 15
8095 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6 16
8096 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7 17
8097 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8 18
8098 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9 19
8099 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8100 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8102 #define NUM_DEBUG_SETUP_TOKENS 22
8105 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8107 #define NUM_OPTIONS_SETUP_TOKENS 1
8110 static struct SetupInfo si;
8111 static struct SetupEditorInfo sei;
8112 static struct SetupEditorCascadeInfo seci;
8113 static struct SetupShortcutInfo ssi;
8114 static struct SetupInputInfo sii;
8115 static struct SetupSystemInfo syi;
8116 static struct SetupInternalInfo sxi;
8117 static struct SetupDebugInfo sdi;
8118 static struct OptionInfo soi;
8120 static struct TokenInfo global_setup_tokens[] =
8122 { TYPE_STRING, &si.player_name, "player_name" },
8123 { TYPE_SWITCH, &si.sound, "sound" },
8124 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8125 { TYPE_SWITCH, &si.sound_music, "background_music" },
8126 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8127 { TYPE_SWITCH, &si.toons, "toons" },
8128 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8129 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8130 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8131 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8132 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8133 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8134 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8135 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8136 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8137 { TYPE_SWITCH, &si.handicap, "handicap" },
8138 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8139 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8140 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8141 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8142 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8143 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8144 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8145 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8146 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8147 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8148 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8149 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8150 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8151 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8152 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8153 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8154 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8155 { TYPE_STRING, &si.music_set, "music_set" },
8156 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8157 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8158 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8159 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8160 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8161 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8162 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8163 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8164 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8167 static struct TokenInfo editor_setup_tokens[] =
8169 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
8170 { TYPE_SWITCH, &sei.el_steel_chars, "editor.el_steel_chars" },
8171 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8172 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8173 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8174 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8177 static struct TokenInfo editor_cascade_setup_tokens[] =
8179 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8180 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8181 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8182 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8183 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8184 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8185 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8186 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8187 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8188 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8189 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8190 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8191 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8192 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8193 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8196 static struct TokenInfo shortcut_setup_tokens[] =
8198 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8199 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8200 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8201 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8202 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8203 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8204 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8205 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8206 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8207 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8208 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8209 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8210 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8211 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8212 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8213 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8214 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8215 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8216 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8217 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8218 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8221 static struct TokenInfo player_setup_tokens[] =
8223 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8224 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8225 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8226 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8227 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8228 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8229 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8230 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8231 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8232 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8233 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8234 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8235 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8236 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8237 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8238 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8241 static struct TokenInfo system_setup_tokens[] =
8243 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8244 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8245 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8248 static struct TokenInfo internal_setup_tokens[] =
8250 { TYPE_STRING, &sxi.program_title, "program_title" },
8251 { TYPE_STRING, &sxi.program_author, "program_author" },
8252 { TYPE_STRING, &sxi.program_email, "program_email" },
8253 { TYPE_STRING, &sxi.program_website, "program_website" },
8254 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8255 { TYPE_STRING, &sxi.program_company, "program_company" },
8256 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8257 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8258 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8259 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8260 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8261 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8262 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8263 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8264 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8265 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8266 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8269 static struct TokenInfo debug_setup_tokens[] =
8271 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8272 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8273 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8274 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8275 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8276 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8277 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8278 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8279 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8280 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8281 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8282 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8283 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8284 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8285 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8286 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8287 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8288 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8289 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8290 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8291 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8292 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8295 static struct TokenInfo options_setup_tokens[] =
8297 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8300 static char *get_corrected_login_name(char *login_name)
8302 /* needed because player name must be a fixed length string */
8303 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8305 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8306 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8308 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8309 if (strchr(login_name_new, ' '))
8310 *strchr(login_name_new, ' ') = '\0';
8312 return login_name_new;
8315 static void setSetupInfoToDefaults(struct SetupInfo *si)
8319 si->player_name = get_corrected_login_name(getLoginName());
8322 si->sound_loops = TRUE;
8323 si->sound_music = TRUE;
8324 si->sound_simple = TRUE;
8326 si->scroll_delay = TRUE;
8327 si->scroll_delay_value = STD_SCROLL_DELAY;
8328 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8329 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8330 si->fade_screens = TRUE;
8331 si->autorecord = TRUE;
8332 si->show_titlescreen = TRUE;
8333 si->quick_doors = FALSE;
8334 si->team_mode = FALSE;
8335 si->handicap = TRUE;
8336 si->skip_levels = TRUE;
8337 si->time_limit = TRUE;
8338 si->fullscreen = FALSE;
8339 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8340 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8341 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8342 si->ask_on_escape = TRUE;
8343 si->ask_on_escape_editor = TRUE;
8344 si->quick_switch = FALSE;
8345 si->input_on_focus = FALSE;
8346 si->prefer_aga_graphics = TRUE;
8347 si->game_frame_delay = GAME_FRAME_DELAY;
8348 si->sp_show_border_elements = FALSE;
8349 si->small_game_graphics = FALSE;
8350 si->show_snapshot_buttons = FALSE;
8352 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8353 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8354 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8356 si->override_level_graphics = FALSE;
8357 si->override_level_sounds = FALSE;
8358 si->override_level_music = FALSE;
8360 si->volume_simple = 100; /* percent */
8361 si->volume_loops = 100; /* percent */
8362 si->volume_music = 100; /* percent */
8364 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8365 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8366 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8368 si->editor.el_boulderdash = TRUE;
8369 si->editor.el_emerald_mine = TRUE;
8370 si->editor.el_emerald_mine_club = TRUE;
8371 si->editor.el_more = TRUE;
8372 si->editor.el_sokoban = TRUE;
8373 si->editor.el_supaplex = TRUE;
8374 si->editor.el_diamond_caves = TRUE;
8375 si->editor.el_dx_boulderdash = TRUE;
8376 si->editor.el_chars = TRUE;
8377 si->editor.el_steel_chars = TRUE;
8378 si->editor.el_custom = TRUE;
8380 si->editor.el_headlines = TRUE;
8381 si->editor.el_user_defined = FALSE;
8382 si->editor.el_dynamic = TRUE;
8384 si->editor.show_element_token = FALSE;
8386 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8387 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8388 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8390 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8391 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8392 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8393 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8394 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8396 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8397 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8398 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8399 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8400 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8401 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8403 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8404 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8405 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8407 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8408 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8409 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8410 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8412 for (i = 0; i < MAX_PLAYERS; i++)
8414 si->input[i].use_joystick = FALSE;
8415 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8416 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8417 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8418 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8419 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8420 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8421 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8422 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8423 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8424 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8425 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8426 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8427 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8428 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8429 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8432 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8433 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8434 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8436 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8437 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8438 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8439 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8440 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8441 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8443 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8445 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8446 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8447 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8449 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8450 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8451 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8453 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8454 si->internal.choose_from_top_leveldir = FALSE;
8456 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
8457 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8459 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8460 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8461 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8462 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8463 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8464 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8465 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8466 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8467 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8468 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8470 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8471 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8472 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8473 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8474 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8475 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8476 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8477 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8478 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8479 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8481 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8482 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
8484 si->options.verbose = FALSE;
8486 #if defined(PLATFORM_ANDROID)
8487 si->fullscreen = TRUE;
8491 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8493 si->editor_cascade.el_bd = TRUE;
8494 si->editor_cascade.el_em = TRUE;
8495 si->editor_cascade.el_emc = TRUE;
8496 si->editor_cascade.el_rnd = TRUE;
8497 si->editor_cascade.el_sb = TRUE;
8498 si->editor_cascade.el_sp = TRUE;
8499 si->editor_cascade.el_dc = TRUE;
8500 si->editor_cascade.el_dx = TRUE;
8502 si->editor_cascade.el_chars = FALSE;
8503 si->editor_cascade.el_steel_chars = FALSE;
8504 si->editor_cascade.el_ce = FALSE;
8505 si->editor_cascade.el_ge = FALSE;
8506 si->editor_cascade.el_ref = FALSE;
8507 si->editor_cascade.el_user = FALSE;
8508 si->editor_cascade.el_dynamic = FALSE;
8511 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8515 if (!setup_file_hash)
8520 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8521 setSetupInfo(global_setup_tokens, i,
8522 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
8527 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8528 setSetupInfo(editor_setup_tokens, i,
8529 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
8532 /* shortcut setup */
8533 ssi = setup.shortcut;
8534 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8535 setSetupInfo(shortcut_setup_tokens, i,
8536 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
8537 setup.shortcut = ssi;
8540 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8544 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8546 sii = setup.input[pnr];
8547 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8549 char full_token[100];
8551 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8552 setSetupInfo(player_setup_tokens, i,
8553 getHashEntry(setup_file_hash, full_token));
8555 setup.input[pnr] = sii;
8560 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8561 setSetupInfo(system_setup_tokens, i,
8562 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
8565 /* internal setup */
8566 sxi = setup.internal;
8567 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8568 setSetupInfo(internal_setup_tokens, i,
8569 getHashEntry(setup_file_hash, internal_setup_tokens[i].text));
8570 setup.internal = sxi;
8574 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8575 setSetupInfo(debug_setup_tokens, i,
8576 getHashEntry(setup_file_hash, debug_setup_tokens[i].text));
8580 soi = setup.options;
8581 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8582 setSetupInfo(options_setup_tokens, i,
8583 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
8584 setup.options = soi;
8587 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8591 if (!setup_file_hash)
8594 /* editor cascade setup */
8595 seci = setup.editor_cascade;
8596 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8597 setSetupInfo(editor_cascade_setup_tokens, i,
8598 getHashEntry(setup_file_hash,
8599 editor_cascade_setup_tokens[i].text));
8600 setup.editor_cascade = seci;
8603 void LoadSetupFromFilename(char *filename)
8605 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
8607 if (setup_file_hash)
8609 decodeSetupFileHash(setup_file_hash);
8611 freeSetupFileHash(setup_file_hash);
8615 Error(ERR_WARN, "using default setup values");
8619 static void LoadSetup_SpecialPostProcessing()
8621 char *player_name_new;
8623 /* needed to work around problems with fixed length strings */
8624 player_name_new = get_corrected_login_name(setup.player_name);
8625 free(setup.player_name);
8626 setup.player_name = player_name_new;
8628 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
8629 if (setup.scroll_delay == FALSE)
8631 setup.scroll_delay_value = MIN_SCROLL_DELAY;
8632 setup.scroll_delay = TRUE; /* now always "on" */
8635 /* make sure that scroll delay value stays inside valid range */
8636 setup.scroll_delay_value =
8637 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
8644 /* always start with reliable default values */
8645 setSetupInfoToDefaults(&setup);
8647 /* try to load setup values from default setup file */
8648 filename = getDefaultSetupFilename();
8650 if (fileExists(filename))
8651 LoadSetupFromFilename(filename);
8653 /* try to load setup values from user setup file */
8654 filename = getSetupFilename();
8656 LoadSetupFromFilename(filename);
8658 LoadSetup_SpecialPostProcessing();
8661 void LoadSetup_EditorCascade()
8663 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8664 SetupFileHash *setup_file_hash = NULL;
8666 /* always start with reliable default values */
8667 setSetupInfoToDefaults_EditorCascade(&setup);
8669 setup_file_hash = loadSetupFileHash(filename);
8671 if (setup_file_hash)
8673 decodeSetupFileHash_EditorCascade(setup_file_hash);
8675 freeSetupFileHash(setup_file_hash);
8683 char *filename = getSetupFilename();
8687 InitUserDataDirectory();
8689 if (!(file = fopen(filename, MODE_WRITE)))
8691 Error(ERR_WARN, "cannot write setup file '%s'", filename);
8695 fprintFileHeader(file, SETUP_FILENAME);
8699 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8701 /* just to make things nicer :) */
8702 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
8703 i == SETUP_TOKEN_GRAPHICS_SET ||
8704 i == SETUP_TOKEN_VOLUME_SIMPLE ||
8705 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
8706 fprintf(file, "\n");
8708 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
8713 fprintf(file, "\n");
8714 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8715 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
8717 /* shortcut setup */
8718 ssi = setup.shortcut;
8719 fprintf(file, "\n");
8720 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8721 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
8724 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8728 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8729 fprintf(file, "\n");
8731 sii = setup.input[pnr];
8732 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8733 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
8738 fprintf(file, "\n");
8739 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8740 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
8742 /* internal setup */
8743 /* (internal setup values not saved to user setup file) */
8747 fprintf(file, "\n");
8748 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8749 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
8752 soi = setup.options;
8753 fprintf(file, "\n");
8754 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8755 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
8759 SetFilePermissions(filename, PERMS_PRIVATE);
8762 void SaveSetup_EditorCascade()
8764 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8768 InitUserDataDirectory();
8770 if (!(file = fopen(filename, MODE_WRITE)))
8772 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
8777 fprintFileHeader(file, EDITORCASCADE_FILENAME);
8779 seci = setup.editor_cascade;
8780 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8781 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
8785 SetFilePermissions(filename, PERMS_PRIVATE);
8790 void LoadCustomElementDescriptions()
8792 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8793 SetupFileHash *setup_file_hash;
8796 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8798 if (element_info[i].custom_description != NULL)
8800 free(element_info[i].custom_description);
8801 element_info[i].custom_description = NULL;
8805 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8808 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8810 char *token = getStringCat2(element_info[i].token_name, ".name");
8811 char *value = getHashEntry(setup_file_hash, token);
8814 element_info[i].custom_description = getStringCopy(value);
8819 freeSetupFileHash(setup_file_hash);
8822 static int getElementFromToken(char *token)
8824 char *value = getHashEntry(element_token_hash, token);
8829 Error(ERR_WARN, "unknown element token '%s'", token);
8831 return EL_UNDEFINED;
8834 static int get_token_parameter_value(char *token, char *value_raw)
8838 if (token == NULL || value_raw == NULL)
8839 return ARG_UNDEFINED_VALUE;
8841 suffix = strrchr(token, '.');
8845 if (strEqual(suffix, ".element"))
8846 return getElementFromToken(value_raw);
8848 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
8849 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
8852 void InitMenuDesignSettings_Static()
8856 /* always start with reliable default values from static default config */
8857 for (i = 0; image_config_vars[i].token != NULL; i++)
8859 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
8862 *image_config_vars[i].value =
8863 get_token_parameter_value(image_config_vars[i].token, value);
8867 static void InitMenuDesignSettings_SpecialPreProcessing()
8871 /* the following initializes hierarchical values from static configuration */
8873 /* special case: initialize "ARG_DEFAULT" values in static default config */
8874 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
8875 titlescreen_initial_first_default.fade_mode =
8876 title_initial_first_default.fade_mode;
8877 titlescreen_initial_first_default.fade_delay =
8878 title_initial_first_default.fade_delay;
8879 titlescreen_initial_first_default.post_delay =
8880 title_initial_first_default.post_delay;
8881 titlescreen_initial_first_default.auto_delay =
8882 title_initial_first_default.auto_delay;
8883 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
8884 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
8885 titlescreen_first_default.post_delay = title_first_default.post_delay;
8886 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
8887 titlemessage_initial_first_default.fade_mode =
8888 title_initial_first_default.fade_mode;
8889 titlemessage_initial_first_default.fade_delay =
8890 title_initial_first_default.fade_delay;
8891 titlemessage_initial_first_default.post_delay =
8892 title_initial_first_default.post_delay;
8893 titlemessage_initial_first_default.auto_delay =
8894 title_initial_first_default.auto_delay;
8895 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
8896 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
8897 titlemessage_first_default.post_delay = title_first_default.post_delay;
8898 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
8900 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
8901 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
8902 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
8903 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
8904 titlescreen_default.fade_mode = title_default.fade_mode;
8905 titlescreen_default.fade_delay = title_default.fade_delay;
8906 titlescreen_default.post_delay = title_default.post_delay;
8907 titlescreen_default.auto_delay = title_default.auto_delay;
8908 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
8909 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
8910 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
8911 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
8912 titlemessage_default.fade_mode = title_default.fade_mode;
8913 titlemessage_default.fade_delay = title_default.fade_delay;
8914 titlemessage_default.post_delay = title_default.post_delay;
8915 titlemessage_default.auto_delay = title_default.auto_delay;
8917 /* special case: initialize "ARG_DEFAULT" values in static default config */
8918 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
8919 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
8921 titlescreen_initial_first[i] = titlescreen_initial_first_default;
8922 titlescreen_first[i] = titlescreen_first_default;
8923 titlemessage_initial_first[i] = titlemessage_initial_first_default;
8924 titlemessage_first[i] = titlemessage_first_default;
8926 titlescreen_initial[i] = titlescreen_initial_default;
8927 titlescreen[i] = titlescreen_default;
8928 titlemessage_initial[i] = titlemessage_initial_default;
8929 titlemessage[i] = titlemessage_default;
8932 /* special case: initialize "ARG_DEFAULT" values in static default config */
8933 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
8934 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8936 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
8939 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
8940 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
8941 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
8944 /* special case: initialize "ARG_DEFAULT" values in static default config */
8945 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
8946 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8948 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
8949 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
8950 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
8952 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
8955 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
8959 static void InitMenuDesignSettings_SpecialPostProcessing()
8963 struct XY *dst, *src;
8967 { &game.button.save, &game.button.stop },
8968 { &game.button.pause2, &game.button.pause },
8969 { &game.button.load, &game.button.play },
8970 { &game.button.undo, &game.button.stop },
8971 { &game.button.redo, &game.button.play },
8977 /* special case: initialize later added SETUP list size from LEVELS value */
8978 if (menu.list_size[GAME_MODE_SETUP] == -1)
8979 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
8981 /* set default position for snapshot buttons to stop/pause/play buttons */
8982 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
8983 if ((*game_buttons_xy[i].dst).x == -1 &&
8984 (*game_buttons_xy[i].dst).y == -1)
8985 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
8988 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
8992 struct XYTileSize *dst, *src;
8995 editor_buttons_xy[] =
8998 &editor.button.element_left, &editor.palette.element_left,
8999 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9002 &editor.button.element_middle, &editor.palette.element_middle,
9003 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9006 &editor.button.element_right, &editor.palette.element_right,
9007 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9014 /* set default position for element buttons to element graphics */
9015 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9017 if ((*editor_buttons_xy[i].dst).x == -1 &&
9018 (*editor_buttons_xy[i].dst).y == -1)
9020 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9022 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9024 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9029 static void LoadMenuDesignSettingsFromFilename(char *filename)
9031 static struct TitleFadingInfo tfi;
9032 static struct TitleMessageInfo tmi;
9033 static struct TokenInfo title_tokens[] =
9035 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9036 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9037 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9038 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9042 static struct TokenInfo titlemessage_tokens[] =
9044 { TYPE_INTEGER, &tmi.x, ".x" },
9045 { TYPE_INTEGER, &tmi.y, ".y" },
9046 { TYPE_INTEGER, &tmi.width, ".width" },
9047 { TYPE_INTEGER, &tmi.height, ".height" },
9048 { TYPE_INTEGER, &tmi.chars, ".chars" },
9049 { TYPE_INTEGER, &tmi.lines, ".lines" },
9050 { TYPE_INTEGER, &tmi.align, ".align" },
9051 { TYPE_INTEGER, &tmi.valign, ".valign" },
9052 { TYPE_INTEGER, &tmi.font, ".font" },
9053 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9054 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9055 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9056 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9057 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9058 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9059 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9060 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9066 struct TitleFadingInfo *info;
9071 /* initialize first titles from "enter screen" definitions, if defined */
9072 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9073 { &title_first_default, "menu.enter_screen.TITLE" },
9075 /* initialize title screens from "next screen" definitions, if defined */
9076 { &title_initial_default, "menu.next_screen.TITLE" },
9077 { &title_default, "menu.next_screen.TITLE" },
9083 struct TitleMessageInfo *array;
9086 titlemessage_arrays[] =
9088 /* initialize first titles from "enter screen" definitions, if defined */
9089 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9090 { titlescreen_first, "menu.enter_screen.TITLE" },
9091 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9092 { titlemessage_first, "menu.enter_screen.TITLE" },
9094 /* initialize titles from "next screen" definitions, if defined */
9095 { titlescreen_initial, "menu.next_screen.TITLE" },
9096 { titlescreen, "menu.next_screen.TITLE" },
9097 { titlemessage_initial, "menu.next_screen.TITLE" },
9098 { titlemessage, "menu.next_screen.TITLE" },
9100 /* overwrite titles with title definitions, if defined */
9101 { titlescreen_initial_first, "[title_initial]" },
9102 { titlescreen_first, "[title]" },
9103 { titlemessage_initial_first, "[title_initial]" },
9104 { titlemessage_first, "[title]" },
9106 { titlescreen_initial, "[title_initial]" },
9107 { titlescreen, "[title]" },
9108 { titlemessage_initial, "[title_initial]" },
9109 { titlemessage, "[title]" },
9111 /* overwrite titles with title screen/message definitions, if defined */
9112 { titlescreen_initial_first, "[titlescreen_initial]" },
9113 { titlescreen_first, "[titlescreen]" },
9114 { titlemessage_initial_first, "[titlemessage_initial]" },
9115 { titlemessage_first, "[titlemessage]" },
9117 { titlescreen_initial, "[titlescreen_initial]" },
9118 { titlescreen, "[titlescreen]" },
9119 { titlemessage_initial, "[titlemessage_initial]" },
9120 { titlemessage, "[titlemessage]" },
9124 SetupFileHash *setup_file_hash;
9127 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9130 /* the following initializes hierarchical values from dynamic configuration */
9132 /* special case: initialize with default values that may be overwritten */
9133 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9134 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9136 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9137 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9138 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9140 if (value_1 != NULL)
9141 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9142 if (value_2 != NULL)
9143 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9144 if (value_3 != NULL)
9145 menu.list_size[i] = get_integer_from_string(value_3);
9148 /* special case: initialize with default values that may be overwritten */
9149 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9150 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9152 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9153 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9155 if (value_1 != NULL)
9156 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9157 if (value_2 != NULL)
9158 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9160 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9162 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9164 if (value_1 != NULL)
9165 menu.list_size_info[i] = get_integer_from_string(value_1);
9169 /* special case: initialize with default values that may be overwritten */
9170 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9171 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9173 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9174 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9176 if (value_1 != NULL)
9177 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9178 if (value_2 != NULL)
9179 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9182 /* special case: initialize with default values that may be overwritten */
9183 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9184 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9186 char *token_1 = "menu.enter_screen.fade_mode";
9187 char *token_2 = "menu.enter_screen.fade_delay";
9188 char *token_3 = "menu.enter_screen.post_delay";
9189 char *token_4 = "menu.leave_screen.fade_mode";
9190 char *token_5 = "menu.leave_screen.fade_delay";
9191 char *token_6 = "menu.leave_screen.post_delay";
9192 char *token_7 = "menu.next_screen.fade_mode";
9193 char *token_8 = "menu.next_screen.fade_delay";
9194 char *token_9 = "menu.next_screen.post_delay";
9195 char *value_1 = getHashEntry(setup_file_hash, token_1);
9196 char *value_2 = getHashEntry(setup_file_hash, token_2);
9197 char *value_3 = getHashEntry(setup_file_hash, token_3);
9198 char *value_4 = getHashEntry(setup_file_hash, token_4);
9199 char *value_5 = getHashEntry(setup_file_hash, token_5);
9200 char *value_6 = getHashEntry(setup_file_hash, token_6);
9201 char *value_7 = getHashEntry(setup_file_hash, token_7);
9202 char *value_8 = getHashEntry(setup_file_hash, token_8);
9203 char *value_9 = getHashEntry(setup_file_hash, token_9);
9205 if (value_1 != NULL)
9206 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9208 if (value_2 != NULL)
9209 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9211 if (value_3 != NULL)
9212 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9214 if (value_4 != NULL)
9215 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9217 if (value_5 != NULL)
9218 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9220 if (value_6 != NULL)
9221 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9223 if (value_7 != NULL)
9224 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9226 if (value_8 != NULL)
9227 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9229 if (value_9 != NULL)
9230 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9234 /* special case: initialize with default values that may be overwritten */
9235 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9236 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9238 char *token_w1 = "viewport.window.width";
9239 char *token_w2 = "viewport.window.height";
9240 char *token_01 = "viewport.playfield.x";
9241 char *token_02 = "viewport.playfield.y";
9242 char *token_03 = "viewport.playfield.width";
9243 char *token_04 = "viewport.playfield.height";
9244 char *token_05 = "viewport.playfield.border_size";
9245 char *token_06 = "viewport.door_1.x";
9246 char *token_07 = "viewport.door_1.y";
9247 char *token_08 = "viewport.door_1.width";
9248 char *token_09 = "viewport.door_1.height";
9249 char *token_10 = "viewport.door_1.border_size";
9250 char *token_11 = "viewport.door_2.x";
9251 char *token_12 = "viewport.door_2.y";
9252 char *token_13 = "viewport.door_2.width";
9253 char *token_14 = "viewport.door_2.height";
9254 char *token_15 = "viewport.door_2.border_size";
9255 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9256 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9257 char *value_01 = getHashEntry(setup_file_hash, token_01);
9258 char *value_02 = getHashEntry(setup_file_hash, token_02);
9259 char *value_03 = getHashEntry(setup_file_hash, token_03);
9260 char *value_04 = getHashEntry(setup_file_hash, token_04);
9261 char *value_05 = getHashEntry(setup_file_hash, token_05);
9262 char *value_06 = getHashEntry(setup_file_hash, token_06);
9263 char *value_07 = getHashEntry(setup_file_hash, token_07);
9264 char *value_08 = getHashEntry(setup_file_hash, token_08);
9265 char *value_09 = getHashEntry(setup_file_hash, token_09);
9266 char *value_10 = getHashEntry(setup_file_hash, token_10);
9267 char *value_11 = getHashEntry(setup_file_hash, token_11);
9268 char *value_12 = getHashEntry(setup_file_hash, token_12);
9269 char *value_13 = getHashEntry(setup_file_hash, token_13);
9270 char *value_14 = getHashEntry(setup_file_hash, token_14);
9271 char *value_15 = getHashEntry(setup_file_hash, token_15);
9273 if (value_w1 != NULL)
9274 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9275 if (value_w2 != NULL)
9276 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9277 if (value_01 != NULL)
9278 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9279 if (value_02 != NULL)
9280 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9281 if (value_03 != NULL)
9282 viewport.playfield[i].width = get_token_parameter_value(token_03,
9284 if (value_04 != NULL)
9285 viewport.playfield[i].height = get_token_parameter_value(token_04,
9287 if (value_05 != NULL)
9288 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9290 if (value_06 != NULL)
9291 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9292 if (value_07 != NULL)
9293 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9294 if (value_08 != NULL)
9295 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9296 if (value_09 != NULL)
9297 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9298 if (value_10 != NULL)
9299 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9301 if (value_11 != NULL)
9302 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9303 if (value_12 != NULL)
9304 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9305 if (value_13 != NULL)
9306 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9307 if (value_14 != NULL)
9308 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9309 if (value_15 != NULL)
9310 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9314 /* special case: initialize with default values that may be overwritten */
9315 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9316 for (i = 0; title_info[i].info != NULL; i++)
9318 struct TitleFadingInfo *info = title_info[i].info;
9319 char *base_token = title_info[i].text;
9321 for (j = 0; title_tokens[j].type != -1; j++)
9323 char *token = getStringCat2(base_token, title_tokens[j].text);
9324 char *value = getHashEntry(setup_file_hash, token);
9328 int parameter_value = get_token_parameter_value(token, value);
9332 *(int *)title_tokens[j].value = (int)parameter_value;
9341 /* special case: initialize with default values that may be overwritten */
9342 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9343 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9345 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9346 char *base_token = titlemessage_arrays[i].text;
9348 for (j = 0; titlemessage_tokens[j].type != -1; j++)
9350 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9351 char *value = getHashEntry(setup_file_hash, token);
9355 int parameter_value = get_token_parameter_value(token, value);
9357 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9361 if (titlemessage_tokens[j].type == TYPE_INTEGER)
9362 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
9364 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9374 /* read (and overwrite with) values that may be specified in config file */
9375 for (i = 0; image_config_vars[i].token != NULL; i++)
9377 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9379 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9380 if (value != NULL && !strEqual(value, ARG_DEFAULT))
9381 *image_config_vars[i].value =
9382 get_token_parameter_value(image_config_vars[i].token, value);
9385 freeSetupFileHash(setup_file_hash);
9388 void LoadMenuDesignSettings()
9390 char *filename_base = UNDEFINED_FILENAME, *filename_local;
9392 InitMenuDesignSettings_Static();
9393 InitMenuDesignSettings_SpecialPreProcessing();
9395 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9397 /* first look for special settings configured in level series config */
9398 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9400 if (fileExists(filename_base))
9401 LoadMenuDesignSettingsFromFilename(filename_base);
9404 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9406 if (filename_local != NULL && !strEqual(filename_base, filename_local))
9407 LoadMenuDesignSettingsFromFilename(filename_local);
9409 InitMenuDesignSettings_SpecialPostProcessing();
9412 void LoadMenuDesignSettings_AfterGraphics()
9414 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9417 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9419 char *filename = getEditorSetupFilename();
9420 SetupFileList *setup_file_list, *list;
9421 SetupFileHash *element_hash;
9422 int num_unknown_tokens = 0;
9425 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9428 element_hash = newSetupFileHash();
9430 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9431 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9433 /* determined size may be larger than needed (due to unknown elements) */
9435 for (list = setup_file_list; list != NULL; list = list->next)
9438 /* add space for up to 3 more elements for padding that may be needed */
9441 /* free memory for old list of elements, if needed */
9442 checked_free(*elements);
9444 /* allocate memory for new list of elements */
9445 *elements = checked_malloc(*num_elements * sizeof(int));
9448 for (list = setup_file_list; list != NULL; list = list->next)
9450 char *value = getHashEntry(element_hash, list->token);
9452 if (value == NULL) /* try to find obsolete token mapping */
9454 char *mapped_token = get_mapped_token(list->token);
9456 if (mapped_token != NULL)
9458 value = getHashEntry(element_hash, mapped_token);
9466 (*elements)[(*num_elements)++] = atoi(value);
9470 if (num_unknown_tokens == 0)
9472 Error(ERR_INFO_LINE, "-");
9473 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9474 Error(ERR_INFO, "- config file: '%s'", filename);
9476 num_unknown_tokens++;
9479 Error(ERR_INFO, "- token: '%s'", list->token);
9483 if (num_unknown_tokens > 0)
9484 Error(ERR_INFO_LINE, "-");
9486 while (*num_elements % 4) /* pad with empty elements, if needed */
9487 (*elements)[(*num_elements)++] = EL_EMPTY;
9489 freeSetupFileList(setup_file_list);
9490 freeSetupFileHash(element_hash);
9493 for (i = 0; i < *num_elements; i++)
9494 printf("editor: element '%s' [%d]\n",
9495 element_info[(*elements)[i]].token_name, (*elements)[i]);
9499 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9502 SetupFileHash *setup_file_hash = NULL;
9503 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9504 char *filename_music, *filename_prefix, *filename_info;
9510 token_to_value_ptr[] =
9512 { "title_header", &tmp_music_file_info.title_header },
9513 { "artist_header", &tmp_music_file_info.artist_header },
9514 { "album_header", &tmp_music_file_info.album_header },
9515 { "year_header", &tmp_music_file_info.year_header },
9517 { "title", &tmp_music_file_info.title },
9518 { "artist", &tmp_music_file_info.artist },
9519 { "album", &tmp_music_file_info.album },
9520 { "year", &tmp_music_file_info.year },
9526 filename_music = (is_sound ? getCustomSoundFilename(basename) :
9527 getCustomMusicFilename(basename));
9529 if (filename_music == NULL)
9532 /* ---------- try to replace file extension ---------- */
9534 filename_prefix = getStringCopy(filename_music);
9535 if (strrchr(filename_prefix, '.') != NULL)
9536 *strrchr(filename_prefix, '.') = '\0';
9537 filename_info = getStringCat2(filename_prefix, ".txt");
9539 if (fileExists(filename_info))
9540 setup_file_hash = loadSetupFileHash(filename_info);
9542 free(filename_prefix);
9543 free(filename_info);
9545 if (setup_file_hash == NULL)
9547 /* ---------- try to add file extension ---------- */
9549 filename_prefix = getStringCopy(filename_music);
9550 filename_info = getStringCat2(filename_prefix, ".txt");
9552 if (fileExists(filename_info))
9553 setup_file_hash = loadSetupFileHash(filename_info);
9555 free(filename_prefix);
9556 free(filename_info);
9559 if (setup_file_hash == NULL)
9562 /* ---------- music file info found ---------- */
9564 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
9566 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
9568 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
9570 *token_to_value_ptr[i].value_ptr =
9571 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
9574 tmp_music_file_info.basename = getStringCopy(basename);
9575 tmp_music_file_info.music = music;
9576 tmp_music_file_info.is_sound = is_sound;
9578 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
9579 *new_music_file_info = tmp_music_file_info;
9581 return new_music_file_info;
9584 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
9586 return get_music_file_info_ext(basename, music, FALSE);
9589 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
9591 return get_music_file_info_ext(basename, sound, TRUE);
9594 static boolean music_info_listed_ext(struct MusicFileInfo *list,
9595 char *basename, boolean is_sound)
9597 for (; list != NULL; list = list->next)
9598 if (list->is_sound == is_sound && strEqual(list->basename, basename))
9604 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
9606 return music_info_listed_ext(list, basename, FALSE);
9609 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
9611 return music_info_listed_ext(list, basename, TRUE);
9614 void LoadMusicInfo()
9616 char *music_directory = getCustomMusicDirectory();
9617 int num_music = getMusicListSize();
9618 int num_music_noconf = 0;
9619 int num_sounds = getSoundListSize();
9621 DirectoryEntry *dir_entry;
9622 struct FileInfo *music, *sound;
9623 struct MusicFileInfo *next, **new;
9626 while (music_file_info != NULL)
9628 next = music_file_info->next;
9630 checked_free(music_file_info->basename);
9632 checked_free(music_file_info->title_header);
9633 checked_free(music_file_info->artist_header);
9634 checked_free(music_file_info->album_header);
9635 checked_free(music_file_info->year_header);
9637 checked_free(music_file_info->title);
9638 checked_free(music_file_info->artist);
9639 checked_free(music_file_info->album);
9640 checked_free(music_file_info->year);
9642 free(music_file_info);
9644 music_file_info = next;
9647 new = &music_file_info;
9649 for (i = 0; i < num_music; i++)
9651 music = getMusicListEntry(i);
9653 if (music->filename == NULL)
9656 if (strEqual(music->filename, UNDEFINED_FILENAME))
9659 /* a configured file may be not recognized as music */
9660 if (!FileIsMusic(music->filename))
9663 if (!music_info_listed(music_file_info, music->filename))
9665 *new = get_music_file_info(music->filename, i);
9668 new = &(*new)->next;
9672 if ((dir = openDirectory(music_directory)) == NULL)
9674 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
9678 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
9680 char *basename = dir_entry->basename;
9681 boolean music_already_used = FALSE;
9684 /* skip all music files that are configured in music config file */
9685 for (i = 0; i < num_music; i++)
9687 music = getMusicListEntry(i);
9689 if (music->filename == NULL)
9692 if (strEqual(basename, music->filename))
9694 music_already_used = TRUE;
9699 if (music_already_used)
9702 if (!FileIsMusic(dir_entry->filename))
9705 if (!music_info_listed(music_file_info, basename))
9707 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
9710 new = &(*new)->next;
9716 closeDirectory(dir);
9718 for (i = 0; i < num_sounds; i++)
9720 sound = getSoundListEntry(i);
9722 if (sound->filename == NULL)
9725 if (strEqual(sound->filename, UNDEFINED_FILENAME))
9728 /* a configured file may be not recognized as sound */
9729 if (!FileIsSound(sound->filename))
9732 if (!sound_info_listed(music_file_info, sound->filename))
9734 *new = get_sound_file_info(sound->filename, i);
9736 new = &(*new)->next;
9741 void add_helpanim_entry(int element, int action, int direction, int delay,
9742 int *num_list_entries)
9744 struct HelpAnimInfo *new_list_entry;
9745 (*num_list_entries)++;
9748 checked_realloc(helpanim_info,
9749 *num_list_entries * sizeof(struct HelpAnimInfo));
9750 new_list_entry = &helpanim_info[*num_list_entries - 1];
9752 new_list_entry->element = element;
9753 new_list_entry->action = action;
9754 new_list_entry->direction = direction;
9755 new_list_entry->delay = delay;
9758 void print_unknown_token(char *filename, char *token, int token_nr)
9762 Error(ERR_INFO_LINE, "-");
9763 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9764 Error(ERR_INFO, "- config file: '%s'", filename);
9767 Error(ERR_INFO, "- token: '%s'", token);
9770 void print_unknown_token_end(int token_nr)
9773 Error(ERR_INFO_LINE, "-");
9776 void LoadHelpAnimInfo()
9778 char *filename = getHelpAnimFilename();
9779 SetupFileList *setup_file_list = NULL, *list;
9780 SetupFileHash *element_hash, *action_hash, *direction_hash;
9781 int num_list_entries = 0;
9782 int num_unknown_tokens = 0;
9785 if (fileExists(filename))
9786 setup_file_list = loadSetupFileList(filename);
9788 if (setup_file_list == NULL)
9790 /* use reliable default values from static configuration */
9791 SetupFileList *insert_ptr;
9793 insert_ptr = setup_file_list =
9794 newSetupFileList(helpanim_config[0].token,
9795 helpanim_config[0].value);
9797 for (i = 1; helpanim_config[i].token; i++)
9798 insert_ptr = addListEntry(insert_ptr,
9799 helpanim_config[i].token,
9800 helpanim_config[i].value);
9803 element_hash = newSetupFileHash();
9804 action_hash = newSetupFileHash();
9805 direction_hash = newSetupFileHash();
9807 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
9808 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9810 for (i = 0; i < NUM_ACTIONS; i++)
9811 setHashEntry(action_hash, element_action_info[i].suffix,
9812 i_to_a(element_action_info[i].value));
9814 /* do not store direction index (bit) here, but direction value! */
9815 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
9816 setHashEntry(direction_hash, element_direction_info[i].suffix,
9817 i_to_a(1 << element_direction_info[i].value));
9819 for (list = setup_file_list; list != NULL; list = list->next)
9821 char *element_token, *action_token, *direction_token;
9822 char *element_value, *action_value, *direction_value;
9823 int delay = atoi(list->value);
9825 if (strEqual(list->token, "end"))
9827 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
9832 /* first try to break element into element/action/direction parts;
9833 if this does not work, also accept combined "element[.act][.dir]"
9834 elements (like "dynamite.active"), which are unique elements */
9836 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
9838 element_value = getHashEntry(element_hash, list->token);
9839 if (element_value != NULL) /* element found */
9840 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9844 /* no further suffixes found -- this is not an element */
9845 print_unknown_token(filename, list->token, num_unknown_tokens++);
9851 /* token has format "<prefix>.<something>" */
9853 action_token = strchr(list->token, '.'); /* suffix may be action ... */
9854 direction_token = action_token; /* ... or direction */
9856 element_token = getStringCopy(list->token);
9857 *strchr(element_token, '.') = '\0';
9859 element_value = getHashEntry(element_hash, element_token);
9861 if (element_value == NULL) /* this is no element */
9863 element_value = getHashEntry(element_hash, list->token);
9864 if (element_value != NULL) /* combined element found */
9865 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9868 print_unknown_token(filename, list->token, num_unknown_tokens++);
9870 free(element_token);
9875 action_value = getHashEntry(action_hash, action_token);
9877 if (action_value != NULL) /* action found */
9879 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
9882 free(element_token);
9887 direction_value = getHashEntry(direction_hash, direction_token);
9889 if (direction_value != NULL) /* direction found */
9891 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
9894 free(element_token);
9899 if (strchr(action_token + 1, '.') == NULL)
9901 /* no further suffixes found -- this is not an action nor direction */
9903 element_value = getHashEntry(element_hash, list->token);
9904 if (element_value != NULL) /* combined element found */
9905 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9908 print_unknown_token(filename, list->token, num_unknown_tokens++);
9910 free(element_token);
9915 /* token has format "<prefix>.<suffix>.<something>" */
9917 direction_token = strchr(action_token + 1, '.');
9919 action_token = getStringCopy(action_token);
9920 *strchr(action_token + 1, '.') = '\0';
9922 action_value = getHashEntry(action_hash, action_token);
9924 if (action_value == NULL) /* this is no action */
9926 element_value = getHashEntry(element_hash, list->token);
9927 if (element_value != NULL) /* combined element found */
9928 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9931 print_unknown_token(filename, list->token, num_unknown_tokens++);
9933 free(element_token);
9939 direction_value = getHashEntry(direction_hash, direction_token);
9941 if (direction_value != NULL) /* direction found */
9943 add_helpanim_entry(atoi(element_value), atoi(action_value),
9944 atoi(direction_value), delay, &num_list_entries);
9946 free(element_token);
9952 /* this is no direction */
9954 element_value = getHashEntry(element_hash, list->token);
9955 if (element_value != NULL) /* combined element found */
9956 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9959 print_unknown_token(filename, list->token, num_unknown_tokens++);
9961 free(element_token);
9965 print_unknown_token_end(num_unknown_tokens);
9967 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
9968 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
9970 freeSetupFileList(setup_file_list);
9971 freeSetupFileHash(element_hash);
9972 freeSetupFileHash(action_hash);
9973 freeSetupFileHash(direction_hash);
9976 for (i = 0; i < num_list_entries; i++)
9977 printf("::: '%s': %d, %d, %d => %d\n",
9978 EL_NAME(helpanim_info[i].element),
9979 helpanim_info[i].element,
9980 helpanim_info[i].action,
9981 helpanim_info[i].direction,
9982 helpanim_info[i].delay);
9986 void LoadHelpTextInfo()
9988 char *filename = getHelpTextFilename();
9991 if (helptext_info != NULL)
9993 freeSetupFileHash(helptext_info);
9994 helptext_info = NULL;
9997 if (fileExists(filename))
9998 helptext_info = loadSetupFileHash(filename);
10000 if (helptext_info == NULL)
10002 /* use reliable default values from static configuration */
10003 helptext_info = newSetupFileHash();
10005 for (i = 0; helptext_config[i].token; i++)
10006 setHashEntry(helptext_info,
10007 helptext_config[i].token,
10008 helptext_config[i].value);
10012 BEGIN_HASH_ITERATION(helptext_info, itr)
10014 printf("::: '%s' => '%s'\n",
10015 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10017 END_HASH_ITERATION(hash, itr)
10022 /* ------------------------------------------------------------------------- */
10023 /* convert levels */
10024 /* ------------------------------------------------------------------------- */
10026 #define MAX_NUM_CONVERT_LEVELS 1000
10028 void ConvertLevels()
10030 static LevelDirTree *convert_leveldir = NULL;
10031 static int convert_level_nr = -1;
10032 static int num_levels_handled = 0;
10033 static int num_levels_converted = 0;
10034 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10037 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10038 global.convert_leveldir);
10040 if (convert_leveldir == NULL)
10041 Error(ERR_EXIT, "no such level identifier: '%s'",
10042 global.convert_leveldir);
10044 leveldir_current = convert_leveldir;
10046 if (global.convert_level_nr != -1)
10048 convert_leveldir->first_level = global.convert_level_nr;
10049 convert_leveldir->last_level = global.convert_level_nr;
10052 convert_level_nr = convert_leveldir->first_level;
10054 PrintLine("=", 79);
10055 Print("Converting levels\n");
10056 PrintLine("-", 79);
10057 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10058 Print("Level series name: '%s'\n", convert_leveldir->name);
10059 Print("Level series author: '%s'\n", convert_leveldir->author);
10060 Print("Number of levels: %d\n", convert_leveldir->levels);
10061 PrintLine("=", 79);
10064 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10065 levels_failed[i] = FALSE;
10067 while (convert_level_nr <= convert_leveldir->last_level)
10069 char *level_filename;
10072 level_nr = convert_level_nr++;
10074 Print("Level %03d: ", level_nr);
10076 LoadLevel(level_nr);
10077 if (level.no_level_file || level.no_valid_file)
10079 Print("(no level)\n");
10083 Print("converting level ... ");
10085 level_filename = getDefaultLevelFilename(level_nr);
10086 new_level = !fileExists(level_filename);
10090 SaveLevel(level_nr);
10092 num_levels_converted++;
10094 Print("converted.\n");
10098 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10099 levels_failed[level_nr] = TRUE;
10101 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10104 num_levels_handled++;
10108 PrintLine("=", 79);
10109 Print("Number of levels handled: %d\n", num_levels_handled);
10110 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10111 (num_levels_handled ?
10112 num_levels_converted * 100 / num_levels_handled : 0));
10113 PrintLine("-", 79);
10114 Print("Summary (for automatic parsing by scripts):\n");
10115 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10116 convert_leveldir->identifier, num_levels_converted,
10117 num_levels_handled,
10118 (num_levels_handled ?
10119 num_levels_converted * 100 / num_levels_handled : 0));
10121 if (num_levels_handled != num_levels_converted)
10123 Print(", FAILED:");
10124 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10125 if (levels_failed[i])
10130 PrintLine("=", 79);
10132 CloseAllAndExit(0);
10136 /* ------------------------------------------------------------------------- */
10137 /* create and save images for use in level sketches (raw BMP format) */
10138 /* ------------------------------------------------------------------------- */
10140 void CreateLevelSketchImages()
10142 #if defined(TARGET_SDL)
10147 InitElementPropertiesGfxElement();
10149 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10150 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10152 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10154 Bitmap *src_bitmap;
10156 int element = getMappedElement(i);
10157 int graphic = el2edimg(element);
10158 char basename1[16];
10159 char basename2[16];
10163 sprintf(basename1, "%03d.bmp", i);
10164 sprintf(basename2, "%03ds.bmp", i);
10166 filename1 = getPath2(global.create_images_dir, basename1);
10167 filename2 = getPath2(global.create_images_dir, basename2);
10169 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10170 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10173 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10174 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10176 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10177 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10179 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10180 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10186 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10189 FreeBitmap(bitmap1);
10190 FreeBitmap(bitmap2);
10195 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10197 CloseAllAndExit(0);
10202 /* ------------------------------------------------------------------------- */
10203 /* create and save images for custom and group elements (raw BMP format) */
10204 /* ------------------------------------------------------------------------- */
10206 void CreateCustomElementImages(char *directory)
10208 #if defined(TARGET_SDL)
10209 char *src_basename = "RocksCE-template.ilbm";
10210 char *dst_basename = "RocksCE.bmp";
10211 char *src_filename = getPath2(directory, src_basename);
10212 char *dst_filename = getPath2(directory, dst_basename);
10213 Bitmap *src_bitmap;
10215 int yoffset_ce = 0;
10216 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10219 SDLInitVideoDisplay();
10221 ReCreateBitmap(&backbuffer, video.width, video.height);
10223 src_bitmap = LoadImage(src_filename);
10225 bitmap = CreateBitmap(TILEX * 16 * 2,
10226 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10229 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10236 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10237 TILEX * x, TILEY * y + yoffset_ce);
10239 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10241 TILEX * x + TILEX * 16,
10242 TILEY * y + yoffset_ce);
10244 for (j = 2; j >= 0; j--)
10248 BlitBitmap(src_bitmap, bitmap,
10249 TILEX + c * 7, 0, 6, 10,
10250 TILEX * x + 6 + j * 7,
10251 TILEY * y + 11 + yoffset_ce);
10253 BlitBitmap(src_bitmap, bitmap,
10254 TILEX + c * 8, TILEY, 6, 10,
10255 TILEX * 16 + TILEX * x + 6 + j * 8,
10256 TILEY * y + 10 + yoffset_ce);
10262 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10269 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10270 TILEX * x, TILEY * y + yoffset_ge);
10272 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10274 TILEX * x + TILEX * 16,
10275 TILEY * y + yoffset_ge);
10277 for (j = 1; j >= 0; j--)
10281 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10282 TILEX * x + 6 + j * 10,
10283 TILEY * y + 11 + yoffset_ge);
10285 BlitBitmap(src_bitmap, bitmap,
10286 TILEX + c * 8, TILEY + 12, 6, 10,
10287 TILEX * 16 + TILEX * x + 10 + j * 8,
10288 TILEY * y + 10 + yoffset_ge);
10294 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10295 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10297 FreeBitmap(bitmap);
10299 CloseAllAndExit(0);