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)
1770 setLevelInfoToDefaults_Level(level);
1772 if (!level_info_only)
1773 setLevelInfoToDefaults_Elements(level);
1775 level->no_valid_file = FALSE;
1777 level->changed = FALSE;
1780 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1782 level_file_info->nr = 0;
1783 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1784 level_file_info->packed = FALSE;
1785 level_file_info->basename = NULL;
1786 level_file_info->filename = NULL;
1789 int getMappedElement_SB(int, boolean);
1791 static void ActivateLevelTemplate()
1795 if (check_special_flags("load_xsb_to_ces"))
1797 /* fill smaller playfields with padding "beyond border wall" elements */
1798 if (level.fieldx < level_template.fieldx ||
1799 level.fieldy < level_template.fieldy)
1801 short field[level.fieldx][level.fieldy];
1802 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1803 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1804 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1805 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1807 /* copy old playfield (which is smaller than the visible area) */
1808 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1809 field[x][y] = level.field[x][y];
1811 /* fill new, larger playfield with "beyond border wall" elements */
1812 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1813 level.field[x][y] = getMappedElement_SB('_', TRUE);
1815 /* copy the old playfield to the middle of the new playfield */
1816 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1817 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1819 level.fieldx = new_fieldx;
1820 level.fieldy = new_fieldy;
1824 /* Currently there is no special action needed to activate the template
1825 data, because 'element_info' property settings overwrite the original
1826 level data, while all other variables do not change. */
1828 /* Exception: 'from_level_template' elements in the original level playfield
1829 are overwritten with the corresponding elements at the same position in
1830 playfield from the level template. */
1832 for (x = 0; x < level.fieldx; x++)
1833 for (y = 0; y < level.fieldy; y++)
1834 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1835 level.field[x][y] = level_template.field[x][y];
1837 if (check_special_flags("load_xsb_to_ces"))
1839 struct LevelInfo level_backup = level;
1841 /* overwrite all individual level settings from template level settings */
1842 level = level_template;
1844 /* restore playfield size */
1845 level.fieldx = level_backup.fieldx;
1846 level.fieldy = level_backup.fieldy;
1848 /* restore playfield content */
1849 for (x = 0; x < level.fieldx; x++)
1850 for (y = 0; y < level.fieldy; y++)
1851 level.field[x][y] = level_backup.field[x][y];
1853 /* restore name and author from individual level */
1854 strcpy(level.name, level_backup.name);
1855 strcpy(level.author, level_backup.author);
1857 /* restore flag "use_custom_template" */
1858 level.use_custom_template = level_backup.use_custom_template;
1862 static char *getLevelFilenameFromBasename(char *basename)
1864 /* use different slots for level template files and regular level files */
1865 static char *filename[2] = { NULL, NULL };
1866 int pos = (strEqual(basename, LEVELTEMPLATE_FILENAME) ? 0 : 1);
1868 checked_free(filename[pos]);
1870 filename[pos] = getPath2(getCurrentLevelDir(), basename);
1872 return filename[pos];
1875 static int getFileTypeFromBasename(char *basename)
1877 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1879 static char *filename = NULL;
1880 struct stat file_status;
1882 /* ---------- try to determine file type from filename ---------- */
1884 /* check for typical filename of a Supaplex level package file */
1885 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1886 return LEVEL_FILE_TYPE_SP;
1888 /* check for typical filename of a Diamond Caves II level package file */
1889 if (strSuffixLower(basename, ".dc") ||
1890 strSuffixLower(basename, ".dc2"))
1891 return LEVEL_FILE_TYPE_DC;
1893 /* check for typical filename of a Sokoban level package file */
1894 if (strSuffixLower(basename, ".xsb") &&
1895 strchr(basename, '%') == NULL)
1896 return LEVEL_FILE_TYPE_SB;
1898 /* ---------- try to determine file type from filesize ---------- */
1900 checked_free(filename);
1901 filename = getPath2(getCurrentLevelDir(), basename);
1903 if (stat(filename, &file_status) == 0)
1905 /* check for typical filesize of a Supaplex level package file */
1906 if (file_status.st_size == 170496)
1907 return LEVEL_FILE_TYPE_SP;
1910 return LEVEL_FILE_TYPE_UNKNOWN;
1913 static boolean checkForPackageFromBasename(char *basename)
1915 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
1916 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
1918 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
1921 static char *getSingleLevelBasenameExt(int nr, char *extension)
1923 static char basename[MAX_FILENAME_LEN];
1926 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
1928 sprintf(basename, "%03d.%s", nr, extension);
1933 static char *getSingleLevelBasename(int nr)
1935 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
1938 static char *getPackedLevelBasename(int type)
1940 static char basename[MAX_FILENAME_LEN];
1941 char *directory = getCurrentLevelDir();
1943 DirectoryEntry *dir_entry;
1945 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
1947 if ((dir = openDirectory(directory)) == NULL)
1949 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
1954 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
1956 char *entry_basename = dir_entry->basename;
1957 int entry_type = getFileTypeFromBasename(entry_basename);
1959 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
1961 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
1964 strcpy(basename, entry_basename);
1971 closeDirectory(dir);
1976 static char *getSingleLevelFilename(int nr)
1978 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
1981 #if ENABLE_UNUSED_CODE
1982 static char *getPackedLevelFilename(int type)
1984 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
1988 char *getDefaultLevelFilename(int nr)
1990 return getSingleLevelFilename(nr);
1993 #if ENABLE_UNUSED_CODE
1994 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
1998 lfi->packed = FALSE;
1999 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
2000 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2004 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2005 int type, char *format, ...)
2007 static char basename[MAX_FILENAME_LEN];
2010 va_start(ap, format);
2011 vsprintf(basename, format, ap);
2015 lfi->packed = FALSE;
2016 lfi->basename = basename;
2017 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2020 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2025 lfi->basename = getPackedLevelBasename(lfi->type);
2026 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2029 static int getFiletypeFromID(char *filetype_id)
2031 char *filetype_id_lower;
2032 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2035 if (filetype_id == NULL)
2036 return LEVEL_FILE_TYPE_UNKNOWN;
2038 filetype_id_lower = getStringToLower(filetype_id);
2040 for (i = 0; filetype_id_list[i].id != NULL; i++)
2042 char *id_lower = getStringToLower(filetype_id_list[i].id);
2044 if (strEqual(filetype_id_lower, id_lower))
2045 filetype = filetype_id_list[i].filetype;
2049 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2053 free(filetype_id_lower);
2058 char *getLocalLevelTemplateFilename()
2060 return getDefaultLevelFilename(-1);
2063 char *getGlobalLevelTemplateFilename()
2065 /* global variable "leveldir_current" must be modified in the loop below */
2066 LevelDirTree *leveldir_current_last = leveldir_current;
2067 char *filename = NULL;
2069 /* check for template level in path from current to topmost tree node */
2071 while (leveldir_current != NULL)
2073 filename = getDefaultLevelFilename(-1);
2075 if (fileExists(filename))
2078 leveldir_current = leveldir_current->node_parent;
2081 /* restore global variable "leveldir_current" modified in above loop */
2082 leveldir_current = leveldir_current_last;
2087 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2091 /* special case: level number is negative => check for level template file */
2094 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2095 getSingleLevelBasename(-1));
2097 /* replace local level template filename with global template filename */
2098 lfi->filename = getGlobalLevelTemplateFilename();
2100 /* no fallback if template file not existing */
2104 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2105 if (leveldir_current->level_filename != NULL)
2107 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2109 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2110 leveldir_current->level_filename, nr);
2112 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2114 if (fileExists(lfi->filename))
2118 /* check for native Rocks'n'Diamonds level file */
2119 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2120 "%03d.%s", nr, LEVELFILE_EXTENSION);
2121 if (fileExists(lfi->filename))
2124 /* check for Emerald Mine level file (V1) */
2125 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2126 'a' + (nr / 10) % 26, '0' + nr % 10);
2127 if (fileExists(lfi->filename))
2129 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2130 'A' + (nr / 10) % 26, '0' + nr % 10);
2131 if (fileExists(lfi->filename))
2134 /* check for Emerald Mine level file (V2 to V5) */
2135 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2136 if (fileExists(lfi->filename))
2139 /* check for Emerald Mine level file (V6 / single mode) */
2140 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2141 if (fileExists(lfi->filename))
2143 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2144 if (fileExists(lfi->filename))
2147 /* check for Emerald Mine level file (V6 / teamwork mode) */
2148 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2149 if (fileExists(lfi->filename))
2151 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2152 if (fileExists(lfi->filename))
2155 /* check for various packed level file formats */
2156 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2157 if (fileExists(lfi->filename))
2160 /* no known level file found -- use default values (and fail later) */
2161 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2162 "%03d.%s", nr, LEVELFILE_EXTENSION);
2165 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2167 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2168 lfi->type = getFileTypeFromBasename(lfi->basename);
2171 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2173 /* always start with reliable default values */
2174 setFileInfoToDefaults(level_file_info);
2176 level_file_info->nr = nr; /* set requested level number */
2178 determineLevelFileInfo_Filename(level_file_info);
2179 determineLevelFileInfo_Filetype(level_file_info);
2182 /* ------------------------------------------------------------------------- */
2183 /* functions for loading R'n'D level */
2184 /* ------------------------------------------------------------------------- */
2186 int getMappedElement(int element)
2188 /* remap some (historic, now obsolete) elements */
2192 case EL_PLAYER_OBSOLETE:
2193 element = EL_PLAYER_1;
2196 case EL_KEY_OBSOLETE:
2200 case EL_EM_KEY_1_FILE_OBSOLETE:
2201 element = EL_EM_KEY_1;
2204 case EL_EM_KEY_2_FILE_OBSOLETE:
2205 element = EL_EM_KEY_2;
2208 case EL_EM_KEY_3_FILE_OBSOLETE:
2209 element = EL_EM_KEY_3;
2212 case EL_EM_KEY_4_FILE_OBSOLETE:
2213 element = EL_EM_KEY_4;
2216 case EL_ENVELOPE_OBSOLETE:
2217 element = EL_ENVELOPE_1;
2225 if (element >= NUM_FILE_ELEMENTS)
2227 Error(ERR_WARN, "invalid level element %d", element);
2229 element = EL_UNKNOWN;
2237 int getMappedElementByVersion(int element, int game_version)
2239 /* remap some elements due to certain game version */
2241 if (game_version <= VERSION_IDENT(2,2,0,0))
2243 /* map game font elements */
2244 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2245 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2246 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2247 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2250 if (game_version < VERSION_IDENT(3,0,0,0))
2252 /* map Supaplex gravity tube elements */
2253 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2254 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2255 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2256 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2263 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2265 level->file_version = getFileVersion(file);
2266 level->game_version = getFileVersion(file);
2271 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2273 level->creation_date.year = getFile16BitBE(file);
2274 level->creation_date.month = getFile8Bit(file);
2275 level->creation_date.day = getFile8Bit(file);
2277 level->creation_date.src = DATE_SRC_LEVELFILE;
2282 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2284 int initial_player_stepsize;
2285 int initial_player_gravity;
2288 level->fieldx = getFile8Bit(file);
2289 level->fieldy = getFile8Bit(file);
2291 level->time = getFile16BitBE(file);
2292 level->gems_needed = getFile16BitBE(file);
2294 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2295 level->name[i] = getFile8Bit(file);
2296 level->name[MAX_LEVEL_NAME_LEN] = 0;
2298 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2299 level->score[i] = getFile8Bit(file);
2301 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2302 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2303 for (y = 0; y < 3; y++)
2304 for (x = 0; x < 3; x++)
2305 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2307 level->amoeba_speed = getFile8Bit(file);
2308 level->time_magic_wall = getFile8Bit(file);
2309 level->time_wheel = getFile8Bit(file);
2310 level->amoeba_content = getMappedElement(getFile8Bit(file));
2312 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2315 for (i = 0; i < MAX_PLAYERS; i++)
2316 level->initial_player_stepsize[i] = initial_player_stepsize;
2318 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2320 for (i = 0; i < MAX_PLAYERS; i++)
2321 level->initial_player_gravity[i] = initial_player_gravity;
2323 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2324 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2326 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2328 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2329 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2330 level->can_move_into_acid_bits = getFile32BitBE(file);
2331 level->dont_collide_with_bits = getFile8Bit(file);
2333 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2334 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2336 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2337 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2338 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2340 level->game_engine_type = getFile8Bit(file);
2342 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2347 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2351 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2352 level->name[i] = getFile8Bit(file);
2353 level->name[MAX_LEVEL_NAME_LEN] = 0;
2358 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2362 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2363 level->author[i] = getFile8Bit(file);
2364 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2369 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2372 int chunk_size_expected = level->fieldx * level->fieldy;
2374 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2375 stored with 16-bit encoding (and should be twice as big then).
2376 Even worse, playfield data was stored 16-bit when only yamyam content
2377 contained 16-bit elements and vice versa. */
2379 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2380 chunk_size_expected *= 2;
2382 if (chunk_size_expected != chunk_size)
2384 ReadUnusedBytesFromFile(file, chunk_size);
2385 return chunk_size_expected;
2388 for (y = 0; y < level->fieldy; y++)
2389 for (x = 0; x < level->fieldx; x++)
2390 level->field[x][y] =
2391 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2396 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2399 int header_size = 4;
2400 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2401 int chunk_size_expected = header_size + content_size;
2403 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2404 stored with 16-bit encoding (and should be twice as big then).
2405 Even worse, playfield data was stored 16-bit when only yamyam content
2406 contained 16-bit elements and vice versa. */
2408 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2409 chunk_size_expected += content_size;
2411 if (chunk_size_expected != chunk_size)
2413 ReadUnusedBytesFromFile(file, chunk_size);
2414 return chunk_size_expected;
2418 level->num_yamyam_contents = getFile8Bit(file);
2422 /* correct invalid number of content fields -- should never happen */
2423 if (level->num_yamyam_contents < 1 ||
2424 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2425 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2427 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2428 for (y = 0; y < 3; y++)
2429 for (x = 0; x < 3; x++)
2430 level->yamyam_content[i].e[x][y] =
2431 getMappedElement(level->encoding_16bit_field ?
2432 getFile16BitBE(file) : getFile8Bit(file));
2436 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2441 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2443 element = getMappedElement(getFile16BitBE(file));
2444 num_contents = getFile8Bit(file);
2446 getFile8Bit(file); /* content x size (unused) */
2447 getFile8Bit(file); /* content y size (unused) */
2449 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2451 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2452 for (y = 0; y < 3; y++)
2453 for (x = 0; x < 3; x++)
2454 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2456 /* correct invalid number of content fields -- should never happen */
2457 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2458 num_contents = STD_ELEMENT_CONTENTS;
2460 if (element == EL_YAMYAM)
2462 level->num_yamyam_contents = num_contents;
2464 for (i = 0; i < num_contents; i++)
2465 for (y = 0; y < 3; y++)
2466 for (x = 0; x < 3; x++)
2467 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2469 else if (element == EL_BD_AMOEBA)
2471 level->amoeba_content = content_array[0][0][0];
2475 Error(ERR_WARN, "cannot load content for element '%d'", element);
2481 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2487 int chunk_size_expected;
2489 element = getMappedElement(getFile16BitBE(file));
2490 if (!IS_ENVELOPE(element))
2491 element = EL_ENVELOPE_1;
2493 envelope_nr = element - EL_ENVELOPE_1;
2495 envelope_len = getFile16BitBE(file);
2497 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2498 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2500 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2502 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2503 if (chunk_size_expected != chunk_size)
2505 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2506 return chunk_size_expected;
2509 for (i = 0; i < envelope_len; i++)
2510 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2515 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2517 int num_changed_custom_elements = getFile16BitBE(file);
2518 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2521 if (chunk_size_expected != chunk_size)
2523 ReadUnusedBytesFromFile(file, chunk_size - 2);
2524 return chunk_size_expected;
2527 for (i = 0; i < num_changed_custom_elements; i++)
2529 int element = getMappedElement(getFile16BitBE(file));
2530 int properties = getFile32BitBE(file);
2532 if (IS_CUSTOM_ELEMENT(element))
2533 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2535 Error(ERR_WARN, "invalid custom element number %d", element);
2537 /* older game versions that wrote level files with CUS1 chunks used
2538 different default push delay values (not yet stored in level file) */
2539 element_info[element].push_delay_fixed = 2;
2540 element_info[element].push_delay_random = 8;
2546 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2548 int num_changed_custom_elements = getFile16BitBE(file);
2549 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2552 if (chunk_size_expected != chunk_size)
2554 ReadUnusedBytesFromFile(file, chunk_size - 2);
2555 return chunk_size_expected;
2558 for (i = 0; i < num_changed_custom_elements; i++)
2560 int element = getMappedElement(getFile16BitBE(file));
2561 int custom_target_element = getMappedElement(getFile16BitBE(file));
2563 if (IS_CUSTOM_ELEMENT(element))
2564 element_info[element].change->target_element = custom_target_element;
2566 Error(ERR_WARN, "invalid custom element number %d", element);
2572 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2574 int num_changed_custom_elements = getFile16BitBE(file);
2575 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2578 if (chunk_size_expected != chunk_size)
2580 ReadUnusedBytesFromFile(file, chunk_size - 2);
2581 return chunk_size_expected;
2584 for (i = 0; i < num_changed_custom_elements; i++)
2586 int element = getMappedElement(getFile16BitBE(file));
2587 struct ElementInfo *ei = &element_info[element];
2588 unsigned int event_bits;
2590 if (!IS_CUSTOM_ELEMENT(element))
2592 Error(ERR_WARN, "invalid custom element number %d", element);
2594 element = EL_INTERNAL_DUMMY;
2597 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2598 ei->description[j] = getFile8Bit(file);
2599 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2601 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2603 /* some free bytes for future properties and padding */
2604 ReadUnusedBytesFromFile(file, 7);
2606 ei->use_gfx_element = getFile8Bit(file);
2607 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2609 ei->collect_score_initial = getFile8Bit(file);
2610 ei->collect_count_initial = getFile8Bit(file);
2612 ei->push_delay_fixed = getFile16BitBE(file);
2613 ei->push_delay_random = getFile16BitBE(file);
2614 ei->move_delay_fixed = getFile16BitBE(file);
2615 ei->move_delay_random = getFile16BitBE(file);
2617 ei->move_pattern = getFile16BitBE(file);
2618 ei->move_direction_initial = getFile8Bit(file);
2619 ei->move_stepsize = getFile8Bit(file);
2621 for (y = 0; y < 3; y++)
2622 for (x = 0; x < 3; x++)
2623 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2625 event_bits = getFile32BitBE(file);
2626 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2627 if (event_bits & (1 << j))
2628 ei->change->has_event[j] = TRUE;
2630 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2632 ei->change->delay_fixed = getFile16BitBE(file);
2633 ei->change->delay_random = getFile16BitBE(file);
2634 ei->change->delay_frames = getFile16BitBE(file);
2636 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2638 ei->change->explode = getFile8Bit(file);
2639 ei->change->use_target_content = getFile8Bit(file);
2640 ei->change->only_if_complete = getFile8Bit(file);
2641 ei->change->use_random_replace = getFile8Bit(file);
2643 ei->change->random_percentage = getFile8Bit(file);
2644 ei->change->replace_when = getFile8Bit(file);
2646 for (y = 0; y < 3; y++)
2647 for (x = 0; x < 3; x++)
2648 ei->change->target_content.e[x][y] =
2649 getMappedElement(getFile16BitBE(file));
2651 ei->slippery_type = getFile8Bit(file);
2653 /* some free bytes for future properties and padding */
2654 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2656 /* mark that this custom element has been modified */
2657 ei->modified_settings = TRUE;
2663 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2665 struct ElementInfo *ei;
2666 int chunk_size_expected;
2670 /* ---------- custom element base property values (96 bytes) ------------- */
2672 element = getMappedElement(getFile16BitBE(file));
2674 if (!IS_CUSTOM_ELEMENT(element))
2676 Error(ERR_WARN, "invalid custom element number %d", element);
2678 ReadUnusedBytesFromFile(file, chunk_size - 2);
2682 ei = &element_info[element];
2684 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2685 ei->description[i] = getFile8Bit(file);
2686 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2688 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2690 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2692 ei->num_change_pages = getFile8Bit(file);
2694 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2695 if (chunk_size_expected != chunk_size)
2697 ReadUnusedBytesFromFile(file, chunk_size - 43);
2698 return chunk_size_expected;
2701 ei->ce_value_fixed_initial = getFile16BitBE(file);
2702 ei->ce_value_random_initial = getFile16BitBE(file);
2703 ei->use_last_ce_value = getFile8Bit(file);
2705 ei->use_gfx_element = getFile8Bit(file);
2706 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2708 ei->collect_score_initial = getFile8Bit(file);
2709 ei->collect_count_initial = getFile8Bit(file);
2711 ei->drop_delay_fixed = getFile8Bit(file);
2712 ei->push_delay_fixed = getFile8Bit(file);
2713 ei->drop_delay_random = getFile8Bit(file);
2714 ei->push_delay_random = getFile8Bit(file);
2715 ei->move_delay_fixed = getFile16BitBE(file);
2716 ei->move_delay_random = getFile16BitBE(file);
2718 /* bits 0 - 15 of "move_pattern" ... */
2719 ei->move_pattern = getFile16BitBE(file);
2720 ei->move_direction_initial = getFile8Bit(file);
2721 ei->move_stepsize = getFile8Bit(file);
2723 ei->slippery_type = getFile8Bit(file);
2725 for (y = 0; y < 3; y++)
2726 for (x = 0; x < 3; x++)
2727 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2729 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2730 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2731 ei->move_leave_type = getFile8Bit(file);
2733 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2734 ei->move_pattern |= (getFile16BitBE(file) << 16);
2736 ei->access_direction = getFile8Bit(file);
2738 ei->explosion_delay = getFile8Bit(file);
2739 ei->ignition_delay = getFile8Bit(file);
2740 ei->explosion_type = getFile8Bit(file);
2742 /* some free bytes for future custom property values and padding */
2743 ReadUnusedBytesFromFile(file, 1);
2745 /* ---------- change page property values (48 bytes) --------------------- */
2747 setElementChangePages(ei, ei->num_change_pages);
2749 for (i = 0; i < ei->num_change_pages; i++)
2751 struct ElementChangeInfo *change = &ei->change_page[i];
2752 unsigned int event_bits;
2754 /* always start with reliable default values */
2755 setElementChangeInfoToDefaults(change);
2757 /* bits 0 - 31 of "has_event[]" ... */
2758 event_bits = getFile32BitBE(file);
2759 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2760 if (event_bits & (1 << j))
2761 change->has_event[j] = TRUE;
2763 change->target_element = getMappedElement(getFile16BitBE(file));
2765 change->delay_fixed = getFile16BitBE(file);
2766 change->delay_random = getFile16BitBE(file);
2767 change->delay_frames = getFile16BitBE(file);
2769 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2771 change->explode = getFile8Bit(file);
2772 change->use_target_content = getFile8Bit(file);
2773 change->only_if_complete = getFile8Bit(file);
2774 change->use_random_replace = getFile8Bit(file);
2776 change->random_percentage = getFile8Bit(file);
2777 change->replace_when = getFile8Bit(file);
2779 for (y = 0; y < 3; y++)
2780 for (x = 0; x < 3; x++)
2781 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2783 change->can_change = getFile8Bit(file);
2785 change->trigger_side = getFile8Bit(file);
2787 change->trigger_player = getFile8Bit(file);
2788 change->trigger_page = getFile8Bit(file);
2790 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2791 CH_PAGE_ANY : (1 << change->trigger_page));
2793 change->has_action = getFile8Bit(file);
2794 change->action_type = getFile8Bit(file);
2795 change->action_mode = getFile8Bit(file);
2796 change->action_arg = getFile16BitBE(file);
2798 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2799 event_bits = getFile8Bit(file);
2800 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2801 if (event_bits & (1 << (j - 32)))
2802 change->has_event[j] = TRUE;
2805 /* mark this custom element as modified */
2806 ei->modified_settings = TRUE;
2811 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2813 struct ElementInfo *ei;
2814 struct ElementGroupInfo *group;
2818 element = getMappedElement(getFile16BitBE(file));
2820 if (!IS_GROUP_ELEMENT(element))
2822 Error(ERR_WARN, "invalid group element number %d", element);
2824 ReadUnusedBytesFromFile(file, chunk_size - 2);
2828 ei = &element_info[element];
2830 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2831 ei->description[i] = getFile8Bit(file);
2832 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2834 group = element_info[element].group;
2836 group->num_elements = getFile8Bit(file);
2838 ei->use_gfx_element = getFile8Bit(file);
2839 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2841 group->choice_mode = getFile8Bit(file);
2843 /* some free bytes for future values and padding */
2844 ReadUnusedBytesFromFile(file, 3);
2846 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2847 group->element[i] = getMappedElement(getFile16BitBE(file));
2849 /* mark this group element as modified */
2850 element_info[element].modified_settings = TRUE;
2855 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2856 int element, int real_element)
2858 int micro_chunk_size = 0;
2859 int conf_type = getFile8Bit(file);
2860 int byte_mask = conf_type & CONF_MASK_BYTES;
2861 boolean element_found = FALSE;
2864 micro_chunk_size += 1;
2866 if (byte_mask == CONF_MASK_MULTI_BYTES)
2868 int num_bytes = getFile16BitBE(file);
2869 byte *buffer = checked_malloc(num_bytes);
2871 ReadBytesFromFile(file, buffer, num_bytes);
2873 for (i = 0; conf[i].data_type != -1; i++)
2875 if (conf[i].element == element &&
2876 conf[i].conf_type == conf_type)
2878 int data_type = conf[i].data_type;
2879 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2880 int max_num_entities = conf[i].max_num_entities;
2882 if (num_entities > max_num_entities)
2885 "truncating number of entities for element %d from %d to %d",
2886 element, num_entities, max_num_entities);
2888 num_entities = max_num_entities;
2891 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
2892 data_type == TYPE_CONTENT_LIST))
2894 /* for element and content lists, zero entities are not allowed */
2895 Error(ERR_WARN, "found empty list of entities for element %d",
2898 /* do not set "num_entities" here to prevent reading behind buffer */
2900 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
2904 *(int *)(conf[i].num_entities) = num_entities;
2907 element_found = TRUE;
2909 if (data_type == TYPE_STRING)
2911 char *string = (char *)(conf[i].value);
2914 for (j = 0; j < max_num_entities; j++)
2915 string[j] = (j < num_entities ? buffer[j] : '\0');
2917 else if (data_type == TYPE_ELEMENT_LIST)
2919 int *element_array = (int *)(conf[i].value);
2922 for (j = 0; j < num_entities; j++)
2924 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
2926 else if (data_type == TYPE_CONTENT_LIST)
2928 struct Content *content= (struct Content *)(conf[i].value);
2931 for (c = 0; c < num_entities; c++)
2932 for (y = 0; y < 3; y++)
2933 for (x = 0; x < 3; x++)
2934 content[c].e[x][y] =
2935 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
2938 element_found = FALSE;
2944 checked_free(buffer);
2946 micro_chunk_size += 2 + num_bytes;
2948 else /* constant size configuration data (1, 2 or 4 bytes) */
2950 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
2951 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
2952 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
2954 for (i = 0; conf[i].data_type != -1; i++)
2956 if (conf[i].element == element &&
2957 conf[i].conf_type == conf_type)
2959 int data_type = conf[i].data_type;
2961 if (data_type == TYPE_ELEMENT)
2962 value = getMappedElement(value);
2964 if (data_type == TYPE_BOOLEAN)
2965 *(boolean *)(conf[i].value) = value;
2967 *(int *) (conf[i].value) = value;
2969 element_found = TRUE;
2975 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
2980 char *error_conf_chunk_bytes =
2981 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
2982 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
2983 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
2984 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
2985 int error_element = real_element;
2987 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
2988 error_conf_chunk_bytes, error_conf_chunk_token,
2989 error_element, EL_NAME(error_element));
2992 return micro_chunk_size;
2995 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
2997 int real_chunk_size = 0;
2999 li = *level; /* copy level data into temporary buffer */
3001 while (!checkEndOfFile(file))
3003 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3005 if (real_chunk_size >= chunk_size)
3009 *level = li; /* copy temporary buffer back to level data */
3011 return real_chunk_size;
3014 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3016 int real_chunk_size = 0;
3018 li = *level; /* copy level data into temporary buffer */
3020 while (!checkEndOfFile(file))
3022 int element = getMappedElement(getFile16BitBE(file));
3024 real_chunk_size += 2;
3025 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3027 if (real_chunk_size >= chunk_size)
3031 *level = li; /* copy temporary buffer back to level data */
3033 return real_chunk_size;
3036 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3038 int real_chunk_size = 0;
3040 li = *level; /* copy level data into temporary buffer */
3042 while (!checkEndOfFile(file))
3044 int element = getMappedElement(getFile16BitBE(file));
3046 real_chunk_size += 2;
3047 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3049 if (real_chunk_size >= chunk_size)
3053 *level = li; /* copy temporary buffer back to level data */
3055 return real_chunk_size;
3058 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3060 int element = getMappedElement(getFile16BitBE(file));
3061 int envelope_nr = element - EL_ENVELOPE_1;
3062 int real_chunk_size = 2;
3064 xx_envelope = level->envelope[envelope_nr]; /* copy into temporary buffer */
3066 while (!checkEndOfFile(file))
3068 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3071 if (real_chunk_size >= chunk_size)
3075 level->envelope[envelope_nr] = xx_envelope; /* copy from temporary buffer */
3077 return real_chunk_size;
3080 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3082 int element = getMappedElement(getFile16BitBE(file));
3083 int real_chunk_size = 2;
3084 struct ElementInfo *ei = &element_info[element];
3087 xx_ei = *ei; /* copy element data into temporary buffer */
3089 xx_ei.num_change_pages = -1;
3091 while (!checkEndOfFile(file))
3093 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3095 if (xx_ei.num_change_pages != -1)
3098 if (real_chunk_size >= chunk_size)
3104 if (ei->num_change_pages == -1)
3106 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3109 ei->num_change_pages = 1;
3111 setElementChangePages(ei, 1);
3112 setElementChangeInfoToDefaults(ei->change);
3114 return real_chunk_size;
3117 /* initialize number of change pages stored for this custom element */
3118 setElementChangePages(ei, ei->num_change_pages);
3119 for (i = 0; i < ei->num_change_pages; i++)
3120 setElementChangeInfoToDefaults(&ei->change_page[i]);
3122 /* start with reading properties for the first change page */
3123 xx_current_change_page = 0;
3125 while (!checkEndOfFile(file))
3127 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3129 xx_change = *change; /* copy change data into temporary buffer */
3131 resetEventBits(); /* reset bits; change page might have changed */
3133 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3136 *change = xx_change;
3138 setEventFlagsFromEventBits(change);
3140 if (real_chunk_size >= chunk_size)
3144 return real_chunk_size;
3147 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3149 int element = getMappedElement(getFile16BitBE(file));
3150 int real_chunk_size = 2;
3151 struct ElementInfo *ei = &element_info[element];
3152 struct ElementGroupInfo *group = ei->group;
3154 xx_ei = *ei; /* copy element data into temporary buffer */
3155 xx_group = *group; /* copy group data into temporary buffer */
3157 while (!checkEndOfFile(file))
3159 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3162 if (real_chunk_size >= chunk_size)
3169 return real_chunk_size;
3172 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3173 struct LevelFileInfo *level_file_info,
3174 boolean level_info_only)
3176 char *filename = level_file_info->filename;
3177 char cookie[MAX_LINE_LEN];
3178 char chunk_name[CHUNK_ID_LEN + 1];
3182 if (!(file = openFile(filename, MODE_READ)))
3184 level->no_valid_file = TRUE;
3186 if (!level_info_only)
3187 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3192 getFileChunkBE(file, chunk_name, NULL);
3193 if (strEqual(chunk_name, "RND1"))
3195 getFile32BitBE(file); /* not used */
3197 getFileChunkBE(file, chunk_name, NULL);
3198 if (!strEqual(chunk_name, "CAVE"))
3200 level->no_valid_file = TRUE;
3202 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3209 else /* check for pre-2.0 file format with cookie string */
3211 strcpy(cookie, chunk_name);
3212 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3214 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3215 cookie[strlen(cookie) - 1] = '\0';
3217 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3219 level->no_valid_file = TRUE;
3221 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3228 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3230 level->no_valid_file = TRUE;
3232 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3239 /* pre-2.0 level files have no game version, so use file version here */
3240 level->game_version = level->file_version;
3243 if (level->file_version < FILE_VERSION_1_2)
3245 /* level files from versions before 1.2.0 without chunk structure */
3246 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3247 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3255 int (*loader)(File *, int, struct LevelInfo *);
3259 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3260 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3261 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3262 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3263 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3264 { "INFO", -1, LoadLevel_INFO },
3265 { "BODY", -1, LoadLevel_BODY },
3266 { "CONT", -1, LoadLevel_CONT },
3267 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3268 { "CNT3", -1, LoadLevel_CNT3 },
3269 { "CUS1", -1, LoadLevel_CUS1 },
3270 { "CUS2", -1, LoadLevel_CUS2 },
3271 { "CUS3", -1, LoadLevel_CUS3 },
3272 { "CUS4", -1, LoadLevel_CUS4 },
3273 { "GRP1", -1, LoadLevel_GRP1 },
3274 { "CONF", -1, LoadLevel_CONF },
3275 { "ELEM", -1, LoadLevel_ELEM },
3276 { "NOTE", -1, LoadLevel_NOTE },
3277 { "CUSX", -1, LoadLevel_CUSX },
3278 { "GRPX", -1, LoadLevel_GRPX },
3283 while (getFileChunkBE(file, chunk_name, &chunk_size))
3287 while (chunk_info[i].name != NULL &&
3288 !strEqual(chunk_name, chunk_info[i].name))
3291 if (chunk_info[i].name == NULL)
3293 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3294 chunk_name, filename);
3295 ReadUnusedBytesFromFile(file, chunk_size);
3297 else if (chunk_info[i].size != -1 &&
3298 chunk_info[i].size != chunk_size)
3300 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3301 chunk_size, chunk_name, filename);
3302 ReadUnusedBytesFromFile(file, chunk_size);
3306 /* call function to load this level chunk */
3307 int chunk_size_expected =
3308 (chunk_info[i].loader)(file, chunk_size, level);
3310 /* the size of some chunks cannot be checked before reading other
3311 chunks first (like "HEAD" and "BODY") that contain some header
3312 information, so check them here */
3313 if (chunk_size_expected != chunk_size)
3315 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3316 chunk_size, chunk_name, filename);
3326 /* ------------------------------------------------------------------------- */
3327 /* functions for loading EM level */
3328 /* ------------------------------------------------------------------------- */
3330 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3332 static int ball_xy[8][2] =
3343 struct LevelInfo_EM *level_em = level->native_em_level;
3344 struct LEVEL *lev = level_em->lev;
3345 struct PLAYER **ply = level_em->ply;
3348 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3349 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3351 lev->time_seconds = level->time;
3352 lev->required_initial = level->gems_needed;
3354 lev->emerald_score = level->score[SC_EMERALD];
3355 lev->diamond_score = level->score[SC_DIAMOND];
3356 lev->alien_score = level->score[SC_ROBOT];
3357 lev->tank_score = level->score[SC_SPACESHIP];
3358 lev->bug_score = level->score[SC_BUG];
3359 lev->eater_score = level->score[SC_YAMYAM];
3360 lev->nut_score = level->score[SC_NUT];
3361 lev->dynamite_score = level->score[SC_DYNAMITE];
3362 lev->key_score = level->score[SC_KEY];
3363 lev->exit_score = level->score[SC_TIME_BONUS];
3365 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3366 for (y = 0; y < 3; y++)
3367 for (x = 0; x < 3; x++)
3368 lev->eater_array[i][y * 3 + x] =
3369 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3371 lev->amoeba_time = level->amoeba_speed;
3372 lev->wonderwall_time_initial = level->time_magic_wall;
3373 lev->wheel_time = level->time_wheel;
3375 lev->android_move_time = level->android_move_time;
3376 lev->android_clone_time = level->android_clone_time;
3377 lev->ball_random = level->ball_random;
3378 lev->ball_state_initial = level->ball_state_initial;
3379 lev->ball_time = level->ball_time;
3380 lev->num_ball_arrays = level->num_ball_contents;
3382 lev->lenses_score = level->lenses_score;
3383 lev->magnify_score = level->magnify_score;
3384 lev->slurp_score = level->slurp_score;
3386 lev->lenses_time = level->lenses_time;
3387 lev->magnify_time = level->magnify_time;
3389 lev->wind_direction_initial =
3390 map_direction_RND_to_EM(level->wind_direction_initial);
3391 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3392 lev->wind_time : 0);
3394 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3395 for (j = 0; j < 8; j++)
3396 lev->ball_array[i][j] =
3397 map_element_RND_to_EM(level->
3398 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3400 map_android_clone_elements_RND_to_EM(level);
3402 /* first fill the complete playfield with the default border element */
3403 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3404 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3405 level_em->cave[x][y] = ZBORDER;
3407 if (BorderElement == EL_STEELWALL)
3409 for (y = 0; y < lev->height + 2; y++)
3410 for (x = 0; x < lev->width + 2; x++)
3411 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3414 /* then copy the real level contents from level file into the playfield */
3415 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3417 int new_element = map_element_RND_to_EM(level->field[x][y]);
3418 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3419 int xx = x + 1 + offset;
3420 int yy = y + 1 + offset;
3422 if (level->field[x][y] == EL_AMOEBA_DEAD)
3423 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3425 level_em->cave[xx][yy] = new_element;
3428 for (i = 0; i < MAX_PLAYERS; i++)
3430 ply[i]->x_initial = 0;
3431 ply[i]->y_initial = 0;
3434 /* initialize player positions and delete players from the playfield */
3435 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3437 if (ELEM_IS_PLAYER(level->field[x][y]))
3439 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3440 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3441 int xx = x + 1 + offset;
3442 int yy = y + 1 + offset;
3444 ply[player_nr]->x_initial = xx;
3445 ply[player_nr]->y_initial = yy;
3447 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3451 if (BorderElement == EL_STEELWALL)
3458 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3460 static int ball_xy[8][2] =
3471 struct LevelInfo_EM *level_em = level->native_em_level;
3472 struct LEVEL *lev = level_em->lev;
3473 struct PLAYER **ply = level_em->ply;
3476 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3477 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3479 level->time = lev->time_seconds;
3480 level->gems_needed = lev->required_initial;
3482 sprintf(level->name, "Level %d", level->file_info.nr);
3484 level->score[SC_EMERALD] = lev->emerald_score;
3485 level->score[SC_DIAMOND] = lev->diamond_score;
3486 level->score[SC_ROBOT] = lev->alien_score;
3487 level->score[SC_SPACESHIP] = lev->tank_score;
3488 level->score[SC_BUG] = lev->bug_score;
3489 level->score[SC_YAMYAM] = lev->eater_score;
3490 level->score[SC_NUT] = lev->nut_score;
3491 level->score[SC_DYNAMITE] = lev->dynamite_score;
3492 level->score[SC_KEY] = lev->key_score;
3493 level->score[SC_TIME_BONUS] = lev->exit_score;
3495 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3497 for (i = 0; i < level->num_yamyam_contents; i++)
3498 for (y = 0; y < 3; y++)
3499 for (x = 0; x < 3; x++)
3500 level->yamyam_content[i].e[x][y] =
3501 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3503 level->amoeba_speed = lev->amoeba_time;
3504 level->time_magic_wall = lev->wonderwall_time_initial;
3505 level->time_wheel = lev->wheel_time;
3507 level->android_move_time = lev->android_move_time;
3508 level->android_clone_time = lev->android_clone_time;
3509 level->ball_random = lev->ball_random;
3510 level->ball_state_initial = lev->ball_state_initial;
3511 level->ball_time = lev->ball_time;
3512 level->num_ball_contents = lev->num_ball_arrays;
3514 level->lenses_score = lev->lenses_score;
3515 level->magnify_score = lev->magnify_score;
3516 level->slurp_score = lev->slurp_score;
3518 level->lenses_time = lev->lenses_time;
3519 level->magnify_time = lev->magnify_time;
3521 level->wind_direction_initial =
3522 map_direction_EM_to_RND(lev->wind_direction_initial);
3524 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3525 for (j = 0; j < 8; j++)
3526 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3527 map_element_EM_to_RND(lev->ball_array[i][j]);
3529 map_android_clone_elements_EM_to_RND(level);
3531 /* convert the playfield (some elements need special treatment) */
3532 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3534 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3536 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3537 new_element = EL_AMOEBA_DEAD;
3539 level->field[x][y] = new_element;
3542 for (i = 0; i < MAX_PLAYERS; i++)
3544 /* in case of all players set to the same field, use the first player */
3545 int nr = MAX_PLAYERS - i - 1;
3546 int jx = ply[nr]->x_initial - 1;
3547 int jy = ply[nr]->y_initial - 1;
3549 if (jx != -1 && jy != -1)
3550 level->field[jx][jy] = EL_PLAYER_1 + nr;
3555 /* ------------------------------------------------------------------------- */
3556 /* functions for loading SP level */
3557 /* ------------------------------------------------------------------------- */
3559 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3561 struct LevelInfo_SP *level_sp = level->native_sp_level;
3562 LevelInfoType *header = &level_sp->header;
3565 level_sp->width = level->fieldx;
3566 level_sp->height = level->fieldy;
3568 for (x = 0; x < level->fieldx; x++)
3569 for (y = 0; y < level->fieldy; y++)
3570 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3572 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3574 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3575 header->LevelTitle[i] = level->name[i];
3576 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3578 header->InfotronsNeeded = level->gems_needed;
3580 header->SpecialPortCount = 0;
3582 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3584 boolean gravity_port_found = FALSE;
3585 boolean gravity_port_valid = FALSE;
3586 int gravity_port_flag;
3587 int gravity_port_base_element;
3588 int element = level->field[x][y];
3590 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3591 element <= EL_SP_GRAVITY_ON_PORT_UP)
3593 gravity_port_found = TRUE;
3594 gravity_port_valid = TRUE;
3595 gravity_port_flag = 1;
3596 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3598 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3599 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3601 gravity_port_found = TRUE;
3602 gravity_port_valid = TRUE;
3603 gravity_port_flag = 0;
3604 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3606 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3607 element <= EL_SP_GRAVITY_PORT_UP)
3609 /* change R'n'D style gravity inverting special port to normal port
3610 (there are no gravity inverting ports in native Supaplex engine) */
3612 gravity_port_found = TRUE;
3613 gravity_port_valid = FALSE;
3614 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3617 if (gravity_port_found)
3619 if (gravity_port_valid &&
3620 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3622 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3624 port->PortLocation = (y * level->fieldx + x) * 2;
3625 port->Gravity = gravity_port_flag;
3627 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3629 header->SpecialPortCount++;
3633 /* change special gravity port to normal port */
3635 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3638 level_sp->playfield[x][y] = element - EL_SP_START;
3643 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3645 struct LevelInfo_SP *level_sp = level->native_sp_level;
3646 LevelInfoType *header = &level_sp->header;
3649 level->fieldx = level_sp->width;
3650 level->fieldy = level_sp->height;
3652 for (x = 0; x < level->fieldx; x++)
3654 for (y = 0; y < level->fieldy; y++)
3656 int element_old = level_sp->playfield[x][y];
3657 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3659 if (element_new == EL_UNKNOWN)
3660 Error(ERR_WARN, "invalid element %d at position %d, %d",
3663 level->field[x][y] = element_new;
3667 for (i = 0; i < MAX_PLAYERS; i++)
3668 level->initial_player_gravity[i] =
3669 (header->InitialGravity == 1 ? TRUE : FALSE);
3671 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3672 level->name[i] = header->LevelTitle[i];
3673 level->name[SP_LEVEL_NAME_LEN] = '\0';
3675 level->gems_needed = header->InfotronsNeeded;
3677 for (i = 0; i < header->SpecialPortCount; i++)
3679 SpecialPortType *port = &header->SpecialPort[i];
3680 int port_location = port->PortLocation;
3681 int gravity = port->Gravity;
3682 int port_x, port_y, port_element;
3684 port_x = (port_location / 2) % level->fieldx;
3685 port_y = (port_location / 2) / level->fieldx;
3687 if (port_x < 0 || port_x >= level->fieldx ||
3688 port_y < 0 || port_y >= level->fieldy)
3690 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3696 port_element = level->field[port_x][port_y];
3698 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3699 port_element > EL_SP_GRAVITY_PORT_UP)
3701 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3706 /* change previous (wrong) gravity inverting special port to either
3707 gravity enabling special port or gravity disabling special port */
3708 level->field[port_x][port_y] +=
3709 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3710 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3713 /* change special gravity ports without database entries to normal ports */
3714 for (x = 0; x < level->fieldx; x++)
3715 for (y = 0; y < level->fieldy; y++)
3716 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3717 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3718 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3720 level->time = 0; /* no time limit */
3721 level->amoeba_speed = 0;
3722 level->time_magic_wall = 0;
3723 level->time_wheel = 0;
3724 level->amoeba_content = EL_EMPTY;
3727 /* original Supaplex does not use score values -- use default values */
3729 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3730 level->score[i] = 0;
3733 /* there are no yamyams in supaplex levels */
3734 for (i = 0; i < level->num_yamyam_contents; i++)
3735 for (x = 0; x < 3; x++)
3736 for (y = 0; y < 3; y++)
3737 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3740 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3742 struct LevelInfo_SP *level_sp = level->native_sp_level;
3743 struct DemoInfo_SP *demo = &level_sp->demo;
3746 /* always start with reliable default values */
3747 demo->is_available = FALSE;
3750 if (TAPE_IS_EMPTY(tape))
3753 demo->level_nr = tape.level_nr; /* (currently not used) */
3755 level_sp->header.DemoRandomSeed = tape.random_seed;
3758 for (i = 0; i < tape.length; i++)
3760 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3761 int demo_repeat = tape.pos[i].delay;
3763 for (j = 0; j < demo_repeat / 16; j++)
3764 demo->data[demo->length++] = 0xf0 | demo_action;
3766 if (demo_repeat % 16)
3767 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3770 demo->data[demo->length++] = 0xff;
3772 demo->is_available = TRUE;
3775 static void setTapeInfoToDefaults();
3777 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3779 struct LevelInfo_SP *level_sp = level->native_sp_level;
3780 struct DemoInfo_SP *demo = &level_sp->demo;
3781 char *filename = level->file_info.filename;
3784 /* always start with reliable default values */
3785 setTapeInfoToDefaults();
3787 if (!demo->is_available)
3790 tape.level_nr = demo->level_nr; /* (currently not used) */
3791 tape.length = demo->length - 1; /* without "end of demo" byte */
3792 tape.random_seed = level_sp->header.DemoRandomSeed;
3794 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3796 for (i = 0; i < demo->length - 1; i++)
3798 int demo_action = demo->data[i] & 0x0f;
3799 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3801 tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
3802 tape.pos[i].delay = demo_repeat + 1;
3805 tape.length_frames = GetTapeLengthFrames();
3806 tape.length_seconds = GetTapeLengthSeconds();
3810 /* ------------------------------------------------------------------------- */
3811 /* functions for loading DC level */
3812 /* ------------------------------------------------------------------------- */
3814 #define DC_LEVEL_HEADER_SIZE 344
3816 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
3818 static int last_data_encoded;
3822 int diff_hi, diff_lo;
3823 int data_hi, data_lo;
3824 unsigned short data_decoded;
3828 last_data_encoded = 0;
3835 diff = data_encoded - last_data_encoded;
3836 diff_hi = diff & ~0xff;
3837 diff_lo = diff & 0xff;
3841 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
3842 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
3843 data_hi = data_hi & 0xff00;
3845 data_decoded = data_hi | data_lo;
3847 last_data_encoded = data_encoded;
3849 offset1 = (offset1 + 1) % 31;
3850 offset2 = offset2 & 0xff;
3852 return data_decoded;
3855 int getMappedElement_DC(int element)
3863 /* 0x0117 - 0x036e: (?) */
3866 /* 0x042d - 0x0684: (?) */
3882 element = EL_CRYSTAL;
3885 case 0x0e77: /* quicksand (boulder) */
3886 element = EL_QUICKSAND_FAST_FULL;
3889 case 0x0e99: /* slow quicksand (boulder) */
3890 element = EL_QUICKSAND_FULL;
3894 element = EL_EM_EXIT_OPEN;
3898 element = EL_EM_EXIT_CLOSED;
3902 element = EL_EM_STEEL_EXIT_OPEN;
3906 element = EL_EM_STEEL_EXIT_CLOSED;
3909 case 0x0f4f: /* dynamite (lit 1) */
3910 element = EL_EM_DYNAMITE_ACTIVE;
3913 case 0x0f57: /* dynamite (lit 2) */
3914 element = EL_EM_DYNAMITE_ACTIVE;
3917 case 0x0f5f: /* dynamite (lit 3) */
3918 element = EL_EM_DYNAMITE_ACTIVE;
3921 case 0x0f67: /* dynamite (lit 4) */
3922 element = EL_EM_DYNAMITE_ACTIVE;
3929 element = EL_AMOEBA_WET;
3933 element = EL_AMOEBA_DROP;
3937 element = EL_DC_MAGIC_WALL;
3941 element = EL_SPACESHIP_UP;
3945 element = EL_SPACESHIP_DOWN;
3949 element = EL_SPACESHIP_LEFT;
3953 element = EL_SPACESHIP_RIGHT;
3957 element = EL_BUG_UP;
3961 element = EL_BUG_DOWN;
3965 element = EL_BUG_LEFT;
3969 element = EL_BUG_RIGHT;
3973 element = EL_MOLE_UP;
3977 element = EL_MOLE_DOWN;
3981 element = EL_MOLE_LEFT;
3985 element = EL_MOLE_RIGHT;
3993 element = EL_YAMYAM;
3997 element = EL_SWITCHGATE_OPEN;
4001 element = EL_SWITCHGATE_CLOSED;
4005 element = EL_DC_SWITCHGATE_SWITCH_UP;
4009 element = EL_TIMEGATE_CLOSED;
4012 case 0x144c: /* conveyor belt switch (green) */
4013 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4016 case 0x144f: /* conveyor belt switch (red) */
4017 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4020 case 0x1452: /* conveyor belt switch (blue) */
4021 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4025 element = EL_CONVEYOR_BELT_3_MIDDLE;
4029 element = EL_CONVEYOR_BELT_3_LEFT;
4033 element = EL_CONVEYOR_BELT_3_RIGHT;
4037 element = EL_CONVEYOR_BELT_1_MIDDLE;
4041 element = EL_CONVEYOR_BELT_1_LEFT;
4045 element = EL_CONVEYOR_BELT_1_RIGHT;
4049 element = EL_CONVEYOR_BELT_4_MIDDLE;
4053 element = EL_CONVEYOR_BELT_4_LEFT;
4057 element = EL_CONVEYOR_BELT_4_RIGHT;
4061 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4065 element = EL_EXPANDABLE_WALL_VERTICAL;
4069 element = EL_EXPANDABLE_WALL_ANY;
4072 case 0x14ce: /* growing steel wall (left/right) */
4073 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4076 case 0x14df: /* growing steel wall (up/down) */
4077 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4080 case 0x14e8: /* growing steel wall (up/down/left/right) */
4081 element = EL_EXPANDABLE_STEELWALL_ANY;
4085 element = EL_SHIELD_DEADLY;
4089 element = EL_EXTRA_TIME;
4097 element = EL_EMPTY_SPACE;
4100 case 0x1578: /* quicksand (empty) */
4101 element = EL_QUICKSAND_FAST_EMPTY;
4104 case 0x1579: /* slow quicksand (empty) */
4105 element = EL_QUICKSAND_EMPTY;
4108 /* 0x157c - 0x158b: */
4111 /* 0x1590 - 0x159f: */
4112 /* EL_DC_LANDMINE */
4115 element = EL_EM_DYNAMITE;
4118 case 0x15a1: /* key (red) */
4119 element = EL_EM_KEY_1;
4122 case 0x15a2: /* key (yellow) */
4123 element = EL_EM_KEY_2;
4126 case 0x15a3: /* key (blue) */
4127 element = EL_EM_KEY_4;
4130 case 0x15a4: /* key (green) */
4131 element = EL_EM_KEY_3;
4134 case 0x15a5: /* key (white) */
4135 element = EL_DC_KEY_WHITE;
4139 element = EL_WALL_SLIPPERY;
4146 case 0x15a8: /* wall (not round) */
4150 case 0x15a9: /* (blue) */
4151 element = EL_CHAR_A;
4154 case 0x15aa: /* (blue) */
4155 element = EL_CHAR_B;
4158 case 0x15ab: /* (blue) */
4159 element = EL_CHAR_C;
4162 case 0x15ac: /* (blue) */
4163 element = EL_CHAR_D;
4166 case 0x15ad: /* (blue) */
4167 element = EL_CHAR_E;
4170 case 0x15ae: /* (blue) */
4171 element = EL_CHAR_F;
4174 case 0x15af: /* (blue) */
4175 element = EL_CHAR_G;
4178 case 0x15b0: /* (blue) */
4179 element = EL_CHAR_H;
4182 case 0x15b1: /* (blue) */
4183 element = EL_CHAR_I;
4186 case 0x15b2: /* (blue) */
4187 element = EL_CHAR_J;
4190 case 0x15b3: /* (blue) */
4191 element = EL_CHAR_K;
4194 case 0x15b4: /* (blue) */
4195 element = EL_CHAR_L;
4198 case 0x15b5: /* (blue) */
4199 element = EL_CHAR_M;
4202 case 0x15b6: /* (blue) */
4203 element = EL_CHAR_N;
4206 case 0x15b7: /* (blue) */
4207 element = EL_CHAR_O;
4210 case 0x15b8: /* (blue) */
4211 element = EL_CHAR_P;
4214 case 0x15b9: /* (blue) */
4215 element = EL_CHAR_Q;
4218 case 0x15ba: /* (blue) */
4219 element = EL_CHAR_R;
4222 case 0x15bb: /* (blue) */
4223 element = EL_CHAR_S;
4226 case 0x15bc: /* (blue) */
4227 element = EL_CHAR_T;
4230 case 0x15bd: /* (blue) */
4231 element = EL_CHAR_U;
4234 case 0x15be: /* (blue) */
4235 element = EL_CHAR_V;
4238 case 0x15bf: /* (blue) */
4239 element = EL_CHAR_W;
4242 case 0x15c0: /* (blue) */
4243 element = EL_CHAR_X;
4246 case 0x15c1: /* (blue) */
4247 element = EL_CHAR_Y;
4250 case 0x15c2: /* (blue) */
4251 element = EL_CHAR_Z;
4254 case 0x15c3: /* (blue) */
4255 element = EL_CHAR_AUMLAUT;
4258 case 0x15c4: /* (blue) */
4259 element = EL_CHAR_OUMLAUT;
4262 case 0x15c5: /* (blue) */
4263 element = EL_CHAR_UUMLAUT;
4266 case 0x15c6: /* (blue) */
4267 element = EL_CHAR_0;
4270 case 0x15c7: /* (blue) */
4271 element = EL_CHAR_1;
4274 case 0x15c8: /* (blue) */
4275 element = EL_CHAR_2;
4278 case 0x15c9: /* (blue) */
4279 element = EL_CHAR_3;
4282 case 0x15ca: /* (blue) */
4283 element = EL_CHAR_4;
4286 case 0x15cb: /* (blue) */
4287 element = EL_CHAR_5;
4290 case 0x15cc: /* (blue) */
4291 element = EL_CHAR_6;
4294 case 0x15cd: /* (blue) */
4295 element = EL_CHAR_7;
4298 case 0x15ce: /* (blue) */
4299 element = EL_CHAR_8;
4302 case 0x15cf: /* (blue) */
4303 element = EL_CHAR_9;
4306 case 0x15d0: /* (blue) */
4307 element = EL_CHAR_PERIOD;
4310 case 0x15d1: /* (blue) */
4311 element = EL_CHAR_EXCLAM;
4314 case 0x15d2: /* (blue) */
4315 element = EL_CHAR_COLON;
4318 case 0x15d3: /* (blue) */
4319 element = EL_CHAR_LESS;
4322 case 0x15d4: /* (blue) */
4323 element = EL_CHAR_GREATER;
4326 case 0x15d5: /* (blue) */
4327 element = EL_CHAR_QUESTION;
4330 case 0x15d6: /* (blue) */
4331 element = EL_CHAR_COPYRIGHT;
4334 case 0x15d7: /* (blue) */
4335 element = EL_CHAR_UP;
4338 case 0x15d8: /* (blue) */
4339 element = EL_CHAR_DOWN;
4342 case 0x15d9: /* (blue) */
4343 element = EL_CHAR_BUTTON;
4346 case 0x15da: /* (blue) */
4347 element = EL_CHAR_PLUS;
4350 case 0x15db: /* (blue) */
4351 element = EL_CHAR_MINUS;
4354 case 0x15dc: /* (blue) */
4355 element = EL_CHAR_APOSTROPHE;
4358 case 0x15dd: /* (blue) */
4359 element = EL_CHAR_PARENLEFT;
4362 case 0x15de: /* (blue) */
4363 element = EL_CHAR_PARENRIGHT;
4366 case 0x15df: /* (green) */
4367 element = EL_CHAR_A;
4370 case 0x15e0: /* (green) */
4371 element = EL_CHAR_B;
4374 case 0x15e1: /* (green) */
4375 element = EL_CHAR_C;
4378 case 0x15e2: /* (green) */
4379 element = EL_CHAR_D;
4382 case 0x15e3: /* (green) */
4383 element = EL_CHAR_E;
4386 case 0x15e4: /* (green) */
4387 element = EL_CHAR_F;
4390 case 0x15e5: /* (green) */
4391 element = EL_CHAR_G;
4394 case 0x15e6: /* (green) */
4395 element = EL_CHAR_H;
4398 case 0x15e7: /* (green) */
4399 element = EL_CHAR_I;
4402 case 0x15e8: /* (green) */
4403 element = EL_CHAR_J;
4406 case 0x15e9: /* (green) */
4407 element = EL_CHAR_K;
4410 case 0x15ea: /* (green) */
4411 element = EL_CHAR_L;
4414 case 0x15eb: /* (green) */
4415 element = EL_CHAR_M;
4418 case 0x15ec: /* (green) */
4419 element = EL_CHAR_N;
4422 case 0x15ed: /* (green) */
4423 element = EL_CHAR_O;
4426 case 0x15ee: /* (green) */
4427 element = EL_CHAR_P;
4430 case 0x15ef: /* (green) */
4431 element = EL_CHAR_Q;
4434 case 0x15f0: /* (green) */
4435 element = EL_CHAR_R;
4438 case 0x15f1: /* (green) */
4439 element = EL_CHAR_S;
4442 case 0x15f2: /* (green) */
4443 element = EL_CHAR_T;
4446 case 0x15f3: /* (green) */
4447 element = EL_CHAR_U;
4450 case 0x15f4: /* (green) */
4451 element = EL_CHAR_V;
4454 case 0x15f5: /* (green) */
4455 element = EL_CHAR_W;
4458 case 0x15f6: /* (green) */
4459 element = EL_CHAR_X;
4462 case 0x15f7: /* (green) */
4463 element = EL_CHAR_Y;
4466 case 0x15f8: /* (green) */
4467 element = EL_CHAR_Z;
4470 case 0x15f9: /* (green) */
4471 element = EL_CHAR_AUMLAUT;
4474 case 0x15fa: /* (green) */
4475 element = EL_CHAR_OUMLAUT;
4478 case 0x15fb: /* (green) */
4479 element = EL_CHAR_UUMLAUT;
4482 case 0x15fc: /* (green) */
4483 element = EL_CHAR_0;
4486 case 0x15fd: /* (green) */
4487 element = EL_CHAR_1;
4490 case 0x15fe: /* (green) */
4491 element = EL_CHAR_2;
4494 case 0x15ff: /* (green) */
4495 element = EL_CHAR_3;
4498 case 0x1600: /* (green) */
4499 element = EL_CHAR_4;
4502 case 0x1601: /* (green) */
4503 element = EL_CHAR_5;
4506 case 0x1602: /* (green) */
4507 element = EL_CHAR_6;
4510 case 0x1603: /* (green) */
4511 element = EL_CHAR_7;
4514 case 0x1604: /* (green) */
4515 element = EL_CHAR_8;
4518 case 0x1605: /* (green) */
4519 element = EL_CHAR_9;
4522 case 0x1606: /* (green) */
4523 element = EL_CHAR_PERIOD;
4526 case 0x1607: /* (green) */
4527 element = EL_CHAR_EXCLAM;
4530 case 0x1608: /* (green) */
4531 element = EL_CHAR_COLON;
4534 case 0x1609: /* (green) */
4535 element = EL_CHAR_LESS;
4538 case 0x160a: /* (green) */
4539 element = EL_CHAR_GREATER;
4542 case 0x160b: /* (green) */
4543 element = EL_CHAR_QUESTION;
4546 case 0x160c: /* (green) */
4547 element = EL_CHAR_COPYRIGHT;
4550 case 0x160d: /* (green) */
4551 element = EL_CHAR_UP;
4554 case 0x160e: /* (green) */
4555 element = EL_CHAR_DOWN;
4558 case 0x160f: /* (green) */
4559 element = EL_CHAR_BUTTON;
4562 case 0x1610: /* (green) */
4563 element = EL_CHAR_PLUS;
4566 case 0x1611: /* (green) */
4567 element = EL_CHAR_MINUS;
4570 case 0x1612: /* (green) */
4571 element = EL_CHAR_APOSTROPHE;
4574 case 0x1613: /* (green) */
4575 element = EL_CHAR_PARENLEFT;
4578 case 0x1614: /* (green) */
4579 element = EL_CHAR_PARENRIGHT;
4582 case 0x1615: /* (blue steel) */
4583 element = EL_STEEL_CHAR_A;
4586 case 0x1616: /* (blue steel) */
4587 element = EL_STEEL_CHAR_B;
4590 case 0x1617: /* (blue steel) */
4591 element = EL_STEEL_CHAR_C;
4594 case 0x1618: /* (blue steel) */
4595 element = EL_STEEL_CHAR_D;
4598 case 0x1619: /* (blue steel) */
4599 element = EL_STEEL_CHAR_E;
4602 case 0x161a: /* (blue steel) */
4603 element = EL_STEEL_CHAR_F;
4606 case 0x161b: /* (blue steel) */
4607 element = EL_STEEL_CHAR_G;
4610 case 0x161c: /* (blue steel) */
4611 element = EL_STEEL_CHAR_H;
4614 case 0x161d: /* (blue steel) */
4615 element = EL_STEEL_CHAR_I;
4618 case 0x161e: /* (blue steel) */
4619 element = EL_STEEL_CHAR_J;
4622 case 0x161f: /* (blue steel) */
4623 element = EL_STEEL_CHAR_K;
4626 case 0x1620: /* (blue steel) */
4627 element = EL_STEEL_CHAR_L;
4630 case 0x1621: /* (blue steel) */
4631 element = EL_STEEL_CHAR_M;
4634 case 0x1622: /* (blue steel) */
4635 element = EL_STEEL_CHAR_N;
4638 case 0x1623: /* (blue steel) */
4639 element = EL_STEEL_CHAR_O;
4642 case 0x1624: /* (blue steel) */
4643 element = EL_STEEL_CHAR_P;
4646 case 0x1625: /* (blue steel) */
4647 element = EL_STEEL_CHAR_Q;
4650 case 0x1626: /* (blue steel) */
4651 element = EL_STEEL_CHAR_R;
4654 case 0x1627: /* (blue steel) */
4655 element = EL_STEEL_CHAR_S;
4658 case 0x1628: /* (blue steel) */
4659 element = EL_STEEL_CHAR_T;
4662 case 0x1629: /* (blue steel) */
4663 element = EL_STEEL_CHAR_U;
4666 case 0x162a: /* (blue steel) */
4667 element = EL_STEEL_CHAR_V;
4670 case 0x162b: /* (blue steel) */
4671 element = EL_STEEL_CHAR_W;
4674 case 0x162c: /* (blue steel) */
4675 element = EL_STEEL_CHAR_X;
4678 case 0x162d: /* (blue steel) */
4679 element = EL_STEEL_CHAR_Y;
4682 case 0x162e: /* (blue steel) */
4683 element = EL_STEEL_CHAR_Z;
4686 case 0x162f: /* (blue steel) */
4687 element = EL_STEEL_CHAR_AUMLAUT;
4690 case 0x1630: /* (blue steel) */
4691 element = EL_STEEL_CHAR_OUMLAUT;
4694 case 0x1631: /* (blue steel) */
4695 element = EL_STEEL_CHAR_UUMLAUT;
4698 case 0x1632: /* (blue steel) */
4699 element = EL_STEEL_CHAR_0;
4702 case 0x1633: /* (blue steel) */
4703 element = EL_STEEL_CHAR_1;
4706 case 0x1634: /* (blue steel) */
4707 element = EL_STEEL_CHAR_2;
4710 case 0x1635: /* (blue steel) */
4711 element = EL_STEEL_CHAR_3;
4714 case 0x1636: /* (blue steel) */
4715 element = EL_STEEL_CHAR_4;
4718 case 0x1637: /* (blue steel) */
4719 element = EL_STEEL_CHAR_5;
4722 case 0x1638: /* (blue steel) */
4723 element = EL_STEEL_CHAR_6;
4726 case 0x1639: /* (blue steel) */
4727 element = EL_STEEL_CHAR_7;
4730 case 0x163a: /* (blue steel) */
4731 element = EL_STEEL_CHAR_8;
4734 case 0x163b: /* (blue steel) */
4735 element = EL_STEEL_CHAR_9;
4738 case 0x163c: /* (blue steel) */
4739 element = EL_STEEL_CHAR_PERIOD;
4742 case 0x163d: /* (blue steel) */
4743 element = EL_STEEL_CHAR_EXCLAM;
4746 case 0x163e: /* (blue steel) */
4747 element = EL_STEEL_CHAR_COLON;
4750 case 0x163f: /* (blue steel) */
4751 element = EL_STEEL_CHAR_LESS;
4754 case 0x1640: /* (blue steel) */
4755 element = EL_STEEL_CHAR_GREATER;
4758 case 0x1641: /* (blue steel) */
4759 element = EL_STEEL_CHAR_QUESTION;
4762 case 0x1642: /* (blue steel) */
4763 element = EL_STEEL_CHAR_COPYRIGHT;
4766 case 0x1643: /* (blue steel) */
4767 element = EL_STEEL_CHAR_UP;
4770 case 0x1644: /* (blue steel) */
4771 element = EL_STEEL_CHAR_DOWN;
4774 case 0x1645: /* (blue steel) */
4775 element = EL_STEEL_CHAR_BUTTON;
4778 case 0x1646: /* (blue steel) */
4779 element = EL_STEEL_CHAR_PLUS;
4782 case 0x1647: /* (blue steel) */
4783 element = EL_STEEL_CHAR_MINUS;
4786 case 0x1648: /* (blue steel) */
4787 element = EL_STEEL_CHAR_APOSTROPHE;
4790 case 0x1649: /* (blue steel) */
4791 element = EL_STEEL_CHAR_PARENLEFT;
4794 case 0x164a: /* (blue steel) */
4795 element = EL_STEEL_CHAR_PARENRIGHT;
4798 case 0x164b: /* (green steel) */
4799 element = EL_STEEL_CHAR_A;
4802 case 0x164c: /* (green steel) */
4803 element = EL_STEEL_CHAR_B;
4806 case 0x164d: /* (green steel) */
4807 element = EL_STEEL_CHAR_C;
4810 case 0x164e: /* (green steel) */
4811 element = EL_STEEL_CHAR_D;
4814 case 0x164f: /* (green steel) */
4815 element = EL_STEEL_CHAR_E;
4818 case 0x1650: /* (green steel) */
4819 element = EL_STEEL_CHAR_F;
4822 case 0x1651: /* (green steel) */
4823 element = EL_STEEL_CHAR_G;
4826 case 0x1652: /* (green steel) */
4827 element = EL_STEEL_CHAR_H;
4830 case 0x1653: /* (green steel) */
4831 element = EL_STEEL_CHAR_I;
4834 case 0x1654: /* (green steel) */
4835 element = EL_STEEL_CHAR_J;
4838 case 0x1655: /* (green steel) */
4839 element = EL_STEEL_CHAR_K;
4842 case 0x1656: /* (green steel) */
4843 element = EL_STEEL_CHAR_L;
4846 case 0x1657: /* (green steel) */
4847 element = EL_STEEL_CHAR_M;
4850 case 0x1658: /* (green steel) */
4851 element = EL_STEEL_CHAR_N;
4854 case 0x1659: /* (green steel) */
4855 element = EL_STEEL_CHAR_O;
4858 case 0x165a: /* (green steel) */
4859 element = EL_STEEL_CHAR_P;
4862 case 0x165b: /* (green steel) */
4863 element = EL_STEEL_CHAR_Q;
4866 case 0x165c: /* (green steel) */
4867 element = EL_STEEL_CHAR_R;
4870 case 0x165d: /* (green steel) */
4871 element = EL_STEEL_CHAR_S;
4874 case 0x165e: /* (green steel) */
4875 element = EL_STEEL_CHAR_T;
4878 case 0x165f: /* (green steel) */
4879 element = EL_STEEL_CHAR_U;
4882 case 0x1660: /* (green steel) */
4883 element = EL_STEEL_CHAR_V;
4886 case 0x1661: /* (green steel) */
4887 element = EL_STEEL_CHAR_W;
4890 case 0x1662: /* (green steel) */
4891 element = EL_STEEL_CHAR_X;
4894 case 0x1663: /* (green steel) */
4895 element = EL_STEEL_CHAR_Y;
4898 case 0x1664: /* (green steel) */
4899 element = EL_STEEL_CHAR_Z;
4902 case 0x1665: /* (green steel) */
4903 element = EL_STEEL_CHAR_AUMLAUT;
4906 case 0x1666: /* (green steel) */
4907 element = EL_STEEL_CHAR_OUMLAUT;
4910 case 0x1667: /* (green steel) */
4911 element = EL_STEEL_CHAR_UUMLAUT;
4914 case 0x1668: /* (green steel) */
4915 element = EL_STEEL_CHAR_0;
4918 case 0x1669: /* (green steel) */
4919 element = EL_STEEL_CHAR_1;
4922 case 0x166a: /* (green steel) */
4923 element = EL_STEEL_CHAR_2;
4926 case 0x166b: /* (green steel) */
4927 element = EL_STEEL_CHAR_3;
4930 case 0x166c: /* (green steel) */
4931 element = EL_STEEL_CHAR_4;
4934 case 0x166d: /* (green steel) */
4935 element = EL_STEEL_CHAR_5;
4938 case 0x166e: /* (green steel) */
4939 element = EL_STEEL_CHAR_6;
4942 case 0x166f: /* (green steel) */
4943 element = EL_STEEL_CHAR_7;
4946 case 0x1670: /* (green steel) */
4947 element = EL_STEEL_CHAR_8;
4950 case 0x1671: /* (green steel) */
4951 element = EL_STEEL_CHAR_9;
4954 case 0x1672: /* (green steel) */
4955 element = EL_STEEL_CHAR_PERIOD;
4958 case 0x1673: /* (green steel) */
4959 element = EL_STEEL_CHAR_EXCLAM;
4962 case 0x1674: /* (green steel) */
4963 element = EL_STEEL_CHAR_COLON;
4966 case 0x1675: /* (green steel) */
4967 element = EL_STEEL_CHAR_LESS;
4970 case 0x1676: /* (green steel) */
4971 element = EL_STEEL_CHAR_GREATER;
4974 case 0x1677: /* (green steel) */
4975 element = EL_STEEL_CHAR_QUESTION;
4978 case 0x1678: /* (green steel) */
4979 element = EL_STEEL_CHAR_COPYRIGHT;
4982 case 0x1679: /* (green steel) */
4983 element = EL_STEEL_CHAR_UP;
4986 case 0x167a: /* (green steel) */
4987 element = EL_STEEL_CHAR_DOWN;
4990 case 0x167b: /* (green steel) */
4991 element = EL_STEEL_CHAR_BUTTON;
4994 case 0x167c: /* (green steel) */
4995 element = EL_STEEL_CHAR_PLUS;
4998 case 0x167d: /* (green steel) */
4999 element = EL_STEEL_CHAR_MINUS;
5002 case 0x167e: /* (green steel) */
5003 element = EL_STEEL_CHAR_APOSTROPHE;
5006 case 0x167f: /* (green steel) */
5007 element = EL_STEEL_CHAR_PARENLEFT;
5010 case 0x1680: /* (green steel) */
5011 element = EL_STEEL_CHAR_PARENRIGHT;
5014 case 0x1681: /* gate (red) */
5015 element = EL_EM_GATE_1;
5018 case 0x1682: /* secret gate (red) */
5019 element = EL_GATE_1_GRAY;
5022 case 0x1683: /* gate (yellow) */
5023 element = EL_EM_GATE_2;
5026 case 0x1684: /* secret gate (yellow) */
5027 element = EL_GATE_2_GRAY;
5030 case 0x1685: /* gate (blue) */
5031 element = EL_EM_GATE_4;
5034 case 0x1686: /* secret gate (blue) */
5035 element = EL_GATE_4_GRAY;
5038 case 0x1687: /* gate (green) */
5039 element = EL_EM_GATE_3;
5042 case 0x1688: /* secret gate (green) */
5043 element = EL_GATE_3_GRAY;
5046 case 0x1689: /* gate (white) */
5047 element = EL_DC_GATE_WHITE;
5050 case 0x168a: /* secret gate (white) */
5051 element = EL_DC_GATE_WHITE_GRAY;
5054 case 0x168b: /* secret gate (no key) */
5055 element = EL_DC_GATE_FAKE_GRAY;
5059 element = EL_ROBOT_WHEEL;
5063 element = EL_DC_TIMEGATE_SWITCH;
5067 element = EL_ACID_POOL_BOTTOM;
5071 element = EL_ACID_POOL_TOPLEFT;
5075 element = EL_ACID_POOL_TOPRIGHT;
5079 element = EL_ACID_POOL_BOTTOMLEFT;
5083 element = EL_ACID_POOL_BOTTOMRIGHT;
5087 element = EL_STEELWALL;
5091 element = EL_STEELWALL_SLIPPERY;
5094 case 0x1695: /* steel wall (not round) */
5095 element = EL_STEELWALL;
5098 case 0x1696: /* steel wall (left) */
5099 element = EL_DC_STEELWALL_1_LEFT;
5102 case 0x1697: /* steel wall (bottom) */
5103 element = EL_DC_STEELWALL_1_BOTTOM;
5106 case 0x1698: /* steel wall (right) */
5107 element = EL_DC_STEELWALL_1_RIGHT;
5110 case 0x1699: /* steel wall (top) */
5111 element = EL_DC_STEELWALL_1_TOP;
5114 case 0x169a: /* steel wall (left/bottom) */
5115 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5118 case 0x169b: /* steel wall (right/bottom) */
5119 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5122 case 0x169c: /* steel wall (right/top) */
5123 element = EL_DC_STEELWALL_1_TOPRIGHT;
5126 case 0x169d: /* steel wall (left/top) */
5127 element = EL_DC_STEELWALL_1_TOPLEFT;
5130 case 0x169e: /* steel wall (right/bottom small) */
5131 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5134 case 0x169f: /* steel wall (left/bottom small) */
5135 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5138 case 0x16a0: /* steel wall (right/top small) */
5139 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5142 case 0x16a1: /* steel wall (left/top small) */
5143 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5146 case 0x16a2: /* steel wall (left/right) */
5147 element = EL_DC_STEELWALL_1_VERTICAL;
5150 case 0x16a3: /* steel wall (top/bottom) */
5151 element = EL_DC_STEELWALL_1_HORIZONTAL;
5154 case 0x16a4: /* steel wall 2 (left end) */
5155 element = EL_DC_STEELWALL_2_LEFT;
5158 case 0x16a5: /* steel wall 2 (right end) */
5159 element = EL_DC_STEELWALL_2_RIGHT;
5162 case 0x16a6: /* steel wall 2 (top end) */
5163 element = EL_DC_STEELWALL_2_TOP;
5166 case 0x16a7: /* steel wall 2 (bottom end) */
5167 element = EL_DC_STEELWALL_2_BOTTOM;
5170 case 0x16a8: /* steel wall 2 (left/right) */
5171 element = EL_DC_STEELWALL_2_HORIZONTAL;
5174 case 0x16a9: /* steel wall 2 (up/down) */
5175 element = EL_DC_STEELWALL_2_VERTICAL;
5178 case 0x16aa: /* steel wall 2 (mid) */
5179 element = EL_DC_STEELWALL_2_MIDDLE;
5183 element = EL_SIGN_EXCLAMATION;
5187 element = EL_SIGN_RADIOACTIVITY;
5191 element = EL_SIGN_STOP;
5195 element = EL_SIGN_WHEELCHAIR;
5199 element = EL_SIGN_PARKING;
5203 element = EL_SIGN_NO_ENTRY;
5207 element = EL_SIGN_HEART;
5211 element = EL_SIGN_GIVE_WAY;
5215 element = EL_SIGN_ENTRY_FORBIDDEN;
5219 element = EL_SIGN_EMERGENCY_EXIT;
5223 element = EL_SIGN_YIN_YANG;
5227 element = EL_WALL_EMERALD;
5231 element = EL_WALL_DIAMOND;
5235 element = EL_WALL_PEARL;
5239 element = EL_WALL_CRYSTAL;
5243 element = EL_INVISIBLE_WALL;
5247 element = EL_INVISIBLE_STEELWALL;
5250 /* 0x16bc - 0x16cb: */
5251 /* EL_INVISIBLE_SAND */
5254 element = EL_LIGHT_SWITCH;
5258 element = EL_ENVELOPE_1;
5262 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5263 element = EL_DIAMOND;
5264 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5265 element = EL_EMERALD;
5266 else if (element >= 0x157c && element <= 0x158b)
5268 else if (element >= 0x1590 && element <= 0x159f)
5269 element = EL_DC_LANDMINE;
5270 else if (element >= 0x16bc && element <= 0x16cb)
5271 element = EL_INVISIBLE_SAND;
5274 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5275 element = EL_UNKNOWN;
5280 return getMappedElement(element);
5283 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5286 byte header[DC_LEVEL_HEADER_SIZE];
5288 int envelope_header_pos = 62;
5289 int envelope_content_pos = 94;
5290 int level_name_pos = 251;
5291 int level_author_pos = 292;
5292 int envelope_header_len;
5293 int envelope_content_len;
5295 int level_author_len;
5297 int num_yamyam_contents;
5300 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5302 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5304 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5306 header[i * 2 + 0] = header_word >> 8;
5307 header[i * 2 + 1] = header_word & 0xff;
5310 /* read some values from level header to check level decoding integrity */
5311 fieldx = header[6] | (header[7] << 8);
5312 fieldy = header[8] | (header[9] << 8);
5313 num_yamyam_contents = header[60] | (header[61] << 8);
5315 /* do some simple sanity checks to ensure that level was correctly decoded */
5316 if (fieldx < 1 || fieldx > 256 ||
5317 fieldy < 1 || fieldy > 256 ||
5318 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5320 level->no_valid_file = TRUE;
5322 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5327 /* maximum envelope header size is 31 bytes */
5328 envelope_header_len = header[envelope_header_pos];
5329 /* maximum envelope content size is 110 (156?) bytes */
5330 envelope_content_len = header[envelope_content_pos];
5332 /* maximum level title size is 40 bytes */
5333 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5334 /* maximum level author size is 30 (51?) bytes */
5335 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5339 for (i = 0; i < envelope_header_len; i++)
5340 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5341 level->envelope[0].text[envelope_size++] =
5342 header[envelope_header_pos + 1 + i];
5344 if (envelope_header_len > 0 && envelope_content_len > 0)
5346 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5347 level->envelope[0].text[envelope_size++] = '\n';
5348 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5349 level->envelope[0].text[envelope_size++] = '\n';
5352 for (i = 0; i < envelope_content_len; i++)
5353 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5354 level->envelope[0].text[envelope_size++] =
5355 header[envelope_content_pos + 1 + i];
5357 level->envelope[0].text[envelope_size] = '\0';
5359 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5360 level->envelope[0].ysize = 10;
5361 level->envelope[0].autowrap = TRUE;
5362 level->envelope[0].centered = TRUE;
5364 for (i = 0; i < level_name_len; i++)
5365 level->name[i] = header[level_name_pos + 1 + i];
5366 level->name[level_name_len] = '\0';
5368 for (i = 0; i < level_author_len; i++)
5369 level->author[i] = header[level_author_pos + 1 + i];
5370 level->author[level_author_len] = '\0';
5372 num_yamyam_contents = header[60] | (header[61] << 8);
5373 level->num_yamyam_contents =
5374 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5376 for (i = 0; i < num_yamyam_contents; i++)
5378 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5380 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5381 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5383 if (i < MAX_ELEMENT_CONTENTS)
5384 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5388 fieldx = header[6] | (header[7] << 8);
5389 fieldy = header[8] | (header[9] << 8);
5390 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5391 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5393 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5395 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5396 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5398 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5399 level->field[x][y] = getMappedElement_DC(element_dc);
5402 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5403 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5404 level->field[x][y] = EL_PLAYER_1;
5406 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5407 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5408 level->field[x][y] = EL_PLAYER_2;
5410 level->gems_needed = header[18] | (header[19] << 8);
5412 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5413 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5414 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5415 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5416 level->score[SC_NUT] = header[28] | (header[29] << 8);
5417 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5418 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5419 level->score[SC_BUG] = header[34] | (header[35] << 8);
5420 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5421 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5422 level->score[SC_KEY] = header[40] | (header[41] << 8);
5423 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5425 level->time = header[44] | (header[45] << 8);
5427 level->amoeba_speed = header[46] | (header[47] << 8);
5428 level->time_light = header[48] | (header[49] << 8);
5429 level->time_timegate = header[50] | (header[51] << 8);
5430 level->time_wheel = header[52] | (header[53] << 8);
5431 level->time_magic_wall = header[54] | (header[55] << 8);
5432 level->extra_time = header[56] | (header[57] << 8);
5433 level->shield_normal_time = header[58] | (header[59] << 8);
5435 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5436 can slip down from flat walls, like normal walls and steel walls */
5437 level->em_slippery_gems = TRUE;
5440 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5441 struct LevelFileInfo *level_file_info,
5442 boolean level_info_only)
5444 char *filename = level_file_info->filename;
5446 int num_magic_bytes = 8;
5447 char magic_bytes[num_magic_bytes + 1];
5448 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5450 if (!(file = openFile(filename, MODE_READ)))
5452 level->no_valid_file = TRUE;
5454 if (!level_info_only)
5455 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5460 // fseek(file, 0x0000, SEEK_SET);
5462 if (level_file_info->packed)
5464 /* read "magic bytes" from start of file */
5465 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5466 magic_bytes[0] = '\0';
5468 /* check "magic bytes" for correct file format */
5469 if (!strPrefix(magic_bytes, "DC2"))
5471 level->no_valid_file = TRUE;
5473 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5479 if (strPrefix(magic_bytes, "DC2Win95") ||
5480 strPrefix(magic_bytes, "DC2Win98"))
5482 int position_first_level = 0x00fa;
5483 int extra_bytes = 4;
5486 /* advance file stream to first level inside the level package */
5487 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5489 /* each block of level data is followed by block of non-level data */
5490 num_levels_to_skip *= 2;
5492 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5493 while (num_levels_to_skip >= 0)
5495 /* advance file stream to next level inside the level package */
5496 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5498 level->no_valid_file = TRUE;
5500 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5506 /* skip apparently unused extra bytes following each level */
5507 ReadUnusedBytesFromFile(file, extra_bytes);
5509 /* read size of next level in level package */
5510 skip_bytes = getFile32BitLE(file);
5512 num_levels_to_skip--;
5517 level->no_valid_file = TRUE;
5519 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5526 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5532 /* ------------------------------------------------------------------------- */
5533 /* functions for loading SB level */
5534 /* ------------------------------------------------------------------------- */
5536 int getMappedElement_SB(int element_ascii, boolean use_ces)
5544 sb_element_mapping[] =
5546 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5547 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5548 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5549 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5550 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5551 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5552 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5553 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5560 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5561 if (element_ascii == sb_element_mapping[i].ascii)
5562 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5564 return EL_UNDEFINED;
5567 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5568 struct LevelFileInfo *level_file_info,
5569 boolean level_info_only)
5571 char *filename = level_file_info->filename;
5572 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5573 char last_comment[MAX_LINE_LEN];
5574 char level_name[MAX_LINE_LEN];
5577 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5578 boolean read_continued_line = FALSE;
5579 boolean reading_playfield = FALSE;
5580 boolean got_valid_playfield_line = FALSE;
5581 boolean invalid_playfield_char = FALSE;
5582 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5583 int file_level_nr = 0;
5585 int x = 0, y = 0; /* initialized to make compilers happy */
5587 last_comment[0] = '\0';
5588 level_name[0] = '\0';
5590 if (!(file = openFile(filename, MODE_READ)))
5592 level->no_valid_file = TRUE;
5594 if (!level_info_only)
5595 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5600 while (!checkEndOfFile(file))
5602 /* level successfully read, but next level may follow here */
5603 if (!got_valid_playfield_line && reading_playfield)
5605 /* read playfield from single level file -- skip remaining file */
5606 if (!level_file_info->packed)
5609 if (file_level_nr >= num_levels_to_skip)
5614 last_comment[0] = '\0';
5615 level_name[0] = '\0';
5617 reading_playfield = FALSE;
5620 got_valid_playfield_line = FALSE;
5622 /* read next line of input file */
5623 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5626 /* check if line was completely read and is terminated by line break */
5627 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5630 /* cut trailing line break (this can be newline and/or carriage return) */
5631 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5632 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5635 /* copy raw input line for later use (mainly debugging output) */
5636 strcpy(line_raw, line);
5638 if (read_continued_line)
5640 /* append new line to existing line, if there is enough space */
5641 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5642 strcat(previous_line, line_ptr);
5644 strcpy(line, previous_line); /* copy storage buffer to line */
5646 read_continued_line = FALSE;
5649 /* if the last character is '\', continue at next line */
5650 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5652 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5653 strcpy(previous_line, line); /* copy line to storage buffer */
5655 read_continued_line = TRUE;
5660 /* skip empty lines */
5661 if (line[0] == '\0')
5664 /* extract comment text from comment line */
5667 for (line_ptr = line; *line_ptr; line_ptr++)
5668 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5671 strcpy(last_comment, line_ptr);
5676 /* extract level title text from line containing level title */
5677 if (line[0] == '\'')
5679 strcpy(level_name, &line[1]);
5681 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5682 level_name[strlen(level_name) - 1] = '\0';
5687 /* skip lines containing only spaces (or empty lines) */
5688 for (line_ptr = line; *line_ptr; line_ptr++)
5689 if (*line_ptr != ' ')
5691 if (*line_ptr == '\0')
5694 /* at this point, we have found a line containing part of a playfield */
5696 got_valid_playfield_line = TRUE;
5698 if (!reading_playfield)
5700 reading_playfield = TRUE;
5701 invalid_playfield_char = FALSE;
5703 for (x = 0; x < MAX_LEV_FIELDX; x++)
5704 for (y = 0; y < MAX_LEV_FIELDY; y++)
5705 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5710 /* start with topmost tile row */
5714 /* skip playfield line if larger row than allowed */
5715 if (y >= MAX_LEV_FIELDY)
5718 /* start with leftmost tile column */
5721 /* read playfield elements from line */
5722 for (line_ptr = line; *line_ptr; line_ptr++)
5724 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5726 /* stop parsing playfield line if larger column than allowed */
5727 if (x >= MAX_LEV_FIELDX)
5730 if (mapped_sb_element == EL_UNDEFINED)
5732 invalid_playfield_char = TRUE;
5737 level->field[x][y] = mapped_sb_element;
5739 /* continue with next tile column */
5742 level->fieldx = MAX(x, level->fieldx);
5745 if (invalid_playfield_char)
5747 /* if first playfield line, treat invalid lines as comment lines */
5749 reading_playfield = FALSE;
5754 /* continue with next tile row */
5762 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
5763 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
5765 if (!reading_playfield)
5767 level->no_valid_file = TRUE;
5769 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5774 if (*level_name != '\0')
5776 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
5777 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5779 else if (*last_comment != '\0')
5781 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
5782 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5786 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
5789 /* set all empty fields beyond the border walls to invisible steel wall */
5790 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5792 if ((x == 0 || x == level->fieldx - 1 ||
5793 y == 0 || y == level->fieldy - 1) &&
5794 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
5795 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
5796 level->field, level->fieldx, level->fieldy);
5799 /* set special level settings for Sokoban levels */
5802 level->use_step_counter = TRUE;
5804 if (load_xsb_to_ces)
5806 /* special global settings can now be set in level template */
5808 level->use_custom_template = TRUE;
5813 /* ------------------------------------------------------------------------- */
5814 /* functions for handling native levels */
5815 /* ------------------------------------------------------------------------- */
5817 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
5818 struct LevelFileInfo *level_file_info,
5819 boolean level_info_only)
5821 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
5822 level->no_valid_file = TRUE;
5825 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
5826 struct LevelFileInfo *level_file_info,
5827 boolean level_info_only)
5831 /* determine position of requested level inside level package */
5832 if (level_file_info->packed)
5833 pos = level_file_info->nr - leveldir_current->first_level;
5835 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
5836 level->no_valid_file = TRUE;
5839 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
5841 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5842 CopyNativeLevel_RND_to_EM(level);
5843 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5844 CopyNativeLevel_RND_to_SP(level);
5847 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
5849 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5850 CopyNativeLevel_EM_to_RND(level);
5851 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5852 CopyNativeLevel_SP_to_RND(level);
5855 void SaveNativeLevel(struct LevelInfo *level)
5857 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5859 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
5860 char *filename = getLevelFilenameFromBasename(basename);
5862 CopyNativeLevel_RND_to_SP(level);
5863 CopyNativeTape_RND_to_SP(level);
5865 SaveNativeLevel_SP(filename);
5870 /* ------------------------------------------------------------------------- */
5871 /* functions for loading generic level */
5872 /* ------------------------------------------------------------------------- */
5874 static void LoadLevelFromFileInfo(struct LevelInfo *level,
5875 struct LevelFileInfo *level_file_info,
5876 boolean level_info_only)
5878 /* always start with reliable default values */
5879 setLevelInfoToDefaults(level, level_info_only);
5881 switch (level_file_info->type)
5883 case LEVEL_FILE_TYPE_RND:
5884 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5887 case LEVEL_FILE_TYPE_EM:
5888 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
5889 level->game_engine_type = GAME_ENGINE_TYPE_EM;
5892 case LEVEL_FILE_TYPE_SP:
5893 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
5894 level->game_engine_type = GAME_ENGINE_TYPE_SP;
5897 case LEVEL_FILE_TYPE_DC:
5898 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
5901 case LEVEL_FILE_TYPE_SB:
5902 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
5906 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5910 /* if level file is invalid, restore level structure to default values */
5911 if (level->no_valid_file)
5913 setLevelInfoToDefaults(level, level_info_only);
5915 level->no_valid_file = TRUE; /* but keep "no valid file" flag */
5918 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
5919 level->game_engine_type = GAME_ENGINE_TYPE_RND;
5921 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
5922 CopyNativeLevel_Native_to_RND(level);
5925 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
5927 static struct LevelFileInfo level_file_info;
5929 /* always start with reliable default values */
5930 setFileInfoToDefaults(&level_file_info);
5932 level_file_info.nr = 0; /* unknown level number */
5933 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
5934 level_file_info.filename = filename;
5936 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
5939 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
5943 if (leveldir_current == NULL) /* only when dumping level */
5946 /* all engine modifications also valid for levels which use latest engine */
5947 if (level->game_version < VERSION_IDENT(3,2,0,5))
5949 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
5950 level->score[SC_TIME_BONUS] /= 10;
5953 if (leveldir_current->latest_engine)
5955 /* ---------- use latest game engine ----------------------------------- */
5957 /* For all levels which are forced to use the latest game engine version
5958 (normally all but user contributed, private and undefined levels), set
5959 the game engine version to the actual version; this allows for actual
5960 corrections in the game engine to take effect for existing, converted
5961 levels (from "classic" or other existing games) to make the emulation
5962 of the corresponding game more accurate, while (hopefully) not breaking
5963 existing levels created from other players. */
5965 level->game_version = GAME_VERSION_ACTUAL;
5967 /* Set special EM style gems behaviour: EM style gems slip down from
5968 normal, steel and growing wall. As this is a more fundamental change,
5969 it seems better to set the default behaviour to "off" (as it is more
5970 natural) and make it configurable in the level editor (as a property
5971 of gem style elements). Already existing converted levels (neither
5972 private nor contributed levels) are changed to the new behaviour. */
5974 if (level->file_version < FILE_VERSION_2_0)
5975 level->em_slippery_gems = TRUE;
5980 /* ---------- use game engine the level was created with ----------------- */
5982 /* For all levels which are not forced to use the latest game engine
5983 version (normally user contributed, private and undefined levels),
5984 use the version of the game engine the levels were created for.
5986 Since 2.0.1, the game engine version is now directly stored
5987 in the level file (chunk "VERS"), so there is no need anymore
5988 to set the game version from the file version (except for old,
5989 pre-2.0 levels, where the game version is still taken from the
5990 file format version used to store the level -- see above). */
5992 /* player was faster than enemies in 1.0.0 and before */
5993 if (level->file_version == FILE_VERSION_1_0)
5994 for (i = 0; i < MAX_PLAYERS; i++)
5995 level->initial_player_stepsize[i] = STEPSIZE_FAST;
5997 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
5998 if (level->game_version == VERSION_IDENT(2,0,1,0))
5999 level->em_slippery_gems = TRUE;
6001 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6002 if (level->game_version < VERSION_IDENT(2,2,0,0))
6003 level->use_spring_bug = TRUE;
6005 if (level->game_version < VERSION_IDENT(3,2,0,5))
6007 /* time orb caused limited time in endless time levels before 3.2.0-5 */
6008 level->use_time_orb_bug = TRUE;
6010 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6011 level->block_snap_field = FALSE;
6013 /* extra time score was same value as time left score before 3.2.0-5 */
6014 level->extra_time_score = level->score[SC_TIME_BONUS];
6017 if (level->game_version < VERSION_IDENT(3,2,0,7))
6019 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6020 level->continuous_snapping = FALSE;
6023 /* only few elements were able to actively move into acid before 3.1.0 */
6024 /* trigger settings did not exist before 3.1.0; set to default "any" */
6025 if (level->game_version < VERSION_IDENT(3,1,0,0))
6027 /* correct "can move into acid" settings (all zero in old levels) */
6029 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6030 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6032 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6033 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6034 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6035 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6037 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6038 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6040 /* correct trigger settings (stored as zero == "none" in old levels) */
6042 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6044 int element = EL_CUSTOM_START + i;
6045 struct ElementInfo *ei = &element_info[element];
6047 for (j = 0; j < ei->num_change_pages; j++)
6049 struct ElementChangeInfo *change = &ei->change_page[j];
6051 change->trigger_player = CH_PLAYER_ANY;
6052 change->trigger_page = CH_PAGE_ANY;
6057 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6059 int element = EL_CUSTOM_256;
6060 struct ElementInfo *ei = &element_info[element];
6061 struct ElementChangeInfo *change = &ei->change_page[0];
6063 /* This is needed to fix a problem that was caused by a bugfix in function
6064 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6065 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6066 not replace walkable elements, but instead just placed the player on it,
6067 without placing the Sokoban field under the player). Unfortunately, this
6068 breaks "Snake Bite" style levels when the snake is halfway through a door
6069 that just closes (the snake head is still alive and can be moved in this
6070 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6071 player (without Sokoban element) which then gets killed as designed). */
6073 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6074 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6075 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6076 change->target_element = EL_PLAYER_1;
6079 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6080 if (level->game_version < VERSION_IDENT(3,2,5,0))
6082 /* This is needed to fix a problem that was caused by a bugfix in function
6083 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6084 corrects the behaviour when a custom element changes to another custom
6085 element with a higher element number that has change actions defined.
6086 Normally, only one change per frame is allowed for custom elements.
6087 Therefore, it is checked if a custom element already changed in the
6088 current frame; if it did, subsequent changes are suppressed.
6089 Unfortunately, this is only checked for element changes, but not for
6090 change actions, which are still executed. As the function above loops
6091 through all custom elements from lower to higher, an element change
6092 resulting in a lower CE number won't be checked again, while a target
6093 element with a higher number will also be checked, and potential change
6094 actions will get executed for this CE, too (which is wrong), while
6095 further changes are ignored (which is correct). As this bugfix breaks
6096 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6097 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6098 behaviour for existing levels and tapes that make use of this bug */
6100 level->use_action_after_change_bug = TRUE;
6103 /* not centering level after relocating player was default only in 3.2.3 */
6104 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6105 level->shifted_relocation = TRUE;
6107 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6108 if (level->game_version < VERSION_IDENT(3,2,6,0))
6109 level->em_explodes_by_fire = TRUE;
6112 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6116 /* map custom element change events that have changed in newer versions
6117 (these following values were accidentally changed in version 3.0.1)
6118 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6119 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6121 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6123 int element = EL_CUSTOM_START + i;
6125 /* order of checking and copying events to be mapped is important */
6126 /* (do not change the start and end value -- they are constant) */
6127 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6129 if (HAS_CHANGE_EVENT(element, j - 2))
6131 SET_CHANGE_EVENT(element, j - 2, FALSE);
6132 SET_CHANGE_EVENT(element, j, TRUE);
6136 /* order of checking and copying events to be mapped is important */
6137 /* (do not change the start and end value -- they are constant) */
6138 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6140 if (HAS_CHANGE_EVENT(element, j - 1))
6142 SET_CHANGE_EVENT(element, j - 1, FALSE);
6143 SET_CHANGE_EVENT(element, j, TRUE);
6149 /* initialize "can_change" field for old levels with only one change page */
6150 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6152 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6154 int element = EL_CUSTOM_START + i;
6156 if (CAN_CHANGE(element))
6157 element_info[element].change->can_change = TRUE;
6161 /* correct custom element values (for old levels without these options) */
6162 if (level->game_version < VERSION_IDENT(3,1,1,0))
6164 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6166 int element = EL_CUSTOM_START + i;
6167 struct ElementInfo *ei = &element_info[element];
6169 if (ei->access_direction == MV_NO_DIRECTION)
6170 ei->access_direction = MV_ALL_DIRECTIONS;
6174 /* correct custom element values (fix invalid values for all versions) */
6177 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6179 int element = EL_CUSTOM_START + i;
6180 struct ElementInfo *ei = &element_info[element];
6182 for (j = 0; j < ei->num_change_pages; j++)
6184 struct ElementChangeInfo *change = &ei->change_page[j];
6186 if (change->trigger_player == CH_PLAYER_NONE)
6187 change->trigger_player = CH_PLAYER_ANY;
6189 if (change->trigger_side == CH_SIDE_NONE)
6190 change->trigger_side = CH_SIDE_ANY;
6195 /* initialize "can_explode" field for old levels which did not store this */
6196 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6197 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6199 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6201 int element = EL_CUSTOM_START + i;
6203 if (EXPLODES_1X1_OLD(element))
6204 element_info[element].explosion_type = EXPLODES_1X1;
6206 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6207 EXPLODES_SMASHED(element) ||
6208 EXPLODES_IMPACT(element)));
6212 /* correct previously hard-coded move delay values for maze runner style */
6213 if (level->game_version < VERSION_IDENT(3,1,1,0))
6215 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6217 int element = EL_CUSTOM_START + i;
6219 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6221 /* previously hard-coded and therefore ignored */
6222 element_info[element].move_delay_fixed = 9;
6223 element_info[element].move_delay_random = 0;
6228 /* map elements that have changed in newer versions */
6229 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6230 level->game_version);
6231 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6232 for (x = 0; x < 3; x++)
6233 for (y = 0; y < 3; y++)
6234 level->yamyam_content[i].e[x][y] =
6235 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6236 level->game_version);
6238 /* initialize element properties for level editor etc. */
6239 InitElementPropertiesEngine(level->game_version);
6240 InitElementPropertiesAfterLoading(level->game_version);
6241 InitElementPropertiesGfxElement();
6244 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6248 /* map elements that have changed in newer versions */
6249 for (y = 0; y < level->fieldy; y++)
6250 for (x = 0; x < level->fieldx; x++)
6251 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6252 level->game_version);
6254 /* clear unused playfield data (nicer if level gets resized in editor) */
6255 for (x = 0; x < MAX_LEV_FIELDX; x++)
6256 for (y = 0; y < MAX_LEV_FIELDY; y++)
6257 if (x >= level->fieldx || y >= level->fieldy)
6258 level->field[x][y] = EL_EMPTY;
6260 /* copy elements to runtime playfield array */
6261 for (x = 0; x < MAX_LEV_FIELDX; x++)
6262 for (y = 0; y < MAX_LEV_FIELDY; y++)
6263 Feld[x][y] = level->field[x][y];
6265 /* initialize level size variables for faster access */
6266 lev_fieldx = level->fieldx;
6267 lev_fieldy = level->fieldy;
6269 /* determine border element for this level */
6270 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6271 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6276 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6278 struct LevelFileInfo *level_file_info = &level->file_info;
6280 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6281 CopyNativeLevel_RND_to_Native(level);
6284 void LoadLevelTemplate(int nr)
6288 setLevelFileInfo(&level_template.file_info, nr);
6289 filename = level_template.file_info.filename;
6291 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6293 LoadLevel_InitVersion(&level_template, filename);
6294 LoadLevel_InitElements(&level_template, filename);
6296 ActivateLevelTemplate();
6299 void LoadLevel(int nr)
6303 setLevelFileInfo(&level.file_info, nr);
6304 filename = level.file_info.filename;
6306 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6308 if (level.use_custom_template)
6309 LoadLevelTemplate(-1);
6311 LoadLevel_InitVersion(&level, filename);
6312 LoadLevel_InitElements(&level, filename);
6313 LoadLevel_InitPlayfield(&level, filename);
6315 LoadLevel_InitNativeEngines(&level, filename);
6318 void LoadLevelInfoOnly(int nr)
6320 setLevelFileInfo(&level.file_info, nr);
6322 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6325 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6329 chunk_size += putFileVersion(file, level->file_version);
6330 chunk_size += putFileVersion(file, level->game_version);
6335 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6339 chunk_size += putFile16BitBE(file, level->creation_date.year);
6340 chunk_size += putFile8Bit(file, level->creation_date.month);
6341 chunk_size += putFile8Bit(file, level->creation_date.day);
6346 #if ENABLE_HISTORIC_CHUNKS
6347 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6351 putFile8Bit(file, level->fieldx);
6352 putFile8Bit(file, level->fieldy);
6354 putFile16BitBE(file, level->time);
6355 putFile16BitBE(file, level->gems_needed);
6357 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6358 putFile8Bit(file, level->name[i]);
6360 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6361 putFile8Bit(file, level->score[i]);
6363 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6364 for (y = 0; y < 3; y++)
6365 for (x = 0; x < 3; x++)
6366 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6367 level->yamyam_content[i].e[x][y]));
6368 putFile8Bit(file, level->amoeba_speed);
6369 putFile8Bit(file, level->time_magic_wall);
6370 putFile8Bit(file, level->time_wheel);
6371 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6372 level->amoeba_content));
6373 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6374 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6375 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6376 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6378 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6380 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6381 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6382 putFile32BitBE(file, level->can_move_into_acid_bits);
6383 putFile8Bit(file, level->dont_collide_with_bits);
6385 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6386 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6388 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6389 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6390 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6392 putFile8Bit(file, level->game_engine_type);
6394 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6398 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6403 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6404 chunk_size += putFile8Bit(file, level->name[i]);
6409 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6414 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6415 chunk_size += putFile8Bit(file, level->author[i]);
6420 #if ENABLE_HISTORIC_CHUNKS
6421 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6426 for (y = 0; y < level->fieldy; y++)
6427 for (x = 0; x < level->fieldx; x++)
6428 if (level->encoding_16bit_field)
6429 chunk_size += putFile16BitBE(file, level->field[x][y]);
6431 chunk_size += putFile8Bit(file, level->field[x][y]);
6437 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6442 for (y = 0; y < level->fieldy; y++)
6443 for (x = 0; x < level->fieldx; x++)
6444 chunk_size += putFile16BitBE(file, level->field[x][y]);
6449 #if ENABLE_HISTORIC_CHUNKS
6450 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6454 putFile8Bit(file, EL_YAMYAM);
6455 putFile8Bit(file, level->num_yamyam_contents);
6456 putFile8Bit(file, 0);
6457 putFile8Bit(file, 0);
6459 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6460 for (y = 0; y < 3; y++)
6461 for (x = 0; x < 3; x++)
6462 if (level->encoding_16bit_field)
6463 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6465 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6469 #if ENABLE_HISTORIC_CHUNKS
6470 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6473 int num_contents, content_xsize, content_ysize;
6474 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6476 if (element == EL_YAMYAM)
6478 num_contents = level->num_yamyam_contents;
6482 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6483 for (y = 0; y < 3; y++)
6484 for (x = 0; x < 3; x++)
6485 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6487 else if (element == EL_BD_AMOEBA)
6493 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6494 for (y = 0; y < 3; y++)
6495 for (x = 0; x < 3; x++)
6496 content_array[i][x][y] = EL_EMPTY;
6497 content_array[0][0][0] = level->amoeba_content;
6501 /* chunk header already written -- write empty chunk data */
6502 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6504 Error(ERR_WARN, "cannot save content for element '%d'", element);
6508 putFile16BitBE(file, element);
6509 putFile8Bit(file, num_contents);
6510 putFile8Bit(file, content_xsize);
6511 putFile8Bit(file, content_ysize);
6513 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6515 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6516 for (y = 0; y < 3; y++)
6517 for (x = 0; x < 3; x++)
6518 putFile16BitBE(file, content_array[i][x][y]);
6522 #if ENABLE_HISTORIC_CHUNKS
6523 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6525 int envelope_nr = element - EL_ENVELOPE_1;
6526 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6530 chunk_size += putFile16BitBE(file, element);
6531 chunk_size += putFile16BitBE(file, envelope_len);
6532 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6533 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6535 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6536 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6538 for (i = 0; i < envelope_len; i++)
6539 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6545 #if ENABLE_HISTORIC_CHUNKS
6546 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6547 int num_changed_custom_elements)
6551 putFile16BitBE(file, num_changed_custom_elements);
6553 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6555 int element = EL_CUSTOM_START + i;
6557 struct ElementInfo *ei = &element_info[element];
6559 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6561 if (check < num_changed_custom_elements)
6563 putFile16BitBE(file, element);
6564 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6571 if (check != num_changed_custom_elements) /* should not happen */
6572 Error(ERR_WARN, "inconsistent number of custom element properties");
6576 #if ENABLE_HISTORIC_CHUNKS
6577 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6578 int num_changed_custom_elements)
6582 putFile16BitBE(file, num_changed_custom_elements);
6584 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6586 int element = EL_CUSTOM_START + i;
6588 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6590 if (check < num_changed_custom_elements)
6592 putFile16BitBE(file, element);
6593 putFile16BitBE(file, element_info[element].change->target_element);
6600 if (check != num_changed_custom_elements) /* should not happen */
6601 Error(ERR_WARN, "inconsistent number of custom target elements");
6605 #if ENABLE_HISTORIC_CHUNKS
6606 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6607 int num_changed_custom_elements)
6609 int i, j, x, y, check = 0;
6611 putFile16BitBE(file, num_changed_custom_elements);
6613 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6615 int element = EL_CUSTOM_START + i;
6616 struct ElementInfo *ei = &element_info[element];
6618 if (ei->modified_settings)
6620 if (check < num_changed_custom_elements)
6622 putFile16BitBE(file, element);
6624 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6625 putFile8Bit(file, ei->description[j]);
6627 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6629 /* some free bytes for future properties and padding */
6630 WriteUnusedBytesToFile(file, 7);
6632 putFile8Bit(file, ei->use_gfx_element);
6633 putFile16BitBE(file, ei->gfx_element_initial);
6635 putFile8Bit(file, ei->collect_score_initial);
6636 putFile8Bit(file, ei->collect_count_initial);
6638 putFile16BitBE(file, ei->push_delay_fixed);
6639 putFile16BitBE(file, ei->push_delay_random);
6640 putFile16BitBE(file, ei->move_delay_fixed);
6641 putFile16BitBE(file, ei->move_delay_random);
6643 putFile16BitBE(file, ei->move_pattern);
6644 putFile8Bit(file, ei->move_direction_initial);
6645 putFile8Bit(file, ei->move_stepsize);
6647 for (y = 0; y < 3; y++)
6648 for (x = 0; x < 3; x++)
6649 putFile16BitBE(file, ei->content.e[x][y]);
6651 putFile32BitBE(file, ei->change->events);
6653 putFile16BitBE(file, ei->change->target_element);
6655 putFile16BitBE(file, ei->change->delay_fixed);
6656 putFile16BitBE(file, ei->change->delay_random);
6657 putFile16BitBE(file, ei->change->delay_frames);
6659 putFile16BitBE(file, ei->change->initial_trigger_element);
6661 putFile8Bit(file, ei->change->explode);
6662 putFile8Bit(file, ei->change->use_target_content);
6663 putFile8Bit(file, ei->change->only_if_complete);
6664 putFile8Bit(file, ei->change->use_random_replace);
6666 putFile8Bit(file, ei->change->random_percentage);
6667 putFile8Bit(file, ei->change->replace_when);
6669 for (y = 0; y < 3; y++)
6670 for (x = 0; x < 3; x++)
6671 putFile16BitBE(file, ei->change->content.e[x][y]);
6673 putFile8Bit(file, ei->slippery_type);
6675 /* some free bytes for future properties and padding */
6676 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6683 if (check != num_changed_custom_elements) /* should not happen */
6684 Error(ERR_WARN, "inconsistent number of custom element properties");
6688 #if ENABLE_HISTORIC_CHUNKS
6689 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6691 struct ElementInfo *ei = &element_info[element];
6694 /* ---------- custom element base property values (96 bytes) ------------- */
6696 putFile16BitBE(file, element);
6698 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6699 putFile8Bit(file, ei->description[i]);
6701 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6703 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
6705 putFile8Bit(file, ei->num_change_pages);
6707 putFile16BitBE(file, ei->ce_value_fixed_initial);
6708 putFile16BitBE(file, ei->ce_value_random_initial);
6709 putFile8Bit(file, ei->use_last_ce_value);
6711 putFile8Bit(file, ei->use_gfx_element);
6712 putFile16BitBE(file, ei->gfx_element_initial);
6714 putFile8Bit(file, ei->collect_score_initial);
6715 putFile8Bit(file, ei->collect_count_initial);
6717 putFile8Bit(file, ei->drop_delay_fixed);
6718 putFile8Bit(file, ei->push_delay_fixed);
6719 putFile8Bit(file, ei->drop_delay_random);
6720 putFile8Bit(file, ei->push_delay_random);
6721 putFile16BitBE(file, ei->move_delay_fixed);
6722 putFile16BitBE(file, ei->move_delay_random);
6724 /* bits 0 - 15 of "move_pattern" ... */
6725 putFile16BitBE(file, ei->move_pattern & 0xffff);
6726 putFile8Bit(file, ei->move_direction_initial);
6727 putFile8Bit(file, ei->move_stepsize);
6729 putFile8Bit(file, ei->slippery_type);
6731 for (y = 0; y < 3; y++)
6732 for (x = 0; x < 3; x++)
6733 putFile16BitBE(file, ei->content.e[x][y]);
6735 putFile16BitBE(file, ei->move_enter_element);
6736 putFile16BitBE(file, ei->move_leave_element);
6737 putFile8Bit(file, ei->move_leave_type);
6739 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
6740 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
6742 putFile8Bit(file, ei->access_direction);
6744 putFile8Bit(file, ei->explosion_delay);
6745 putFile8Bit(file, ei->ignition_delay);
6746 putFile8Bit(file, ei->explosion_type);
6748 /* some free bytes for future custom property values and padding */
6749 WriteUnusedBytesToFile(file, 1);
6751 /* ---------- change page property values (48 bytes) --------------------- */
6753 for (i = 0; i < ei->num_change_pages; i++)
6755 struct ElementChangeInfo *change = &ei->change_page[i];
6756 unsigned int event_bits;
6758 /* bits 0 - 31 of "has_event[]" ... */
6760 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
6761 if (change->has_event[j])
6762 event_bits |= (1 << j);
6763 putFile32BitBE(file, event_bits);
6765 putFile16BitBE(file, change->target_element);
6767 putFile16BitBE(file, change->delay_fixed);
6768 putFile16BitBE(file, change->delay_random);
6769 putFile16BitBE(file, change->delay_frames);
6771 putFile16BitBE(file, change->initial_trigger_element);
6773 putFile8Bit(file, change->explode);
6774 putFile8Bit(file, change->use_target_content);
6775 putFile8Bit(file, change->only_if_complete);
6776 putFile8Bit(file, change->use_random_replace);
6778 putFile8Bit(file, change->random_percentage);
6779 putFile8Bit(file, change->replace_when);
6781 for (y = 0; y < 3; y++)
6782 for (x = 0; x < 3; x++)
6783 putFile16BitBE(file, change->target_content.e[x][y]);
6785 putFile8Bit(file, change->can_change);
6787 putFile8Bit(file, change->trigger_side);
6789 putFile8Bit(file, change->trigger_player);
6790 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
6791 log_2(change->trigger_page)));
6793 putFile8Bit(file, change->has_action);
6794 putFile8Bit(file, change->action_type);
6795 putFile8Bit(file, change->action_mode);
6796 putFile16BitBE(file, change->action_arg);
6798 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
6800 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
6801 if (change->has_event[j])
6802 event_bits |= (1 << (j - 32));
6803 putFile8Bit(file, event_bits);
6808 #if ENABLE_HISTORIC_CHUNKS
6809 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
6811 struct ElementInfo *ei = &element_info[element];
6812 struct ElementGroupInfo *group = ei->group;
6815 putFile16BitBE(file, element);
6817 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6818 putFile8Bit(file, ei->description[i]);
6820 putFile8Bit(file, group->num_elements);
6822 putFile8Bit(file, ei->use_gfx_element);
6823 putFile16BitBE(file, ei->gfx_element_initial);
6825 putFile8Bit(file, group->choice_mode);
6827 /* some free bytes for future values and padding */
6828 WriteUnusedBytesToFile(file, 3);
6830 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
6831 putFile16BitBE(file, group->element[i]);
6835 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
6836 boolean write_element)
6838 int save_type = entry->save_type;
6839 int data_type = entry->data_type;
6840 int conf_type = entry->conf_type;
6841 int byte_mask = conf_type & CONF_MASK_BYTES;
6842 int element = entry->element;
6843 int default_value = entry->default_value;
6845 boolean modified = FALSE;
6847 if (byte_mask != CONF_MASK_MULTI_BYTES)
6849 void *value_ptr = entry->value;
6850 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
6853 /* check if any settings have been modified before saving them */
6854 if (value != default_value)
6857 /* do not save if explicitly told or if unmodified default settings */
6858 if ((save_type == SAVE_CONF_NEVER) ||
6859 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6863 num_bytes += putFile16BitBE(file, element);
6865 num_bytes += putFile8Bit(file, conf_type);
6866 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
6867 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
6868 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
6871 else if (data_type == TYPE_STRING)
6873 char *default_string = entry->default_string;
6874 char *string = (char *)(entry->value);
6875 int string_length = strlen(string);
6878 /* check if any settings have been modified before saving them */
6879 if (!strEqual(string, default_string))
6882 /* do not save if explicitly told or if unmodified default settings */
6883 if ((save_type == SAVE_CONF_NEVER) ||
6884 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6888 num_bytes += putFile16BitBE(file, element);
6890 num_bytes += putFile8Bit(file, conf_type);
6891 num_bytes += putFile16BitBE(file, string_length);
6893 for (i = 0; i < string_length; i++)
6894 num_bytes += putFile8Bit(file, string[i]);
6896 else if (data_type == TYPE_ELEMENT_LIST)
6898 int *element_array = (int *)(entry->value);
6899 int num_elements = *(int *)(entry->num_entities);
6902 /* check if any settings have been modified before saving them */
6903 for (i = 0; i < num_elements; i++)
6904 if (element_array[i] != default_value)
6907 /* do not save if explicitly told or if unmodified default settings */
6908 if ((save_type == SAVE_CONF_NEVER) ||
6909 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6913 num_bytes += putFile16BitBE(file, element);
6915 num_bytes += putFile8Bit(file, conf_type);
6916 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
6918 for (i = 0; i < num_elements; i++)
6919 num_bytes += putFile16BitBE(file, element_array[i]);
6921 else if (data_type == TYPE_CONTENT_LIST)
6923 struct Content *content = (struct Content *)(entry->value);
6924 int num_contents = *(int *)(entry->num_entities);
6927 /* check if any settings have been modified before saving them */
6928 for (i = 0; i < num_contents; i++)
6929 for (y = 0; y < 3; y++)
6930 for (x = 0; x < 3; x++)
6931 if (content[i].e[x][y] != default_value)
6934 /* do not save if explicitly told or if unmodified default settings */
6935 if ((save_type == SAVE_CONF_NEVER) ||
6936 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6940 num_bytes += putFile16BitBE(file, element);
6942 num_bytes += putFile8Bit(file, conf_type);
6943 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
6945 for (i = 0; i < num_contents; i++)
6946 for (y = 0; y < 3; y++)
6947 for (x = 0; x < 3; x++)
6948 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
6954 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
6959 li = *level; /* copy level data into temporary buffer */
6961 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
6962 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
6967 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
6972 li = *level; /* copy level data into temporary buffer */
6974 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
6975 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
6980 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
6982 int envelope_nr = element - EL_ENVELOPE_1;
6986 chunk_size += putFile16BitBE(file, element);
6988 /* copy envelope data into temporary buffer */
6989 xx_envelope = level->envelope[envelope_nr];
6991 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
6992 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
6997 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
6999 struct ElementInfo *ei = &element_info[element];
7003 chunk_size += putFile16BitBE(file, element);
7005 xx_ei = *ei; /* copy element data into temporary buffer */
7007 /* set default description string for this specific element */
7008 strcpy(xx_default_description, getDefaultElementDescription(ei));
7010 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7011 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7013 for (i = 0; i < ei->num_change_pages; i++)
7015 struct ElementChangeInfo *change = &ei->change_page[i];
7017 xx_current_change_page = i;
7019 xx_change = *change; /* copy change data into temporary buffer */
7022 setEventBitsFromEventFlags(change);
7024 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7025 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7032 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7034 struct ElementInfo *ei = &element_info[element];
7035 struct ElementGroupInfo *group = ei->group;
7039 chunk_size += putFile16BitBE(file, element);
7041 xx_ei = *ei; /* copy element data into temporary buffer */
7042 xx_group = *group; /* copy group data into temporary buffer */
7044 /* set default description string for this specific element */
7045 strcpy(xx_default_description, getDefaultElementDescription(ei));
7047 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7048 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7053 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
7059 if (!(file = fopen(filename, MODE_WRITE)))
7061 Error(ERR_WARN, "cannot save level file '%s'", filename);
7065 level->file_version = FILE_VERSION_ACTUAL;
7066 level->game_version = GAME_VERSION_ACTUAL;
7068 level->creation_date = getCurrentDate();
7070 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7071 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7073 chunk_size = SaveLevel_VERS(NULL, level);
7074 putFileChunkBE(file, "VERS", chunk_size);
7075 SaveLevel_VERS(file, level);
7077 chunk_size = SaveLevel_DATE(NULL, level);
7078 putFileChunkBE(file, "DATE", chunk_size);
7079 SaveLevel_DATE(file, level);
7081 chunk_size = SaveLevel_NAME(NULL, level);
7082 putFileChunkBE(file, "NAME", chunk_size);
7083 SaveLevel_NAME(file, level);
7085 chunk_size = SaveLevel_AUTH(NULL, level);
7086 putFileChunkBE(file, "AUTH", chunk_size);
7087 SaveLevel_AUTH(file, level);
7089 chunk_size = SaveLevel_INFO(NULL, level);
7090 putFileChunkBE(file, "INFO", chunk_size);
7091 SaveLevel_INFO(file, level);
7093 chunk_size = SaveLevel_BODY(NULL, level);
7094 putFileChunkBE(file, "BODY", chunk_size);
7095 SaveLevel_BODY(file, level);
7097 chunk_size = SaveLevel_ELEM(NULL, level);
7098 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7100 putFileChunkBE(file, "ELEM", chunk_size);
7101 SaveLevel_ELEM(file, level);
7104 for (i = 0; i < NUM_ENVELOPES; i++)
7106 int element = EL_ENVELOPE_1 + i;
7108 chunk_size = SaveLevel_NOTE(NULL, level, element);
7109 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7111 putFileChunkBE(file, "NOTE", chunk_size);
7112 SaveLevel_NOTE(file, level, element);
7116 /* if not using template level, check for non-default custom/group elements */
7117 if (!level->use_custom_template)
7119 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7121 int element = EL_CUSTOM_START + i;
7123 chunk_size = SaveLevel_CUSX(NULL, level, element);
7124 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7126 putFileChunkBE(file, "CUSX", chunk_size);
7127 SaveLevel_CUSX(file, level, element);
7131 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7133 int element = EL_GROUP_START + i;
7135 chunk_size = SaveLevel_GRPX(NULL, level, element);
7136 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7138 putFileChunkBE(file, "GRPX", chunk_size);
7139 SaveLevel_GRPX(file, level, element);
7146 SetFilePermissions(filename, PERMS_PRIVATE);
7149 void SaveLevel(int nr)
7151 char *filename = getDefaultLevelFilename(nr);
7153 SaveLevelFromFilename(&level, filename);
7156 void SaveLevelTemplate()
7158 char *filename = getDefaultLevelFilename(-1);
7160 SaveLevelFromFilename(&level, filename);
7163 boolean SaveLevelChecked(int nr)
7165 char *filename = getDefaultLevelFilename(nr);
7166 boolean new_level = !fileExists(filename);
7167 boolean level_saved = FALSE;
7169 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7174 Request("Level saved!", REQ_CONFIRM);
7182 void DumpLevel(struct LevelInfo *level)
7184 if (level->no_valid_file)
7186 Error(ERR_WARN, "cannot dump -- no valid level file found");
7192 Print("Level xxx (file version %08d, game version %08d)\n",
7193 level->file_version, level->game_version);
7196 Print("Level author: '%s'\n", level->author);
7197 Print("Level title: '%s'\n", level->name);
7199 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7201 Print("Level time: %d seconds\n", level->time);
7202 Print("Gems needed: %d\n", level->gems_needed);
7204 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7205 Print("Time for wheel: %d seconds\n", level->time_wheel);
7206 Print("Time for light: %d seconds\n", level->time_light);
7207 Print("Time for timegate: %d seconds\n", level->time_timegate);
7209 Print("Amoeba speed: %d\n", level->amoeba_speed);
7212 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7213 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7214 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7215 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7216 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7222 /* ========================================================================= */
7223 /* tape file functions */
7224 /* ========================================================================= */
7226 static void setTapeInfoToDefaults()
7230 /* always start with reliable default values (empty tape) */
7233 /* default values (also for pre-1.2 tapes) with only the first player */
7234 tape.player_participates[0] = TRUE;
7235 for (i = 1; i < MAX_PLAYERS; i++)
7236 tape.player_participates[i] = FALSE;
7238 /* at least one (default: the first) player participates in every tape */
7239 tape.num_participating_players = 1;
7241 tape.level_nr = level_nr;
7243 tape.changed = FALSE;
7245 tape.recording = FALSE;
7246 tape.playing = FALSE;
7247 tape.pausing = FALSE;
7249 tape.no_valid_file = FALSE;
7252 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7254 tape->file_version = getFileVersion(file);
7255 tape->game_version = getFileVersion(file);
7260 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7264 tape->random_seed = getFile32BitBE(file);
7265 tape->date = getFile32BitBE(file);
7266 tape->length = getFile32BitBE(file);
7268 /* read header fields that are new since version 1.2 */
7269 if (tape->file_version >= FILE_VERSION_1_2)
7271 byte store_participating_players = getFile8Bit(file);
7274 /* since version 1.2, tapes store which players participate in the tape */
7275 tape->num_participating_players = 0;
7276 for (i = 0; i < MAX_PLAYERS; i++)
7278 tape->player_participates[i] = FALSE;
7280 if (store_participating_players & (1 << i))
7282 tape->player_participates[i] = TRUE;
7283 tape->num_participating_players++;
7287 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7289 engine_version = getFileVersion(file);
7290 if (engine_version > 0)
7291 tape->engine_version = engine_version;
7293 tape->engine_version = tape->game_version;
7299 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7301 int level_identifier_size;
7304 level_identifier_size = getFile16BitBE(file);
7306 tape->level_identifier =
7307 checked_realloc(tape->level_identifier, level_identifier_size);
7309 for (i = 0; i < level_identifier_size; i++)
7310 tape->level_identifier[i] = getFile8Bit(file);
7312 tape->level_nr = getFile16BitBE(file);
7314 chunk_size = 2 + level_identifier_size + 2;
7319 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7322 int chunk_size_expected =
7323 (tape->num_participating_players + 1) * tape->length;
7325 if (chunk_size_expected != chunk_size)
7327 ReadUnusedBytesFromFile(file, chunk_size);
7328 return chunk_size_expected;
7331 for (i = 0; i < tape->length; i++)
7333 if (i >= MAX_TAPE_LEN)
7336 for (j = 0; j < MAX_PLAYERS; j++)
7338 tape->pos[i].action[j] = MV_NONE;
7340 if (tape->player_participates[j])
7341 tape->pos[i].action[j] = getFile8Bit(file);
7344 tape->pos[i].delay = getFile8Bit(file);
7346 if (tape->file_version == FILE_VERSION_1_0)
7348 /* eliminate possible diagonal moves in old tapes */
7349 /* this is only for backward compatibility */
7351 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7352 byte action = tape->pos[i].action[0];
7353 int k, num_moves = 0;
7355 for (k = 0; k<4; k++)
7357 if (action & joy_dir[k])
7359 tape->pos[i + num_moves].action[0] = joy_dir[k];
7361 tape->pos[i + num_moves].delay = 0;
7370 tape->length += num_moves;
7373 else if (tape->file_version < FILE_VERSION_2_0)
7375 /* convert pre-2.0 tapes to new tape format */
7377 if (tape->pos[i].delay > 1)
7380 tape->pos[i + 1] = tape->pos[i];
7381 tape->pos[i + 1].delay = 1;
7384 for (j = 0; j < MAX_PLAYERS; j++)
7385 tape->pos[i].action[j] = MV_NONE;
7386 tape->pos[i].delay--;
7393 if (checkEndOfFile(file))
7397 if (i != tape->length)
7398 chunk_size = (tape->num_participating_players + 1) * i;
7403 void LoadTape_SokobanSolution(char *filename)
7406 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7408 if (!(file = openFile(filename, MODE_READ)))
7410 tape.no_valid_file = TRUE;
7415 while (!checkEndOfFile(file))
7417 unsigned char c = getByteFromFile(file);
7419 if (checkEndOfFile(file))
7426 tape.pos[tape.length].action[0] = MV_UP;
7427 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7433 tape.pos[tape.length].action[0] = MV_DOWN;
7434 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7440 tape.pos[tape.length].action[0] = MV_LEFT;
7441 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7447 tape.pos[tape.length].action[0] = MV_RIGHT;
7448 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7456 /* ignore white-space characters */
7460 tape.no_valid_file = TRUE;
7462 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7470 if (tape.no_valid_file)
7473 tape.length_frames = GetTapeLengthFrames();
7474 tape.length_seconds = GetTapeLengthSeconds();
7477 void LoadTapeFromFilename(char *filename)
7479 char cookie[MAX_LINE_LEN];
7480 char chunk_name[CHUNK_ID_LEN + 1];
7484 /* always start with reliable default values */
7485 setTapeInfoToDefaults();
7487 if (strSuffix(filename, ".sln"))
7489 LoadTape_SokobanSolution(filename);
7494 if (!(file = openFile(filename, MODE_READ)))
7496 tape.no_valid_file = TRUE;
7501 getFileChunkBE(file, chunk_name, NULL);
7502 if (strEqual(chunk_name, "RND1"))
7504 getFile32BitBE(file); /* not used */
7506 getFileChunkBE(file, chunk_name, NULL);
7507 if (!strEqual(chunk_name, "TAPE"))
7509 tape.no_valid_file = TRUE;
7511 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7518 else /* check for pre-2.0 file format with cookie string */
7520 strcpy(cookie, chunk_name);
7521 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7523 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7524 cookie[strlen(cookie) - 1] = '\0';
7526 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7528 tape.no_valid_file = TRUE;
7530 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7537 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7539 tape.no_valid_file = TRUE;
7541 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7548 /* pre-2.0 tape files have no game version, so use file version here */
7549 tape.game_version = tape.file_version;
7552 if (tape.file_version < FILE_VERSION_1_2)
7554 /* tape files from versions before 1.2.0 without chunk structure */
7555 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7556 LoadTape_BODY(file, 2 * tape.length, &tape);
7564 int (*loader)(File *, int, struct TapeInfo *);
7568 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7569 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7570 { "INFO", -1, LoadTape_INFO },
7571 { "BODY", -1, LoadTape_BODY },
7575 while (getFileChunkBE(file, chunk_name, &chunk_size))
7579 while (chunk_info[i].name != NULL &&
7580 !strEqual(chunk_name, chunk_info[i].name))
7583 if (chunk_info[i].name == NULL)
7585 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7586 chunk_name, filename);
7587 ReadUnusedBytesFromFile(file, chunk_size);
7589 else if (chunk_info[i].size != -1 &&
7590 chunk_info[i].size != chunk_size)
7592 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7593 chunk_size, chunk_name, filename);
7594 ReadUnusedBytesFromFile(file, chunk_size);
7598 /* call function to load this tape chunk */
7599 int chunk_size_expected =
7600 (chunk_info[i].loader)(file, chunk_size, &tape);
7602 /* the size of some chunks cannot be checked before reading other
7603 chunks first (like "HEAD" and "BODY") that contain some header
7604 information, so check them here */
7605 if (chunk_size_expected != chunk_size)
7607 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7608 chunk_size, chunk_name, filename);
7616 tape.length_frames = GetTapeLengthFrames();
7617 tape.length_seconds = GetTapeLengthSeconds();
7620 printf("::: tape file version: %d\n", tape.file_version);
7621 printf("::: tape game version: %d\n", tape.game_version);
7622 printf("::: tape engine version: %d\n", tape.engine_version);
7626 void LoadTape(int nr)
7628 char *filename = getTapeFilename(nr);
7630 LoadTapeFromFilename(filename);
7633 void LoadSolutionTape(int nr)
7635 char *filename = getSolutionTapeFilename(nr);
7637 LoadTapeFromFilename(filename);
7639 if (TAPE_IS_EMPTY(tape) &&
7640 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7641 level.native_sp_level->demo.is_available)
7642 CopyNativeTape_SP_to_RND(&level);
7645 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7647 putFileVersion(file, tape->file_version);
7648 putFileVersion(file, tape->game_version);
7651 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7654 byte store_participating_players = 0;
7656 /* set bits for participating players for compact storage */
7657 for (i = 0; i < MAX_PLAYERS; i++)
7658 if (tape->player_participates[i])
7659 store_participating_players |= (1 << i);
7661 putFile32BitBE(file, tape->random_seed);
7662 putFile32BitBE(file, tape->date);
7663 putFile32BitBE(file, tape->length);
7665 putFile8Bit(file, store_participating_players);
7667 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7668 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7670 putFileVersion(file, tape->engine_version);
7673 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7675 int level_identifier_size = strlen(tape->level_identifier) + 1;
7678 putFile16BitBE(file, level_identifier_size);
7680 for (i = 0; i < level_identifier_size; i++)
7681 putFile8Bit(file, tape->level_identifier[i]);
7683 putFile16BitBE(file, tape->level_nr);
7686 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7690 for (i = 0; i < tape->length; i++)
7692 for (j = 0; j < MAX_PLAYERS; j++)
7693 if (tape->player_participates[j])
7694 putFile8Bit(file, tape->pos[i].action[j]);
7696 putFile8Bit(file, tape->pos[i].delay);
7700 void SaveTape(int nr)
7702 char *filename = getTapeFilename(nr);
7704 int num_participating_players = 0;
7705 int info_chunk_size;
7706 int body_chunk_size;
7709 InitTapeDirectory(leveldir_current->subdir);
7711 if (!(file = fopen(filename, MODE_WRITE)))
7713 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7717 tape.file_version = FILE_VERSION_ACTUAL;
7718 tape.game_version = GAME_VERSION_ACTUAL;
7720 /* count number of participating players */
7721 for (i = 0; i < MAX_PLAYERS; i++)
7722 if (tape.player_participates[i])
7723 num_participating_players++;
7725 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7726 body_chunk_size = (num_participating_players + 1) * tape.length;
7728 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7729 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7731 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7732 SaveTape_VERS(file, &tape);
7734 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7735 SaveTape_HEAD(file, &tape);
7737 putFileChunkBE(file, "INFO", info_chunk_size);
7738 SaveTape_INFO(file, &tape);
7740 putFileChunkBE(file, "BODY", body_chunk_size);
7741 SaveTape_BODY(file, &tape);
7745 SetFilePermissions(filename, PERMS_PRIVATE);
7747 tape.changed = FALSE;
7750 boolean SaveTapeChecked(int nr)
7752 char *filename = getTapeFilename(nr);
7753 boolean new_tape = !fileExists(filename);
7754 boolean tape_saved = FALSE;
7756 if (new_tape || Request("Replace old tape?", REQ_ASK))
7761 Request("Tape saved!", REQ_CONFIRM);
7769 void DumpTape(struct TapeInfo *tape)
7771 int tape_frame_counter;
7774 if (tape->no_valid_file)
7776 Error(ERR_WARN, "cannot dump -- no valid tape file found");
7782 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
7783 tape->level_nr, tape->file_version, tape->game_version);
7784 Print(" (effective engine version %08d)\n",
7785 tape->engine_version);
7786 Print("Level series identifier: '%s'\n", tape->level_identifier);
7789 tape_frame_counter = 0;
7791 for (i = 0; i < tape->length; i++)
7793 if (i >= MAX_TAPE_LEN)
7798 for (j = 0; j < MAX_PLAYERS; j++)
7800 if (tape->player_participates[j])
7802 int action = tape->pos[i].action[j];
7804 Print("%d:%02x ", j, action);
7805 Print("[%c%c%c%c|%c%c] - ",
7806 (action & JOY_LEFT ? '<' : ' '),
7807 (action & JOY_RIGHT ? '>' : ' '),
7808 (action & JOY_UP ? '^' : ' '),
7809 (action & JOY_DOWN ? 'v' : ' '),
7810 (action & JOY_BUTTON_1 ? '1' : ' '),
7811 (action & JOY_BUTTON_2 ? '2' : ' '));
7815 Print("(%03d) ", tape->pos[i].delay);
7816 Print("[%05d]\n", tape_frame_counter);
7818 tape_frame_counter += tape->pos[i].delay;
7825 /* ========================================================================= */
7826 /* score file functions */
7827 /* ========================================================================= */
7829 void LoadScore(int nr)
7832 char *filename = getScoreFilename(nr);
7833 char cookie[MAX_LINE_LEN];
7834 char line[MAX_LINE_LEN];
7838 /* always start with reliable default values */
7839 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7841 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
7842 highscore[i].Score = 0;
7845 if (!(file = fopen(filename, MODE_READ)))
7848 /* check file identifier */
7849 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
7851 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7852 cookie[strlen(cookie) - 1] = '\0';
7854 if (!checkCookieString(cookie, SCORE_COOKIE))
7856 Error(ERR_WARN, "unknown format of score file '%s'", filename);
7861 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7863 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
7864 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
7865 if (fgets(line, MAX_LINE_LEN, file) == NULL)
7868 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
7869 line[strlen(line) - 1] = '\0';
7871 for (line_ptr = line; *line_ptr; line_ptr++)
7873 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
7875 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
7876 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
7885 void SaveScore(int nr)
7888 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
7889 char *filename = getScoreFilename(nr);
7892 InitScoreDirectory(leveldir_current->subdir);
7894 if (!(file = fopen(filename, MODE_WRITE)))
7896 Error(ERR_WARN, "cannot save score for level %d", nr);
7900 fprintf(file, "%s\n\n", SCORE_COOKIE);
7902 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7903 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
7907 SetFilePermissions(filename, permissions);
7911 /* ========================================================================= */
7912 /* setup file functions */
7913 /* ========================================================================= */
7915 #define TOKEN_STR_PLAYER_PREFIX "player_"
7918 #define SETUP_TOKEN_PLAYER_NAME 0
7919 #define SETUP_TOKEN_SOUND 1
7920 #define SETUP_TOKEN_SOUND_LOOPS 2
7921 #define SETUP_TOKEN_SOUND_MUSIC 3
7922 #define SETUP_TOKEN_SOUND_SIMPLE 4
7923 #define SETUP_TOKEN_TOONS 5
7924 #define SETUP_TOKEN_SCROLL_DELAY 6
7925 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
7926 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE 8
7927 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY 9
7928 #define SETUP_TOKEN_FADE_SCREENS 10
7929 #define SETUP_TOKEN_AUTORECORD 11
7930 #define SETUP_TOKEN_SHOW_TITLESCREEN 12
7931 #define SETUP_TOKEN_QUICK_DOORS 13
7932 #define SETUP_TOKEN_TEAM_MODE 14
7933 #define SETUP_TOKEN_HANDICAP 15
7934 #define SETUP_TOKEN_SKIP_LEVELS 16
7935 #define SETUP_TOKEN_TIME_LIMIT 17
7936 #define SETUP_TOKEN_FULLSCREEN 18
7937 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT 19
7938 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY 20
7939 #define SETUP_TOKEN_SCREEN_RENDERING_MODE 21
7940 #define SETUP_TOKEN_ASK_ON_ESCAPE 22
7941 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 23
7942 #define SETUP_TOKEN_QUICK_SWITCH 24
7943 #define SETUP_TOKEN_INPUT_ON_FOCUS 25
7944 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 26
7945 #define SETUP_TOKEN_GAME_FRAME_DELAY 27
7946 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 28
7947 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 29
7948 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS 30
7949 #define SETUP_TOKEN_GRAPHICS_SET 31
7950 #define SETUP_TOKEN_SOUNDS_SET 32
7951 #define SETUP_TOKEN_MUSIC_SET 33
7952 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 34
7953 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 35
7954 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 36
7955 #define SETUP_TOKEN_VOLUME_SIMPLE 37
7956 #define SETUP_TOKEN_VOLUME_LOOPS 38
7957 #define SETUP_TOKEN_VOLUME_MUSIC 39
7958 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE 40
7959 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 41
7960 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE 42
7962 #define NUM_GLOBAL_SETUP_TOKENS 43
7965 #define SETUP_TOKEN_EDITOR_EL_CHARS 0
7966 #define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS 1
7967 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 2
7968 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 3
7969 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 4
7970 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 5
7972 #define NUM_EDITOR_SETUP_TOKENS 6
7974 /* editor cascade setup */
7975 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
7976 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
7977 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
7978 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
7979 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
7980 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
7981 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
7982 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
7983 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
7984 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
7985 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
7986 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
7987 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
7988 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
7989 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
7991 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
7993 /* shortcut setup */
7994 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
7995 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
7996 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
7997 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
7998 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
7999 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
8000 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
8001 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
8002 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
8003 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
8004 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
8005 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
8006 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
8007 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
8008 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
8009 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
8010 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
8011 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
8012 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
8013 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
8014 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
8016 #define NUM_SHORTCUT_SETUP_TOKENS 21
8019 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
8020 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
8021 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
8022 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
8023 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
8024 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
8025 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
8026 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
8027 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
8028 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
8029 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
8030 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
8031 #define SETUP_TOKEN_PLAYER_KEY_UP 12
8032 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
8033 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8034 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8036 #define NUM_PLAYER_SETUP_TOKENS 16
8039 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8040 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8041 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8043 #define NUM_SYSTEM_SETUP_TOKENS 3
8045 /* internal setup */
8046 #define SETUP_TOKEN_INT_PROGRAM_TITLE 0
8047 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR 1
8048 #define SETUP_TOKEN_INT_PROGRAM_EMAIL 2
8049 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE 3
8050 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 4
8051 #define SETUP_TOKEN_INT_PROGRAM_COMPANY 5
8052 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 6
8053 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 7
8054 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 8
8055 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 9
8056 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 10
8057 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 11
8058 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 12
8059 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 13
8060 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 14
8061 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH 15
8062 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT 16
8064 #define NUM_INTERNAL_SETUP_TOKENS 17
8067 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0 0
8068 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1 1
8069 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2 2
8070 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3 3
8071 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4 4
8072 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5 5
8073 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6 6
8074 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7 7
8075 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8 8
8076 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9 9
8077 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0 10
8078 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1 11
8079 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2 12
8080 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3 13
8081 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4 14
8082 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5 15
8083 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6 16
8084 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7 17
8085 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8 18
8086 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9 19
8087 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8088 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8090 #define NUM_DEBUG_SETUP_TOKENS 22
8093 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8095 #define NUM_OPTIONS_SETUP_TOKENS 1
8098 static struct SetupInfo si;
8099 static struct SetupEditorInfo sei;
8100 static struct SetupEditorCascadeInfo seci;
8101 static struct SetupShortcutInfo ssi;
8102 static struct SetupInputInfo sii;
8103 static struct SetupSystemInfo syi;
8104 static struct SetupInternalInfo sxi;
8105 static struct SetupDebugInfo sdi;
8106 static struct OptionInfo soi;
8108 static struct TokenInfo global_setup_tokens[] =
8110 { TYPE_STRING, &si.player_name, "player_name" },
8111 { TYPE_SWITCH, &si.sound, "sound" },
8112 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8113 { TYPE_SWITCH, &si.sound_music, "background_music" },
8114 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8115 { TYPE_SWITCH, &si.toons, "toons" },
8116 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8117 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8118 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8119 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8120 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8121 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8122 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8123 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8124 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8125 { TYPE_SWITCH, &si.handicap, "handicap" },
8126 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8127 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8128 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8129 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8130 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8131 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8132 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8133 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8134 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8135 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8136 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8137 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8138 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8139 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8140 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8141 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8142 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8143 { TYPE_STRING, &si.music_set, "music_set" },
8144 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8145 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8146 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8147 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8148 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8149 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8150 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8151 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8152 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8155 static struct TokenInfo editor_setup_tokens[] =
8157 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
8158 { TYPE_SWITCH, &sei.el_steel_chars, "editor.el_steel_chars" },
8159 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8160 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8161 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8162 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8165 static struct TokenInfo editor_cascade_setup_tokens[] =
8167 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8168 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8169 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8170 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8171 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8172 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8173 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8174 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8175 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8176 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8177 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8178 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8179 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8180 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8181 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8184 static struct TokenInfo shortcut_setup_tokens[] =
8186 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8187 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8188 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8189 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8190 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8191 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8192 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8193 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8194 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8195 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8196 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8197 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8198 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8199 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8200 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8201 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8202 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8203 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8204 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8205 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8206 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8209 static struct TokenInfo player_setup_tokens[] =
8211 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8212 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8213 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8214 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8215 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8216 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8217 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8218 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8219 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8220 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8221 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8222 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8223 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8224 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8225 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8226 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8229 static struct TokenInfo system_setup_tokens[] =
8231 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8232 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8233 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8236 static struct TokenInfo internal_setup_tokens[] =
8238 { TYPE_STRING, &sxi.program_title, "program_title" },
8239 { TYPE_STRING, &sxi.program_author, "program_author" },
8240 { TYPE_STRING, &sxi.program_email, "program_email" },
8241 { TYPE_STRING, &sxi.program_website, "program_website" },
8242 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8243 { TYPE_STRING, &sxi.program_company, "program_company" },
8244 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8245 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8246 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8247 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8248 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8249 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8250 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8251 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8252 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8253 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8254 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8257 static struct TokenInfo debug_setup_tokens[] =
8259 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8260 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8261 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8262 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8263 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8264 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8265 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8266 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8267 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8268 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8269 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8270 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8271 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8272 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8273 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8274 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8275 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8276 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8277 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8278 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8279 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8280 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8283 static struct TokenInfo options_setup_tokens[] =
8285 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8288 static char *get_corrected_login_name(char *login_name)
8290 /* needed because player name must be a fixed length string */
8291 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8293 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8294 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8296 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8297 if (strchr(login_name_new, ' '))
8298 *strchr(login_name_new, ' ') = '\0';
8300 return login_name_new;
8303 static void setSetupInfoToDefaults(struct SetupInfo *si)
8307 si->player_name = get_corrected_login_name(getLoginName());
8310 si->sound_loops = TRUE;
8311 si->sound_music = TRUE;
8312 si->sound_simple = TRUE;
8314 si->scroll_delay = TRUE;
8315 si->scroll_delay_value = STD_SCROLL_DELAY;
8316 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8317 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8318 si->fade_screens = TRUE;
8319 si->autorecord = TRUE;
8320 si->show_titlescreen = TRUE;
8321 si->quick_doors = FALSE;
8322 si->team_mode = FALSE;
8323 si->handicap = TRUE;
8324 si->skip_levels = TRUE;
8325 si->time_limit = TRUE;
8326 si->fullscreen = FALSE;
8327 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8328 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8329 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8330 si->ask_on_escape = TRUE;
8331 si->ask_on_escape_editor = TRUE;
8332 si->quick_switch = FALSE;
8333 si->input_on_focus = FALSE;
8334 si->prefer_aga_graphics = TRUE;
8335 si->game_frame_delay = GAME_FRAME_DELAY;
8336 si->sp_show_border_elements = FALSE;
8337 si->small_game_graphics = FALSE;
8338 si->show_snapshot_buttons = FALSE;
8340 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8341 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8342 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8344 si->override_level_graphics = FALSE;
8345 si->override_level_sounds = FALSE;
8346 si->override_level_music = FALSE;
8348 si->volume_simple = 100; /* percent */
8349 si->volume_loops = 100; /* percent */
8350 si->volume_music = 100; /* percent */
8352 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8353 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8354 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8356 si->editor.el_boulderdash = TRUE;
8357 si->editor.el_emerald_mine = TRUE;
8358 si->editor.el_emerald_mine_club = TRUE;
8359 si->editor.el_more = TRUE;
8360 si->editor.el_sokoban = TRUE;
8361 si->editor.el_supaplex = TRUE;
8362 si->editor.el_diamond_caves = TRUE;
8363 si->editor.el_dx_boulderdash = TRUE;
8364 si->editor.el_chars = TRUE;
8365 si->editor.el_steel_chars = TRUE;
8366 si->editor.el_custom = TRUE;
8368 si->editor.el_headlines = TRUE;
8369 si->editor.el_user_defined = FALSE;
8370 si->editor.el_dynamic = TRUE;
8372 si->editor.show_element_token = FALSE;
8374 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8375 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8376 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8378 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8379 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8380 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8381 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8382 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8384 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8385 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8386 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8387 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8388 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8389 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8391 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8392 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8393 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8395 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8396 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8397 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8398 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8400 for (i = 0; i < MAX_PLAYERS; i++)
8402 si->input[i].use_joystick = FALSE;
8403 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8404 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8405 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8406 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8407 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8408 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8409 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8410 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8411 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8412 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8413 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8414 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8415 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8416 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8417 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8420 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8421 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8422 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8424 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8425 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8426 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8427 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8428 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8429 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8431 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8433 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8434 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8435 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8437 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8438 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8439 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8441 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8442 si->internal.choose_from_top_leveldir = FALSE;
8444 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
8445 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8447 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8448 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8449 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8450 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8451 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8452 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8453 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8454 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8455 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8456 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8458 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8459 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8460 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8461 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8462 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8463 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8464 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8465 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8466 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8467 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8469 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8470 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
8472 si->options.verbose = FALSE;
8474 #if defined(PLATFORM_ANDROID)
8475 si->fullscreen = TRUE;
8479 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8481 si->editor_cascade.el_bd = TRUE;
8482 si->editor_cascade.el_em = TRUE;
8483 si->editor_cascade.el_emc = TRUE;
8484 si->editor_cascade.el_rnd = TRUE;
8485 si->editor_cascade.el_sb = TRUE;
8486 si->editor_cascade.el_sp = TRUE;
8487 si->editor_cascade.el_dc = TRUE;
8488 si->editor_cascade.el_dx = TRUE;
8490 si->editor_cascade.el_chars = FALSE;
8491 si->editor_cascade.el_steel_chars = FALSE;
8492 si->editor_cascade.el_ce = FALSE;
8493 si->editor_cascade.el_ge = FALSE;
8494 si->editor_cascade.el_ref = FALSE;
8495 si->editor_cascade.el_user = FALSE;
8496 si->editor_cascade.el_dynamic = FALSE;
8499 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8503 if (!setup_file_hash)
8508 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8509 setSetupInfo(global_setup_tokens, i,
8510 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
8515 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8516 setSetupInfo(editor_setup_tokens, i,
8517 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
8520 /* shortcut setup */
8521 ssi = setup.shortcut;
8522 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8523 setSetupInfo(shortcut_setup_tokens, i,
8524 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
8525 setup.shortcut = ssi;
8528 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8532 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8534 sii = setup.input[pnr];
8535 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8537 char full_token[100];
8539 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8540 setSetupInfo(player_setup_tokens, i,
8541 getHashEntry(setup_file_hash, full_token));
8543 setup.input[pnr] = sii;
8548 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8549 setSetupInfo(system_setup_tokens, i,
8550 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
8553 /* internal setup */
8554 sxi = setup.internal;
8555 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8556 setSetupInfo(internal_setup_tokens, i,
8557 getHashEntry(setup_file_hash, internal_setup_tokens[i].text));
8558 setup.internal = sxi;
8562 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8563 setSetupInfo(debug_setup_tokens, i,
8564 getHashEntry(setup_file_hash, debug_setup_tokens[i].text));
8568 soi = setup.options;
8569 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8570 setSetupInfo(options_setup_tokens, i,
8571 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
8572 setup.options = soi;
8575 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8579 if (!setup_file_hash)
8582 /* editor cascade setup */
8583 seci = setup.editor_cascade;
8584 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8585 setSetupInfo(editor_cascade_setup_tokens, i,
8586 getHashEntry(setup_file_hash,
8587 editor_cascade_setup_tokens[i].text));
8588 setup.editor_cascade = seci;
8591 void LoadSetupFromFilename(char *filename)
8593 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
8595 if (setup_file_hash)
8597 decodeSetupFileHash(setup_file_hash);
8599 freeSetupFileHash(setup_file_hash);
8603 Error(ERR_WARN, "using default setup values");
8607 static void LoadSetup_SpecialPostProcessing()
8609 char *player_name_new;
8611 /* needed to work around problems with fixed length strings */
8612 player_name_new = get_corrected_login_name(setup.player_name);
8613 free(setup.player_name);
8614 setup.player_name = player_name_new;
8616 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
8617 if (setup.scroll_delay == FALSE)
8619 setup.scroll_delay_value = MIN_SCROLL_DELAY;
8620 setup.scroll_delay = TRUE; /* now always "on" */
8623 /* make sure that scroll delay value stays inside valid range */
8624 setup.scroll_delay_value =
8625 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
8632 /* always start with reliable default values */
8633 setSetupInfoToDefaults(&setup);
8635 /* try to load setup values from default setup file */
8636 filename = getDefaultSetupFilename();
8638 if (fileExists(filename))
8639 LoadSetupFromFilename(filename);
8641 /* try to load setup values from user setup file */
8642 filename = getSetupFilename();
8644 LoadSetupFromFilename(filename);
8646 LoadSetup_SpecialPostProcessing();
8649 void LoadSetup_EditorCascade()
8651 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8652 SetupFileHash *setup_file_hash = NULL;
8654 /* always start with reliable default values */
8655 setSetupInfoToDefaults_EditorCascade(&setup);
8657 setup_file_hash = loadSetupFileHash(filename);
8659 if (setup_file_hash)
8661 decodeSetupFileHash_EditorCascade(setup_file_hash);
8663 freeSetupFileHash(setup_file_hash);
8671 char *filename = getSetupFilename();
8675 InitUserDataDirectory();
8677 if (!(file = fopen(filename, MODE_WRITE)))
8679 Error(ERR_WARN, "cannot write setup file '%s'", filename);
8683 fprintFileHeader(file, SETUP_FILENAME);
8687 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8689 /* just to make things nicer :) */
8690 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
8691 i == SETUP_TOKEN_GRAPHICS_SET ||
8692 i == SETUP_TOKEN_VOLUME_SIMPLE ||
8693 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
8694 fprintf(file, "\n");
8696 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
8701 fprintf(file, "\n");
8702 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8703 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
8705 /* shortcut setup */
8706 ssi = setup.shortcut;
8707 fprintf(file, "\n");
8708 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8709 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
8712 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8716 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8717 fprintf(file, "\n");
8719 sii = setup.input[pnr];
8720 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8721 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
8726 fprintf(file, "\n");
8727 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8728 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
8730 /* internal setup */
8731 /* (internal setup values not saved to user setup file) */
8735 fprintf(file, "\n");
8736 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8737 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
8740 soi = setup.options;
8741 fprintf(file, "\n");
8742 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8743 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
8747 SetFilePermissions(filename, PERMS_PRIVATE);
8750 void SaveSetup_EditorCascade()
8752 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8756 InitUserDataDirectory();
8758 if (!(file = fopen(filename, MODE_WRITE)))
8760 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
8765 fprintFileHeader(file, EDITORCASCADE_FILENAME);
8767 seci = setup.editor_cascade;
8768 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8769 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
8773 SetFilePermissions(filename, PERMS_PRIVATE);
8778 void LoadCustomElementDescriptions()
8780 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8781 SetupFileHash *setup_file_hash;
8784 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8786 if (element_info[i].custom_description != NULL)
8788 free(element_info[i].custom_description);
8789 element_info[i].custom_description = NULL;
8793 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8796 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8798 char *token = getStringCat2(element_info[i].token_name, ".name");
8799 char *value = getHashEntry(setup_file_hash, token);
8802 element_info[i].custom_description = getStringCopy(value);
8807 freeSetupFileHash(setup_file_hash);
8810 static int getElementFromToken(char *token)
8812 char *value = getHashEntry(element_token_hash, token);
8817 Error(ERR_WARN, "unknown element token '%s'", token);
8819 return EL_UNDEFINED;
8822 static int get_token_parameter_value(char *token, char *value_raw)
8826 if (token == NULL || value_raw == NULL)
8827 return ARG_UNDEFINED_VALUE;
8829 suffix = strrchr(token, '.');
8833 if (strEqual(suffix, ".element"))
8834 return getElementFromToken(value_raw);
8836 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
8837 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
8840 void InitMenuDesignSettings_Static()
8844 /* always start with reliable default values from static default config */
8845 for (i = 0; image_config_vars[i].token != NULL; i++)
8847 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
8850 *image_config_vars[i].value =
8851 get_token_parameter_value(image_config_vars[i].token, value);
8855 static void InitMenuDesignSettings_SpecialPreProcessing()
8859 /* the following initializes hierarchical values from static configuration */
8861 /* special case: initialize "ARG_DEFAULT" values in static default config */
8862 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
8863 titlescreen_initial_first_default.fade_mode =
8864 title_initial_first_default.fade_mode;
8865 titlescreen_initial_first_default.fade_delay =
8866 title_initial_first_default.fade_delay;
8867 titlescreen_initial_first_default.post_delay =
8868 title_initial_first_default.post_delay;
8869 titlescreen_initial_first_default.auto_delay =
8870 title_initial_first_default.auto_delay;
8871 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
8872 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
8873 titlescreen_first_default.post_delay = title_first_default.post_delay;
8874 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
8875 titlemessage_initial_first_default.fade_mode =
8876 title_initial_first_default.fade_mode;
8877 titlemessage_initial_first_default.fade_delay =
8878 title_initial_first_default.fade_delay;
8879 titlemessage_initial_first_default.post_delay =
8880 title_initial_first_default.post_delay;
8881 titlemessage_initial_first_default.auto_delay =
8882 title_initial_first_default.auto_delay;
8883 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
8884 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
8885 titlemessage_first_default.post_delay = title_first_default.post_delay;
8886 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
8888 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
8889 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
8890 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
8891 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
8892 titlescreen_default.fade_mode = title_default.fade_mode;
8893 titlescreen_default.fade_delay = title_default.fade_delay;
8894 titlescreen_default.post_delay = title_default.post_delay;
8895 titlescreen_default.auto_delay = title_default.auto_delay;
8896 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
8897 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
8898 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
8899 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
8900 titlemessage_default.fade_mode = title_default.fade_mode;
8901 titlemessage_default.fade_delay = title_default.fade_delay;
8902 titlemessage_default.post_delay = title_default.post_delay;
8903 titlemessage_default.auto_delay = title_default.auto_delay;
8905 /* special case: initialize "ARG_DEFAULT" values in static default config */
8906 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
8907 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
8909 titlescreen_initial_first[i] = titlescreen_initial_first_default;
8910 titlescreen_first[i] = titlescreen_first_default;
8911 titlemessage_initial_first[i] = titlemessage_initial_first_default;
8912 titlemessage_first[i] = titlemessage_first_default;
8914 titlescreen_initial[i] = titlescreen_initial_default;
8915 titlescreen[i] = titlescreen_default;
8916 titlemessage_initial[i] = titlemessage_initial_default;
8917 titlemessage[i] = titlemessage_default;
8920 /* special case: initialize "ARG_DEFAULT" values in static default config */
8921 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
8922 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8924 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
8927 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
8928 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
8929 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
8932 /* special case: initialize "ARG_DEFAULT" values in static default config */
8933 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
8934 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8936 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
8937 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
8938 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
8940 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
8943 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
8947 static void InitMenuDesignSettings_SpecialPostProcessing()
8951 struct XY *dst, *src;
8955 { &game.button.save, &game.button.stop },
8956 { &game.button.pause2, &game.button.pause },
8957 { &game.button.load, &game.button.play },
8958 { &game.button.undo, &game.button.stop },
8959 { &game.button.redo, &game.button.play },
8965 /* special case: initialize later added SETUP list size from LEVELS value */
8966 if (menu.list_size[GAME_MODE_SETUP] == -1)
8967 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
8969 /* set default position for snapshot buttons to stop/pause/play buttons */
8970 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
8971 if ((*game_buttons_xy[i].dst).x == -1 &&
8972 (*game_buttons_xy[i].dst).y == -1)
8973 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
8976 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
8980 struct XYTileSize *dst, *src;
8983 editor_buttons_xy[] =
8986 &editor.button.element_left, &editor.palette.element_left,
8987 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
8990 &editor.button.element_middle, &editor.palette.element_middle,
8991 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
8994 &editor.button.element_right, &editor.palette.element_right,
8995 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9002 /* set default position for element buttons to element graphics */
9003 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9005 if ((*editor_buttons_xy[i].dst).x == -1 &&
9006 (*editor_buttons_xy[i].dst).y == -1)
9008 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9010 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9012 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9017 static void LoadMenuDesignSettingsFromFilename(char *filename)
9019 static struct TitleFadingInfo tfi;
9020 static struct TitleMessageInfo tmi;
9021 static struct TokenInfo title_tokens[] =
9023 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9024 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9025 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9026 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9030 static struct TokenInfo titlemessage_tokens[] =
9032 { TYPE_INTEGER, &tmi.x, ".x" },
9033 { TYPE_INTEGER, &tmi.y, ".y" },
9034 { TYPE_INTEGER, &tmi.width, ".width" },
9035 { TYPE_INTEGER, &tmi.height, ".height" },
9036 { TYPE_INTEGER, &tmi.chars, ".chars" },
9037 { TYPE_INTEGER, &tmi.lines, ".lines" },
9038 { TYPE_INTEGER, &tmi.align, ".align" },
9039 { TYPE_INTEGER, &tmi.valign, ".valign" },
9040 { TYPE_INTEGER, &tmi.font, ".font" },
9041 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9042 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9043 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9044 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9045 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9046 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9047 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9048 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9054 struct TitleFadingInfo *info;
9059 /* initialize first titles from "enter screen" definitions, if defined */
9060 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9061 { &title_first_default, "menu.enter_screen.TITLE" },
9063 /* initialize title screens from "next screen" definitions, if defined */
9064 { &title_initial_default, "menu.next_screen.TITLE" },
9065 { &title_default, "menu.next_screen.TITLE" },
9071 struct TitleMessageInfo *array;
9074 titlemessage_arrays[] =
9076 /* initialize first titles from "enter screen" definitions, if defined */
9077 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9078 { titlescreen_first, "menu.enter_screen.TITLE" },
9079 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9080 { titlemessage_first, "menu.enter_screen.TITLE" },
9082 /* initialize titles from "next screen" definitions, if defined */
9083 { titlescreen_initial, "menu.next_screen.TITLE" },
9084 { titlescreen, "menu.next_screen.TITLE" },
9085 { titlemessage_initial, "menu.next_screen.TITLE" },
9086 { titlemessage, "menu.next_screen.TITLE" },
9088 /* overwrite titles with title definitions, if defined */
9089 { titlescreen_initial_first, "[title_initial]" },
9090 { titlescreen_first, "[title]" },
9091 { titlemessage_initial_first, "[title_initial]" },
9092 { titlemessage_first, "[title]" },
9094 { titlescreen_initial, "[title_initial]" },
9095 { titlescreen, "[title]" },
9096 { titlemessage_initial, "[title_initial]" },
9097 { titlemessage, "[title]" },
9099 /* overwrite titles with title screen/message definitions, if defined */
9100 { titlescreen_initial_first, "[titlescreen_initial]" },
9101 { titlescreen_first, "[titlescreen]" },
9102 { titlemessage_initial_first, "[titlemessage_initial]" },
9103 { titlemessage_first, "[titlemessage]" },
9105 { titlescreen_initial, "[titlescreen_initial]" },
9106 { titlescreen, "[titlescreen]" },
9107 { titlemessage_initial, "[titlemessage_initial]" },
9108 { titlemessage, "[titlemessage]" },
9112 SetupFileHash *setup_file_hash;
9115 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9118 /* the following initializes hierarchical values from dynamic configuration */
9120 /* special case: initialize with default values that may be overwritten */
9121 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9122 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9124 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9125 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9126 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9128 if (value_1 != NULL)
9129 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9130 if (value_2 != NULL)
9131 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9132 if (value_3 != NULL)
9133 menu.list_size[i] = get_integer_from_string(value_3);
9136 /* special case: initialize with default values that may be overwritten */
9137 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9138 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9140 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9141 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9143 if (value_1 != NULL)
9144 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9145 if (value_2 != NULL)
9146 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9148 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9150 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9152 if (value_1 != NULL)
9153 menu.list_size_info[i] = get_integer_from_string(value_1);
9157 /* special case: initialize with default values that may be overwritten */
9158 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9159 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9161 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9162 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9164 if (value_1 != NULL)
9165 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9166 if (value_2 != NULL)
9167 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9170 /* special case: initialize with default values that may be overwritten */
9171 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9172 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9174 char *token_1 = "menu.enter_screen.fade_mode";
9175 char *token_2 = "menu.enter_screen.fade_delay";
9176 char *token_3 = "menu.enter_screen.post_delay";
9177 char *token_4 = "menu.leave_screen.fade_mode";
9178 char *token_5 = "menu.leave_screen.fade_delay";
9179 char *token_6 = "menu.leave_screen.post_delay";
9180 char *token_7 = "menu.next_screen.fade_mode";
9181 char *token_8 = "menu.next_screen.fade_delay";
9182 char *token_9 = "menu.next_screen.post_delay";
9183 char *value_1 = getHashEntry(setup_file_hash, token_1);
9184 char *value_2 = getHashEntry(setup_file_hash, token_2);
9185 char *value_3 = getHashEntry(setup_file_hash, token_3);
9186 char *value_4 = getHashEntry(setup_file_hash, token_4);
9187 char *value_5 = getHashEntry(setup_file_hash, token_5);
9188 char *value_6 = getHashEntry(setup_file_hash, token_6);
9189 char *value_7 = getHashEntry(setup_file_hash, token_7);
9190 char *value_8 = getHashEntry(setup_file_hash, token_8);
9191 char *value_9 = getHashEntry(setup_file_hash, token_9);
9193 if (value_1 != NULL)
9194 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9196 if (value_2 != NULL)
9197 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9199 if (value_3 != NULL)
9200 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9202 if (value_4 != NULL)
9203 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9205 if (value_5 != NULL)
9206 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9208 if (value_6 != NULL)
9209 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9211 if (value_7 != NULL)
9212 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9214 if (value_8 != NULL)
9215 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9217 if (value_9 != NULL)
9218 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9222 /* special case: initialize with default values that may be overwritten */
9223 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9224 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9226 char *token_w1 = "viewport.window.width";
9227 char *token_w2 = "viewport.window.height";
9228 char *token_01 = "viewport.playfield.x";
9229 char *token_02 = "viewport.playfield.y";
9230 char *token_03 = "viewport.playfield.width";
9231 char *token_04 = "viewport.playfield.height";
9232 char *token_05 = "viewport.playfield.border_size";
9233 char *token_06 = "viewport.door_1.x";
9234 char *token_07 = "viewport.door_1.y";
9235 char *token_08 = "viewport.door_1.width";
9236 char *token_09 = "viewport.door_1.height";
9237 char *token_10 = "viewport.door_1.border_size";
9238 char *token_11 = "viewport.door_2.x";
9239 char *token_12 = "viewport.door_2.y";
9240 char *token_13 = "viewport.door_2.width";
9241 char *token_14 = "viewport.door_2.height";
9242 char *token_15 = "viewport.door_2.border_size";
9243 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9244 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9245 char *value_01 = getHashEntry(setup_file_hash, token_01);
9246 char *value_02 = getHashEntry(setup_file_hash, token_02);
9247 char *value_03 = getHashEntry(setup_file_hash, token_03);
9248 char *value_04 = getHashEntry(setup_file_hash, token_04);
9249 char *value_05 = getHashEntry(setup_file_hash, token_05);
9250 char *value_06 = getHashEntry(setup_file_hash, token_06);
9251 char *value_07 = getHashEntry(setup_file_hash, token_07);
9252 char *value_08 = getHashEntry(setup_file_hash, token_08);
9253 char *value_09 = getHashEntry(setup_file_hash, token_09);
9254 char *value_10 = getHashEntry(setup_file_hash, token_10);
9255 char *value_11 = getHashEntry(setup_file_hash, token_11);
9256 char *value_12 = getHashEntry(setup_file_hash, token_12);
9257 char *value_13 = getHashEntry(setup_file_hash, token_13);
9258 char *value_14 = getHashEntry(setup_file_hash, token_14);
9259 char *value_15 = getHashEntry(setup_file_hash, token_15);
9261 if (value_w1 != NULL)
9262 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9263 if (value_w2 != NULL)
9264 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9265 if (value_01 != NULL)
9266 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9267 if (value_02 != NULL)
9268 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9269 if (value_03 != NULL)
9270 viewport.playfield[i].width = get_token_parameter_value(token_03,
9272 if (value_04 != NULL)
9273 viewport.playfield[i].height = get_token_parameter_value(token_04,
9275 if (value_05 != NULL)
9276 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9278 if (value_06 != NULL)
9279 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9280 if (value_07 != NULL)
9281 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9282 if (value_08 != NULL)
9283 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9284 if (value_09 != NULL)
9285 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9286 if (value_10 != NULL)
9287 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9289 if (value_11 != NULL)
9290 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9291 if (value_12 != NULL)
9292 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9293 if (value_13 != NULL)
9294 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9295 if (value_14 != NULL)
9296 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9297 if (value_15 != NULL)
9298 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9302 /* special case: initialize with default values that may be overwritten */
9303 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9304 for (i = 0; title_info[i].info != NULL; i++)
9306 struct TitleFadingInfo *info = title_info[i].info;
9307 char *base_token = title_info[i].text;
9309 for (j = 0; title_tokens[j].type != -1; j++)
9311 char *token = getStringCat2(base_token, title_tokens[j].text);
9312 char *value = getHashEntry(setup_file_hash, token);
9316 int parameter_value = get_token_parameter_value(token, value);
9320 *(int *)title_tokens[j].value = (int)parameter_value;
9329 /* special case: initialize with default values that may be overwritten */
9330 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9331 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9333 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9334 char *base_token = titlemessage_arrays[i].text;
9336 for (j = 0; titlemessage_tokens[j].type != -1; j++)
9338 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9339 char *value = getHashEntry(setup_file_hash, token);
9343 int parameter_value = get_token_parameter_value(token, value);
9345 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9349 if (titlemessage_tokens[j].type == TYPE_INTEGER)
9350 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
9352 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9362 /* read (and overwrite with) values that may be specified in config file */
9363 for (i = 0; image_config_vars[i].token != NULL; i++)
9365 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9367 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9368 if (value != NULL && !strEqual(value, ARG_DEFAULT))
9369 *image_config_vars[i].value =
9370 get_token_parameter_value(image_config_vars[i].token, value);
9373 freeSetupFileHash(setup_file_hash);
9376 void LoadMenuDesignSettings()
9378 char *filename_base = UNDEFINED_FILENAME, *filename_local;
9380 InitMenuDesignSettings_Static();
9381 InitMenuDesignSettings_SpecialPreProcessing();
9383 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9385 /* first look for special settings configured in level series config */
9386 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9388 if (fileExists(filename_base))
9389 LoadMenuDesignSettingsFromFilename(filename_base);
9392 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9394 if (filename_local != NULL && !strEqual(filename_base, filename_local))
9395 LoadMenuDesignSettingsFromFilename(filename_local);
9397 InitMenuDesignSettings_SpecialPostProcessing();
9400 void LoadMenuDesignSettings_AfterGraphics()
9402 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9405 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9407 char *filename = getEditorSetupFilename();
9408 SetupFileList *setup_file_list, *list;
9409 SetupFileHash *element_hash;
9410 int num_unknown_tokens = 0;
9413 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9416 element_hash = newSetupFileHash();
9418 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9419 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9421 /* determined size may be larger than needed (due to unknown elements) */
9423 for (list = setup_file_list; list != NULL; list = list->next)
9426 /* add space for up to 3 more elements for padding that may be needed */
9429 /* free memory for old list of elements, if needed */
9430 checked_free(*elements);
9432 /* allocate memory for new list of elements */
9433 *elements = checked_malloc(*num_elements * sizeof(int));
9436 for (list = setup_file_list; list != NULL; list = list->next)
9438 char *value = getHashEntry(element_hash, list->token);
9440 if (value == NULL) /* try to find obsolete token mapping */
9442 char *mapped_token = get_mapped_token(list->token);
9444 if (mapped_token != NULL)
9446 value = getHashEntry(element_hash, mapped_token);
9454 (*elements)[(*num_elements)++] = atoi(value);
9458 if (num_unknown_tokens == 0)
9460 Error(ERR_INFO_LINE, "-");
9461 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9462 Error(ERR_INFO, "- config file: '%s'", filename);
9464 num_unknown_tokens++;
9467 Error(ERR_INFO, "- token: '%s'", list->token);
9471 if (num_unknown_tokens > 0)
9472 Error(ERR_INFO_LINE, "-");
9474 while (*num_elements % 4) /* pad with empty elements, if needed */
9475 (*elements)[(*num_elements)++] = EL_EMPTY;
9477 freeSetupFileList(setup_file_list);
9478 freeSetupFileHash(element_hash);
9481 for (i = 0; i < *num_elements; i++)
9482 printf("editor: element '%s' [%d]\n",
9483 element_info[(*elements)[i]].token_name, (*elements)[i]);
9487 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9490 SetupFileHash *setup_file_hash = NULL;
9491 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9492 char *filename_music, *filename_prefix, *filename_info;
9498 token_to_value_ptr[] =
9500 { "title_header", &tmp_music_file_info.title_header },
9501 { "artist_header", &tmp_music_file_info.artist_header },
9502 { "album_header", &tmp_music_file_info.album_header },
9503 { "year_header", &tmp_music_file_info.year_header },
9505 { "title", &tmp_music_file_info.title },
9506 { "artist", &tmp_music_file_info.artist },
9507 { "album", &tmp_music_file_info.album },
9508 { "year", &tmp_music_file_info.year },
9514 filename_music = (is_sound ? getCustomSoundFilename(basename) :
9515 getCustomMusicFilename(basename));
9517 if (filename_music == NULL)
9520 /* ---------- try to replace file extension ---------- */
9522 filename_prefix = getStringCopy(filename_music);
9523 if (strrchr(filename_prefix, '.') != NULL)
9524 *strrchr(filename_prefix, '.') = '\0';
9525 filename_info = getStringCat2(filename_prefix, ".txt");
9527 if (fileExists(filename_info))
9528 setup_file_hash = loadSetupFileHash(filename_info);
9530 free(filename_prefix);
9531 free(filename_info);
9533 if (setup_file_hash == NULL)
9535 /* ---------- try to add file extension ---------- */
9537 filename_prefix = getStringCopy(filename_music);
9538 filename_info = getStringCat2(filename_prefix, ".txt");
9540 if (fileExists(filename_info))
9541 setup_file_hash = loadSetupFileHash(filename_info);
9543 free(filename_prefix);
9544 free(filename_info);
9547 if (setup_file_hash == NULL)
9550 /* ---------- music file info found ---------- */
9552 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
9554 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
9556 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
9558 *token_to_value_ptr[i].value_ptr =
9559 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
9562 tmp_music_file_info.basename = getStringCopy(basename);
9563 tmp_music_file_info.music = music;
9564 tmp_music_file_info.is_sound = is_sound;
9566 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
9567 *new_music_file_info = tmp_music_file_info;
9569 return new_music_file_info;
9572 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
9574 return get_music_file_info_ext(basename, music, FALSE);
9577 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
9579 return get_music_file_info_ext(basename, sound, TRUE);
9582 static boolean music_info_listed_ext(struct MusicFileInfo *list,
9583 char *basename, boolean is_sound)
9585 for (; list != NULL; list = list->next)
9586 if (list->is_sound == is_sound && strEqual(list->basename, basename))
9592 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
9594 return music_info_listed_ext(list, basename, FALSE);
9597 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
9599 return music_info_listed_ext(list, basename, TRUE);
9602 void LoadMusicInfo()
9604 char *music_directory = getCustomMusicDirectory();
9605 int num_music = getMusicListSize();
9606 int num_music_noconf = 0;
9607 int num_sounds = getSoundListSize();
9609 DirectoryEntry *dir_entry;
9610 struct FileInfo *music, *sound;
9611 struct MusicFileInfo *next, **new;
9614 while (music_file_info != NULL)
9616 next = music_file_info->next;
9618 checked_free(music_file_info->basename);
9620 checked_free(music_file_info->title_header);
9621 checked_free(music_file_info->artist_header);
9622 checked_free(music_file_info->album_header);
9623 checked_free(music_file_info->year_header);
9625 checked_free(music_file_info->title);
9626 checked_free(music_file_info->artist);
9627 checked_free(music_file_info->album);
9628 checked_free(music_file_info->year);
9630 free(music_file_info);
9632 music_file_info = next;
9635 new = &music_file_info;
9637 for (i = 0; i < num_music; i++)
9639 music = getMusicListEntry(i);
9641 if (music->filename == NULL)
9644 if (strEqual(music->filename, UNDEFINED_FILENAME))
9647 /* a configured file may be not recognized as music */
9648 if (!FileIsMusic(music->filename))
9651 if (!music_info_listed(music_file_info, music->filename))
9653 *new = get_music_file_info(music->filename, i);
9656 new = &(*new)->next;
9660 if ((dir = openDirectory(music_directory)) == NULL)
9662 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
9666 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
9668 char *basename = dir_entry->basename;
9669 boolean music_already_used = FALSE;
9672 /* skip all music files that are configured in music config file */
9673 for (i = 0; i < num_music; i++)
9675 music = getMusicListEntry(i);
9677 if (music->filename == NULL)
9680 if (strEqual(basename, music->filename))
9682 music_already_used = TRUE;
9687 if (music_already_used)
9690 if (!FileIsMusic(dir_entry->filename))
9693 if (!music_info_listed(music_file_info, basename))
9695 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
9698 new = &(*new)->next;
9704 closeDirectory(dir);
9706 for (i = 0; i < num_sounds; i++)
9708 sound = getSoundListEntry(i);
9710 if (sound->filename == NULL)
9713 if (strEqual(sound->filename, UNDEFINED_FILENAME))
9716 /* a configured file may be not recognized as sound */
9717 if (!FileIsSound(sound->filename))
9720 if (!sound_info_listed(music_file_info, sound->filename))
9722 *new = get_sound_file_info(sound->filename, i);
9724 new = &(*new)->next;
9729 void add_helpanim_entry(int element, int action, int direction, int delay,
9730 int *num_list_entries)
9732 struct HelpAnimInfo *new_list_entry;
9733 (*num_list_entries)++;
9736 checked_realloc(helpanim_info,
9737 *num_list_entries * sizeof(struct HelpAnimInfo));
9738 new_list_entry = &helpanim_info[*num_list_entries - 1];
9740 new_list_entry->element = element;
9741 new_list_entry->action = action;
9742 new_list_entry->direction = direction;
9743 new_list_entry->delay = delay;
9746 void print_unknown_token(char *filename, char *token, int token_nr)
9750 Error(ERR_INFO_LINE, "-");
9751 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9752 Error(ERR_INFO, "- config file: '%s'", filename);
9755 Error(ERR_INFO, "- token: '%s'", token);
9758 void print_unknown_token_end(int token_nr)
9761 Error(ERR_INFO_LINE, "-");
9764 void LoadHelpAnimInfo()
9766 char *filename = getHelpAnimFilename();
9767 SetupFileList *setup_file_list = NULL, *list;
9768 SetupFileHash *element_hash, *action_hash, *direction_hash;
9769 int num_list_entries = 0;
9770 int num_unknown_tokens = 0;
9773 if (fileExists(filename))
9774 setup_file_list = loadSetupFileList(filename);
9776 if (setup_file_list == NULL)
9778 /* use reliable default values from static configuration */
9779 SetupFileList *insert_ptr;
9781 insert_ptr = setup_file_list =
9782 newSetupFileList(helpanim_config[0].token,
9783 helpanim_config[0].value);
9785 for (i = 1; helpanim_config[i].token; i++)
9786 insert_ptr = addListEntry(insert_ptr,
9787 helpanim_config[i].token,
9788 helpanim_config[i].value);
9791 element_hash = newSetupFileHash();
9792 action_hash = newSetupFileHash();
9793 direction_hash = newSetupFileHash();
9795 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
9796 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9798 for (i = 0; i < NUM_ACTIONS; i++)
9799 setHashEntry(action_hash, element_action_info[i].suffix,
9800 i_to_a(element_action_info[i].value));
9802 /* do not store direction index (bit) here, but direction value! */
9803 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
9804 setHashEntry(direction_hash, element_direction_info[i].suffix,
9805 i_to_a(1 << element_direction_info[i].value));
9807 for (list = setup_file_list; list != NULL; list = list->next)
9809 char *element_token, *action_token, *direction_token;
9810 char *element_value, *action_value, *direction_value;
9811 int delay = atoi(list->value);
9813 if (strEqual(list->token, "end"))
9815 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
9820 /* first try to break element into element/action/direction parts;
9821 if this does not work, also accept combined "element[.act][.dir]"
9822 elements (like "dynamite.active"), which are unique elements */
9824 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
9826 element_value = getHashEntry(element_hash, list->token);
9827 if (element_value != NULL) /* element found */
9828 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9832 /* no further suffixes found -- this is not an element */
9833 print_unknown_token(filename, list->token, num_unknown_tokens++);
9839 /* token has format "<prefix>.<something>" */
9841 action_token = strchr(list->token, '.'); /* suffix may be action ... */
9842 direction_token = action_token; /* ... or direction */
9844 element_token = getStringCopy(list->token);
9845 *strchr(element_token, '.') = '\0';
9847 element_value = getHashEntry(element_hash, element_token);
9849 if (element_value == NULL) /* this is no element */
9851 element_value = getHashEntry(element_hash, list->token);
9852 if (element_value != NULL) /* combined element found */
9853 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9856 print_unknown_token(filename, list->token, num_unknown_tokens++);
9858 free(element_token);
9863 action_value = getHashEntry(action_hash, action_token);
9865 if (action_value != NULL) /* action found */
9867 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
9870 free(element_token);
9875 direction_value = getHashEntry(direction_hash, direction_token);
9877 if (direction_value != NULL) /* direction found */
9879 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
9882 free(element_token);
9887 if (strchr(action_token + 1, '.') == NULL)
9889 /* no further suffixes found -- this is not an action nor direction */
9891 element_value = getHashEntry(element_hash, list->token);
9892 if (element_value != NULL) /* combined element found */
9893 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9896 print_unknown_token(filename, list->token, num_unknown_tokens++);
9898 free(element_token);
9903 /* token has format "<prefix>.<suffix>.<something>" */
9905 direction_token = strchr(action_token + 1, '.');
9907 action_token = getStringCopy(action_token);
9908 *strchr(action_token + 1, '.') = '\0';
9910 action_value = getHashEntry(action_hash, action_token);
9912 if (action_value == NULL) /* this is no action */
9914 element_value = getHashEntry(element_hash, list->token);
9915 if (element_value != NULL) /* combined element found */
9916 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9919 print_unknown_token(filename, list->token, num_unknown_tokens++);
9921 free(element_token);
9927 direction_value = getHashEntry(direction_hash, direction_token);
9929 if (direction_value != NULL) /* direction found */
9931 add_helpanim_entry(atoi(element_value), atoi(action_value),
9932 atoi(direction_value), delay, &num_list_entries);
9934 free(element_token);
9940 /* this is no direction */
9942 element_value = getHashEntry(element_hash, list->token);
9943 if (element_value != NULL) /* combined element found */
9944 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9947 print_unknown_token(filename, list->token, num_unknown_tokens++);
9949 free(element_token);
9953 print_unknown_token_end(num_unknown_tokens);
9955 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
9956 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
9958 freeSetupFileList(setup_file_list);
9959 freeSetupFileHash(element_hash);
9960 freeSetupFileHash(action_hash);
9961 freeSetupFileHash(direction_hash);
9964 for (i = 0; i < num_list_entries; i++)
9965 printf("::: '%s': %d, %d, %d => %d\n",
9966 EL_NAME(helpanim_info[i].element),
9967 helpanim_info[i].element,
9968 helpanim_info[i].action,
9969 helpanim_info[i].direction,
9970 helpanim_info[i].delay);
9974 void LoadHelpTextInfo()
9976 char *filename = getHelpTextFilename();
9979 if (helptext_info != NULL)
9981 freeSetupFileHash(helptext_info);
9982 helptext_info = NULL;
9985 if (fileExists(filename))
9986 helptext_info = loadSetupFileHash(filename);
9988 if (helptext_info == NULL)
9990 /* use reliable default values from static configuration */
9991 helptext_info = newSetupFileHash();
9993 for (i = 0; helptext_config[i].token; i++)
9994 setHashEntry(helptext_info,
9995 helptext_config[i].token,
9996 helptext_config[i].value);
10000 BEGIN_HASH_ITERATION(helptext_info, itr)
10002 printf("::: '%s' => '%s'\n",
10003 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10005 END_HASH_ITERATION(hash, itr)
10010 /* ------------------------------------------------------------------------- */
10011 /* convert levels */
10012 /* ------------------------------------------------------------------------- */
10014 #define MAX_NUM_CONVERT_LEVELS 1000
10016 void ConvertLevels()
10018 static LevelDirTree *convert_leveldir = NULL;
10019 static int convert_level_nr = -1;
10020 static int num_levels_handled = 0;
10021 static int num_levels_converted = 0;
10022 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10025 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10026 global.convert_leveldir);
10028 if (convert_leveldir == NULL)
10029 Error(ERR_EXIT, "no such level identifier: '%s'",
10030 global.convert_leveldir);
10032 leveldir_current = convert_leveldir;
10034 if (global.convert_level_nr != -1)
10036 convert_leveldir->first_level = global.convert_level_nr;
10037 convert_leveldir->last_level = global.convert_level_nr;
10040 convert_level_nr = convert_leveldir->first_level;
10042 PrintLine("=", 79);
10043 Print("Converting levels\n");
10044 PrintLine("-", 79);
10045 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10046 Print("Level series name: '%s'\n", convert_leveldir->name);
10047 Print("Level series author: '%s'\n", convert_leveldir->author);
10048 Print("Number of levels: %d\n", convert_leveldir->levels);
10049 PrintLine("=", 79);
10052 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10053 levels_failed[i] = FALSE;
10055 while (convert_level_nr <= convert_leveldir->last_level)
10057 char *level_filename;
10060 level_nr = convert_level_nr++;
10062 Print("Level %03d: ", level_nr);
10064 LoadLevel(level_nr);
10065 if (level.no_valid_file)
10067 Print("(no level)\n");
10071 Print("converting level ... ");
10073 level_filename = getDefaultLevelFilename(level_nr);
10074 new_level = !fileExists(level_filename);
10078 SaveLevel(level_nr);
10080 num_levels_converted++;
10082 Print("converted.\n");
10086 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10087 levels_failed[level_nr] = TRUE;
10089 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10092 num_levels_handled++;
10096 PrintLine("=", 79);
10097 Print("Number of levels handled: %d\n", num_levels_handled);
10098 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10099 (num_levels_handled ?
10100 num_levels_converted * 100 / num_levels_handled : 0));
10101 PrintLine("-", 79);
10102 Print("Summary (for automatic parsing by scripts):\n");
10103 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10104 convert_leveldir->identifier, num_levels_converted,
10105 num_levels_handled,
10106 (num_levels_handled ?
10107 num_levels_converted * 100 / num_levels_handled : 0));
10109 if (num_levels_handled != num_levels_converted)
10111 Print(", FAILED:");
10112 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10113 if (levels_failed[i])
10118 PrintLine("=", 79);
10120 CloseAllAndExit(0);
10124 /* ------------------------------------------------------------------------- */
10125 /* create and save images for use in level sketches (raw BMP format) */
10126 /* ------------------------------------------------------------------------- */
10128 void CreateLevelSketchImages()
10130 #if defined(TARGET_SDL)
10135 InitElementPropertiesGfxElement();
10137 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10138 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10140 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10142 Bitmap *src_bitmap;
10144 int element = getMappedElement(i);
10145 int graphic = el2edimg(element);
10146 char basename1[16];
10147 char basename2[16];
10151 sprintf(basename1, "%03d.bmp", i);
10152 sprintf(basename2, "%03ds.bmp", i);
10154 filename1 = getPath2(global.create_images_dir, basename1);
10155 filename2 = getPath2(global.create_images_dir, basename2);
10157 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10158 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10161 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10162 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10164 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10165 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10167 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10168 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10174 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10177 FreeBitmap(bitmap1);
10178 FreeBitmap(bitmap2);
10183 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10185 CloseAllAndExit(0);
10190 /* ------------------------------------------------------------------------- */
10191 /* create and save images for custom and group elements (raw BMP format) */
10192 /* ------------------------------------------------------------------------- */
10194 void CreateCustomElementImages(char *directory)
10196 #if defined(TARGET_SDL)
10197 char *src_basename = "RocksCE-template.ilbm";
10198 char *dst_basename = "RocksCE.bmp";
10199 char *src_filename = getPath2(directory, src_basename);
10200 char *dst_filename = getPath2(directory, dst_basename);
10201 Bitmap *src_bitmap;
10203 int yoffset_ce = 0;
10204 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10207 SDLInitVideoDisplay();
10209 ReCreateBitmap(&backbuffer, video.width, video.height);
10211 src_bitmap = LoadImage(src_filename);
10213 bitmap = CreateBitmap(TILEX * 16 * 2,
10214 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10217 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10224 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10225 TILEX * x, TILEY * y + yoffset_ce);
10227 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10229 TILEX * x + TILEX * 16,
10230 TILEY * y + yoffset_ce);
10232 for (j = 2; j >= 0; j--)
10236 BlitBitmap(src_bitmap, bitmap,
10237 TILEX + c * 7, 0, 6, 10,
10238 TILEX * x + 6 + j * 7,
10239 TILEY * y + 11 + yoffset_ce);
10241 BlitBitmap(src_bitmap, bitmap,
10242 TILEX + c * 8, TILEY, 6, 10,
10243 TILEX * 16 + TILEX * x + 6 + j * 8,
10244 TILEY * y + 10 + yoffset_ce);
10250 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10257 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10258 TILEX * x, TILEY * y + yoffset_ge);
10260 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10262 TILEX * x + TILEX * 16,
10263 TILEY * y + yoffset_ge);
10265 for (j = 1; j >= 0; j--)
10269 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10270 TILEX * x + 6 + j * 10,
10271 TILEY * y + 11 + yoffset_ge);
10273 BlitBitmap(src_bitmap, bitmap,
10274 TILEX + c * 8, TILEY + 12, 6, 10,
10275 TILEX * 16 + TILEX * x + 10 + j * 8,
10276 TILEY * y + 10 + yoffset_ge);
10282 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10283 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10285 FreeBitmap(bitmap);
10287 CloseAllAndExit(0);