1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
25 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
26 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
27 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
29 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
30 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
32 #define LEVEL_CHUNK_VERS_SIZE 8 /* size of file version chunk */
33 #define LEVEL_CHUNK_DATE_SIZE 4 /* size of file date chunk */
34 #define LEVEL_CHUNK_HEAD_SIZE 80 /* size of level file header */
35 #define LEVEL_CHUNK_HEAD_UNUSED 0 /* unused level header bytes */
36 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
37 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
38 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
39 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
40 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
41 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
42 #define LEVEL_CHUNK_GRP1_SIZE 74 /* size of level GRP1 chunk */
44 /* (element number, number of change pages, change page number) */
45 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
47 /* (element number only) */
48 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
49 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
51 /* (nothing at all if unchanged) */
52 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
54 #define TAPE_CHUNK_VERS_SIZE 8 /* size of file version chunk */
55 #define TAPE_CHUNK_HEAD_SIZE 20 /* size of tape file header */
56 #define TAPE_CHUNK_HEAD_UNUSED 3 /* unused tape header bytes */
58 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
59 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
60 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
62 /* file identifier strings */
63 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
64 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
65 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
67 /* values for deciding when (not) to save configuration data */
68 #define SAVE_CONF_NEVER 0
69 #define SAVE_CONF_ALWAYS 1
70 #define SAVE_CONF_WHEN_CHANGED -1
72 /* values for chunks using micro chunks */
73 #define CONF_MASK_1_BYTE 0x00
74 #define CONF_MASK_2_BYTE 0x40
75 #define CONF_MASK_4_BYTE 0x80
76 #define CONF_MASK_MULTI_BYTES 0xc0
78 #define CONF_MASK_BYTES 0xc0
79 #define CONF_MASK_TOKEN 0x3f
81 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
82 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
83 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
84 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
86 /* these definitions are just for convenience of use and readability */
87 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
88 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
89 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
90 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
92 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
93 (x) == CONF_MASK_2_BYTE ? 2 : \
94 (x) == CONF_MASK_4_BYTE ? 4 : 0)
96 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
97 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
98 #define CONF_ELEMENT_NUM_BYTES (2)
100 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
101 (t) == TYPE_ELEMENT_LIST ? \
102 CONF_ELEMENT_NUM_BYTES : \
103 (t) == TYPE_CONTENT || \
104 (t) == TYPE_CONTENT_LIST ? \
105 CONF_CONTENT_NUM_BYTES : 1)
107 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
108 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
109 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
111 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
113 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
114 CONF_ELEMENT_NUM_BYTES)
115 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
116 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
118 /* temporary variables used to store pointers to structure members */
119 static struct LevelInfo li;
120 static struct ElementInfo xx_ei, yy_ei;
121 static struct ElementChangeInfo xx_change;
122 static struct ElementGroupInfo xx_group;
123 static struct EnvelopeInfo xx_envelope;
124 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
125 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
126 static int xx_num_contents;
127 static int xx_current_change_page;
128 static char xx_default_string_empty[1] = "";
129 static int xx_string_length_unused;
131 struct LevelFileConfigInfo
133 int element; /* element for which data is to be stored */
134 int save_type; /* save data always, never or when changed */
135 int data_type; /* data type (used internally, not stored) */
136 int conf_type; /* micro chunk identifier (stored in file) */
139 void *value; /* variable that holds the data to be stored */
140 int default_value; /* initial default value for this variable */
143 void *value_copy; /* variable that holds the data to be copied */
144 void *num_entities; /* number of entities for multi-byte data */
145 int default_num_entities; /* default number of entities for this data */
146 int max_num_entities; /* maximal number of entities for this data */
147 char *default_string; /* optional default string for string data */
150 static struct LevelFileConfigInfo chunk_config_INFO[] =
152 /* ---------- values not related to single elements ----------------------- */
155 -1, SAVE_CONF_ALWAYS,
156 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
157 &li.game_engine_type, GAME_ENGINE_TYPE_RND
161 -1, SAVE_CONF_ALWAYS,
162 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
163 &li.fieldx, STD_LEV_FIELDX
166 -1, SAVE_CONF_ALWAYS,
167 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
168 &li.fieldy, STD_LEV_FIELDY
172 -1, SAVE_CONF_ALWAYS,
173 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
178 -1, SAVE_CONF_ALWAYS,
179 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
185 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
191 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
192 &li.use_step_counter, FALSE
197 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
198 &li.wind_direction_initial, MV_NONE
203 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
204 &li.em_slippery_gems, FALSE
209 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
210 &li.use_custom_template, FALSE
215 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
216 &li.can_move_into_acid_bits, ~0 /* default: everything can */
221 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
222 &li.dont_collide_with_bits, ~0 /* default: always deadly */
227 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
228 &li.em_explodes_by_fire, FALSE
233 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
234 &li.score[SC_TIME_BONUS], 1
239 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
240 &li.auto_exit_sokoban, FALSE
250 static struct LevelFileConfigInfo chunk_config_ELEM[] =
252 /* (these values are the same for each player) */
255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
256 &li.block_last_field, FALSE /* default case for EM levels */
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
261 &li.sp_block_last_field, TRUE /* default case for SP levels */
265 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
266 &li.instant_relocation, FALSE
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
271 &li.can_pass_to_walkable, FALSE
275 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
276 &li.block_snap_field, TRUE
280 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
281 &li.continuous_snapping, TRUE
285 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
286 &li.shifted_relocation, FALSE
289 /* (these values are different for each player) */
292 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
293 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
297 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
298 &li.initial_player_gravity[0], FALSE
302 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
303 &li.use_start_element[0], FALSE
307 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
308 &li.start_element[0], EL_PLAYER_1
312 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
313 &li.use_artwork_element[0], FALSE
317 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
318 &li.artwork_element[0], EL_PLAYER_1
322 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
323 &li.use_explosion_element[0], FALSE
327 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
328 &li.explosion_element[0], EL_PLAYER_1
332 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
333 &li.use_initial_inventory[0], FALSE
337 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
338 &li.initial_inventory_size[0], 1
342 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
343 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
344 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
349 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
350 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
354 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
355 &li.initial_player_gravity[1], FALSE
359 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
360 &li.use_start_element[1], FALSE
364 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
365 &li.start_element[1], EL_PLAYER_2
369 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
370 &li.use_artwork_element[1], FALSE
374 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
375 &li.artwork_element[1], EL_PLAYER_2
379 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
380 &li.use_explosion_element[1], FALSE
384 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
385 &li.explosion_element[1], EL_PLAYER_2
389 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
390 &li.use_initial_inventory[1], FALSE
394 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
395 &li.initial_inventory_size[1], 1
399 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
400 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
401 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
406 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
407 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
411 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
412 &li.initial_player_gravity[2], FALSE
416 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
417 &li.use_start_element[2], FALSE
421 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
422 &li.start_element[2], EL_PLAYER_3
426 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
427 &li.use_artwork_element[2], FALSE
431 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
432 &li.artwork_element[2], EL_PLAYER_3
436 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
437 &li.use_explosion_element[2], FALSE
441 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
442 &li.explosion_element[2], EL_PLAYER_3
446 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
447 &li.use_initial_inventory[2], FALSE
451 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
452 &li.initial_inventory_size[2], 1
456 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
457 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
458 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
463 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
464 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
468 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
469 &li.initial_player_gravity[3], FALSE
473 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
474 &li.use_start_element[3], FALSE
478 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
479 &li.start_element[3], EL_PLAYER_4
483 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
484 &li.use_artwork_element[3], FALSE
488 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
489 &li.artwork_element[3], EL_PLAYER_4
493 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
494 &li.use_explosion_element[3], FALSE
498 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
499 &li.explosion_element[3], EL_PLAYER_4
503 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
504 &li.use_initial_inventory[3], FALSE
508 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
509 &li.initial_inventory_size[3], 1
513 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
514 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
515 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
520 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
521 &li.score[SC_EMERALD], 10
526 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
527 &li.score[SC_DIAMOND], 10
532 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
533 &li.score[SC_BUG], 10
538 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
539 &li.score[SC_SPACESHIP], 10
544 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
545 &li.score[SC_PACMAN], 10
550 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
551 &li.score[SC_NUT], 10
556 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
557 &li.score[SC_DYNAMITE], 10
562 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
563 &li.score[SC_KEY], 10
568 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
569 &li.score[SC_PEARL], 10
574 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
575 &li.score[SC_CRYSTAL], 10
580 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
581 &li.amoeba_content, EL_DIAMOND
585 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
590 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
591 &li.grow_into_diggable, TRUE
596 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
597 &li.yamyam_content, EL_ROCK, NULL,
598 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
602 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
603 &li.score[SC_YAMYAM], 10
608 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
609 &li.score[SC_ROBOT], 10
613 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
619 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
625 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
626 &li.time_magic_wall, 10
631 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
632 &li.game_of_life[0], 2
636 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
637 &li.game_of_life[1], 3
641 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
642 &li.game_of_life[2], 3
646 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
647 &li.game_of_life[3], 3
652 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
657 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
662 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
667 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
672 EL_TIMEGATE_SWITCH, -1,
673 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
674 &li.time_timegate, 10
678 EL_LIGHT_SWITCH_ACTIVE, -1,
679 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
684 EL_SHIELD_NORMAL, -1,
685 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
686 &li.shield_normal_time, 10
689 EL_SHIELD_NORMAL, -1,
690 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
691 &li.score[SC_SHIELD], 10
695 EL_SHIELD_DEADLY, -1,
696 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
697 &li.shield_deadly_time, 10
700 EL_SHIELD_DEADLY, -1,
701 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
702 &li.score[SC_SHIELD], 10
707 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
712 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
713 &li.extra_time_score, 10
717 EL_TIME_ORB_FULL, -1,
718 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
719 &li.time_orb_time, 10
722 EL_TIME_ORB_FULL, -1,
723 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
724 &li.use_time_orb_bug, FALSE
729 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
730 &li.use_spring_bug, FALSE
735 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
736 &li.android_move_time, 10
740 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
741 &li.android_clone_time, 10
745 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
746 &li.android_clone_element[0], EL_EMPTY, NULL,
747 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
752 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
757 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
762 EL_EMC_MAGNIFIER, -1,
763 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
764 &li.magnify_score, 10
767 EL_EMC_MAGNIFIER, -1,
768 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
773 EL_EMC_MAGIC_BALL, -1,
774 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
778 EL_EMC_MAGIC_BALL, -1,
779 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
780 &li.ball_random, FALSE
783 EL_EMC_MAGIC_BALL, -1,
784 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
785 &li.ball_state_initial, FALSE
788 EL_EMC_MAGIC_BALL, -1,
789 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
790 &li.ball_content, EL_EMPTY, NULL,
791 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
794 /* ---------- unused values ----------------------------------------------- */
797 EL_UNKNOWN, SAVE_CONF_NEVER,
798 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
799 &li.score[SC_UNKNOWN_14], 10
802 EL_UNKNOWN, SAVE_CONF_NEVER,
803 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
804 &li.score[SC_UNKNOWN_15], 10
814 static struct LevelFileConfigInfo chunk_config_NOTE[] =
818 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
819 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
823 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
824 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
829 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
830 &xx_envelope.autowrap, FALSE
834 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
835 &xx_envelope.centered, FALSE
840 TYPE_STRING, CONF_VALUE_BYTES(1),
841 &xx_envelope.text, -1, NULL,
842 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
843 &xx_default_string_empty[0]
853 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
857 TYPE_STRING, CONF_VALUE_BYTES(1),
858 &xx_ei.description[0], -1,
859 &yy_ei.description[0],
860 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
861 &xx_default_description[0]
866 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
867 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
868 &yy_ei.properties[EP_BITFIELD_BASE_NR]
874 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
875 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
876 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
882 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
883 &xx_ei.use_gfx_element, FALSE,
884 &yy_ei.use_gfx_element
888 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
889 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
890 &yy_ei.gfx_element_initial
895 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
896 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
897 &yy_ei.access_direction
902 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
903 &xx_ei.collect_score_initial, 10,
904 &yy_ei.collect_score_initial
908 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
909 &xx_ei.collect_count_initial, 1,
910 &yy_ei.collect_count_initial
915 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
916 &xx_ei.ce_value_fixed_initial, 0,
917 &yy_ei.ce_value_fixed_initial
921 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
922 &xx_ei.ce_value_random_initial, 0,
923 &yy_ei.ce_value_random_initial
927 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
928 &xx_ei.use_last_ce_value, FALSE,
929 &yy_ei.use_last_ce_value
934 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
935 &xx_ei.push_delay_fixed, 8,
936 &yy_ei.push_delay_fixed
940 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
941 &xx_ei.push_delay_random, 8,
942 &yy_ei.push_delay_random
946 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
947 &xx_ei.drop_delay_fixed, 0,
948 &yy_ei.drop_delay_fixed
952 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
953 &xx_ei.drop_delay_random, 0,
954 &yy_ei.drop_delay_random
958 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
959 &xx_ei.move_delay_fixed, 0,
960 &yy_ei.move_delay_fixed
964 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
965 &xx_ei.move_delay_random, 0,
966 &yy_ei.move_delay_random
971 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
972 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
977 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
978 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
979 &yy_ei.move_direction_initial
983 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
984 &xx_ei.move_stepsize, TILEX / 8,
990 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
991 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
992 &yy_ei.move_enter_element
996 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
997 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
998 &yy_ei.move_leave_element
1002 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1003 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1004 &yy_ei.move_leave_type
1009 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1010 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1011 &yy_ei.slippery_type
1016 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1017 &xx_ei.explosion_type, EXPLODES_3X3,
1018 &yy_ei.explosion_type
1022 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1023 &xx_ei.explosion_delay, 16,
1024 &yy_ei.explosion_delay
1028 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1029 &xx_ei.ignition_delay, 8,
1030 &yy_ei.ignition_delay
1035 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1036 &xx_ei.content, EL_EMPTY_SPACE,
1038 &xx_num_contents, 1, 1
1041 /* ---------- "num_change_pages" must be the last entry ------------------- */
1044 -1, SAVE_CONF_ALWAYS,
1045 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1046 &xx_ei.num_change_pages, 1,
1047 &yy_ei.num_change_pages
1058 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1060 /* ---------- "current_change_page" must be the first entry --------------- */
1063 -1, SAVE_CONF_ALWAYS,
1064 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1065 &xx_current_change_page, -1
1068 /* ---------- (the remaining entries can be in any order) ----------------- */
1072 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1073 &xx_change.can_change, FALSE
1078 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1079 &xx_event_bits[0], 0
1083 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1084 &xx_event_bits[1], 0
1089 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1090 &xx_change.trigger_player, CH_PLAYER_ANY
1094 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1095 &xx_change.trigger_side, CH_SIDE_ANY
1099 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1100 &xx_change.trigger_page, CH_PAGE_ANY
1105 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1106 &xx_change.target_element, EL_EMPTY_SPACE
1111 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1112 &xx_change.delay_fixed, 0
1116 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1117 &xx_change.delay_random, 0
1121 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1122 &xx_change.delay_frames, FRAMES_PER_SECOND
1127 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1128 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1133 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1134 &xx_change.explode, FALSE
1138 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1139 &xx_change.use_target_content, FALSE
1143 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1144 &xx_change.only_if_complete, FALSE
1148 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1149 &xx_change.use_random_replace, FALSE
1153 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1154 &xx_change.random_percentage, 100
1158 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1159 &xx_change.replace_when, CP_WHEN_EMPTY
1164 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1165 &xx_change.has_action, FALSE
1169 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1170 &xx_change.action_type, CA_NO_ACTION
1174 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1175 &xx_change.action_mode, CA_MODE_UNDEFINED
1179 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1180 &xx_change.action_arg, CA_ARG_UNDEFINED
1185 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1186 &xx_change.action_element, EL_EMPTY_SPACE
1191 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1192 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1193 &xx_num_contents, 1, 1
1203 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1207 TYPE_STRING, CONF_VALUE_BYTES(1),
1208 &xx_ei.description[0], -1, NULL,
1209 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1210 &xx_default_description[0]
1215 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1216 &xx_ei.use_gfx_element, FALSE
1220 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1221 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1226 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1227 &xx_group.choice_mode, ANIM_RANDOM
1232 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1233 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1234 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1244 static struct LevelFileConfigInfo chunk_config_CONF[] = /* (OBSOLETE) */
1248 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1249 &li.block_snap_field, TRUE
1253 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1254 &li.continuous_snapping, TRUE
1258 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1259 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1263 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1264 &li.use_start_element[0], FALSE
1268 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1269 &li.start_element[0], EL_PLAYER_1
1273 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1274 &li.use_artwork_element[0], FALSE
1278 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1279 &li.artwork_element[0], EL_PLAYER_1
1283 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1284 &li.use_explosion_element[0], FALSE
1288 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1289 &li.explosion_element[0], EL_PLAYER_1
1304 filetype_id_list[] =
1306 { LEVEL_FILE_TYPE_RND, "RND" },
1307 { LEVEL_FILE_TYPE_BD, "BD" },
1308 { LEVEL_FILE_TYPE_EM, "EM" },
1309 { LEVEL_FILE_TYPE_SP, "SP" },
1310 { LEVEL_FILE_TYPE_DX, "DX" },
1311 { LEVEL_FILE_TYPE_SB, "SB" },
1312 { LEVEL_FILE_TYPE_DC, "DC" },
1317 /* ========================================================================= */
1318 /* level file functions */
1319 /* ========================================================================= */
1321 static boolean check_special_flags(char *flag)
1324 printf("::: '%s', '%s', '%s'\n",
1326 options.special_flags,
1327 leveldir_current->special_flags);
1330 if (strEqual(options.special_flags, flag) ||
1331 strEqual(leveldir_current->special_flags, flag))
1337 static struct DateInfo getCurrentDate()
1339 time_t epoch_seconds = time(NULL);
1340 struct tm *now = localtime(&epoch_seconds);
1341 struct DateInfo date;
1343 date.year = now->tm_year + 1900;
1344 date.month = now->tm_mon + 1;
1345 date.day = now->tm_mday;
1347 date.src = DATE_SRC_CLOCK;
1352 static void resetEventFlags(struct ElementChangeInfo *change)
1356 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1357 change->has_event[i] = FALSE;
1360 static void resetEventBits()
1364 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1365 xx_event_bits[i] = 0;
1368 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1372 /* important: only change event flag if corresponding event bit is set
1373 (this is because all xx_event_bits[] values are loaded separately,
1374 and all xx_event_bits[] values are set back to zero before loading
1375 another value xx_event_bits[x] (each value representing 32 flags)) */
1377 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1378 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1379 change->has_event[i] = TRUE;
1382 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1386 /* in contrast to the above function setEventFlagsFromEventBits(), it
1387 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1388 depending on the corresponding change->has_event[i] values here, as
1389 all xx_event_bits[] values are reset in resetEventBits() before */
1391 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1392 if (change->has_event[i])
1393 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1396 static char *getDefaultElementDescription(struct ElementInfo *ei)
1398 static char description[MAX_ELEMENT_NAME_LEN + 1];
1399 char *default_description = (ei->custom_description != NULL ?
1400 ei->custom_description :
1401 ei->editor_description);
1404 /* always start with reliable default values */
1405 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1406 description[i] = '\0';
1408 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1409 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1411 return &description[0];
1414 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1416 char *default_description = getDefaultElementDescription(ei);
1419 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1420 ei->description[i] = default_description[i];
1423 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1427 for (i = 0; conf[i].data_type != -1; i++)
1429 int default_value = conf[i].default_value;
1430 int data_type = conf[i].data_type;
1431 int conf_type = conf[i].conf_type;
1432 int byte_mask = conf_type & CONF_MASK_BYTES;
1434 if (byte_mask == CONF_MASK_MULTI_BYTES)
1436 int default_num_entities = conf[i].default_num_entities;
1437 int max_num_entities = conf[i].max_num_entities;
1439 *(int *)(conf[i].num_entities) = default_num_entities;
1441 if (data_type == TYPE_STRING)
1443 char *default_string = conf[i].default_string;
1444 char *string = (char *)(conf[i].value);
1446 strncpy(string, default_string, max_num_entities);
1448 else if (data_type == TYPE_ELEMENT_LIST)
1450 int *element_array = (int *)(conf[i].value);
1453 for (j = 0; j < max_num_entities; j++)
1454 element_array[j] = default_value;
1456 else if (data_type == TYPE_CONTENT_LIST)
1458 struct Content *content = (struct Content *)(conf[i].value);
1461 for (c = 0; c < max_num_entities; c++)
1462 for (y = 0; y < 3; y++)
1463 for (x = 0; x < 3; x++)
1464 content[c].e[x][y] = default_value;
1467 else /* constant size configuration data (1, 2 or 4 bytes) */
1469 if (data_type == TYPE_BOOLEAN)
1470 *(boolean *)(conf[i].value) = default_value;
1472 *(int *) (conf[i].value) = default_value;
1477 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1481 for (i = 0; conf[i].data_type != -1; i++)
1483 int data_type = conf[i].data_type;
1484 int conf_type = conf[i].conf_type;
1485 int byte_mask = conf_type & CONF_MASK_BYTES;
1487 if (byte_mask == CONF_MASK_MULTI_BYTES)
1489 int max_num_entities = conf[i].max_num_entities;
1491 if (data_type == TYPE_STRING)
1493 char *string = (char *)(conf[i].value);
1494 char *string_copy = (char *)(conf[i].value_copy);
1496 strncpy(string_copy, string, max_num_entities);
1498 else if (data_type == TYPE_ELEMENT_LIST)
1500 int *element_array = (int *)(conf[i].value);
1501 int *element_array_copy = (int *)(conf[i].value_copy);
1504 for (j = 0; j < max_num_entities; j++)
1505 element_array_copy[j] = element_array[j];
1507 else if (data_type == TYPE_CONTENT_LIST)
1509 struct Content *content = (struct Content *)(conf[i].value);
1510 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1513 for (c = 0; c < max_num_entities; c++)
1514 for (y = 0; y < 3; y++)
1515 for (x = 0; x < 3; x++)
1516 content_copy[c].e[x][y] = content[c].e[x][y];
1519 else /* constant size configuration data (1, 2 or 4 bytes) */
1521 if (data_type == TYPE_BOOLEAN)
1522 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1524 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1529 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1533 xx_ei = *ei_from; /* copy element data into temporary buffer */
1534 yy_ei = *ei_to; /* copy element data into temporary buffer */
1536 copyConfigFromConfigList(chunk_config_CUSX_base);
1541 /* ---------- reinitialize and copy change pages ---------- */
1543 ei_to->num_change_pages = ei_from->num_change_pages;
1544 ei_to->current_change_page = ei_from->current_change_page;
1546 setElementChangePages(ei_to, ei_to->num_change_pages);
1548 for (i = 0; i < ei_to->num_change_pages; i++)
1549 ei_to->change_page[i] = ei_from->change_page[i];
1551 /* ---------- copy group element info ---------- */
1552 if (ei_from->group != NULL && ei_to->group != NULL) /* group or internal */
1553 *ei_to->group = *ei_from->group;
1555 /* mark this custom element as modified */
1556 ei_to->modified_settings = TRUE;
1559 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1561 int change_page_size = sizeof(struct ElementChangeInfo);
1563 ei->num_change_pages = MAX(1, change_pages);
1566 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1568 if (ei->current_change_page >= ei->num_change_pages)
1569 ei->current_change_page = ei->num_change_pages - 1;
1571 ei->change = &ei->change_page[ei->current_change_page];
1574 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1576 xx_change = *change; /* copy change data into temporary buffer */
1579 /* (not needed; set by setConfigToDefaultsFromConfigList()) */
1580 xx_num_contents = 1;
1583 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1585 *change = xx_change;
1587 resetEventFlags(change);
1589 change->direct_action = 0;
1590 change->other_action = 0;
1592 change->pre_change_function = NULL;
1593 change->change_function = NULL;
1594 change->post_change_function = NULL;
1599 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1603 li = *level; /* copy level data into temporary buffer */
1604 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1605 *level = li; /* copy temporary buffer back to level data */
1607 setLevelInfoToDefaults_EM();
1608 setLevelInfoToDefaults_SP();
1610 level->native_em_level = &native_em_level;
1611 level->native_sp_level = &native_sp_level;
1613 level->file_version = FILE_VERSION_ACTUAL;
1614 level->game_version = GAME_VERSION_ACTUAL;
1616 level->creation_date = getCurrentDate();
1618 level->encoding_16bit_field = TRUE;
1619 level->encoding_16bit_yamyam = TRUE;
1620 level->encoding_16bit_amoeba = TRUE;
1622 for (x = 0; x < MAX_LEV_FIELDX; x++)
1623 for (y = 0; y < MAX_LEV_FIELDY; y++)
1624 level->field[x][y] = EL_SAND;
1626 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1627 level->name[i] = '\0';
1628 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1629 level->author[i] = '\0';
1631 strcpy(level->name, NAMELESS_LEVEL_NAME);
1632 strcpy(level->author, ANONYMOUS_NAME);
1634 level->field[0][0] = EL_PLAYER_1;
1635 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1637 BorderElement = EL_STEELWALL;
1639 /* set all bug compatibility flags to "false" => do not emulate this bug */
1640 level->use_action_after_change_bug = FALSE;
1642 if (leveldir_current)
1644 /* try to determine better author name than 'anonymous' */
1645 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1647 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1648 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1652 switch (LEVELCLASS(leveldir_current))
1654 case LEVELCLASS_TUTORIAL:
1655 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1658 case LEVELCLASS_CONTRIB:
1659 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1660 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1663 case LEVELCLASS_PRIVATE:
1664 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1665 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1669 /* keep default value */
1676 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1678 static boolean clipboard_elements_initialized = FALSE;
1681 InitElementPropertiesStatic();
1683 li = *level; /* copy level data into temporary buffer */
1684 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1685 *level = li; /* copy temporary buffer back to level data */
1687 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1690 struct ElementInfo *ei = &element_info[element];
1692 /* never initialize clipboard elements after the very first time */
1693 /* (to be able to use clipboard elements between several levels) */
1694 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1697 if (IS_ENVELOPE(element))
1699 int envelope_nr = element - EL_ENVELOPE_1;
1701 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1703 level->envelope[envelope_nr] = xx_envelope;
1706 if (IS_CUSTOM_ELEMENT(element) ||
1707 IS_GROUP_ELEMENT(element) ||
1708 IS_INTERNAL_ELEMENT(element))
1710 xx_ei = *ei; /* copy element data into temporary buffer */
1712 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1717 setElementChangePages(ei, 1);
1718 setElementChangeInfoToDefaults(ei->change);
1720 if (IS_CUSTOM_ELEMENT(element) ||
1721 IS_GROUP_ELEMENT(element) ||
1722 IS_INTERNAL_ELEMENT(element))
1724 setElementDescriptionToDefault(ei);
1726 ei->modified_settings = FALSE;
1729 if (IS_CUSTOM_ELEMENT(element) ||
1730 IS_INTERNAL_ELEMENT(element))
1732 /* internal values used in level editor */
1734 ei->access_type = 0;
1735 ei->access_layer = 0;
1736 ei->access_protected = 0;
1737 ei->walk_to_action = 0;
1738 ei->smash_targets = 0;
1741 ei->can_explode_by_fire = FALSE;
1742 ei->can_explode_smashed = FALSE;
1743 ei->can_explode_impact = FALSE;
1745 ei->current_change_page = 0;
1748 if (IS_GROUP_ELEMENT(element) ||
1749 IS_INTERNAL_ELEMENT(element))
1751 struct ElementGroupInfo *group;
1753 /* initialize memory for list of elements in group */
1754 if (ei->group == NULL)
1755 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1759 xx_group = *group; /* copy group data into temporary buffer */
1761 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1767 clipboard_elements_initialized = TRUE;
1770 static void setLevelInfoToDefaults(struct LevelInfo *level,
1771 boolean level_info_only)
1773 setLevelInfoToDefaults_Level(level);
1775 if (!level_info_only)
1776 setLevelInfoToDefaults_Elements(level);
1778 level->no_valid_file = FALSE;
1780 level->changed = FALSE;
1785 static void setLevelInfoToDefaults(struct LevelInfo *level,
1786 boolean level_info_only)
1788 static boolean clipboard_elements_initialized = FALSE;
1791 if (level_info_only)
1794 InitElementPropertiesStatic();
1796 li = *level; /* copy level data into temporary buffer */
1798 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1799 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1801 *level = li; /* copy temporary buffer back to level data */
1803 setLevelInfoToDefaults_EM();
1804 setLevelInfoToDefaults_SP();
1806 level->native_em_level = &native_em_level;
1807 level->native_sp_level = &native_sp_level;
1809 level->file_version = FILE_VERSION_ACTUAL;
1810 level->game_version = GAME_VERSION_ACTUAL;
1812 level->creation_date = getCurrentDate();
1814 level->encoding_16bit_field = TRUE;
1815 level->encoding_16bit_yamyam = TRUE;
1816 level->encoding_16bit_amoeba = TRUE;
1818 for (x = 0; x < MAX_LEV_FIELDX; x++)
1819 for (y = 0; y < MAX_LEV_FIELDY; y++)
1820 level->field[x][y] = EL_SAND;
1822 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1823 level->name[i] = '\0';
1824 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1825 level->author[i] = '\0';
1827 strcpy(level->name, NAMELESS_LEVEL_NAME);
1828 strcpy(level->author, ANONYMOUS_NAME);
1830 level->field[0][0] = EL_PLAYER_1;
1831 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1833 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1836 struct ElementInfo *ei = &element_info[element];
1838 /* never initialize clipboard elements after the very first time */
1839 /* (to be able to use clipboard elements between several levels) */
1840 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1843 if (IS_ENVELOPE(element))
1845 int envelope_nr = element - EL_ENVELOPE_1;
1847 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1849 level->envelope[envelope_nr] = xx_envelope;
1852 if (IS_CUSTOM_ELEMENT(element) ||
1853 IS_GROUP_ELEMENT(element) ||
1854 IS_INTERNAL_ELEMENT(element))
1856 xx_ei = *ei; /* copy element data into temporary buffer */
1858 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1863 setElementChangePages(ei, 1);
1864 setElementChangeInfoToDefaults(ei->change);
1866 if (IS_CUSTOM_ELEMENT(element) ||
1867 IS_GROUP_ELEMENT(element) ||
1868 IS_INTERNAL_ELEMENT(element))
1870 setElementDescriptionToDefault(ei);
1872 ei->modified_settings = FALSE;
1875 if (IS_CUSTOM_ELEMENT(element) ||
1876 IS_INTERNAL_ELEMENT(element))
1878 /* internal values used in level editor */
1880 ei->access_type = 0;
1881 ei->access_layer = 0;
1882 ei->access_protected = 0;
1883 ei->walk_to_action = 0;
1884 ei->smash_targets = 0;
1887 ei->can_explode_by_fire = FALSE;
1888 ei->can_explode_smashed = FALSE;
1889 ei->can_explode_impact = FALSE;
1891 ei->current_change_page = 0;
1894 if (IS_GROUP_ELEMENT(element) ||
1895 IS_INTERNAL_ELEMENT(element))
1897 struct ElementGroupInfo *group;
1899 /* initialize memory for list of elements in group */
1900 if (ei->group == NULL)
1901 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1905 xx_group = *group; /* copy group data into temporary buffer */
1907 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1913 clipboard_elements_initialized = TRUE;
1915 BorderElement = EL_STEELWALL;
1917 level->no_valid_file = FALSE;
1919 level->changed = FALSE;
1921 /* set all bug compatibility flags to "false" => do not emulate this bug */
1922 level->use_action_after_change_bug = FALSE;
1924 if (leveldir_current)
1926 /* try to determine better author name than 'anonymous' */
1927 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1929 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1930 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1934 switch (LEVELCLASS(leveldir_current))
1936 case LEVELCLASS_TUTORIAL:
1937 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1940 case LEVELCLASS_CONTRIB:
1941 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1942 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1945 case LEVELCLASS_PRIVATE:
1946 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1947 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1951 /* keep default value */
1960 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1962 level_file_info->nr = 0;
1963 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1964 level_file_info->packed = FALSE;
1965 level_file_info->basename = NULL;
1966 level_file_info->filename = NULL;
1969 static void ActivateLevelTemplate()
1973 /* Currently there is no special action needed to activate the template
1974 data, because 'element_info' property settings overwrite the original
1975 level data, while all other variables do not change. */
1977 /* Exception: 'from_level_template' elements in the original level playfield
1978 are overwritten with the corresponding elements at the same position in
1979 playfield from the level template. */
1981 for (x = 0; x < level.fieldx; x++)
1982 for (y = 0; y < level.fieldy; y++)
1983 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1984 level.field[x][y] = level_template.field[x][y];
1986 if (check_special_flags("load_xsb_to_ces"))
1988 struct LevelInfo level_backup = level;
1990 /* overwrite all individual level settings from template level settings */
1991 level = level_template;
1993 /* restore playfield size */
1994 level.fieldx = level_backup.fieldx;
1995 level.fieldy = level_backup.fieldy;
1997 /* restore playfield content */
1998 for (x = 0; x < level.fieldx; x++)
1999 for (y = 0; y < level.fieldy; y++)
2000 level.field[x][y] = level_backup.field[x][y];
2002 /* restore name and author from individual level */
2003 strcpy(level.name, level_backup.name);
2004 strcpy(level.author, level_backup.author);
2006 /* restore flag "use_custom_template" */
2007 level.use_custom_template = level_backup.use_custom_template;
2011 static char *getLevelFilenameFromBasename(char *basename)
2013 static char *filename = NULL;
2015 checked_free(filename);
2017 filename = getPath2(getCurrentLevelDir(), basename);
2022 static int getFileTypeFromBasename(char *basename)
2024 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
2026 static char *filename = NULL;
2027 struct stat file_status;
2029 /* ---------- try to determine file type from filename ---------- */
2031 /* check for typical filename of a Supaplex level package file */
2033 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2034 return LEVEL_FILE_TYPE_SP;
2036 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
2037 strncmp(basename, "LEVELS.D", 8) == 0))
2038 return LEVEL_FILE_TYPE_SP;
2041 /* check for typical filename of a Diamond Caves II level package file */
2042 if (strSuffixLower(basename, ".dc") ||
2043 strSuffixLower(basename, ".dc2"))
2044 return LEVEL_FILE_TYPE_DC;
2046 /* check for typical filename of a Sokoban level package file */
2047 if (strSuffixLower(basename, ".xsb") &&
2048 strchr(basename, '%') == NULL)
2049 return LEVEL_FILE_TYPE_SB;
2051 /* ---------- try to determine file type from filesize ---------- */
2053 checked_free(filename);
2054 filename = getPath2(getCurrentLevelDir(), basename);
2056 if (stat(filename, &file_status) == 0)
2058 /* check for typical filesize of a Supaplex level package file */
2059 if (file_status.st_size == 170496)
2060 return LEVEL_FILE_TYPE_SP;
2063 return LEVEL_FILE_TYPE_UNKNOWN;
2066 static boolean checkForPackageFromBasename(char *basename)
2068 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2069 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
2071 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2074 static char *getSingleLevelBasenameExt(int nr, char *extension)
2076 static char basename[MAX_FILENAME_LEN];
2079 sprintf(basename, "template.%s", extension);
2081 sprintf(basename, "%03d.%s", nr, extension);
2086 static char *getSingleLevelBasename(int nr)
2088 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2093 static char *getPackedLevelBasename(int type)
2095 static char basename[MAX_FILENAME_LEN];
2096 char *directory = getCurrentLevelDir();
2098 DirectoryEntry *dir_entry;
2100 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
2102 if ((dir = openDirectory(directory)) == NULL)
2104 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
2109 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
2111 char *entry_basename = dir_entry->basename;
2112 int entry_type = getFileTypeFromBasename(entry_basename);
2114 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
2116 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2119 strcpy(basename, entry_basename);
2126 closeDirectory(dir);
2133 static char *getPackedLevelBasename(int type)
2135 static char basename[MAX_FILENAME_LEN];
2136 char *directory = getCurrentLevelDir();
2138 struct dirent *dir_entry;
2140 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
2142 if ((dir = opendir(directory)) == NULL)
2144 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
2149 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2151 char *entry_basename = dir_entry->d_name;
2152 int entry_type = getFileTypeFromBasename(entry_basename);
2154 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
2156 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2159 strcpy(basename, entry_basename);
2173 static char *getSingleLevelFilename(int nr)
2175 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2179 static char *getPackedLevelFilename(int type)
2181 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2185 char *getDefaultLevelFilename(int nr)
2187 return getSingleLevelFilename(nr);
2191 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2195 lfi->packed = FALSE;
2196 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
2197 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2201 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2202 int type, char *format, ...)
2204 static char basename[MAX_FILENAME_LEN];
2207 va_start(ap, format);
2208 vsprintf(basename, format, ap);
2212 lfi->packed = FALSE;
2213 lfi->basename = basename;
2214 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2217 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2222 lfi->basename = getPackedLevelBasename(lfi->type);
2223 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2226 static int getFiletypeFromID(char *filetype_id)
2228 char *filetype_id_lower;
2229 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2232 if (filetype_id == NULL)
2233 return LEVEL_FILE_TYPE_UNKNOWN;
2235 filetype_id_lower = getStringToLower(filetype_id);
2237 for (i = 0; filetype_id_list[i].id != NULL; i++)
2239 char *id_lower = getStringToLower(filetype_id_list[i].id);
2241 if (strEqual(filetype_id_lower, id_lower))
2242 filetype = filetype_id_list[i].filetype;
2246 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2250 free(filetype_id_lower);
2255 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2259 /* special case: level number is negative => check for level template file */
2263 /* global variable "leveldir_current" must be modified in the loop below */
2264 LevelDirTree *leveldir_current_last = leveldir_current;
2266 /* check for template level in path from current to topmost tree node */
2268 while (leveldir_current != NULL)
2270 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2271 "template.%s", LEVELFILE_EXTENSION);
2273 if (fileExists(lfi->filename))
2276 leveldir_current = leveldir_current->node_parent;
2279 /* restore global variable "leveldir_current" modified in above loop */
2280 leveldir_current = leveldir_current_last;
2284 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2285 "template.%s", LEVELFILE_EXTENSION);
2289 /* no fallback if template file not existing */
2293 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2294 if (leveldir_current->level_filename != NULL)
2296 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2298 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2299 leveldir_current->level_filename, nr);
2301 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2303 if (fileExists(lfi->filename))
2307 /* check for native Rocks'n'Diamonds level file */
2308 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2309 "%03d.%s", nr, LEVELFILE_EXTENSION);
2310 if (fileExists(lfi->filename))
2313 /* check for Emerald Mine level file (V1) */
2314 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2315 'a' + (nr / 10) % 26, '0' + nr % 10);
2316 if (fileExists(lfi->filename))
2318 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2319 'A' + (nr / 10) % 26, '0' + nr % 10);
2320 if (fileExists(lfi->filename))
2323 /* check for Emerald Mine level file (V2 to V5) */
2324 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2325 if (fileExists(lfi->filename))
2328 /* check for Emerald Mine level file (V6 / single mode) */
2329 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2330 if (fileExists(lfi->filename))
2332 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2333 if (fileExists(lfi->filename))
2336 /* check for Emerald Mine level file (V6 / teamwork mode) */
2337 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2338 if (fileExists(lfi->filename))
2340 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2341 if (fileExists(lfi->filename))
2344 /* check for various packed level file formats */
2345 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2346 if (fileExists(lfi->filename))
2349 /* no known level file found -- use default values (and fail later) */
2350 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2351 "%03d.%s", nr, LEVELFILE_EXTENSION);
2354 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2356 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2357 lfi->type = getFileTypeFromBasename(lfi->basename);
2360 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2362 /* always start with reliable default values */
2363 setFileInfoToDefaults(level_file_info);
2365 level_file_info->nr = nr; /* set requested level number */
2367 determineLevelFileInfo_Filename(level_file_info);
2368 determineLevelFileInfo_Filetype(level_file_info);
2371 /* ------------------------------------------------------------------------- */
2372 /* functions for loading R'n'D level */
2373 /* ------------------------------------------------------------------------- */
2375 int getMappedElement(int element)
2377 /* remap some (historic, now obsolete) elements */
2381 case EL_PLAYER_OBSOLETE:
2382 element = EL_PLAYER_1;
2385 case EL_KEY_OBSOLETE:
2389 case EL_EM_KEY_1_FILE_OBSOLETE:
2390 element = EL_EM_KEY_1;
2393 case EL_EM_KEY_2_FILE_OBSOLETE:
2394 element = EL_EM_KEY_2;
2397 case EL_EM_KEY_3_FILE_OBSOLETE:
2398 element = EL_EM_KEY_3;
2401 case EL_EM_KEY_4_FILE_OBSOLETE:
2402 element = EL_EM_KEY_4;
2405 case EL_ENVELOPE_OBSOLETE:
2406 element = EL_ENVELOPE_1;
2414 if (element >= NUM_FILE_ELEMENTS)
2416 Error(ERR_WARN, "invalid level element %d", element);
2418 element = EL_UNKNOWN;
2426 int getMappedElementByVersion(int element, int game_version)
2428 /* remap some elements due to certain game version */
2430 if (game_version <= VERSION_IDENT(2,2,0,0))
2432 /* map game font elements */
2433 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2434 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2435 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2436 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2439 if (game_version < VERSION_IDENT(3,0,0,0))
2441 /* map Supaplex gravity tube elements */
2442 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2443 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2444 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2445 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2454 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2456 level->file_version = getFileVersion(file);
2457 level->game_version = getFileVersion(file);
2462 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2464 level->creation_date.year = getFile16BitBE(file);
2465 level->creation_date.month = getFile8Bit(file);
2466 level->creation_date.day = getFile8Bit(file);
2468 level->creation_date.src = DATE_SRC_LEVELFILE;
2473 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2475 int initial_player_stepsize;
2476 int initial_player_gravity;
2479 level->fieldx = getFile8Bit(file);
2480 level->fieldy = getFile8Bit(file);
2482 level->time = getFile16BitBE(file);
2483 level->gems_needed = getFile16BitBE(file);
2485 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2486 level->name[i] = getFile8Bit(file);
2487 level->name[MAX_LEVEL_NAME_LEN] = 0;
2489 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2490 level->score[i] = getFile8Bit(file);
2492 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2493 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2494 for (y = 0; y < 3; y++)
2495 for (x = 0; x < 3; x++)
2496 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2498 level->amoeba_speed = getFile8Bit(file);
2499 level->time_magic_wall = getFile8Bit(file);
2500 level->time_wheel = getFile8Bit(file);
2501 level->amoeba_content = getMappedElement(getFile8Bit(file));
2503 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2506 for (i = 0; i < MAX_PLAYERS; i++)
2507 level->initial_player_stepsize[i] = initial_player_stepsize;
2509 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2511 for (i = 0; i < MAX_PLAYERS; i++)
2512 level->initial_player_gravity[i] = initial_player_gravity;
2514 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2515 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2517 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2519 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2520 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2521 level->can_move_into_acid_bits = getFile32BitBE(file);
2522 level->dont_collide_with_bits = getFile8Bit(file);
2524 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2525 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2527 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2528 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2529 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2531 level->game_engine_type = getFile8Bit(file);
2533 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2538 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2542 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2543 level->name[i] = getFile8Bit(file);
2544 level->name[MAX_LEVEL_NAME_LEN] = 0;
2549 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2553 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2554 level->author[i] = getFile8Bit(file);
2555 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2560 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2563 int chunk_size_expected = level->fieldx * level->fieldy;
2565 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2566 stored with 16-bit encoding (and should be twice as big then).
2567 Even worse, playfield data was stored 16-bit when only yamyam content
2568 contained 16-bit elements and vice versa. */
2570 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2571 chunk_size_expected *= 2;
2573 if (chunk_size_expected != chunk_size)
2575 ReadUnusedBytesFromFile(file, chunk_size);
2576 return chunk_size_expected;
2579 for (y = 0; y < level->fieldy; y++)
2580 for (x = 0; x < level->fieldx; x++)
2581 level->field[x][y] =
2582 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2587 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2590 int header_size = 4;
2591 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2592 int chunk_size_expected = header_size + content_size;
2594 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2595 stored with 16-bit encoding (and should be twice as big then).
2596 Even worse, playfield data was stored 16-bit when only yamyam content
2597 contained 16-bit elements and vice versa. */
2599 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2600 chunk_size_expected += content_size;
2602 if (chunk_size_expected != chunk_size)
2604 ReadUnusedBytesFromFile(file, chunk_size);
2605 return chunk_size_expected;
2609 level->num_yamyam_contents = getFile8Bit(file);
2613 /* correct invalid number of content fields -- should never happen */
2614 if (level->num_yamyam_contents < 1 ||
2615 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2616 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2618 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2619 for (y = 0; y < 3; y++)
2620 for (x = 0; x < 3; x++)
2621 level->yamyam_content[i].e[x][y] =
2622 getMappedElement(level->encoding_16bit_field ?
2623 getFile16BitBE(file) : getFile8Bit(file));
2627 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2633 int content_xsize, content_ysize;
2635 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2637 element = getMappedElement(getFile16BitBE(file));
2638 num_contents = getFile8Bit(file);
2640 getFile8Bit(file); /* content x size (unused) */
2641 getFile8Bit(file); /* content y size (unused) */
2643 content_xsize = getFile8Bit(file);
2644 content_ysize = getFile8Bit(file);
2647 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2649 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2650 for (y = 0; y < 3; y++)
2651 for (x = 0; x < 3; x++)
2652 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2654 /* correct invalid number of content fields -- should never happen */
2655 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2656 num_contents = STD_ELEMENT_CONTENTS;
2658 if (element == EL_YAMYAM)
2660 level->num_yamyam_contents = num_contents;
2662 for (i = 0; i < num_contents; i++)
2663 for (y = 0; y < 3; y++)
2664 for (x = 0; x < 3; x++)
2665 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2667 else if (element == EL_BD_AMOEBA)
2669 level->amoeba_content = content_array[0][0][0];
2673 Error(ERR_WARN, "cannot load content for element '%d'", element);
2679 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2685 int chunk_size_expected;
2687 element = getMappedElement(getFile16BitBE(file));
2688 if (!IS_ENVELOPE(element))
2689 element = EL_ENVELOPE_1;
2691 envelope_nr = element - EL_ENVELOPE_1;
2693 envelope_len = getFile16BitBE(file);
2695 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2696 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2698 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2700 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2701 if (chunk_size_expected != chunk_size)
2703 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2704 return chunk_size_expected;
2707 for (i = 0; i < envelope_len; i++)
2708 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2713 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2715 int num_changed_custom_elements = getFile16BitBE(file);
2716 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2719 if (chunk_size_expected != chunk_size)
2721 ReadUnusedBytesFromFile(file, chunk_size - 2);
2722 return chunk_size_expected;
2725 for (i = 0; i < num_changed_custom_elements; i++)
2727 int element = getMappedElement(getFile16BitBE(file));
2728 int properties = getFile32BitBE(file);
2730 if (IS_CUSTOM_ELEMENT(element))
2731 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2733 Error(ERR_WARN, "invalid custom element number %d", element);
2735 /* older game versions that wrote level files with CUS1 chunks used
2736 different default push delay values (not yet stored in level file) */
2737 element_info[element].push_delay_fixed = 2;
2738 element_info[element].push_delay_random = 8;
2744 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2746 int num_changed_custom_elements = getFile16BitBE(file);
2747 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2750 if (chunk_size_expected != chunk_size)
2752 ReadUnusedBytesFromFile(file, chunk_size - 2);
2753 return chunk_size_expected;
2756 for (i = 0; i < num_changed_custom_elements; i++)
2758 int element = getMappedElement(getFile16BitBE(file));
2759 int custom_target_element = getMappedElement(getFile16BitBE(file));
2761 if (IS_CUSTOM_ELEMENT(element))
2762 element_info[element].change->target_element = custom_target_element;
2764 Error(ERR_WARN, "invalid custom element number %d", element);
2770 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2772 int num_changed_custom_elements = getFile16BitBE(file);
2773 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2776 if (chunk_size_expected != chunk_size)
2778 ReadUnusedBytesFromFile(file, chunk_size - 2);
2779 return chunk_size_expected;
2782 for (i = 0; i < num_changed_custom_elements; i++)
2784 int element = getMappedElement(getFile16BitBE(file));
2785 struct ElementInfo *ei = &element_info[element];
2786 unsigned int event_bits;
2788 if (!IS_CUSTOM_ELEMENT(element))
2790 Error(ERR_WARN, "invalid custom element number %d", element);
2792 element = EL_INTERNAL_DUMMY;
2795 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2796 ei->description[j] = getFile8Bit(file);
2797 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2799 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2801 /* some free bytes for future properties and padding */
2802 ReadUnusedBytesFromFile(file, 7);
2804 ei->use_gfx_element = getFile8Bit(file);
2805 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2807 ei->collect_score_initial = getFile8Bit(file);
2808 ei->collect_count_initial = getFile8Bit(file);
2810 ei->push_delay_fixed = getFile16BitBE(file);
2811 ei->push_delay_random = getFile16BitBE(file);
2812 ei->move_delay_fixed = getFile16BitBE(file);
2813 ei->move_delay_random = getFile16BitBE(file);
2815 ei->move_pattern = getFile16BitBE(file);
2816 ei->move_direction_initial = getFile8Bit(file);
2817 ei->move_stepsize = getFile8Bit(file);
2819 for (y = 0; y < 3; y++)
2820 for (x = 0; x < 3; x++)
2821 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2823 event_bits = getFile32BitBE(file);
2824 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2825 if (event_bits & (1 << j))
2826 ei->change->has_event[j] = TRUE;
2828 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2830 ei->change->delay_fixed = getFile16BitBE(file);
2831 ei->change->delay_random = getFile16BitBE(file);
2832 ei->change->delay_frames = getFile16BitBE(file);
2834 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2836 ei->change->explode = getFile8Bit(file);
2837 ei->change->use_target_content = getFile8Bit(file);
2838 ei->change->only_if_complete = getFile8Bit(file);
2839 ei->change->use_random_replace = getFile8Bit(file);
2841 ei->change->random_percentage = getFile8Bit(file);
2842 ei->change->replace_when = getFile8Bit(file);
2844 for (y = 0; y < 3; y++)
2845 for (x = 0; x < 3; x++)
2846 ei->change->target_content.e[x][y] =
2847 getMappedElement(getFile16BitBE(file));
2849 ei->slippery_type = getFile8Bit(file);
2851 /* some free bytes for future properties and padding */
2852 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2854 /* mark that this custom element has been modified */
2855 ei->modified_settings = TRUE;
2861 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2863 struct ElementInfo *ei;
2864 int chunk_size_expected;
2868 /* ---------- custom element base property values (96 bytes) ------------- */
2870 element = getMappedElement(getFile16BitBE(file));
2872 if (!IS_CUSTOM_ELEMENT(element))
2874 Error(ERR_WARN, "invalid custom element number %d", element);
2876 ReadUnusedBytesFromFile(file, chunk_size - 2);
2880 ei = &element_info[element];
2882 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2883 ei->description[i] = getFile8Bit(file);
2884 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2886 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2888 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2890 ei->num_change_pages = getFile8Bit(file);
2892 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2893 if (chunk_size_expected != chunk_size)
2895 ReadUnusedBytesFromFile(file, chunk_size - 43);
2896 return chunk_size_expected;
2899 ei->ce_value_fixed_initial = getFile16BitBE(file);
2900 ei->ce_value_random_initial = getFile16BitBE(file);
2901 ei->use_last_ce_value = getFile8Bit(file);
2903 ei->use_gfx_element = getFile8Bit(file);
2904 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2906 ei->collect_score_initial = getFile8Bit(file);
2907 ei->collect_count_initial = getFile8Bit(file);
2909 ei->drop_delay_fixed = getFile8Bit(file);
2910 ei->push_delay_fixed = getFile8Bit(file);
2911 ei->drop_delay_random = getFile8Bit(file);
2912 ei->push_delay_random = getFile8Bit(file);
2913 ei->move_delay_fixed = getFile16BitBE(file);
2914 ei->move_delay_random = getFile16BitBE(file);
2916 /* bits 0 - 15 of "move_pattern" ... */
2917 ei->move_pattern = getFile16BitBE(file);
2918 ei->move_direction_initial = getFile8Bit(file);
2919 ei->move_stepsize = getFile8Bit(file);
2921 ei->slippery_type = getFile8Bit(file);
2923 for (y = 0; y < 3; y++)
2924 for (x = 0; x < 3; x++)
2925 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2927 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2928 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2929 ei->move_leave_type = getFile8Bit(file);
2931 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2932 ei->move_pattern |= (getFile16BitBE(file) << 16);
2934 ei->access_direction = getFile8Bit(file);
2936 ei->explosion_delay = getFile8Bit(file);
2937 ei->ignition_delay = getFile8Bit(file);
2938 ei->explosion_type = getFile8Bit(file);
2940 /* some free bytes for future custom property values and padding */
2941 ReadUnusedBytesFromFile(file, 1);
2943 /* ---------- change page property values (48 bytes) --------------------- */
2945 setElementChangePages(ei, ei->num_change_pages);
2947 for (i = 0; i < ei->num_change_pages; i++)
2949 struct ElementChangeInfo *change = &ei->change_page[i];
2950 unsigned int event_bits;
2952 /* always start with reliable default values */
2953 setElementChangeInfoToDefaults(change);
2955 /* bits 0 - 31 of "has_event[]" ... */
2956 event_bits = getFile32BitBE(file);
2957 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2958 if (event_bits & (1 << j))
2959 change->has_event[j] = TRUE;
2961 change->target_element = getMappedElement(getFile16BitBE(file));
2963 change->delay_fixed = getFile16BitBE(file);
2964 change->delay_random = getFile16BitBE(file);
2965 change->delay_frames = getFile16BitBE(file);
2967 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2969 change->explode = getFile8Bit(file);
2970 change->use_target_content = getFile8Bit(file);
2971 change->only_if_complete = getFile8Bit(file);
2972 change->use_random_replace = getFile8Bit(file);
2974 change->random_percentage = getFile8Bit(file);
2975 change->replace_when = getFile8Bit(file);
2977 for (y = 0; y < 3; y++)
2978 for (x = 0; x < 3; x++)
2979 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2981 change->can_change = getFile8Bit(file);
2983 change->trigger_side = getFile8Bit(file);
2985 change->trigger_player = getFile8Bit(file);
2986 change->trigger_page = getFile8Bit(file);
2988 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2989 CH_PAGE_ANY : (1 << change->trigger_page));
2991 change->has_action = getFile8Bit(file);
2992 change->action_type = getFile8Bit(file);
2993 change->action_mode = getFile8Bit(file);
2994 change->action_arg = getFile16BitBE(file);
2996 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2997 event_bits = getFile8Bit(file);
2998 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2999 if (event_bits & (1 << (j - 32)))
3000 change->has_event[j] = TRUE;
3003 /* mark this custom element as modified */
3004 ei->modified_settings = TRUE;
3009 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3011 struct ElementInfo *ei;
3012 struct ElementGroupInfo *group;
3016 element = getMappedElement(getFile16BitBE(file));
3018 if (!IS_GROUP_ELEMENT(element))
3020 Error(ERR_WARN, "invalid group element number %d", element);
3022 ReadUnusedBytesFromFile(file, chunk_size - 2);
3026 ei = &element_info[element];
3028 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3029 ei->description[i] = getFile8Bit(file);
3030 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3032 group = element_info[element].group;
3034 group->num_elements = getFile8Bit(file);
3036 ei->use_gfx_element = getFile8Bit(file);
3037 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3039 group->choice_mode = getFile8Bit(file);
3041 /* some free bytes for future values and padding */
3042 ReadUnusedBytesFromFile(file, 3);
3044 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3045 group->element[i] = getMappedElement(getFile16BitBE(file));
3047 /* mark this group element as modified */
3048 element_info[element].modified_settings = TRUE;
3053 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3054 int element, int real_element)
3056 int micro_chunk_size = 0;
3057 int conf_type = getFile8Bit(file);
3058 int byte_mask = conf_type & CONF_MASK_BYTES;
3059 boolean element_found = FALSE;
3062 micro_chunk_size += 1;
3064 if (byte_mask == CONF_MASK_MULTI_BYTES)
3066 int num_bytes = getFile16BitBE(file);
3067 byte *buffer = checked_malloc(num_bytes);
3069 ReadBytesFromFile(file, buffer, num_bytes);
3071 for (i = 0; conf[i].data_type != -1; i++)
3073 if (conf[i].element == element &&
3074 conf[i].conf_type == conf_type)
3076 int data_type = conf[i].data_type;
3077 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3078 int max_num_entities = conf[i].max_num_entities;
3080 if (num_entities > max_num_entities)
3083 "truncating number of entities for element %d from %d to %d",
3084 element, num_entities, max_num_entities);
3086 num_entities = max_num_entities;
3089 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3090 data_type == TYPE_CONTENT_LIST))
3092 /* for element and content lists, zero entities are not allowed */
3093 Error(ERR_WARN, "found empty list of entities for element %d",
3096 /* do not set "num_entities" here to prevent reading behind buffer */
3098 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
3102 *(int *)(conf[i].num_entities) = num_entities;
3105 element_found = TRUE;
3107 if (data_type == TYPE_STRING)
3109 char *string = (char *)(conf[i].value);
3112 for (j = 0; j < max_num_entities; j++)
3113 string[j] = (j < num_entities ? buffer[j] : '\0');
3115 else if (data_type == TYPE_ELEMENT_LIST)
3117 int *element_array = (int *)(conf[i].value);
3120 for (j = 0; j < num_entities; j++)
3122 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3124 else if (data_type == TYPE_CONTENT_LIST)
3126 struct Content *content= (struct Content *)(conf[i].value);
3129 for (c = 0; c < num_entities; c++)
3130 for (y = 0; y < 3; y++)
3131 for (x = 0; x < 3; x++)
3132 content[c].e[x][y] =
3133 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3136 element_found = FALSE;
3142 checked_free(buffer);
3144 micro_chunk_size += 2 + num_bytes;
3146 else /* constant size configuration data (1, 2 or 4 bytes) */
3148 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3149 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3150 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3152 for (i = 0; conf[i].data_type != -1; i++)
3154 if (conf[i].element == element &&
3155 conf[i].conf_type == conf_type)
3157 int data_type = conf[i].data_type;
3159 if (data_type == TYPE_ELEMENT)
3160 value = getMappedElement(value);
3162 if (data_type == TYPE_BOOLEAN)
3163 *(boolean *)(conf[i].value) = value;
3165 *(int *) (conf[i].value) = value;
3167 element_found = TRUE;
3173 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3178 char *error_conf_chunk_bytes =
3179 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3180 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3181 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3182 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3183 int error_element = real_element;
3185 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3186 error_conf_chunk_bytes, error_conf_chunk_token,
3187 error_element, EL_NAME(error_element));
3190 return micro_chunk_size;
3193 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3195 int real_chunk_size = 0;
3197 li = *level; /* copy level data into temporary buffer */
3199 while (!checkEndOfFile(file))
3201 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3203 if (real_chunk_size >= chunk_size)
3207 *level = li; /* copy temporary buffer back to level data */
3209 return real_chunk_size;
3212 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3214 int real_chunk_size = 0;
3216 li = *level; /* copy level data into temporary buffer */
3218 while (!checkEndOfFile(file))
3220 int element = getMappedElement(getFile16BitBE(file));
3222 real_chunk_size += 2;
3223 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3225 if (real_chunk_size >= chunk_size)
3229 *level = li; /* copy temporary buffer back to level data */
3231 return real_chunk_size;
3234 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3236 int real_chunk_size = 0;
3238 li = *level; /* copy level data into temporary buffer */
3240 while (!checkEndOfFile(file))
3242 int element = getMappedElement(getFile16BitBE(file));
3244 real_chunk_size += 2;
3245 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3247 if (real_chunk_size >= chunk_size)
3251 *level = li; /* copy temporary buffer back to level data */
3253 return real_chunk_size;
3256 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3258 int element = getMappedElement(getFile16BitBE(file));
3259 int envelope_nr = element - EL_ENVELOPE_1;
3260 int real_chunk_size = 2;
3262 while (!checkEndOfFile(file))
3264 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3267 if (real_chunk_size >= chunk_size)
3271 level->envelope[envelope_nr] = xx_envelope;
3273 return real_chunk_size;
3276 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3278 int element = getMappedElement(getFile16BitBE(file));
3279 int real_chunk_size = 2;
3280 struct ElementInfo *ei = &element_info[element];
3283 xx_ei = *ei; /* copy element data into temporary buffer */
3285 xx_ei.num_change_pages = -1;
3287 while (!checkEndOfFile(file))
3289 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3291 if (xx_ei.num_change_pages != -1)
3294 if (real_chunk_size >= chunk_size)
3300 if (ei->num_change_pages == -1)
3302 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3305 ei->num_change_pages = 1;
3307 setElementChangePages(ei, 1);
3308 setElementChangeInfoToDefaults(ei->change);
3310 return real_chunk_size;
3313 /* initialize number of change pages stored for this custom element */
3314 setElementChangePages(ei, ei->num_change_pages);
3315 for (i = 0; i < ei->num_change_pages; i++)
3316 setElementChangeInfoToDefaults(&ei->change_page[i]);
3318 /* start with reading properties for the first change page */
3319 xx_current_change_page = 0;
3321 while (!checkEndOfFile(file))
3323 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3325 xx_change = *change; /* copy change data into temporary buffer */
3327 resetEventBits(); /* reset bits; change page might have changed */
3329 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3332 *change = xx_change;
3334 setEventFlagsFromEventBits(change);
3336 if (real_chunk_size >= chunk_size)
3340 return real_chunk_size;
3343 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3345 int element = getMappedElement(getFile16BitBE(file));
3346 int real_chunk_size = 2;
3347 struct ElementInfo *ei = &element_info[element];
3348 struct ElementGroupInfo *group = ei->group;
3350 xx_ei = *ei; /* copy element data into temporary buffer */
3351 xx_group = *group; /* copy group data into temporary buffer */
3353 while (!checkEndOfFile(file))
3355 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3358 if (real_chunk_size >= chunk_size)
3365 return real_chunk_size;
3370 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
3372 level->file_version = getFileVersion(file);
3373 level->game_version = getFileVersion(file);
3378 static int LoadLevel_DATE(FILE *file, int chunk_size, struct LevelInfo *level)
3380 level->creation_date.year = getFile16BitBE(file);
3381 level->creation_date.month = getFile8Bit(file);
3382 level->creation_date.day = getFile8Bit(file);
3384 level->creation_date.src = DATE_SRC_LEVELFILE;
3389 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
3391 int initial_player_stepsize;
3392 int initial_player_gravity;
3395 level->fieldx = getFile8Bit(file);
3396 level->fieldy = getFile8Bit(file);
3398 level->time = getFile16BitBE(file);
3399 level->gems_needed = getFile16BitBE(file);
3401 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3402 level->name[i] = getFile8Bit(file);
3403 level->name[MAX_LEVEL_NAME_LEN] = 0;
3405 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3406 level->score[i] = getFile8Bit(file);
3408 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3409 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3410 for (y = 0; y < 3; y++)
3411 for (x = 0; x < 3; x++)
3412 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3414 level->amoeba_speed = getFile8Bit(file);
3415 level->time_magic_wall = getFile8Bit(file);
3416 level->time_wheel = getFile8Bit(file);
3417 level->amoeba_content = getMappedElement(getFile8Bit(file));
3419 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3422 for (i = 0; i < MAX_PLAYERS; i++)
3423 level->initial_player_stepsize[i] = initial_player_stepsize;
3425 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3427 for (i = 0; i < MAX_PLAYERS; i++)
3428 level->initial_player_gravity[i] = initial_player_gravity;
3430 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3431 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3433 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3435 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3436 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3437 level->can_move_into_acid_bits = getFile32BitBE(file);
3438 level->dont_collide_with_bits = getFile8Bit(file);
3440 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3441 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3443 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3444 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3445 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3447 level->game_engine_type = getFile8Bit(file);
3449 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3454 static int LoadLevel_NAME(FILE *file, int chunk_size, struct LevelInfo *level)
3458 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3459 level->name[i] = getFile8Bit(file);
3460 level->name[MAX_LEVEL_NAME_LEN] = 0;
3465 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
3469 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3470 level->author[i] = getFile8Bit(file);
3471 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3476 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
3479 int chunk_size_expected = level->fieldx * level->fieldy;
3481 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3482 stored with 16-bit encoding (and should be twice as big then).
3483 Even worse, playfield data was stored 16-bit when only yamyam content
3484 contained 16-bit elements and vice versa. */
3486 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3487 chunk_size_expected *= 2;
3489 if (chunk_size_expected != chunk_size)
3491 ReadUnusedBytesFromFile(file, chunk_size);
3492 return chunk_size_expected;
3495 for (y = 0; y < level->fieldy; y++)
3496 for (x = 0; x < level->fieldx; x++)
3497 level->field[x][y] =
3498 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3503 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
3506 int header_size = 4;
3507 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3508 int chunk_size_expected = header_size + content_size;
3510 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3511 stored with 16-bit encoding (and should be twice as big then).
3512 Even worse, playfield data was stored 16-bit when only yamyam content
3513 contained 16-bit elements and vice versa. */
3515 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3516 chunk_size_expected += content_size;
3518 if (chunk_size_expected != chunk_size)
3520 ReadUnusedBytesFromFile(file, chunk_size);
3521 return chunk_size_expected;
3525 level->num_yamyam_contents = getFile8Bit(file);
3529 /* correct invalid number of content fields -- should never happen */
3530 if (level->num_yamyam_contents < 1 ||
3531 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3532 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3534 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3535 for (y = 0; y < 3; y++)
3536 for (x = 0; x < 3; x++)
3537 level->yamyam_content[i].e[x][y] =
3538 getMappedElement(level->encoding_16bit_field ?
3539 getFile16BitBE(file) : getFile8Bit(file));
3543 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
3549 int content_xsize, content_ysize;
3551 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3553 element = getMappedElement(getFile16BitBE(file));
3554 num_contents = getFile8Bit(file);
3556 getFile8Bit(file); /* content x size (unused) */
3557 getFile8Bit(file); /* content y size (unused) */
3559 content_xsize = getFile8Bit(file);
3560 content_ysize = getFile8Bit(file);
3563 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3565 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3566 for (y = 0; y < 3; y++)
3567 for (x = 0; x < 3; x++)
3568 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3570 /* correct invalid number of content fields -- should never happen */
3571 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3572 num_contents = STD_ELEMENT_CONTENTS;
3574 if (element == EL_YAMYAM)
3576 level->num_yamyam_contents = num_contents;
3578 for (i = 0; i < num_contents; i++)
3579 for (y = 0; y < 3; y++)
3580 for (x = 0; x < 3; x++)
3581 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3583 else if (element == EL_BD_AMOEBA)
3585 level->amoeba_content = content_array[0][0][0];
3589 Error(ERR_WARN, "cannot load content for element '%d'", element);
3595 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
3601 int chunk_size_expected;
3603 element = getMappedElement(getFile16BitBE(file));
3604 if (!IS_ENVELOPE(element))
3605 element = EL_ENVELOPE_1;
3607 envelope_nr = element - EL_ENVELOPE_1;
3609 envelope_len = getFile16BitBE(file);
3611 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3612 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3614 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3616 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3617 if (chunk_size_expected != chunk_size)
3619 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3620 return chunk_size_expected;
3623 for (i = 0; i < envelope_len; i++)
3624 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3629 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
3631 int num_changed_custom_elements = getFile16BitBE(file);
3632 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3635 if (chunk_size_expected != chunk_size)
3637 ReadUnusedBytesFromFile(file, chunk_size - 2);
3638 return chunk_size_expected;
3641 for (i = 0; i < num_changed_custom_elements; i++)
3643 int element = getMappedElement(getFile16BitBE(file));
3644 int properties = getFile32BitBE(file);
3646 if (IS_CUSTOM_ELEMENT(element))
3647 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3649 Error(ERR_WARN, "invalid custom element number %d", element);
3651 /* older game versions that wrote level files with CUS1 chunks used
3652 different default push delay values (not yet stored in level file) */
3653 element_info[element].push_delay_fixed = 2;
3654 element_info[element].push_delay_random = 8;
3660 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
3662 int num_changed_custom_elements = getFile16BitBE(file);
3663 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3666 if (chunk_size_expected != chunk_size)
3668 ReadUnusedBytesFromFile(file, chunk_size - 2);
3669 return chunk_size_expected;
3672 for (i = 0; i < num_changed_custom_elements; i++)
3674 int element = getMappedElement(getFile16BitBE(file));
3675 int custom_target_element = getMappedElement(getFile16BitBE(file));
3677 if (IS_CUSTOM_ELEMENT(element))
3678 element_info[element].change->target_element = custom_target_element;
3680 Error(ERR_WARN, "invalid custom element number %d", element);
3686 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
3688 int num_changed_custom_elements = getFile16BitBE(file);
3689 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3692 if (chunk_size_expected != chunk_size)
3694 ReadUnusedBytesFromFile(file, chunk_size - 2);
3695 return chunk_size_expected;
3698 for (i = 0; i < num_changed_custom_elements; i++)
3700 int element = getMappedElement(getFile16BitBE(file));
3701 struct ElementInfo *ei = &element_info[element];
3702 unsigned int event_bits;
3704 if (!IS_CUSTOM_ELEMENT(element))
3706 Error(ERR_WARN, "invalid custom element number %d", element);
3708 element = EL_INTERNAL_DUMMY;
3711 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3712 ei->description[j] = getFile8Bit(file);
3713 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3715 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3717 /* some free bytes for future properties and padding */
3718 ReadUnusedBytesFromFile(file, 7);
3720 ei->use_gfx_element = getFile8Bit(file);
3721 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3723 ei->collect_score_initial = getFile8Bit(file);
3724 ei->collect_count_initial = getFile8Bit(file);
3726 ei->push_delay_fixed = getFile16BitBE(file);
3727 ei->push_delay_random = getFile16BitBE(file);
3728 ei->move_delay_fixed = getFile16BitBE(file);
3729 ei->move_delay_random = getFile16BitBE(file);
3731 ei->move_pattern = getFile16BitBE(file);
3732 ei->move_direction_initial = getFile8Bit(file);
3733 ei->move_stepsize = getFile8Bit(file);
3735 for (y = 0; y < 3; y++)
3736 for (x = 0; x < 3; x++)
3737 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3739 event_bits = getFile32BitBE(file);
3740 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3741 if (event_bits & (1 << j))
3742 ei->change->has_event[j] = TRUE;
3744 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3746 ei->change->delay_fixed = getFile16BitBE(file);
3747 ei->change->delay_random = getFile16BitBE(file);
3748 ei->change->delay_frames = getFile16BitBE(file);
3750 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
3752 ei->change->explode = getFile8Bit(file);
3753 ei->change->use_target_content = getFile8Bit(file);
3754 ei->change->only_if_complete = getFile8Bit(file);
3755 ei->change->use_random_replace = getFile8Bit(file);
3757 ei->change->random_percentage = getFile8Bit(file);
3758 ei->change->replace_when = getFile8Bit(file);
3760 for (y = 0; y < 3; y++)
3761 for (x = 0; x < 3; x++)
3762 ei->change->target_content.e[x][y] =
3763 getMappedElement(getFile16BitBE(file));
3765 ei->slippery_type = getFile8Bit(file);
3767 /* some free bytes for future properties and padding */
3768 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3770 /* mark that this custom element has been modified */
3771 ei->modified_settings = TRUE;
3777 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
3779 struct ElementInfo *ei;
3780 int chunk_size_expected;
3784 /* ---------- custom element base property values (96 bytes) ------------- */
3786 element = getMappedElement(getFile16BitBE(file));
3788 if (!IS_CUSTOM_ELEMENT(element))
3790 Error(ERR_WARN, "invalid custom element number %d", element);
3792 ReadUnusedBytesFromFile(file, chunk_size - 2);
3796 ei = &element_info[element];
3798 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3799 ei->description[i] = getFile8Bit(file);
3800 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3802 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3804 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
3806 ei->num_change_pages = getFile8Bit(file);
3808 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3809 if (chunk_size_expected != chunk_size)
3811 ReadUnusedBytesFromFile(file, chunk_size - 43);
3812 return chunk_size_expected;
3815 ei->ce_value_fixed_initial = getFile16BitBE(file);
3816 ei->ce_value_random_initial = getFile16BitBE(file);
3817 ei->use_last_ce_value = getFile8Bit(file);
3819 ei->use_gfx_element = getFile8Bit(file);
3820 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3822 ei->collect_score_initial = getFile8Bit(file);
3823 ei->collect_count_initial = getFile8Bit(file);
3825 ei->drop_delay_fixed = getFile8Bit(file);
3826 ei->push_delay_fixed = getFile8Bit(file);
3827 ei->drop_delay_random = getFile8Bit(file);
3828 ei->push_delay_random = getFile8Bit(file);
3829 ei->move_delay_fixed = getFile16BitBE(file);
3830 ei->move_delay_random = getFile16BitBE(file);
3832 /* bits 0 - 15 of "move_pattern" ... */
3833 ei->move_pattern = getFile16BitBE(file);
3834 ei->move_direction_initial = getFile8Bit(file);
3835 ei->move_stepsize = getFile8Bit(file);
3837 ei->slippery_type = getFile8Bit(file);
3839 for (y = 0; y < 3; y++)
3840 for (x = 0; x < 3; x++)
3841 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3843 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3844 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3845 ei->move_leave_type = getFile8Bit(file);
3847 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
3848 ei->move_pattern |= (getFile16BitBE(file) << 16);
3850 ei->access_direction = getFile8Bit(file);
3852 ei->explosion_delay = getFile8Bit(file);
3853 ei->ignition_delay = getFile8Bit(file);
3854 ei->explosion_type = getFile8Bit(file);
3856 /* some free bytes for future custom property values and padding */
3857 ReadUnusedBytesFromFile(file, 1);
3859 /* ---------- change page property values (48 bytes) --------------------- */
3861 setElementChangePages(ei, ei->num_change_pages);
3863 for (i = 0; i < ei->num_change_pages; i++)
3865 struct ElementChangeInfo *change = &ei->change_page[i];
3866 unsigned int event_bits;
3868 /* always start with reliable default values */
3869 setElementChangeInfoToDefaults(change);
3871 /* bits 0 - 31 of "has_event[]" ... */
3872 event_bits = getFile32BitBE(file);
3873 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3874 if (event_bits & (1 << j))
3875 change->has_event[j] = TRUE;
3877 change->target_element = getMappedElement(getFile16BitBE(file));
3879 change->delay_fixed = getFile16BitBE(file);
3880 change->delay_random = getFile16BitBE(file);
3881 change->delay_frames = getFile16BitBE(file);
3883 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3885 change->explode = getFile8Bit(file);
3886 change->use_target_content = getFile8Bit(file);
3887 change->only_if_complete = getFile8Bit(file);
3888 change->use_random_replace = getFile8Bit(file);
3890 change->random_percentage = getFile8Bit(file);
3891 change->replace_when = getFile8Bit(file);
3893 for (y = 0; y < 3; y++)
3894 for (x = 0; x < 3; x++)
3895 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3897 change->can_change = getFile8Bit(file);
3899 change->trigger_side = getFile8Bit(file);
3901 change->trigger_player = getFile8Bit(file);
3902 change->trigger_page = getFile8Bit(file);
3904 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3905 CH_PAGE_ANY : (1 << change->trigger_page));
3907 change->has_action = getFile8Bit(file);
3908 change->action_type = getFile8Bit(file);
3909 change->action_mode = getFile8Bit(file);
3910 change->action_arg = getFile16BitBE(file);
3912 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
3913 event_bits = getFile8Bit(file);
3914 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3915 if (event_bits & (1 << (j - 32)))
3916 change->has_event[j] = TRUE;
3919 /* mark this custom element as modified */
3920 ei->modified_settings = TRUE;
3925 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
3927 struct ElementInfo *ei;
3928 struct ElementGroupInfo *group;
3932 element = getMappedElement(getFile16BitBE(file));
3934 if (!IS_GROUP_ELEMENT(element))
3936 Error(ERR_WARN, "invalid group element number %d", element);
3938 ReadUnusedBytesFromFile(file, chunk_size - 2);
3942 ei = &element_info[element];
3944 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3945 ei->description[i] = getFile8Bit(file);
3946 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3948 group = element_info[element].group;
3950 group->num_elements = getFile8Bit(file);
3952 ei->use_gfx_element = getFile8Bit(file);
3953 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3955 group->choice_mode = getFile8Bit(file);
3957 /* some free bytes for future values and padding */
3958 ReadUnusedBytesFromFile(file, 3);
3960 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3961 group->element[i] = getMappedElement(getFile16BitBE(file));
3963 /* mark this group element as modified */
3964 element_info[element].modified_settings = TRUE;
3969 static int LoadLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *conf,
3970 int element, int real_element)
3972 int micro_chunk_size = 0;
3973 int conf_type = getFile8Bit(file);
3974 int byte_mask = conf_type & CONF_MASK_BYTES;
3975 boolean element_found = FALSE;
3978 micro_chunk_size += 1;
3980 if (byte_mask == CONF_MASK_MULTI_BYTES)
3982 int num_bytes = getFile16BitBE(file);
3983 byte *buffer = checked_malloc(num_bytes);
3985 ReadBytesFromFile(file, buffer, num_bytes);
3987 for (i = 0; conf[i].data_type != -1; i++)
3989 if (conf[i].element == element &&
3990 conf[i].conf_type == conf_type)
3992 int data_type = conf[i].data_type;
3993 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3994 int max_num_entities = conf[i].max_num_entities;
3996 if (num_entities > max_num_entities)
3999 "truncating number of entities for element %d from %d to %d",
4000 element, num_entities, max_num_entities);
4002 num_entities = max_num_entities;
4005 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
4006 data_type == TYPE_CONTENT_LIST))
4008 /* for element and content lists, zero entities are not allowed */
4009 Error(ERR_WARN, "found empty list of entities for element %d",
4012 /* do not set "num_entities" here to prevent reading behind buffer */
4014 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
4018 *(int *)(conf[i].num_entities) = num_entities;
4021 element_found = TRUE;
4023 if (data_type == TYPE_STRING)
4025 char *string = (char *)(conf[i].value);
4028 for (j = 0; j < max_num_entities; j++)
4029 string[j] = (j < num_entities ? buffer[j] : '\0');
4031 else if (data_type == TYPE_ELEMENT_LIST)
4033 int *element_array = (int *)(conf[i].value);
4036 for (j = 0; j < num_entities; j++)
4038 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
4040 else if (data_type == TYPE_CONTENT_LIST)
4042 struct Content *content= (struct Content *)(conf[i].value);
4045 for (c = 0; c < num_entities; c++)
4046 for (y = 0; y < 3; y++)
4047 for (x = 0; x < 3; x++)
4048 content[c].e[x][y] =
4049 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
4052 element_found = FALSE;
4058 checked_free(buffer);
4060 micro_chunk_size += 2 + num_bytes;
4062 else /* constant size configuration data (1, 2 or 4 bytes) */
4064 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
4065 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
4066 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
4068 for (i = 0; conf[i].data_type != -1; i++)
4070 if (conf[i].element == element &&
4071 conf[i].conf_type == conf_type)
4073 int data_type = conf[i].data_type;
4075 if (data_type == TYPE_ELEMENT)
4076 value = getMappedElement(value);
4078 if (data_type == TYPE_BOOLEAN)
4079 *(boolean *)(conf[i].value) = value;
4081 *(int *) (conf[i].value) = value;
4083 element_found = TRUE;
4089 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
4094 char *error_conf_chunk_bytes =
4095 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
4096 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
4097 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
4098 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
4099 int error_element = real_element;
4101 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
4102 error_conf_chunk_bytes, error_conf_chunk_token,
4103 error_element, EL_NAME(error_element));
4106 return micro_chunk_size;
4109 static int LoadLevel_INFO(FILE *file, int chunk_size, struct LevelInfo *level)
4111 int real_chunk_size = 0;
4113 li = *level; /* copy level data into temporary buffer */
4117 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
4119 if (real_chunk_size >= chunk_size)
4123 *level = li; /* copy temporary buffer back to level data */
4125 return real_chunk_size;
4128 static int LoadLevel_CONF(FILE *file, int chunk_size, struct LevelInfo *level)
4130 int real_chunk_size = 0;
4132 li = *level; /* copy level data into temporary buffer */
4136 int element = getMappedElement(getFile16BitBE(file));
4138 real_chunk_size += 2;
4139 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
4141 if (real_chunk_size >= chunk_size)
4145 *level = li; /* copy temporary buffer back to level data */
4147 return real_chunk_size;
4150 static int LoadLevel_ELEM(FILE *file, int chunk_size, struct LevelInfo *level)
4152 int real_chunk_size = 0;
4154 li = *level; /* copy level data into temporary buffer */
4158 int element = getMappedElement(getFile16BitBE(file));
4160 real_chunk_size += 2;
4161 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
4163 if (real_chunk_size >= chunk_size)
4167 *level = li; /* copy temporary buffer back to level data */
4169 return real_chunk_size;
4172 static int LoadLevel_NOTE(FILE *file, int chunk_size, struct LevelInfo *level)
4174 int element = getMappedElement(getFile16BitBE(file));
4175 int envelope_nr = element - EL_ENVELOPE_1;
4176 int real_chunk_size = 2;
4180 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
4183 if (real_chunk_size >= chunk_size)
4187 level->envelope[envelope_nr] = xx_envelope;
4189 return real_chunk_size;
4192 static int LoadLevel_CUSX(FILE *file, int chunk_size, struct LevelInfo *level)
4194 int element = getMappedElement(getFile16BitBE(file));
4195 int real_chunk_size = 2;
4196 struct ElementInfo *ei = &element_info[element];
4199 xx_ei = *ei; /* copy element data into temporary buffer */
4201 xx_ei.num_change_pages = -1;
4205 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
4207 if (xx_ei.num_change_pages != -1)
4210 if (real_chunk_size >= chunk_size)
4216 if (ei->num_change_pages == -1)
4218 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
4221 ei->num_change_pages = 1;
4223 setElementChangePages(ei, 1);
4224 setElementChangeInfoToDefaults(ei->change);
4226 return real_chunk_size;
4229 /* initialize number of change pages stored for this custom element */
4230 setElementChangePages(ei, ei->num_change_pages);
4231 for (i = 0; i < ei->num_change_pages; i++)
4232 setElementChangeInfoToDefaults(&ei->change_page[i]);
4234 /* start with reading properties for the first change page */
4235 xx_current_change_page = 0;
4239 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4241 xx_change = *change; /* copy change data into temporary buffer */
4243 resetEventBits(); /* reset bits; change page might have changed */
4245 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4248 *change = xx_change;
4250 setEventFlagsFromEventBits(change);
4252 if (real_chunk_size >= chunk_size)
4256 return real_chunk_size;
4259 static int LoadLevel_GRPX(FILE *file, int chunk_size, struct LevelInfo *level)
4261 int element = getMappedElement(getFile16BitBE(file));
4262 int real_chunk_size = 2;
4263 struct ElementInfo *ei = &element_info[element];
4264 struct ElementGroupInfo *group = ei->group;
4266 xx_ei = *ei; /* copy element data into temporary buffer */
4267 xx_group = *group; /* copy group data into temporary buffer */
4271 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4274 if (real_chunk_size >= chunk_size)
4281 return real_chunk_size;
4288 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4289 struct LevelFileInfo *level_file_info,
4290 boolean level_info_only)
4292 char *filename = level_file_info->filename;
4293 char cookie[MAX_LINE_LEN];
4294 char chunk_name[CHUNK_ID_LEN + 1];
4298 if (!(file = openFile(filename, MODE_READ)))
4300 level->no_valid_file = TRUE;
4303 if (!level_info_only)
4304 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4306 if (level != &level_template)
4307 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4313 getFileChunkBE(file, chunk_name, NULL);
4314 if (strEqual(chunk_name, "RND1"))
4316 getFile32BitBE(file); /* not used */
4318 getFileChunkBE(file, chunk_name, NULL);
4319 if (!strEqual(chunk_name, "CAVE"))
4321 level->no_valid_file = TRUE;
4323 Error(ERR_WARN, "unknown format of level file '%s'", filename);
4330 else /* check for pre-2.0 file format with cookie string */
4332 strcpy(cookie, chunk_name);
4333 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4335 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4336 cookie[strlen(cookie) - 1] = '\0';
4338 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4340 level->no_valid_file = TRUE;
4342 Error(ERR_WARN, "unknown format of level file '%s'", filename);
4349 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4351 level->no_valid_file = TRUE;
4353 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
4360 /* pre-2.0 level files have no game version, so use file version here */
4361 level->game_version = level->file_version;
4364 if (level->file_version < FILE_VERSION_1_2)
4366 /* level files from versions before 1.2.0 without chunk structure */
4367 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4368 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4376 int (*loader)(File *, int, struct LevelInfo *);
4380 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4381 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4382 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4383 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4384 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4385 { "INFO", -1, LoadLevel_INFO },
4386 { "BODY", -1, LoadLevel_BODY },
4387 { "CONT", -1, LoadLevel_CONT },
4388 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4389 { "CNT3", -1, LoadLevel_CNT3 },
4390 { "CUS1", -1, LoadLevel_CUS1 },
4391 { "CUS2", -1, LoadLevel_CUS2 },
4392 { "CUS3", -1, LoadLevel_CUS3 },
4393 { "CUS4", -1, LoadLevel_CUS4 },
4394 { "GRP1", -1, LoadLevel_GRP1 },
4395 { "CONF", -1, LoadLevel_CONF },
4396 { "ELEM", -1, LoadLevel_ELEM },
4397 { "NOTE", -1, LoadLevel_NOTE },
4398 { "CUSX", -1, LoadLevel_CUSX },
4399 { "GRPX", -1, LoadLevel_GRPX },
4404 while (getFileChunkBE(file, chunk_name, &chunk_size))
4408 while (chunk_info[i].name != NULL &&
4409 !strEqual(chunk_name, chunk_info[i].name))
4412 if (chunk_info[i].name == NULL)
4414 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
4415 chunk_name, filename);
4416 ReadUnusedBytesFromFile(file, chunk_size);
4418 else if (chunk_info[i].size != -1 &&
4419 chunk_info[i].size != chunk_size)
4421 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
4422 chunk_size, chunk_name, filename);
4423 ReadUnusedBytesFromFile(file, chunk_size);
4427 /* call function to load this level chunk */
4428 int chunk_size_expected =
4429 (chunk_info[i].loader)(file, chunk_size, level);
4431 /* the size of some chunks cannot be checked before reading other
4432 chunks first (like "HEAD" and "BODY") that contain some header
4433 information, so check them here */
4434 if (chunk_size_expected != chunk_size)
4436 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
4437 chunk_size, chunk_name, filename);
4448 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4449 struct LevelFileInfo *level_file_info,
4450 boolean level_info_only)
4452 char *filename = level_file_info->filename;
4453 char cookie[MAX_LINE_LEN];
4454 char chunk_name[CHUNK_ID_LEN + 1];
4458 if (!(file = fopen(filename, MODE_READ)))
4460 level->no_valid_file = TRUE;
4463 if (!level_info_only)
4464 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4466 if (level != &level_template)
4467 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4473 getFileChunkBE(file, chunk_name, NULL);
4474 if (strEqual(chunk_name, "RND1"))
4476 getFile32BitBE(file); /* not used */
4478 getFileChunkBE(file, chunk_name, NULL);
4479 if (!strEqual(chunk_name, "CAVE"))
4481 level->no_valid_file = TRUE;
4483 Error(ERR_WARN, "unknown format of level file '%s'", filename);
4488 else /* check for pre-2.0 file format with cookie string */
4490 strcpy(cookie, chunk_name);
4491 if (fgets(&cookie[4], MAX_LINE_LEN - 4, file) == NULL)
4493 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4494 cookie[strlen(cookie) - 1] = '\0';
4496 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4498 level->no_valid_file = TRUE;
4500 Error(ERR_WARN, "unknown format of level file '%s'", filename);
4505 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4507 level->no_valid_file = TRUE;
4509 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
4514 /* pre-2.0 level files have no game version, so use file version here */
4515 level->game_version = level->file_version;
4518 if (level->file_version < FILE_VERSION_1_2)
4520 /* level files from versions before 1.2.0 without chunk structure */
4521 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4522 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4530 int (*loader)(FILE *, int, struct LevelInfo *);
4534 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4535 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4536 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4537 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4538 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4539 { "INFO", -1, LoadLevel_INFO },
4540 { "BODY", -1, LoadLevel_BODY },
4541 { "CONT", -1, LoadLevel_CONT },
4542 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4543 { "CNT3", -1, LoadLevel_CNT3 },
4544 { "CUS1", -1, LoadLevel_CUS1 },
4545 { "CUS2", -1, LoadLevel_CUS2 },
4546 { "CUS3", -1, LoadLevel_CUS3 },
4547 { "CUS4", -1, LoadLevel_CUS4 },
4548 { "GRP1", -1, LoadLevel_GRP1 },
4549 { "CONF", -1, LoadLevel_CONF },
4550 { "ELEM", -1, LoadLevel_ELEM },
4551 { "NOTE", -1, LoadLevel_NOTE },
4552 { "CUSX", -1, LoadLevel_CUSX },
4553 { "GRPX", -1, LoadLevel_GRPX },
4558 while (getFileChunkBE(file, chunk_name, &chunk_size))
4562 while (chunk_info[i].name != NULL &&
4563 !strEqual(chunk_name, chunk_info[i].name))
4566 if (chunk_info[i].name == NULL)
4568 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
4569 chunk_name, filename);
4570 ReadUnusedBytesFromFile(file, chunk_size);
4572 else if (chunk_info[i].size != -1 &&
4573 chunk_info[i].size != chunk_size)
4575 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
4576 chunk_size, chunk_name, filename);
4577 ReadUnusedBytesFromFile(file, chunk_size);
4581 /* call function to load this level chunk */
4582 int chunk_size_expected =
4583 (chunk_info[i].loader)(file, chunk_size, level);
4585 /* the size of some chunks cannot be checked before reading other
4586 chunks first (like "HEAD" and "BODY") that contain some header
4587 information, so check them here */
4588 if (chunk_size_expected != chunk_size)
4590 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
4591 chunk_size, chunk_name, filename);
4603 /* ------------------------------------------------------------------------- */
4604 /* functions for loading EM level */
4605 /* ------------------------------------------------------------------------- */
4609 static int map_em_element_yam(int element)
4613 case 0x00: return EL_EMPTY;
4614 case 0x01: return EL_EMERALD;
4615 case 0x02: return EL_DIAMOND;
4616 case 0x03: return EL_ROCK;
4617 case 0x04: return EL_ROBOT;
4618 case 0x05: return EL_SPACESHIP_UP;
4619 case 0x06: return EL_BOMB;
4620 case 0x07: return EL_BUG_UP;
4621 case 0x08: return EL_AMOEBA_DROP;
4622 case 0x09: return EL_NUT;
4623 case 0x0a: return EL_YAMYAM;
4624 case 0x0b: return EL_QUICKSAND_FULL;
4625 case 0x0c: return EL_SAND;
4626 case 0x0d: return EL_WALL_SLIPPERY;
4627 case 0x0e: return EL_STEELWALL;
4628 case 0x0f: return EL_WALL;
4629 case 0x10: return EL_EM_KEY_1;
4630 case 0x11: return EL_EM_KEY_2;
4631 case 0x12: return EL_EM_KEY_4;
4632 case 0x13: return EL_EM_KEY_3;
4633 case 0x14: return EL_MAGIC_WALL;
4634 case 0x15: return EL_ROBOT_WHEEL;
4635 case 0x16: return EL_DYNAMITE;
4637 case 0x17: return EL_EM_KEY_1; /* EMC */
4638 case 0x18: return EL_BUG_UP; /* EMC */
4639 case 0x1a: return EL_DIAMOND; /* EMC */
4640 case 0x1b: return EL_EMERALD; /* EMC */
4641 case 0x25: return EL_NUT; /* EMC */
4642 case 0x80: return EL_EMPTY; /* EMC */
4643 case 0x85: return EL_EM_KEY_1; /* EMC */
4644 case 0x86: return EL_EM_KEY_2; /* EMC */
4645 case 0x87: return EL_EM_KEY_4; /* EMC */
4646 case 0x88: return EL_EM_KEY_3; /* EMC */
4647 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
4648 case 0x9a: return EL_AMOEBA_WET; /* EMC */
4649 case 0xaf: return EL_DYNAMITE; /* EMC */
4650 case 0xbd: return EL_SAND; /* EMC */
4653 Error(ERR_WARN, "invalid level element %d", element);
4658 static int map_em_element_field(int element)
4660 if (element >= 0xc8 && element <= 0xe1)
4661 return EL_CHAR_A + (element - 0xc8);
4662 else if (element >= 0xe2 && element <= 0xeb)
4663 return EL_CHAR_0 + (element - 0xe2);
4667 case 0x00: return EL_ROCK;
4668 case 0x01: return EL_ROCK; /* EMC */
4669 case 0x02: return EL_DIAMOND;
4670 case 0x03: return EL_DIAMOND;
4671 case 0x04: return EL_ROBOT;
4672 case 0x05: return EL_ROBOT; /* EMC */
4673 case 0x06: return EL_EMPTY_SPACE; /* EMC */
4674 case 0x07: return EL_EMPTY_SPACE; /* EMC */
4675 case 0x08: return EL_SPACESHIP_UP;
4676 case 0x09: return EL_SPACESHIP_RIGHT;
4677 case 0x0a: return EL_SPACESHIP_DOWN;
4678 case 0x0b: return EL_SPACESHIP_LEFT;
4679 case 0x0c: return EL_SPACESHIP_UP;
4680 case 0x0d: return EL_SPACESHIP_RIGHT;
4681 case 0x0e: return EL_SPACESHIP_DOWN;
4682 case 0x0f: return EL_SPACESHIP_LEFT;
4684 case 0x10: return EL_BOMB;
4685 case 0x11: return EL_BOMB; /* EMC */
4686 case 0x12: return EL_EMERALD;
4687 case 0x13: return EL_EMERALD;
4688 case 0x14: return EL_BUG_UP;
4689 case 0x15: return EL_BUG_RIGHT;
4690 case 0x16: return EL_BUG_DOWN;
4691 case 0x17: return EL_BUG_LEFT;
4692 case 0x18: return EL_BUG_UP;
4693 case 0x19: return EL_BUG_RIGHT;
4694 case 0x1a: return EL_BUG_DOWN;
4695 case 0x1b: return EL_BUG_LEFT;
4696 case 0x1c: return EL_AMOEBA_DROP;
4697 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
4698 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
4699 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
4701 case 0x20: return EL_ROCK;
4702 case 0x21: return EL_BOMB; /* EMC */
4703 case 0x22: return EL_DIAMOND; /* EMC */
4704 case 0x23: return EL_EMERALD; /* EMC */
4705 case 0x24: return EL_MAGIC_WALL;
4706 case 0x25: return EL_NUT;
4707 case 0x26: return EL_NUT; /* EMC */
4708 case 0x27: return EL_NUT; /* EMC */
4710 /* looks like magic wheel, but is _always_ activated */
4711 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
4713 case 0x29: return EL_YAMYAM; /* up */
4714 case 0x2a: return EL_YAMYAM; /* down */
4715 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
4716 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
4717 case 0x2d: return EL_QUICKSAND_FULL;
4718 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
4719 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
4721 case 0x30: return EL_EMPTY_SPACE; /* EMC */
4722 case 0x31: return EL_SAND; /* EMC */
4723 case 0x32: return EL_SAND; /* EMC */
4724 case 0x33: return EL_SAND; /* EMC */
4725 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
4726 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
4727 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
4728 case 0x37: return EL_SAND; /* EMC */
4729 case 0x38: return EL_ROCK; /* EMC */
4730 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
4731 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
4732 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
4733 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
4734 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
4735 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
4736 case 0x3f: return EL_ACID_POOL_BOTTOM;
4738 case 0x40: return EL_EXIT_OPEN; /* 1 */
4739 case 0x41: return EL_EXIT_OPEN; /* 2 */
4740 case 0x42: return EL_EXIT_OPEN; /* 3 */
4741 case 0x43: return EL_BALLOON; /* EMC */
4742 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
4743 case 0x45: return EL_SPRING; /* EMC */
4744 case 0x46: return EL_SPRING; /* falling */ /* EMC */
4745 case 0x47: return EL_SPRING; /* left */ /* EMC */
4746 case 0x48: return EL_SPRING; /* right */ /* EMC */
4747 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
4748 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
4749 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
4750 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
4751 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
4752 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
4753 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
4755 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
4756 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
4757 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
4758 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
4759 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
4760 case 0x55: return EL_EMPTY_SPACE; /* EMC */
4761 case 0x56: return EL_EMPTY_SPACE; /* EMC */
4762 case 0x57: return EL_EMPTY_SPACE; /* EMC */
4763 case 0x58: return EL_EMPTY_SPACE; /* EMC */
4764 case 0x59: return EL_EMPTY_SPACE; /* EMC */
4765 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
4766 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
4767 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
4768 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
4769 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
4770 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
4772 case 0x60: return EL_EMPTY_SPACE; /* EMC */
4773 case 0x61: return EL_EMPTY_SPACE; /* EMC */
4774 case 0x62: return EL_EMPTY_SPACE; /* EMC */
4775 case 0x63: return EL_SPRING; /* left */ /* EMC */
4776 case 0x64: return EL_SPRING; /* right */ /* EMC */
4777 case 0x65: return EL_ACID; /* 1 */ /* EMC */
4778 case 0x66: return EL_ACID; /* 2 */ /* EMC */
4779 case 0x67: return EL_ACID; /* 3 */ /* EMC */
4780 case 0x68: return EL_ACID; /* 4 */ /* EMC */
4781 case 0x69: return EL_ACID; /* 5 */ /* EMC */
4782 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
4783 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
4784 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
4785 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
4786 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
4787 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
4789 case 0x70: return EL_EMPTY_SPACE; /* EMC */
4790 case 0x71: return EL_EMPTY_SPACE; /* EMC */
4791 case 0x72: return EL_NUT; /* left */ /* EMC */
4792 case 0x73: return EL_SAND; /* EMC (? "nut") */
4793 case 0x74: return EL_STEELWALL;
4794 case 0x75: return EL_EMPTY_SPACE; /* EMC */
4795 case 0x76: return EL_EMPTY_SPACE; /* EMC */
4796 case 0x77: return EL_BOMB; /* left */ /* EMC */
4797 case 0x78: return EL_BOMB; /* right */ /* EMC */
4798 case 0x79: return EL_ROCK; /* left */ /* EMC */
4799 case 0x7a: return EL_ROCK; /* right */ /* EMC */
4800 case 0x7b: return EL_ACID; /* (? EMC "blank") */
4801 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
4802 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
4803 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
4804 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
4806 case 0x80: return EL_EMPTY;
4807 case 0x81: return EL_WALL_SLIPPERY;
4808 case 0x82: return EL_SAND;
4809 case 0x83: return EL_STEELWALL;
4810 case 0x84: return EL_WALL;
4811 case 0x85: return EL_EM_KEY_1;
4812 case 0x86: return EL_EM_KEY_2;
4813 case 0x87: return EL_EM_KEY_4;
4814 case 0x88: return EL_EM_KEY_3;
4815 case 0x89: return EL_EM_GATE_1;
4816 case 0x8a: return EL_EM_GATE_2;
4817 case 0x8b: return EL_EM_GATE_4;
4818 case 0x8c: return EL_EM_GATE_3;
4819 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
4820 case 0x8e: return EL_EM_GATE_1_GRAY;
4821 case 0x8f: return EL_EM_GATE_2_GRAY;
4823 case 0x90: return EL_EM_GATE_4_GRAY;
4824 case 0x91: return EL_EM_GATE_3_GRAY;
4825 case 0x92: return EL_MAGIC_WALL;
4826 case 0x93: return EL_ROBOT_WHEEL;
4827 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
4828 case 0x95: return EL_ACID_POOL_TOPLEFT;
4829 case 0x96: return EL_ACID_POOL_TOPRIGHT;
4830 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
4831 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
4832 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
4833 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
4834 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
4835 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
4836 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
4837 case 0x9e: return EL_EXIT_CLOSED;
4838 case 0x9f: return EL_CHAR_LESS; /* arrow left */
4840 /* looks like normal sand, but behaves like wall */
4841 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
4842 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
4843 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
4844 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
4845 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
4846 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
4847 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
4848 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
4849 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
4850 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
4851 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
4852 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
4853 case 0xac: return EL_CHAR_COMMA; /* EMC */
4854 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
4855 case 0xae: return EL_CHAR_MINUS; /* EMC */
4856 case 0xaf: return EL_DYNAMITE;
4858 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
4859 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
4860 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
4861 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
4862 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
4863 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
4864 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
4865 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
4866 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
4867 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
4868 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
4869 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
4870 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
4871 case 0xbd: return EL_SAND; /* EMC ("dirt") */
4872 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
4873 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
4875 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
4876 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
4877 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
4878 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
4879 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
4880 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
4881 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
4882 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
4884 /* characters: see above */
4886 case 0xec: return EL_CHAR_PERIOD;
4887 case 0xed: return EL_CHAR_EXCLAM;
4888 case 0xee: return EL_CHAR_COLON;
4889 case 0xef: return EL_CHAR_QUESTION;
4891 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
4892 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
4893 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
4894 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
4895 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
4896 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
4897 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
4898 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
4900 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
4901 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
4902 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
4903 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
4904 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
4905 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
4907 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
4908 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
4911 /* should never happen (all 8-bit value cases should be handled) */
4912 Error(ERR_WARN, "invalid level element %d", element);
4917 #define EM_LEVEL_SIZE 2106
4918 #define EM_LEVEL_XSIZE 64
4919 #define EM_LEVEL_YSIZE 32
4921 static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
4922 struct LevelFileInfo *level_file_info)
4924 char *filename = level_file_info->filename;
4926 unsigned char leveldata[EM_LEVEL_SIZE];
4927 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
4928 int nr = level_file_info->nr;
4931 if (!(file = fopen(filename, MODE_READ)))
4933 level->no_valid_file = TRUE;
4935 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4940 for (i = 0; i < EM_LEVEL_SIZE; i++)
4941 leveldata[i] = fgetc(file);
4945 /* check if level data is crypted by testing against known starting bytes
4946 of the few existing crypted level files (from Emerald Mine 1 + 2) */
4948 if ((leveldata[0] == 0xf1 ||
4949 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
4951 unsigned char code0 = 0x65;
4952 unsigned char code1 = 0x11;
4954 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
4955 leveldata[0] = 0xf1;
4957 /* decode crypted level data */
4959 for (i = 0; i < EM_LEVEL_SIZE; i++)
4961 leveldata[i] ^= code0;
4962 leveldata[i] -= code1;
4964 code0 = (code0 + 7) & 0xff;
4968 level->fieldx = EM_LEVEL_XSIZE;
4969 level->fieldy = EM_LEVEL_YSIZE;
4971 level->time = header[46] * 10;
4972 level->gems_needed = header[47];
4974 /* The original Emerald Mine levels have their level number stored
4975 at the second byte of the level file...
4976 Do not trust this information at other level files, e.g. EMC,
4977 but correct it anyway (normally the first row is completely
4978 steel wall, so the correction does not hurt anyway). */
4980 if (leveldata[1] == nr)
4981 leveldata[1] = leveldata[2]; /* correct level number field */
4983 sprintf(level->name, "Level %d", nr); /* set level name */
4985 level->score[SC_EMERALD] = header[36];
4986 level->score[SC_DIAMOND] = header[37];
4987 level->score[SC_ROBOT] = header[38];
4988 level->score[SC_SPACESHIP] = header[39];
4989 level->score[SC_BUG] = header[40];
4990 level->score[SC_YAMYAM] = header[41];
4991 level->score[SC_NUT] = header[42];
4992 level->score[SC_DYNAMITE] = header[43];
4993 level->score[SC_TIME_BONUS] = header[44];
4995 level->num_yamyam_contents = 4;
4997 for (i = 0; i < level->num_yamyam_contents; i++)
4998 for (y = 0; y < 3; y++)
4999 for (x = 0; x < 3; x++)
5000 level->yamyam_content[i].e[x][y] =
5001 map_em_element_yam(header[i * 9 + y * 3 + x]);
5003 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
5004 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
5005 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
5006 level->amoeba_content = EL_DIAMOND;
5008 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5010 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
5012 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
5013 new_element = EL_AMOEBA_WET;
5015 level->field[x][y] = new_element;
5018 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
5019 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
5020 level->field[x][y] = EL_PLAYER_1;
5022 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
5023 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
5024 level->field[x][y] = EL_PLAYER_2;
5029 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
5031 static int ball_xy[8][2] =
5042 struct LevelInfo_EM *level_em = level->native_em_level;
5043 struct LEVEL *lev = level_em->lev;
5044 struct PLAYER **ply = level_em->ply;
5047 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
5048 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
5050 lev->time_seconds = level->time;
5051 lev->required_initial = level->gems_needed;
5053 lev->emerald_score = level->score[SC_EMERALD];
5054 lev->diamond_score = level->score[SC_DIAMOND];
5055 lev->alien_score = level->score[SC_ROBOT];
5056 lev->tank_score = level->score[SC_SPACESHIP];
5057 lev->bug_score = level->score[SC_BUG];
5058 lev->eater_score = level->score[SC_YAMYAM];
5059 lev->nut_score = level->score[SC_NUT];
5060 lev->dynamite_score = level->score[SC_DYNAMITE];
5061 lev->key_score = level->score[SC_KEY];
5062 lev->exit_score = level->score[SC_TIME_BONUS];
5064 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
5065 for (y = 0; y < 3; y++)
5066 for (x = 0; x < 3; x++)
5067 lev->eater_array[i][y * 3 + x] =
5068 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
5070 lev->amoeba_time = level->amoeba_speed;
5071 lev->wonderwall_time_initial = level->time_magic_wall;
5072 lev->wheel_time = level->time_wheel;
5074 lev->android_move_time = level->android_move_time;
5075 lev->android_clone_time = level->android_clone_time;
5076 lev->ball_random = level->ball_random;
5077 lev->ball_state_initial = level->ball_state_initial;
5078 lev->ball_time = level->ball_time;
5079 lev->num_ball_arrays = level->num_ball_contents;
5081 lev->lenses_score = level->lenses_score;
5082 lev->magnify_score = level->magnify_score;
5083 lev->slurp_score = level->slurp_score;
5085 lev->lenses_time = level->lenses_time;
5086 lev->magnify_time = level->magnify_time;
5088 lev->wind_direction_initial =
5089 map_direction_RND_to_EM(level->wind_direction_initial);
5090 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
5091 lev->wind_time : 0);
5093 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
5094 for (j = 0; j < 8; j++)
5095 lev->ball_array[i][j] =
5096 map_element_RND_to_EM(level->
5097 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
5099 map_android_clone_elements_RND_to_EM(level);
5101 /* first fill the complete playfield with the default border element */
5102 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
5103 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
5104 level_em->cave[x][y] = ZBORDER;
5106 if (BorderElement == EL_STEELWALL)
5108 for (y = 0; y < lev->height + 2; y++)
5109 for (x = 0; x < lev->width + 2; x++)
5110 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
5113 /* then copy the real level contents from level file into the playfield */
5114 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
5116 int new_element = map_element_RND_to_EM(level->field[x][y]);
5117 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
5118 int xx = x + 1 + offset;
5119 int yy = y + 1 + offset;
5121 if (level->field[x][y] == EL_AMOEBA_DEAD)
5122 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
5124 level_em->cave[xx][yy] = new_element;
5127 for (i = 0; i < MAX_PLAYERS; i++)
5129 ply[i]->x_initial = 0;
5130 ply[i]->y_initial = 0;
5133 /* initialize player positions and delete players from the playfield */
5134 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
5136 if (ELEM_IS_PLAYER(level->field[x][y]))
5138 int player_nr = GET_PLAYER_NR(level->field[x][y]);
5139 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
5140 int xx = x + 1 + offset;
5141 int yy = y + 1 + offset;
5143 ply[player_nr]->x_initial = xx;
5144 ply[player_nr]->y_initial = yy;
5146 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
5150 if (BorderElement == EL_STEELWALL)
5157 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
5159 static int ball_xy[8][2] =
5170 struct LevelInfo_EM *level_em = level->native_em_level;
5171 struct LEVEL *lev = level_em->lev;
5172 struct PLAYER **ply = level_em->ply;
5175 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
5176 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
5178 level->time = lev->time_seconds;
5179 level->gems_needed = lev->required_initial;
5181 sprintf(level->name, "Level %d", level->file_info.nr);
5183 level->score[SC_EMERALD] = lev->emerald_score;
5184 level->score[SC_DIAMOND] = lev->diamond_score;
5185 level->score[SC_ROBOT] = lev->alien_score;
5186 level->score[SC_SPACESHIP] = lev->tank_score;
5187 level->score[SC_BUG] = lev->bug_score;
5188 level->score[SC_YAMYAM] = lev->eater_score;
5189 level->score[SC_NUT] = lev->nut_score;
5190 level->score[SC_DYNAMITE] = lev->dynamite_score;
5191 level->score[SC_KEY] = lev->key_score;
5192 level->score[SC_TIME_BONUS] = lev->exit_score;
5194 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
5196 for (i = 0; i < level->num_yamyam_contents; i++)
5197 for (y = 0; y < 3; y++)
5198 for (x = 0; x < 3; x++)
5199 level->yamyam_content[i].e[x][y] =
5200 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
5202 level->amoeba_speed = lev->amoeba_time;
5203 level->time_magic_wall = lev->wonderwall_time_initial;
5204 level->time_wheel = lev->wheel_time;
5206 level->android_move_time = lev->android_move_time;
5207 level->android_clone_time = lev->android_clone_time;
5208 level->ball_random = lev->ball_random;
5209 level->ball_state_initial = lev->ball_state_initial;
5210 level->ball_time = lev->ball_time;
5211 level->num_ball_contents = lev->num_ball_arrays;
5213 level->lenses_score = lev->lenses_score;
5214 level->magnify_score = lev->magnify_score;
5215 level->slurp_score = lev->slurp_score;
5217 level->lenses_time = lev->lenses_time;
5218 level->magnify_time = lev->magnify_time;
5220 level->wind_direction_initial =
5221 map_direction_EM_to_RND(lev->wind_direction_initial);
5223 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
5224 for (j = 0; j < 8; j++)
5225 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
5226 map_element_EM_to_RND(lev->ball_array[i][j]);
5228 map_android_clone_elements_EM_to_RND(level);
5230 /* convert the playfield (some elements need special treatment) */
5231 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5233 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
5235 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
5236 new_element = EL_AMOEBA_DEAD;
5238 level->field[x][y] = new_element;
5241 for (i = 0; i < MAX_PLAYERS; i++)
5243 /* in case of all players set to the same field, use the first player */
5244 int nr = MAX_PLAYERS - i - 1;
5245 int jx = ply[nr]->x_initial - 1;
5246 int jy = ply[nr]->y_initial - 1;
5248 if (jx != -1 && jy != -1)
5249 level->field[jx][jy] = EL_PLAYER_1 + nr;
5254 /* ------------------------------------------------------------------------- */
5255 /* functions for loading SP level */
5256 /* ------------------------------------------------------------------------- */
5260 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
5261 #define SP_LEVEL_SIZE 1536
5262 #define SP_LEVEL_XSIZE 60
5263 #define SP_LEVEL_YSIZE 24
5264 #define SP_LEVEL_NAME_LEN 23
5266 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
5269 int initial_player_gravity;
5270 int num_special_ports;
5273 /* for details of the Supaplex level format, see Herman Perk's Supaplex
5274 documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
5276 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
5277 for (y = 0; y < SP_LEVEL_YSIZE; y++)
5279 for (x = 0; x < SP_LEVEL_XSIZE; x++)
5281 int element_old = fgetc(file);
5284 if (element_old <= 0x27)
5285 element_new = getMappedElement(EL_SP_START + element_old);
5286 else if (element_old == 0x28)
5287 element_new = EL_INVISIBLE_WALL;
5290 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
5291 Error(ERR_WARN, "invalid level element %d", element_old);
5293 element_new = EL_UNKNOWN;
5296 level->field[x][y] = element_new;
5300 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
5302 /* initial gravity: 1 == "on", anything else (0) == "off" */
5303 initial_player_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
5305 for (i = 0; i < MAX_PLAYERS; i++)
5306 level->initial_player_gravity[i] = initial_player_gravity;
5308 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
5310 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
5311 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5312 level->name[i] = fgetc(file);
5313 level->name[SP_LEVEL_NAME_LEN] = '\0';
5315 /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
5316 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
5318 /* number of infotrons needed; 0 means that Supaplex will count the total
5319 amount of infotrons in the level and use the low byte of that number
5320 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
5321 level->gems_needed = fgetc(file);
5323 /* number of special ("gravity") port entries below (maximum 10 allowed) */
5324 num_special_ports = fgetc(file);
5326 /* database of properties of up to 10 special ports (6 bytes per port) */
5327 for (i = 0; i < 10; i++)
5329 int port_location, port_x, port_y, port_element;
5332 /* high and low byte of the location of a special port; if (x, y) are the
5333 coordinates of a port in the field and (0, 0) is the top-left corner,
5334 the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
5335 of what may be expected: Supaplex works with a game field in memory
5336 which is 2 bytes per tile) */
5337 port_location = getFile16BitBE(file);
5339 /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
5340 gravity = fgetc(file);
5342 /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
5343 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
5345 /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
5346 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
5348 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
5350 if (i >= num_special_ports)
5353 port_x = (port_location / 2) % SP_LEVEL_XSIZE;
5354 port_y = (port_location / 2) / SP_LEVEL_XSIZE;
5356 if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
5357 port_y < 0 || port_y >= SP_LEVEL_YSIZE)
5359 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
5365 port_element = level->field[port_x][port_y];
5367 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5368 port_element > EL_SP_GRAVITY_PORT_UP)
5370 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
5375 /* change previous (wrong) gravity inverting special port to either
5376 gravity enabling special port or gravity disabling special port */
5377 level->field[port_x][port_y] +=
5378 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5379 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5382 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
5384 /* change special gravity ports without database entries to normal ports */
5385 for (y = 0; y < SP_LEVEL_YSIZE; y++)
5386 for (x = 0; x < SP_LEVEL_XSIZE; x++)
5387 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5388 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5389 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5391 /* auto-determine number of infotrons if it was stored as "0" -- see above */
5392 if (level->gems_needed == 0)
5394 for (y = 0; y < SP_LEVEL_YSIZE; y++)
5395 for (x = 0; x < SP_LEVEL_XSIZE; x++)
5396 if (level->field[x][y] == EL_SP_INFOTRON)
5397 level->gems_needed++;
5399 level->gems_needed &= 0xff; /* only use low byte -- see above */
5402 level->fieldx = SP_LEVEL_XSIZE;
5403 level->fieldy = SP_LEVEL_YSIZE;
5405 level->time = 0; /* no time limit */
5406 level->amoeba_speed = 0;
5407 level->time_magic_wall = 0;
5408 level->time_wheel = 0;
5409 level->amoeba_content = EL_EMPTY;
5412 /* original Supaplex does not use score values -- use default values */
5414 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5415 level->score[i] = 0;
5418 /* there are no yamyams in supaplex levels */
5419 for (i = 0; i < level->num_yamyam_contents; i++)
5420 for (y = 0; y < 3; y++)
5421 for (x = 0; x < 3; x++)
5422 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5425 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
5426 struct LevelFileInfo *level_file_info,
5427 boolean level_info_only)
5429 char *filename = level_file_info->filename;
5431 int nr = level_file_info->nr - leveldir_current->first_level;
5433 char name_first, name_last;
5434 struct LevelInfo multipart_level;
5435 int multipart_xpos, multipart_ypos;
5436 boolean is_multipart_level;
5437 boolean is_first_part;
5438 boolean reading_multipart_level = FALSE;
5439 boolean use_empty_level = FALSE;
5441 if (!(file = fopen(filename, MODE_READ)))
5443 level->no_valid_file = TRUE;
5445 if (!level_info_only)
5446 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5451 /* position file stream to the requested level inside the level package */
5452 if (level_file_info->packed &&
5453 fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
5455 level->no_valid_file = TRUE;
5457 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
5462 /* there exist Supaplex level package files with multi-part levels which
5463 can be detected as follows: instead of leading and trailing dashes ('-')
5464 to pad the level name, they have leading and trailing numbers which are
5465 the x and y coordinations of the current part of the multi-part level;
5466 if there are '?' characters instead of numbers on the left or right side
5467 of the level name, the multi-part level consists of only horizontal or
5470 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
5472 LoadLevelFromFileStream_SP(file, level, l);
5474 /* check if this level is a part of a bigger multi-part level */
5476 name_first = level->name[0];
5477 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
5479 is_multipart_level =
5480 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
5481 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
5484 ((name_first == '?' || name_first == '1') &&
5485 (name_last == '?' || name_last == '1'));
5487 /* correct leading multipart level meta information in level name */
5488 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
5489 level->name[i] = '-';
5491 /* correct trailing multipart level meta information in level name */
5492 for (i = SP_LEVEL_NAME_LEN - 1; i >= 0 && level->name[i] == name_last; i--)
5493 level->name[i] = '-';
5495 /* ---------- check for normal single level ---------- */
5497 if (!reading_multipart_level && !is_multipart_level)
5499 /* the current level is simply a normal single-part level, and we are
5500 not reading a multi-part level yet, so return the level as it is */
5505 /* ---------- check for empty level (unused multi-part) ---------- */
5507 if (!reading_multipart_level && is_multipart_level && !is_first_part)
5509 /* this is a part of a multi-part level, but not the first part
5510 (and we are not already reading parts of a multi-part level);
5511 in this case, use an empty level instead of the single part */
5513 use_empty_level = TRUE;
5518 /* ---------- check for finished multi-part level ---------- */
5520 if (reading_multipart_level &&
5521 (!is_multipart_level ||
5522 !strEqual(level->name, multipart_level.name)))
5524 /* we are already reading parts of a multi-part level, but this level is
5525 either not a multi-part level, or a part of a different multi-part
5526 level; in both cases, the multi-part level seems to be complete */
5531 /* ---------- here we have one part of a multi-part level ---------- */
5533 reading_multipart_level = TRUE;
5535 if (is_first_part) /* start with first part of new multi-part level */
5537 /* copy level info structure from first part */
5538 multipart_level = *level;
5540 /* clear playfield of new multi-part level */
5541 for (y = 0; y < MAX_LEV_FIELDY; y++)
5542 for (x = 0; x < MAX_LEV_FIELDX; x++)
5543 multipart_level.field[x][y] = EL_EMPTY;
5546 if (name_first == '?')
5548 if (name_last == '?')
5551 multipart_xpos = (int)(name_first - '0');
5552 multipart_ypos = (int)(name_last - '0');
5555 printf("----------> part (%d/%d) of multi-part level '%s'\n",
5556 multipart_xpos, multipart_ypos, multipart_level.name);
5559 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
5560 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
5562 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
5567 multipart_level.fieldx = MAX(multipart_level.fieldx,
5568 multipart_xpos * SP_LEVEL_XSIZE);
5569 multipart_level.fieldy = MAX(multipart_level.fieldy,
5570 multipart_ypos * SP_LEVEL_YSIZE);
5572 /* copy level part at the right position of multi-part level */
5573 for (y = 0; y < SP_LEVEL_YSIZE; y++)
5575 for (x = 0; x < SP_LEVEL_XSIZE; x++)
5577 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
5578 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
5580 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
5587 if (use_empty_level)
5589 setLevelInfoToDefaults(level);
5591 level->fieldx = SP_LEVEL_XSIZE;
5592 level->fieldy = SP_LEVEL_YSIZE;
5594 for (y = 0; y < SP_LEVEL_YSIZE; y++)
5595 for (x = 0; x < SP_LEVEL_XSIZE; x++)
5596 level->field[x][y] = EL_EMPTY;
5598 strcpy(level->name, "-------- EMPTY --------");
5600 Error(ERR_WARN, "single part of multi-part level -- using empty level");
5603 if (reading_multipart_level)
5604 *level = multipart_level;
5609 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
5611 struct LevelInfo_SP *level_sp = level->native_sp_level;
5612 LevelInfoType *header = &level_sp->header;
5615 level_sp->width = level->fieldx;
5616 level_sp->height = level->fieldy;
5618 for (x = 0; x < level->fieldx; x++)
5619 for (y = 0; y < level->fieldy; y++)
5620 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
5622 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
5624 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5625 header->LevelTitle[i] = level->name[i];
5626 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
5628 header->InfotronsNeeded = level->gems_needed;
5630 header->SpecialPortCount = 0;
5632 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
5634 boolean gravity_port_found = FALSE;
5635 boolean gravity_port_valid = FALSE;
5636 int gravity_port_flag;
5637 int gravity_port_base_element;
5638 int element = level->field[x][y];
5640 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
5641 element <= EL_SP_GRAVITY_ON_PORT_UP)
5643 gravity_port_found = TRUE;
5644 gravity_port_valid = TRUE;
5645 gravity_port_flag = 1;
5646 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
5648 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
5649 element <= EL_SP_GRAVITY_OFF_PORT_UP)
5651 gravity_port_found = TRUE;
5652 gravity_port_valid = TRUE;
5653 gravity_port_flag = 0;
5654 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
5656 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
5657 element <= EL_SP_GRAVITY_PORT_UP)
5659 /* change R'n'D style gravity inverting special port to normal port
5660 (there are no gravity inverting ports in native Supaplex engine) */
5662 gravity_port_found = TRUE;
5663 gravity_port_valid = FALSE;
5664 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
5667 if (gravity_port_found)
5669 if (gravity_port_valid &&
5670 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
5672 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
5674 port->PortLocation = (y * level->fieldx + x) * 2;
5675 port->Gravity = gravity_port_flag;
5677 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
5679 header->SpecialPortCount++;
5683 /* change special gravity port to normal port */
5685 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
5688 level_sp->playfield[x][y] = element - EL_SP_START;
5693 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
5695 struct LevelInfo_SP *level_sp = level->native_sp_level;
5696 LevelInfoType *header = &level_sp->header;
5699 level->fieldx = level_sp->width;
5700 level->fieldy = level_sp->height;
5702 for (x = 0; x < level->fieldx; x++)
5704 for (y = 0; y < level->fieldy; y++)
5706 int element_old = level_sp->playfield[x][y];
5707 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5709 if (element_new == EL_UNKNOWN)
5710 Error(ERR_WARN, "invalid element %d at position %d, %d",
5713 level->field[x][y] = element_new;
5717 for (i = 0; i < MAX_PLAYERS; i++)
5718 level->initial_player_gravity[i] =
5719 (header->InitialGravity == 1 ? TRUE : FALSE);
5721 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5722 level->name[i] = header->LevelTitle[i];
5723 level->name[SP_LEVEL_NAME_LEN] = '\0';
5725 level->gems_needed = header->InfotronsNeeded;
5727 for (i = 0; i < header->SpecialPortCount; i++)
5729 SpecialPortType *port = &header->SpecialPort[i];
5730 int port_location = port->PortLocation;
5731 int gravity = port->Gravity;
5732 int port_x, port_y, port_element;
5734 port_x = (port_location / 2) % level->fieldx;
5735 port_y = (port_location / 2) / level->fieldx;
5737 if (port_x < 0 || port_x >= level->fieldx ||
5738 port_y < 0 || port_y >= level->fieldy)
5740 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
5746 port_element = level->field[port_x][port_y];
5748 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5749 port_element > EL_SP_GRAVITY_PORT_UP)
5751 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
5756 /* change previous (wrong) gravity inverting special port to either
5757 gravity enabling special port or gravity disabling special port */
5758 level->field[port_x][port_y] +=
5759 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5760 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5763 /* change special gravity ports without database entries to normal ports */
5764 for (x = 0; x < level->fieldx; x++)
5765 for (y = 0; y < level->fieldy; y++)
5766 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5767 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5768 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5770 level->time = 0; /* no time limit */
5771 level->amoeba_speed = 0;
5772 level->time_magic_wall = 0;
5773 level->time_wheel = 0;
5774 level->amoeba_content = EL_EMPTY;
5777 /* original Supaplex does not use score values -- use default values */
5779 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5780 level->score[i] = 0;
5783 /* there are no yamyams in supaplex levels */
5784 for (i = 0; i < level->num_yamyam_contents; i++)
5785 for (x = 0; x < 3; x++)
5786 for (y = 0; y < 3; y++)
5787 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5790 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5792 struct LevelInfo_SP *level_sp = level->native_sp_level;
5793 struct DemoInfo_SP *demo = &level_sp->demo;
5796 /* always start with reliable default values */
5797 demo->is_available = FALSE;
5800 if (TAPE_IS_EMPTY(tape))
5803 demo->level_nr = tape.level_nr; /* (currently not used) */
5805 level_sp->header.DemoRandomSeed = tape.random_seed;
5808 for (i = 0; i < tape.length; i++)
5810 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5811 int demo_repeat = tape.pos[i].delay;
5813 for (j = 0; j < demo_repeat / 16; j++)
5814 demo->data[demo->length++] = 0xf0 | demo_action;
5816 if (demo_repeat % 16)
5817 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5820 demo->data[demo->length++] = 0xff;
5822 demo->is_available = TRUE;
5825 static void setTapeInfoToDefaults();
5827 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5829 struct LevelInfo_SP *level_sp = level->native_sp_level;
5830 struct DemoInfo_SP *demo = &level_sp->demo;
5831 char *filename = level->file_info.filename;
5834 /* always start with reliable default values */
5835 setTapeInfoToDefaults();
5837 if (!demo->is_available)
5840 tape.level_nr = demo->level_nr; /* (currently not used) */
5841 tape.length = demo->length - 1; /* without "end of demo" byte */
5842 tape.random_seed = level_sp->header.DemoRandomSeed;
5844 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5846 for (i = 0; i < demo->length - 1; i++)
5848 int demo_action = demo->data[i] & 0x0f;
5849 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5851 tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
5852 tape.pos[i].delay = demo_repeat + 1;
5855 tape.length_seconds = GetTapeLength();
5859 /* ------------------------------------------------------------------------- */
5860 /* functions for loading DC level */
5861 /* ------------------------------------------------------------------------- */
5863 #define DC_LEVEL_HEADER_SIZE 344
5865 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
5867 static int last_data_encoded;
5871 int diff_hi, diff_lo;
5872 int data_hi, data_lo;
5873 unsigned short data_decoded;
5877 last_data_encoded = 0;
5884 diff = data_encoded - last_data_encoded;
5885 diff_hi = diff & ~0xff;
5886 diff_lo = diff & 0xff;
5890 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5891 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5892 data_hi = data_hi & 0xff00;
5894 data_decoded = data_hi | data_lo;
5896 last_data_encoded = data_encoded;
5898 offset1 = (offset1 + 1) % 31;
5899 offset2 = offset2 & 0xff;
5901 return data_decoded;
5904 int getMappedElement_DC(int element)
5912 /* 0x0117 - 0x036e: (?) */
5915 /* 0x042d - 0x0684: (?) */
5931 element = EL_CRYSTAL;
5934 case 0x0e77: /* quicksand (boulder) */
5935 element = EL_QUICKSAND_FAST_FULL;
5938 case 0x0e99: /* slow quicksand (boulder) */
5939 element = EL_QUICKSAND_FULL;
5943 element = EL_EM_EXIT_OPEN;
5947 element = EL_EM_EXIT_CLOSED;
5951 element = EL_EM_STEEL_EXIT_OPEN;
5955 element = EL_EM_STEEL_EXIT_CLOSED;
5958 case 0x0f4f: /* dynamite (lit 1) */
5959 element = EL_EM_DYNAMITE_ACTIVE;
5962 case 0x0f57: /* dynamite (lit 2) */
5963 element = EL_EM_DYNAMITE_ACTIVE;
5966 case 0x0f5f: /* dynamite (lit 3) */
5967 element = EL_EM_DYNAMITE_ACTIVE;
5970 case 0x0f67: /* dynamite (lit 4) */
5971 element = EL_EM_DYNAMITE_ACTIVE;
5978 element = EL_AMOEBA_WET;
5982 element = EL_AMOEBA_DROP;
5986 element = EL_DC_MAGIC_WALL;
5990 element = EL_SPACESHIP_UP;
5994 element = EL_SPACESHIP_DOWN;
5998 element = EL_SPACESHIP_LEFT;
6002 element = EL_SPACESHIP_RIGHT;
6006 element = EL_BUG_UP;
6010 element = EL_BUG_DOWN;
6014 element = EL_BUG_LEFT;
6018 element = EL_BUG_RIGHT;
6022 element = EL_MOLE_UP;
6026 element = EL_MOLE_DOWN;
6030 element = EL_MOLE_LEFT;
6034 element = EL_MOLE_RIGHT;
6042 element = EL_YAMYAM;
6046 element = EL_SWITCHGATE_OPEN;
6050 element = EL_SWITCHGATE_CLOSED;
6054 element = EL_DC_SWITCHGATE_SWITCH_UP;
6058 element = EL_TIMEGATE_CLOSED;
6061 case 0x144c: /* conveyor belt switch (green) */
6062 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
6065 case 0x144f: /* conveyor belt switch (red) */
6066 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
6069 case 0x1452: /* conveyor belt switch (blue) */
6070 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
6074 element = EL_CONVEYOR_BELT_3_MIDDLE;
6078 element = EL_CONVEYOR_BELT_3_LEFT;
6082 element = EL_CONVEYOR_BELT_3_RIGHT;
6086 element = EL_CONVEYOR_BELT_1_MIDDLE;
6090 element = EL_CONVEYOR_BELT_1_LEFT;
6094 element = EL_CONVEYOR_BELT_1_RIGHT;
6098 element = EL_CONVEYOR_BELT_4_MIDDLE;
6102 element = EL_CONVEYOR_BELT_4_LEFT;
6106 element = EL_CONVEYOR_BELT_4_RIGHT;
6110 element = EL_EXPANDABLE_WALL_HORIZONTAL;
6114 element = EL_EXPANDABLE_WALL_VERTICAL;
6118 element = EL_EXPANDABLE_WALL_ANY;
6121 case 0x14ce: /* growing steel wall (left/right) */
6122 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
6125 case 0x14df: /* growing steel wall (up/down) */
6126 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
6129 case 0x14e8: /* growing steel wall (up/down/left/right) */
6130 element = EL_EXPANDABLE_STEELWALL_ANY;
6134 element = EL_SHIELD_DEADLY;
6138 element = EL_EXTRA_TIME;
6146 element = EL_EMPTY_SPACE;
6149 case 0x1578: /* quicksand (empty) */
6150 element = EL_QUICKSAND_FAST_EMPTY;
6153 case 0x1579: /* slow quicksand (empty) */
6154 element = EL_QUICKSAND_EMPTY;
6157 /* 0x157c - 0x158b: */
6160 /* 0x1590 - 0x159f: */
6161 /* EL_DC_LANDMINE */
6164 element = EL_EM_DYNAMITE;
6167 case 0x15a1: /* key (red) */
6168 element = EL_EM_KEY_1;
6171 case 0x15a2: /* key (yellow) */
6172 element = EL_EM_KEY_2;
6175 case 0x15a3: /* key (blue) */
6176 element = EL_EM_KEY_4;
6179 case 0x15a4: /* key (green) */
6180 element = EL_EM_KEY_3;
6183 case 0x15a5: /* key (white) */
6184 element = EL_DC_KEY_WHITE;
6188 element = EL_WALL_SLIPPERY;
6195 case 0x15a8: /* wall (not round) */
6199 case 0x15a9: /* (blue) */
6200 element = EL_CHAR_A;
6203 case 0x15aa: /* (blue) */
6204 element = EL_CHAR_B;
6207 case 0x15ab: /* (blue) */
6208 element = EL_CHAR_C;
6211 case 0x15ac: /* (blue) */
6212 element = EL_CHAR_D;
6215 case 0x15ad: /* (blue) */
6216 element = EL_CHAR_E;
6219 case 0x15ae: /* (blue) */
6220 element = EL_CHAR_F;
6223 case 0x15af: /* (blue) */
6224 element = EL_CHAR_G;
6227 case 0x15b0: /* (blue) */
6228 element = EL_CHAR_H;
6231 case 0x15b1: /* (blue) */
6232 element = EL_CHAR_I;
6235 case 0x15b2: /* (blue) */
6236 element = EL_CHAR_J;
6239 case 0x15b3: /* (blue) */
6240 element = EL_CHAR_K;
6243 case 0x15b4: /* (blue) */
6244 element = EL_CHAR_L;
6247 case 0x15b5: /* (blue) */
6248 element = EL_CHAR_M;
6251 case 0x15b6: /* (blue) */
6252 element = EL_CHAR_N;
6255 case 0x15b7: /* (blue) */
6256 element = EL_CHAR_O;
6259 case 0x15b8: /* (blue) */
6260 element = EL_CHAR_P;
6263 case 0x15b9: /* (blue) */
6264 element = EL_CHAR_Q;
6267 case 0x15ba: /* (blue) */
6268 element = EL_CHAR_R;
6271 case 0x15bb: /* (blue) */
6272 element = EL_CHAR_S;
6275 case 0x15bc: /* (blue) */
6276 element = EL_CHAR_T;
6279 case 0x15bd: /* (blue) */
6280 element = EL_CHAR_U;
6283 case 0x15be: /* (blue) */
6284 element = EL_CHAR_V;
6287 case 0x15bf: /* (blue) */
6288 element = EL_CHAR_W;
6291 case 0x15c0: /* (blue) */
6292 element = EL_CHAR_X;
6295 case 0x15c1: /* (blue) */
6296 element = EL_CHAR_Y;
6299 case 0x15c2: /* (blue) */
6300 element = EL_CHAR_Z;
6303 case 0x15c3: /* (blue) */
6304 element = EL_CHAR_AUMLAUT;
6307 case 0x15c4: /* (blue) */
6308 element = EL_CHAR_OUMLAUT;
6311 case 0x15c5: /* (blue) */
6312 element = EL_CHAR_UUMLAUT;
6315 case 0x15c6: /* (blue) */
6316 element = EL_CHAR_0;
6319 case 0x15c7: /* (blue) */
6320 element = EL_CHAR_1;
6323 case 0x15c8: /* (blue) */
6324 element = EL_CHAR_2;
6327 case 0x15c9: /* (blue) */
6328 element = EL_CHAR_3;
6331 case 0x15ca: /* (blue) */
6332 element = EL_CHAR_4;
6335 case 0x15cb: /* (blue) */
6336 element = EL_CHAR_5;
6339 case 0x15cc: /* (blue) */
6340 element = EL_CHAR_6;
6343 case 0x15cd: /* (blue) */
6344 element = EL_CHAR_7;
6347 case 0x15ce: /* (blue) */
6348 element = EL_CHAR_8;
6351 case 0x15cf: /* (blue) */
6352 element = EL_CHAR_9;
6355 case 0x15d0: /* (blue) */
6356 element = EL_CHAR_PERIOD;
6359 case 0x15d1: /* (blue) */
6360 element = EL_CHAR_EXCLAM;
6363 case 0x15d2: /* (blue) */
6364 element = EL_CHAR_COLON;
6367 case 0x15d3: /* (blue) */
6368 element = EL_CHAR_LESS;
6371 case 0x15d4: /* (blue) */
6372 element = EL_CHAR_GREATER;
6375 case 0x15d5: /* (blue) */
6376 element = EL_CHAR_QUESTION;
6379 case 0x15d6: /* (blue) */
6380 element = EL_CHAR_COPYRIGHT;
6383 case 0x15d7: /* (blue) */
6384 element = EL_CHAR_UP;
6387 case 0x15d8: /* (blue) */
6388 element = EL_CHAR_DOWN;
6391 case 0x15d9: /* (blue) */
6392 element = EL_CHAR_BUTTON;
6395 case 0x15da: /* (blue) */
6396 element = EL_CHAR_PLUS;
6399 case 0x15db: /* (blue) */
6400 element = EL_CHAR_MINUS;
6403 case 0x15dc: /* (blue) */
6404 element = EL_CHAR_APOSTROPHE;
6407 case 0x15dd: /* (blue) */
6408 element = EL_CHAR_PARENLEFT;
6411 case 0x15de: /* (blue) */
6412 element = EL_CHAR_PARENRIGHT;
6415 case 0x15df: /* (green) */
6416 element = EL_CHAR_A;
6419 case 0x15e0: /* (green) */
6420 element = EL_CHAR_B;
6423 case 0x15e1: /* (green) */
6424 element = EL_CHAR_C;
6427 case 0x15e2: /* (green) */
6428 element = EL_CHAR_D;
6431 case 0x15e3: /* (green) */
6432 element = EL_CHAR_E;
6435 case 0x15e4: /* (green) */
6436 element = EL_CHAR_F;
6439 case 0x15e5: /* (green) */
6440 element = EL_CHAR_G;
6443 case 0x15e6: /* (green) */
6444 element = EL_CHAR_H;
6447 case 0x15e7: /* (green) */
6448 element = EL_CHAR_I;
6451 case 0x15e8: /* (green) */
6452 element = EL_CHAR_J;
6455 case 0x15e9: /* (green) */
6456 element = EL_CHAR_K;
6459 case 0x15ea: /* (green) */
6460 element = EL_CHAR_L;
6463 case 0x15eb: /* (green) */
6464 element = EL_CHAR_M;
6467 case 0x15ec: /* (green) */
6468 element = EL_CHAR_N;
6471 case 0x15ed: /* (green) */
6472 element = EL_CHAR_O;
6475 case 0x15ee: /* (green) */
6476 element = EL_CHAR_P;
6479 case 0x15ef: /* (green) */
6480 element = EL_CHAR_Q;
6483 case 0x15f0: /* (green) */
6484 element = EL_CHAR_R;
6487 case 0x15f1: /* (green) */
6488 element = EL_CHAR_S;
6491 case 0x15f2: /* (green) */
6492 element = EL_CHAR_T;
6495 case 0x15f3: /* (green) */
6496 element = EL_CHAR_U;
6499 case 0x15f4: /* (green) */
6500 element = EL_CHAR_V;
6503 case 0x15f5: /* (green) */
6504 element = EL_CHAR_W;
6507 case 0x15f6: /* (green) */
6508 element = EL_CHAR_X;
6511 case 0x15f7: /* (green) */
6512 element = EL_CHAR_Y;
6515 case 0x15f8: /* (green) */
6516 element = EL_CHAR_Z;
6519 case 0x15f9: /* (green) */
6520 element = EL_CHAR_AUMLAUT;
6523 case 0x15fa: /* (green) */
6524 element = EL_CHAR_OUMLAUT;
6527 case 0x15fb: /* (green) */
6528 element = EL_CHAR_UUMLAUT;
6531 case 0x15fc: /* (green) */
6532 element = EL_CHAR_0;
6535 case 0x15fd: /* (green) */
6536 element = EL_CHAR_1;
6539 case 0x15fe: /* (green) */
6540 element = EL_CHAR_2;
6543 case 0x15ff: /* (green) */
6544 element = EL_CHAR_3;
6547 case 0x1600: /* (green) */
6548 element = EL_CHAR_4;
6551 case 0x1601: /* (green) */
6552 element = EL_CHAR_5;
6555 case 0x1602: /* (green) */
6556 element = EL_CHAR_6;
6559 case 0x1603: /* (green) */
6560 element = EL_CHAR_7;
6563 case 0x1604: /* (green) */
6564 element = EL_CHAR_8;
6567 case 0x1605: /* (green) */
6568 element = EL_CHAR_9;
6571 case 0x1606: /* (green) */
6572 element = EL_CHAR_PERIOD;
6575 case 0x1607: /* (green) */
6576 element = EL_CHAR_EXCLAM;
6579 case 0x1608: /* (green) */
6580 element = EL_CHAR_COLON;
6583 case 0x1609: /* (green) */
6584 element = EL_CHAR_LESS;
6587 case 0x160a: /* (green) */
6588 element = EL_CHAR_GREATER;
6591 case 0x160b: /* (green) */
6592 element = EL_CHAR_QUESTION;
6595 case 0x160c: /* (green) */
6596 element = EL_CHAR_COPYRIGHT;
6599 case 0x160d: /* (green) */
6600 element = EL_CHAR_UP;
6603 case 0x160e: /* (green) */
6604 element = EL_CHAR_DOWN;
6607 case 0x160f: /* (green) */
6608 element = EL_CHAR_BUTTON;
6611 case 0x1610: /* (green) */
6612 element = EL_CHAR_PLUS;
6615 case 0x1611: /* (green) */
6616 element = EL_CHAR_MINUS;
6619 case 0x1612: /* (green) */
6620 element = EL_CHAR_APOSTROPHE;
6623 case 0x1613: /* (green) */
6624 element = EL_CHAR_PARENLEFT;
6627 case 0x1614: /* (green) */
6628 element = EL_CHAR_PARENRIGHT;
6631 case 0x1615: /* (blue steel) */
6632 element = EL_STEEL_CHAR_A;
6635 case 0x1616: /* (blue steel) */
6636 element = EL_STEEL_CHAR_B;
6639 case 0x1617: /* (blue steel) */
6640 element = EL_STEEL_CHAR_C;
6643 case 0x1618: /* (blue steel) */
6644 element = EL_STEEL_CHAR_D;
6647 case 0x1619: /* (blue steel) */
6648 element = EL_STEEL_CHAR_E;
6651 case 0x161a: /* (blue steel) */
6652 element = EL_STEEL_CHAR_F;
6655 case 0x161b: /* (blue steel) */
6656 element = EL_STEEL_CHAR_G;
6659 case 0x161c: /* (blue steel) */
6660 element = EL_STEEL_CHAR_H;
6663 case 0x161d: /* (blue steel) */
6664 element = EL_STEEL_CHAR_I;
6667 case 0x161e: /* (blue steel) */
6668 element = EL_STEEL_CHAR_J;
6671 case 0x161f: /* (blue steel) */
6672 element = EL_STEEL_CHAR_K;
6675 case 0x1620: /* (blue steel) */
6676 element = EL_STEEL_CHAR_L;
6679 case 0x1621: /* (blue steel) */
6680 element = EL_STEEL_CHAR_M;
6683 case 0x1622: /* (blue steel) */
6684 element = EL_STEEL_CHAR_N;
6687 case 0x1623: /* (blue steel) */
6688 element = EL_STEEL_CHAR_O;
6691 case 0x1624: /* (blue steel) */
6692 element = EL_STEEL_CHAR_P;
6695 case 0x1625: /* (blue steel) */
6696 element = EL_STEEL_CHAR_Q;
6699 case 0x1626: /* (blue steel) */
6700 element = EL_STEEL_CHAR_R;
6703 case 0x1627: /* (blue steel) */
6704 element = EL_STEEL_CHAR_S;
6707 case 0x1628: /* (blue steel) */
6708 element = EL_STEEL_CHAR_T;
6711 case 0x1629: /* (blue steel) */
6712 element = EL_STEEL_CHAR_U;
6715 case 0x162a: /* (blue steel) */
6716 element = EL_STEEL_CHAR_V;
6719 case 0x162b: /* (blue steel) */
6720 element = EL_STEEL_CHAR_W;
6723 case 0x162c: /* (blue steel) */
6724 element = EL_STEEL_CHAR_X;
6727 case 0x162d: /* (blue steel) */
6728 element = EL_STEEL_CHAR_Y;
6731 case 0x162e: /* (blue steel) */
6732 element = EL_STEEL_CHAR_Z;
6735 case 0x162f: /* (blue steel) */
6736 element = EL_STEEL_CHAR_AUMLAUT;
6739 case 0x1630: /* (blue steel) */
6740 element = EL_STEEL_CHAR_OUMLAUT;
6743 case 0x1631: /* (blue steel) */
6744 element = EL_STEEL_CHAR_UUMLAUT;
6747 case 0x1632: /* (blue steel) */
6748 element = EL_STEEL_CHAR_0;
6751 case 0x1633: /* (blue steel) */
6752 element = EL_STEEL_CHAR_1;
6755 case 0x1634: /* (blue steel) */
6756 element = EL_STEEL_CHAR_2;
6759 case 0x1635: /* (blue steel) */
6760 element = EL_STEEL_CHAR_3;
6763 case 0x1636: /* (blue steel) */
6764 element = EL_STEEL_CHAR_4;
6767 case 0x1637: /* (blue steel) */
6768 element = EL_STEEL_CHAR_5;
6771 case 0x1638: /* (blue steel) */
6772 element = EL_STEEL_CHAR_6;
6775 case 0x1639: /* (blue steel) */
6776 element = EL_STEEL_CHAR_7;
6779 case 0x163a: /* (blue steel) */
6780 element = EL_STEEL_CHAR_8;
6783 case 0x163b: /* (blue steel) */
6784 element = EL_STEEL_CHAR_9;
6787 case 0x163c: /* (blue steel) */
6788 element = EL_STEEL_CHAR_PERIOD;
6791 case 0x163d: /* (blue steel) */
6792 element = EL_STEEL_CHAR_EXCLAM;
6795 case 0x163e: /* (blue steel) */
6796 element = EL_STEEL_CHAR_COLON;
6799 case 0x163f: /* (blue steel) */
6800 element = EL_STEEL_CHAR_LESS;
6803 case 0x1640: /* (blue steel) */
6804 element = EL_STEEL_CHAR_GREATER;
6807 case 0x1641: /* (blue steel) */
6808 element = EL_STEEL_CHAR_QUESTION;
6811 case 0x1642: /* (blue steel) */
6812 element = EL_STEEL_CHAR_COPYRIGHT;
6815 case 0x1643: /* (blue steel) */
6816 element = EL_STEEL_CHAR_UP;
6819 case 0x1644: /* (blue steel) */
6820 element = EL_STEEL_CHAR_DOWN;
6823 case 0x1645: /* (blue steel) */
6824 element = EL_STEEL_CHAR_BUTTON;
6827 case 0x1646: /* (blue steel) */
6828 element = EL_STEEL_CHAR_PLUS;
6831 case 0x1647: /* (blue steel) */
6832 element = EL_STEEL_CHAR_MINUS;
6835 case 0x1648: /* (blue steel) */
6836 element = EL_STEEL_CHAR_APOSTROPHE;
6839 case 0x1649: /* (blue steel) */
6840 element = EL_STEEL_CHAR_PARENLEFT;
6843 case 0x164a: /* (blue steel) */
6844 element = EL_STEEL_CHAR_PARENRIGHT;
6847 case 0x164b: /* (green steel) */
6848 element = EL_STEEL_CHAR_A;
6851 case 0x164c: /* (green steel) */
6852 element = EL_STEEL_CHAR_B;
6855 case 0x164d: /* (green steel) */
6856 element = EL_STEEL_CHAR_C;
6859 case 0x164e: /* (green steel) */
6860 element = EL_STEEL_CHAR_D;
6863 case 0x164f: /* (green steel) */
6864 element = EL_STEEL_CHAR_E;
6867 case 0x1650: /* (green steel) */
6868 element = EL_STEEL_CHAR_F;
6871 case 0x1651: /* (green steel) */
6872 element = EL_STEEL_CHAR_G;
6875 case 0x1652: /* (green steel) */
6876 element = EL_STEEL_CHAR_H;
6879 case 0x1653: /* (green steel) */
6880 element = EL_STEEL_CHAR_I;
6883 case 0x1654: /* (green steel) */
6884 element = EL_STEEL_CHAR_J;
6887 case 0x1655: /* (green steel) */
6888 element = EL_STEEL_CHAR_K;
6891 case 0x1656: /* (green steel) */
6892 element = EL_STEEL_CHAR_L;
6895 case 0x1657: /* (green steel) */
6896 element = EL_STEEL_CHAR_M;
6899 case 0x1658: /* (green steel) */
6900 element = EL_STEEL_CHAR_N;
6903 case 0x1659: /* (green steel) */
6904 element = EL_STEEL_CHAR_O;
6907 case 0x165a: /* (green steel) */
6908 element = EL_STEEL_CHAR_P;
6911 case 0x165b: /* (green steel) */
6912 element = EL_STEEL_CHAR_Q;
6915 case 0x165c: /* (green steel) */
6916 element = EL_STEEL_CHAR_R;
6919 case 0x165d: /* (green steel) */
6920 element = EL_STEEL_CHAR_S;
6923 case 0x165e: /* (green steel) */
6924 element = EL_STEEL_CHAR_T;
6927 case 0x165f: /* (green steel) */
6928 element = EL_STEEL_CHAR_U;
6931 case 0x1660: /* (green steel) */
6932 element = EL_STEEL_CHAR_V;
6935 case 0x1661: /* (green steel) */
6936 element = EL_STEEL_CHAR_W;
6939 case 0x1662: /* (green steel) */
6940 element = EL_STEEL_CHAR_X;
6943 case 0x1663: /* (green steel) */
6944 element = EL_STEEL_CHAR_Y;
6947 case 0x1664: /* (green steel) */
6948 element = EL_STEEL_CHAR_Z;
6951 case 0x1665: /* (green steel) */
6952 element = EL_STEEL_CHAR_AUMLAUT;
6955 case 0x1666: /* (green steel) */
6956 element = EL_STEEL_CHAR_OUMLAUT;
6959 case 0x1667: /* (green steel) */
6960 element = EL_STEEL_CHAR_UUMLAUT;
6963 case 0x1668: /* (green steel) */
6964 element = EL_STEEL_CHAR_0;
6967 case 0x1669: /* (green steel) */
6968 element = EL_STEEL_CHAR_1;
6971 case 0x166a: /* (green steel) */
6972 element = EL_STEEL_CHAR_2;
6975 case 0x166b: /* (green steel) */
6976 element = EL_STEEL_CHAR_3;
6979 case 0x166c: /* (green steel) */
6980 element = EL_STEEL_CHAR_4;
6983 case 0x166d: /* (green steel) */
6984 element = EL_STEEL_CHAR_5;
6987 case 0x166e: /* (green steel) */
6988 element = EL_STEEL_CHAR_6;
6991 case 0x166f: /* (green steel) */
6992 element = EL_STEEL_CHAR_7;
6995 case 0x1670: /* (green steel) */
6996 element = EL_STEEL_CHAR_8;
6999 case 0x1671: /* (green steel) */
7000 element = EL_STEEL_CHAR_9;
7003 case 0x1672: /* (green steel) */
7004 element = EL_STEEL_CHAR_PERIOD;
7007 case 0x1673: /* (green steel) */
7008 element = EL_STEEL_CHAR_EXCLAM;
7011 case 0x1674: /* (green steel) */
7012 element = EL_STEEL_CHAR_COLON;
7015 case 0x1675: /* (green steel) */
7016 element = EL_STEEL_CHAR_LESS;
7019 case 0x1676: /* (green steel) */
7020 element = EL_STEEL_CHAR_GREATER;
7023 case 0x1677: /* (green steel) */
7024 element = EL_STEEL_CHAR_QUESTION;
7027 case 0x1678: /* (green steel) */
7028 element = EL_STEEL_CHAR_COPYRIGHT;
7031 case 0x1679: /* (green steel) */
7032 element = EL_STEEL_CHAR_UP;
7035 case 0x167a: /* (green steel) */
7036 element = EL_STEEL_CHAR_DOWN;
7039 case 0x167b: /* (green steel) */
7040 element = EL_STEEL_CHAR_BUTTON;
7043 case 0x167c: /* (green steel) */
7044 element = EL_STEEL_CHAR_PLUS;
7047 case 0x167d: /* (green steel) */
7048 element = EL_STEEL_CHAR_MINUS;
7051 case 0x167e: /* (green steel) */
7052 element = EL_STEEL_CHAR_APOSTROPHE;
7055 case 0x167f: /* (green steel) */
7056 element = EL_STEEL_CHAR_PARENLEFT;
7059 case 0x1680: /* (green steel) */
7060 element = EL_STEEL_CHAR_PARENRIGHT;
7063 case 0x1681: /* gate (red) */
7064 element = EL_EM_GATE_1;
7067 case 0x1682: /* secret gate (red) */
7068 element = EL_GATE_1_GRAY;
7071 case 0x1683: /* gate (yellow) */
7072 element = EL_EM_GATE_2;
7075 case 0x1684: /* secret gate (yellow) */
7076 element = EL_GATE_2_GRAY;
7079 case 0x1685: /* gate (blue) */
7080 element = EL_EM_GATE_4;
7083 case 0x1686: /* secret gate (blue) */
7084 element = EL_GATE_4_GRAY;
7087 case 0x1687: /* gate (green) */
7088 element = EL_EM_GATE_3;
7091 case 0x1688: /* secret gate (green) */
7092 element = EL_GATE_3_GRAY;
7095 case 0x1689: /* gate (white) */
7096 element = EL_DC_GATE_WHITE;
7099 case 0x168a: /* secret gate (white) */
7100 element = EL_DC_GATE_WHITE_GRAY;
7103 case 0x168b: /* secret gate (no key) */
7104 element = EL_DC_GATE_FAKE_GRAY;
7108 element = EL_ROBOT_WHEEL;
7112 element = EL_DC_TIMEGATE_SWITCH;
7116 element = EL_ACID_POOL_BOTTOM;
7120 element = EL_ACID_POOL_TOPLEFT;
7124 element = EL_ACID_POOL_TOPRIGHT;
7128 element = EL_ACID_POOL_BOTTOMLEFT;
7132 element = EL_ACID_POOL_BOTTOMRIGHT;
7136 element = EL_STEELWALL;
7140 element = EL_STEELWALL_SLIPPERY;
7143 case 0x1695: /* steel wall (not round) */
7144 element = EL_STEELWALL;
7147 case 0x1696: /* steel wall (left) */
7148 element = EL_DC_STEELWALL_1_LEFT;
7151 case 0x1697: /* steel wall (bottom) */
7152 element = EL_DC_STEELWALL_1_BOTTOM;
7155 case 0x1698: /* steel wall (right) */
7156 element = EL_DC_STEELWALL_1_RIGHT;
7159 case 0x1699: /* steel wall (top) */
7160 element = EL_DC_STEELWALL_1_TOP;
7163 case 0x169a: /* steel wall (left/bottom) */
7164 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
7167 case 0x169b: /* steel wall (right/bottom) */
7168 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
7171 case 0x169c: /* steel wall (right/top) */
7172 element = EL_DC_STEELWALL_1_TOPRIGHT;
7175 case 0x169d: /* steel wall (left/top) */
7176 element = EL_DC_STEELWALL_1_TOPLEFT;
7179 case 0x169e: /* steel wall (right/bottom small) */
7180 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
7183 case 0x169f: /* steel wall (left/bottom small) */
7184 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
7187 case 0x16a0: /* steel wall (right/top small) */
7188 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
7191 case 0x16a1: /* steel wall (left/top small) */
7192 element = EL_DC_STEELWALL_1_TOPLEFT_2;
7195 case 0x16a2: /* steel wall (left/right) */
7196 element = EL_DC_STEELWALL_1_VERTICAL;
7199 case 0x16a3: /* steel wall (top/bottom) */
7200 element = EL_DC_STEELWALL_1_HORIZONTAL;
7203 case 0x16a4: /* steel wall 2 (left end) */
7204 element = EL_DC_STEELWALL_2_LEFT;
7207 case 0x16a5: /* steel wall 2 (right end) */
7208 element = EL_DC_STEELWALL_2_RIGHT;
7211 case 0x16a6: /* steel wall 2 (top end) */
7212 element = EL_DC_STEELWALL_2_TOP;
7215 case 0x16a7: /* steel wall 2 (bottom end) */
7216 element = EL_DC_STEELWALL_2_BOTTOM;
7219 case 0x16a8: /* steel wall 2 (left/right) */
7220 element = EL_DC_STEELWALL_2_HORIZONTAL;
7223 case 0x16a9: /* steel wall 2 (up/down) */
7224 element = EL_DC_STEELWALL_2_VERTICAL;
7227 case 0x16aa: /* steel wall 2 (mid) */
7228 element = EL_DC_STEELWALL_2_MIDDLE;
7232 element = EL_SIGN_EXCLAMATION;
7236 element = EL_SIGN_RADIOACTIVITY;
7240 element = EL_SIGN_STOP;
7244 element = EL_SIGN_WHEELCHAIR;
7248 element = EL_SIGN_PARKING;
7252 element = EL_SIGN_NO_ENTRY;
7256 element = EL_SIGN_HEART;
7260 element = EL_SIGN_GIVE_WAY;
7264 element = EL_SIGN_ENTRY_FORBIDDEN;
7268 element = EL_SIGN_EMERGENCY_EXIT;
7272 element = EL_SIGN_YIN_YANG;
7276 element = EL_WALL_EMERALD;
7280 element = EL_WALL_DIAMOND;
7284 element = EL_WALL_PEARL;
7288 element = EL_WALL_CRYSTAL;
7292 element = EL_INVISIBLE_WALL;
7296 element = EL_INVISIBLE_STEELWALL;
7299 /* 0x16bc - 0x16cb: */
7300 /* EL_INVISIBLE_SAND */
7303 element = EL_LIGHT_SWITCH;
7307 element = EL_ENVELOPE_1;
7311 if (element >= 0x0117 && element <= 0x036e) /* (?) */
7312 element = EL_DIAMOND;
7313 else if (element >= 0x042d && element <= 0x0684) /* (?) */
7314 element = EL_EMERALD;
7315 else if (element >= 0x157c && element <= 0x158b)
7317 else if (element >= 0x1590 && element <= 0x159f)
7318 element = EL_DC_LANDMINE;
7319 else if (element >= 0x16bc && element <= 0x16cb)
7320 element = EL_INVISIBLE_SAND;
7323 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
7324 element = EL_UNKNOWN;
7329 return getMappedElement(element);
7336 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
7339 byte header[DC_LEVEL_HEADER_SIZE];
7341 int envelope_header_pos = 62;
7342 int envelope_content_pos = 94;
7343 int level_name_pos = 251;
7344 int level_author_pos = 292;
7345 int envelope_header_len;
7346 int envelope_content_len;
7348 int level_author_len;
7350 int num_yamyam_contents;
7353 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
7355 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
7357 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7359 header[i * 2 + 0] = header_word >> 8;
7360 header[i * 2 + 1] = header_word & 0xff;
7363 /* read some values from level header to check level decoding integrity */
7364 fieldx = header[6] | (header[7] << 8);
7365 fieldy = header[8] | (header[9] << 8);
7366 num_yamyam_contents = header[60] | (header[61] << 8);
7368 /* do some simple sanity checks to ensure that level was correctly decoded */
7369 if (fieldx < 1 || fieldx > 256 ||
7370 fieldy < 1 || fieldy > 256 ||
7371 num_yamyam_contents < 1 || num_yamyam_contents > 8)
7373 level->no_valid_file = TRUE;
7375 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
7380 /* maximum envelope header size is 31 bytes */
7381 envelope_header_len = header[envelope_header_pos];
7382 /* maximum envelope content size is 110 (156?) bytes */
7383 envelope_content_len = header[envelope_content_pos];
7385 /* maximum level title size is 40 bytes */
7386 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
7387 /* maximum level author size is 30 (51?) bytes */
7388 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
7392 for (i = 0; i < envelope_header_len; i++)
7393 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7394 level->envelope[0].text[envelope_size++] =
7395 header[envelope_header_pos + 1 + i];
7397 if (envelope_header_len > 0 && envelope_content_len > 0)
7399 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7400 level->envelope[0].text[envelope_size++] = '\n';
7401 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7402 level->envelope[0].text[envelope_size++] = '\n';
7405 for (i = 0; i < envelope_content_len; i++)
7406 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7407 level->envelope[0].text[envelope_size++] =
7408 header[envelope_content_pos + 1 + i];
7410 level->envelope[0].text[envelope_size] = '\0';
7412 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
7413 level->envelope[0].ysize = 10;
7414 level->envelope[0].autowrap = TRUE;
7415 level->envelope[0].centered = TRUE;
7417 for (i = 0; i < level_name_len; i++)
7418 level->name[i] = header[level_name_pos + 1 + i];
7419 level->name[level_name_len] = '\0';
7421 for (i = 0; i < level_author_len; i++)
7422 level->author[i] = header[level_author_pos + 1 + i];
7423 level->author[level_author_len] = '\0';
7425 num_yamyam_contents = header[60] | (header[61] << 8);
7426 level->num_yamyam_contents =
7427 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
7429 for (i = 0; i < num_yamyam_contents; i++)
7431 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
7433 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7435 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
7437 int element_dc = word;
7440 if (i < MAX_ELEMENT_CONTENTS)
7441 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
7445 fieldx = header[6] | (header[7] << 8);
7446 fieldy = header[8] | (header[9] << 8);
7447 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
7448 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
7450 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
7452 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7454 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
7456 int element_dc = word;
7459 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
7460 level->field[x][y] = getMappedElement_DC(element_dc);
7463 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
7464 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
7465 level->field[x][y] = EL_PLAYER_1;
7467 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
7468 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
7469 level->field[x][y] = EL_PLAYER_2;
7471 level->gems_needed = header[18] | (header[19] << 8);
7473 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
7474 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
7475 level->score[SC_PEARL] = header[24] | (header[25] << 8);
7476 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
7477 level->score[SC_NUT] = header[28] | (header[29] << 8);
7478 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
7479 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
7480 level->score[SC_BUG] = header[34] | (header[35] << 8);
7481 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
7482 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
7483 level->score[SC_KEY] = header[40] | (header[41] << 8);
7484 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
7486 level->time = header[44] | (header[45] << 8);
7488 level->amoeba_speed = header[46] | (header[47] << 8);
7489 level->time_light = header[48] | (header[49] << 8);
7490 level->time_timegate = header[50] | (header[51] << 8);
7491 level->time_wheel = header[52] | (header[53] << 8);
7492 level->time_magic_wall = header[54] | (header[55] << 8);
7493 level->extra_time = header[56] | (header[57] << 8);
7494 level->shield_normal_time = header[58] | (header[59] << 8);
7496 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
7497 can slip down from flat walls, like normal walls and steel walls */
7498 level->em_slippery_gems = TRUE;
7501 /* Diamond Caves II levels are always surrounded by indestructible wall, but
7502 not necessarily in a rectangular way -- fill with invisible steel wall */
7504 /* !!! not always true !!! keep level and set BorderElement instead !!! */
7506 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7509 if ((x == 0 || x == level->fieldx - 1 ||
7510 y == 0 || y == level->fieldy - 1) &&
7511 level->field[x][y] == EL_EMPTY)
7512 level->field[x][y] = EL_INVISIBLE_STEELWALL;
7514 if ((x == 0 || x == level->fieldx - 1 ||
7515 y == 0 || y == level->fieldy - 1) &&
7516 level->field[x][y] == EL_EMPTY)
7517 FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
7518 level->field, level->fieldx, level->fieldy);
7524 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
7525 struct LevelFileInfo *level_file_info,
7526 boolean level_info_only)
7528 char *filename = level_file_info->filename;
7530 int num_magic_bytes = 8;
7531 char magic_bytes[num_magic_bytes + 1];
7532 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7534 if (!(file = openFile(filename, MODE_READ)))
7536 level->no_valid_file = TRUE;
7538 if (!level_info_only)
7539 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
7544 // fseek(file, 0x0000, SEEK_SET);
7546 if (level_file_info->packed)
7548 /* read "magic bytes" from start of file */
7549 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
7550 magic_bytes[0] = '\0';
7552 /* check "magic bytes" for correct file format */
7553 if (!strPrefix(magic_bytes, "DC2"))
7555 level->no_valid_file = TRUE;
7557 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
7563 if (strPrefix(magic_bytes, "DC2Win95") ||
7564 strPrefix(magic_bytes, "DC2Win98"))
7566 int position_first_level = 0x00fa;
7567 int extra_bytes = 4;
7570 /* advance file stream to first level inside the level package */
7571 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7573 /* each block of level data is followed by block of non-level data */
7574 num_levels_to_skip *= 2;
7576 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
7577 while (num_levels_to_skip >= 0)
7579 /* advance file stream to next level inside the level package */
7580 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7582 level->no_valid_file = TRUE;
7584 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
7590 /* skip apparently unused extra bytes following each level */
7591 ReadUnusedBytesFromFile(file, extra_bytes);
7593 /* read size of next level in level package */
7594 skip_bytes = getFile32BitLE(file);
7596 num_levels_to_skip--;
7601 level->no_valid_file = TRUE;
7603 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
7610 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
7617 static void LoadLevelFromFileStream_DC(FILE *file, struct LevelInfo *level,
7620 byte header[DC_LEVEL_HEADER_SIZE];
7622 int envelope_header_pos = 62;
7623 int envelope_content_pos = 94;
7624 int level_name_pos = 251;
7625 int level_author_pos = 292;
7626 int envelope_header_len;
7627 int envelope_content_len;
7629 int level_author_len;
7631 int num_yamyam_contents;
7634 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
7636 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
7638 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7640 header[i * 2 + 0] = header_word >> 8;
7641 header[i * 2 + 1] = header_word & 0xff;
7644 /* read some values from level header to check level decoding integrity */
7645 fieldx = header[6] | (header[7] << 8);
7646 fieldy = header[8] | (header[9] << 8);
7647 num_yamyam_contents = header[60] | (header[61] << 8);
7649 /* do some simple sanity checks to ensure that level was correctly decoded */
7650 if (fieldx < 1 || fieldx > 256 ||
7651 fieldy < 1 || fieldy > 256 ||
7652 num_yamyam_contents < 1 || num_yamyam_contents > 8)
7654 level->no_valid_file = TRUE;
7656 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
7661 /* maximum envelope header size is 31 bytes */
7662 envelope_header_len = header[envelope_header_pos];
7663 /* maximum envelope content size is 110 (156?) bytes */
7664 envelope_content_len = header[envelope_content_pos];
7666 /* maximum level title size is 40 bytes */
7667 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
7668 /* maximum level author size is 30 (51?) bytes */
7669 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
7673 for (i = 0; i < envelope_header_len; i++)
7674 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7675 level->envelope[0].text[envelope_size++] =
7676 header[envelope_header_pos + 1 + i];
7678 if (envelope_header_len > 0 && envelope_content_len > 0)
7680 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7681 level->envelope[0].text[envelope_size++] = '\n';
7682 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7683 level->envelope[0].text[envelope_size++] = '\n';
7686 for (i = 0; i < envelope_content_len; i++)
7687 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7688 level->envelope[0].text[envelope_size++] =
7689 header[envelope_content_pos + 1 + i];
7691 level->envelope[0].text[envelope_size] = '\0';
7693 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
7694 level->envelope[0].ysize = 10;
7695 level->envelope[0].autowrap = TRUE;
7696 level->envelope[0].centered = TRUE;
7698 for (i = 0; i < level_name_len; i++)
7699 level->name[i] = header[level_name_pos + 1 + i];
7700 level->name[level_name_len] = '\0';
7702 for (i = 0; i < level_author_len; i++)
7703 level->author[i] = header[level_author_pos + 1 + i];
7704 level->author[level_author_len] = '\0';
7706 num_yamyam_contents = header[60] | (header[61] << 8);
7707 level->num_yamyam_contents =
7708 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
7710 for (i = 0; i < num_yamyam_contents; i++)
7712 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
7714 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7716 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
7718 int element_dc = word;
7721 if (i < MAX_ELEMENT_CONTENTS)
7722 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
7726 fieldx = header[6] | (header[7] << 8);
7727 fieldy = header[8] | (header[9] << 8);
7728 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
7729 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
7731 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
7733 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7735 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
7737 int element_dc = word;
7740 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
7741 level->field[x][y] = getMappedElement_DC(element_dc);
7744 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
7745 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
7746 level->field[x][y] = EL_PLAYER_1;
7748 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
7749 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
7750 level->field[x][y] = EL_PLAYER_2;
7752 level->gems_needed = header[18] | (header[19] << 8);
7754 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
7755 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
7756 level->score[SC_PEARL] = header[24] | (header[25] << 8);
7757 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
7758 level->score[SC_NUT] = header[28] | (header[29] << 8);
7759 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
7760 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
7761 level->score[SC_BUG] = header[34] | (header[35] << 8);
7762 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
7763 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
7764 level->score[SC_KEY] = header[40] | (header[41] << 8);
7765 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
7767 level->time = header[44] | (header[45] << 8);
7769 level->amoeba_speed = header[46] | (header[47] << 8);
7770 level->time_light = header[48] | (header[49] << 8);
7771 level->time_timegate = header[50] | (header[51] << 8);
7772 level->time_wheel = header[52] | (header[53] << 8);
7773 level->time_magic_wall = header[54] | (header[55] << 8);
7774 level->extra_time = header[56] | (header[57] << 8);
7775 level->shield_normal_time = header[58] | (header[59] << 8);
7777 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
7778 can slip down from flat walls, like normal walls and steel walls */
7779 level->em_slippery_gems = TRUE;
7782 /* Diamond Caves II levels are always surrounded by indestructible wall, but
7783 not necessarily in a rectangular way -- fill with invisible steel wall */
7785 /* !!! not always true !!! keep level and set BorderElement instead !!! */
7787 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7790 if ((x == 0 || x == level->fieldx - 1 ||
7791 y == 0 || y == level->fieldy - 1) &&
7792 level->field[x][y] == EL_EMPTY)
7793 level->field[x][y] = EL_INVISIBLE_STEELWALL;
7795 if ((x == 0 || x == level->fieldx - 1 ||
7796 y == 0 || y == level->fieldy - 1) &&
7797 level->field[x][y] == EL_EMPTY)
7798 FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
7799 level->field, level->fieldx, level->fieldy);
7805 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
7806 struct LevelFileInfo *level_file_info,
7807 boolean level_info_only)
7809 char *filename = level_file_info->filename;
7811 int num_magic_bytes = 8;
7812 char magic_bytes[num_magic_bytes + 1];
7813 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7815 if (!(file = fopen(filename, MODE_READ)))
7817 level->no_valid_file = TRUE;
7819 if (!level_info_only)
7820 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
7825 // fseek(file, 0x0000, SEEK_SET);
7827 if (level_file_info->packed)
7829 /* read "magic bytes" from start of file */
7830 if (fgets(magic_bytes, num_magic_bytes + 1, file) == NULL)
7831 magic_bytes[0] = '\0';
7833 /* check "magic bytes" for correct file format */
7834 if (!strPrefix(magic_bytes, "DC2"))
7836 level->no_valid_file = TRUE;
7838 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
7844 if (strPrefix(magic_bytes, "DC2Win95") ||
7845 strPrefix(magic_bytes, "DC2Win98"))
7847 int position_first_level = 0x00fa;
7848 int extra_bytes = 4;
7851 /* advance file stream to first level inside the level package */
7852 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7854 /* each block of level data is followed by block of non-level data */
7855 num_levels_to_skip *= 2;
7857 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
7858 while (num_levels_to_skip >= 0)
7860 /* advance file stream to next level inside the level package */
7861 if (fseek(file, skip_bytes, SEEK_CUR) != 0)
7863 level->no_valid_file = TRUE;
7865 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
7871 /* skip apparently unused extra bytes following each level */
7872 ReadUnusedBytesFromFile(file, extra_bytes);
7874 /* read size of next level in level package */
7875 skip_bytes = getFile32BitLE(file);
7877 num_levels_to_skip--;
7882 level->no_valid_file = TRUE;
7884 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
7891 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
7900 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
7901 struct LevelFileInfo *level_file_info)
7903 char *filename = level_file_info->filename;
7906 int nr = level_file_info->nr - leveldir_current->first_level;
7908 byte header[DC_LEVEL_HEADER_SIZE];
7910 int envelope_header_pos = 62;
7911 int envelope_content_pos = 94;
7912 int level_name_pos = 251;
7913 int level_author_pos = 292;
7914 int envelope_header_len;
7915 int envelope_content_len;
7917 int level_author_len;
7919 int num_yamyam_contents;
7922 if (!(file = fopen(filename, MODE_READ)))
7924 level->no_valid_file = TRUE;
7926 if (!level_info_only)
7927 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
7933 /* position file stream to the requested level inside the level package */
7934 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
7936 level->no_valid_file = TRUE;
7938 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
7944 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
7946 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
7948 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7950 header[i * 2 + 0] = header_word >> 8;
7951 header[i * 2 + 1] = header_word & 0xff;
7954 /* read some values from level header to check level decoding integrity */
7955 fieldx = header[6] | (header[7] << 8);
7956 fieldy = header[8] | (header[9] << 8);
7957 num_yamyam_contents = header[60] | (header[61] << 8);
7959 /* do some simple sanity checks to ensure that level was correctly decoded */
7960 if (fieldx < 1 || fieldx > 256 ||
7961 fieldy < 1 || fieldy > 256 ||
7962 num_yamyam_contents < 1 || num_yamyam_contents > 8)
7964 level->no_valid_file = TRUE;
7966 Error(ERR_WARN, "cannot read level from file '%s' -- using empty level",
7972 /* maximum envelope header size is 31 bytes */
7973 envelope_header_len = header[envelope_header_pos];
7974 /* maximum envelope content size is 110 (156?) bytes */
7975 envelope_content_len = header[envelope_content_pos];
7977 /* maximum level title size is 40 bytes */
7978 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
7979 /* maximum level author size is 30 (51?) bytes */
7980 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
7984 for (i = 0; i < envelope_header_len; i++)
7985 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7986 level->envelope[0].text[envelope_size++] =
7987 header[envelope_header_pos + 1 + i];
7989 if (envelope_header_len > 0 && envelope_content_len > 0)
7991 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7992 level->envelope[0].text[envelope_size++] = '\n';
7993 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7994 level->envelope[0].text[envelope_size++] = '\n';
7997 for (i = 0; i < envelope_content_len; i++)
7998 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7999 level->envelope[0].text[envelope_size++] =
8000 header[envelope_content_pos + 1 + i];
8002 level->envelope[0].text[envelope_size] = '\0';
8004 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
8005 level->envelope[0].ysize = 10;
8006 level->envelope[0].autowrap = TRUE;
8007 level->envelope[0].centered = TRUE;
8009 for (i = 0; i < level_name_len; i++)
8010 level->name[i] = header[level_name_pos + 1 + i];
8011 level->name[level_name_len] = '\0';
8013 for (i = 0; i < level_author_len; i++)
8014 level->author[i] = header[level_author_pos + 1 + i];
8015 level->author[level_author_len] = '\0';
8017 num_yamyam_contents = header[60] | (header[61] << 8);
8018 level->num_yamyam_contents =
8019 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
8021 for (i = 0; i < num_yamyam_contents; i++)
8023 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
8025 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
8027 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
8029 int element_dc = word;
8032 if (i < MAX_ELEMENT_CONTENTS)
8033 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
8037 fieldx = header[6] | (header[7] << 8);
8038 fieldy = header[8] | (header[9] << 8);
8039 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
8040 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
8042 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
8044 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
8046 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
8048 int element_dc = word;
8051 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
8052 level->field[x][y] = getMappedElement_DC(element_dc);
8055 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
8056 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
8057 level->field[x][y] = EL_PLAYER_1;
8059 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
8060 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
8061 level->field[x][y] = EL_PLAYER_2;
8063 level->gems_needed = header[18] | (header[19] << 8);
8065 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
8066 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
8067 level->score[SC_PEARL] = header[24] | (header[25] << 8);
8068 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
8069 level->score[SC_NUT] = header[28] | (header[29] << 8);
8070 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
8071 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
8072 level->score[SC_BUG] = header[34] | (header[35] << 8);
8073 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
8074 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
8075 level->score[SC_KEY] = header[40] | (header[41] << 8);
8076 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
8078 level->time = header[44] | (header[45] << 8);
8080 level->amoeba_speed = header[46] | (header[47] << 8);
8081 level->time_light = header[48] | (header[49] << 8);
8082 level->time_timegate = header[50] | (header[51] << 8);
8083 level->time_wheel = header[52] | (header[53] << 8);
8084 level->time_magic_wall = header[54] | (header[55] << 8);
8085 level->extra_time = header[56] | (header[57] << 8);
8086 level->shield_normal_time = header[58] | (header[59] << 8);
8090 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
8091 can slip down from flat walls, like normal walls and steel walls */
8092 level->em_slippery_gems = TRUE;
8095 /* Diamond Caves II levels are always surrounded by indestructible wall, but
8096 not necessarily in a rectangular way -- fill with invisible steel wall */
8098 /* !!! not always true !!! keep level and set BorderElement instead !!! */
8100 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8103 if ((x == 0 || x == level->fieldx - 1 ||
8104 y == 0 || y == level->fieldy - 1) &&
8105 level->field[x][y] == EL_EMPTY)
8106 level->field[x][y] = EL_INVISIBLE_STEELWALL;
8108 if ((x == 0 || x == level->fieldx - 1 ||
8109 y == 0 || y == level->fieldy - 1) &&
8110 level->field[x][y] == EL_EMPTY)
8111 FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
8112 level->field, level->fieldx, level->fieldy);
8121 /* ------------------------------------------------------------------------- */
8122 /* functions for loading SB level */
8123 /* ------------------------------------------------------------------------- */
8125 int getMappedElement_SB(int element_ascii, boolean use_ces)
8133 sb_element_mapping[] =
8135 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
8136 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
8137 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
8138 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
8139 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
8140 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
8141 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
8143 { '_', EL_INVISIBLE_STEELWALL, EL_CUSTOM_8 }, /* floor beyond border */
8145 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
8153 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
8154 if (element_ascii == sb_element_mapping[i].ascii)
8155 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
8157 return EL_UNDEFINED;
8162 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
8163 struct LevelFileInfo *level_file_info,
8164 boolean level_info_only)
8166 char *filename = level_file_info->filename;
8167 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
8168 char last_comment[MAX_LINE_LEN];
8169 char level_name[MAX_LINE_LEN];
8172 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
8173 boolean read_continued_line = FALSE;
8174 boolean reading_playfield = FALSE;
8175 boolean got_valid_playfield_line = FALSE;
8176 boolean invalid_playfield_char = FALSE;
8177 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
8178 int file_level_nr = 0;
8180 int x = 0, y = 0; /* initialized to make compilers happy */
8183 printf("::: looking for level number %d [%d]\n",
8184 level_file_info->nr, num_levels_to_skip);
8187 last_comment[0] = '\0';
8188 level_name[0] = '\0';
8190 if (!(file = openFile(filename, MODE_READ)))
8192 level->no_valid_file = TRUE;
8194 if (!level_info_only)
8195 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
8200 while (!checkEndOfFile(file))
8202 /* level successfully read, but next level may follow here */
8203 if (!got_valid_playfield_line && reading_playfield)
8206 printf("::: read complete playfield\n");
8209 /* read playfield from single level file -- skip remaining file */
8210 if (!level_file_info->packed)
8213 if (file_level_nr >= num_levels_to_skip)
8218 last_comment[0] = '\0';
8219 level_name[0] = '\0';
8221 reading_playfield = FALSE;
8224 got_valid_playfield_line = FALSE;
8226 /* read next line of input file */
8227 if (!getStringFromFile(file, line, MAX_LINE_LEN))
8230 /* check if line was completely read and is terminated by line break */
8231 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8234 /* cut trailing line break (this can be newline and/or carriage return) */
8235 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
8236 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
8239 /* copy raw input line for later use (mainly debugging output) */
8240 strcpy(line_raw, line);
8242 if (read_continued_line)
8244 /* append new line to existing line, if there is enough space */
8245 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
8246 strcat(previous_line, line_ptr);
8248 strcpy(line, previous_line); /* copy storage buffer to line */
8250 read_continued_line = FALSE;
8253 /* if the last character is '\', continue at next line */
8254 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
8256 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
8257 strcpy(previous_line, line); /* copy line to storage buffer */
8259 read_continued_line = TRUE;
8264 /* skip empty lines */
8265 if (line[0] == '\0')
8268 /* extract comment text from comment line */
8271 for (line_ptr = line; *line_ptr; line_ptr++)
8272 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
8275 strcpy(last_comment, line_ptr);
8278 printf("::: found comment '%s' in line %d\n", last_comment, line_nr);
8284 /* extract level title text from line containing level title */
8285 if (line[0] == '\'')
8287 strcpy(level_name, &line[1]);
8289 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
8290 level_name[strlen(level_name) - 1] = '\0';
8293 printf("::: found level name '%s' in line %d\n", level_name, line_nr);
8299 /* skip lines containing only spaces (or empty lines) */
8300 for (line_ptr = line; *line_ptr; line_ptr++)
8301 if (*line_ptr != ' ')
8303 if (*line_ptr == '\0')
8306 /* at this point, we have found a line containing part of a playfield */
8309 printf("::: found playfield row in line %d\n", line_nr);
8312 got_valid_playfield_line = TRUE;
8314 if (!reading_playfield)
8316 reading_playfield = TRUE;
8317 invalid_playfield_char = FALSE;
8319 for (x = 0; x < MAX_LEV_FIELDX; x++)
8320 for (y = 0; y < MAX_LEV_FIELDY; y++)
8321 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
8326 /* start with topmost tile row */
8330 /* skip playfield line if larger row than allowed */
8331 if (y >= MAX_LEV_FIELDY)
8334 /* start with leftmost tile column */
8337 /* read playfield elements from line */
8338 for (line_ptr = line; *line_ptr; line_ptr++)
8340 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
8342 /* stop parsing playfield line if larger column than allowed */
8343 if (x >= MAX_LEV_FIELDX)
8346 if (mapped_sb_element == EL_UNDEFINED)
8348 invalid_playfield_char = TRUE;
8353 level->field[x][y] = mapped_sb_element;
8355 /* continue with next tile column */
8358 level->fieldx = MAX(x, level->fieldx);
8361 if (invalid_playfield_char)
8363 /* if first playfield line, treat invalid lines as comment lines */
8365 reading_playfield = FALSE;
8370 /* continue with next tile row */
8378 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
8379 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
8381 if (!reading_playfield)
8383 level->no_valid_file = TRUE;
8385 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
8390 if (*level_name != '\0')
8392 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
8393 level->name[MAX_LEVEL_NAME_LEN] = '\0';
8396 printf(":1: level name: '%s'\n", level->name);
8399 else if (*last_comment != '\0')
8401 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
8402 level->name[MAX_LEVEL_NAME_LEN] = '\0';
8405 printf(":2: level name: '%s'\n", level->name);
8410 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
8413 /* set all empty fields beyond the border walls to invisible steel wall */
8414 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8416 if ((x == 0 || x == level->fieldx - 1 ||
8417 y == 0 || y == level->fieldy - 1) &&
8418 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
8419 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
8420 level->field, level->fieldx, level->fieldy);
8423 /* set special level settings for Sokoban levels */
8426 level->use_step_counter = TRUE;
8428 if (load_xsb_to_ces)
8431 /* !!! special global settings can now be set in level template !!! */
8433 level->initial_player_stepsize[0] = STEPSIZE_SLOW;
8436 /* fill smaller playfields with padding "beyond border wall" elements */
8437 if (level->fieldx < SCR_FIELDX ||
8438 level->fieldy < SCR_FIELDY)
8440 short field[level->fieldx][level->fieldy];
8441 int new_fieldx = MAX(level->fieldx, SCR_FIELDX);
8442 int new_fieldy = MAX(level->fieldy, SCR_FIELDY);
8443 int pos_fieldx = (new_fieldx - level->fieldx) / 2;
8444 int pos_fieldy = (new_fieldy - level->fieldy) / 2;
8446 /* copy old playfield (which is smaller than the visible area) */
8447 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8448 field[x][y] = level->field[x][y];
8450 /* fill new, larger playfield with "beyond border wall" elements */
8451 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
8452 level->field[x][y] = getMappedElement_SB('_', load_xsb_to_ces);
8454 /* copy the old playfield to the middle of the new playfield */
8455 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8456 level->field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
8458 level->fieldx = new_fieldx;
8459 level->fieldy = new_fieldy;
8462 level->use_custom_template = TRUE;
8468 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
8469 struct LevelFileInfo *level_file_info,
8470 boolean level_info_only)
8472 char *filename = level_file_info->filename;
8473 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
8474 char last_comment[MAX_LINE_LEN];
8475 char level_name[MAX_LINE_LEN];
8478 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
8479 boolean read_continued_line = FALSE;
8480 boolean reading_playfield = FALSE;
8481 boolean got_valid_playfield_line = FALSE;
8482 boolean invalid_playfield_char = FALSE;
8483 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
8484 int file_level_nr = 0;
8486 int x = 0, y = 0; /* initialized to make compilers happy */
8489 printf("::: looking for level number %d [%d]\n",
8490 level_file_info->nr, num_levels_to_skip);
8493 last_comment[0] = '\0';
8494 level_name[0] = '\0';
8496 if (!(file = fopen(filename, MODE_READ)))
8498 level->no_valid_file = TRUE;
8500 if (!level_info_only)
8501 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
8508 /* level successfully read, but next level may follow here */
8509 if (!got_valid_playfield_line && reading_playfield)
8512 printf("::: read complete playfield\n");
8515 /* read playfield from single level file -- skip remaining file */
8516 if (!level_file_info->packed)
8519 if (file_level_nr >= num_levels_to_skip)
8524 last_comment[0] = '\0';
8525 level_name[0] = '\0';
8527 reading_playfield = FALSE;
8530 got_valid_playfield_line = FALSE;
8532 /* read next line of input file */
8533 if (!fgets(line, MAX_LINE_LEN, file))
8536 /* check if line was completely read and is terminated by line break */
8537 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8540 /* cut trailing line break (this can be newline and/or carriage return) */
8541 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
8542 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
8545 /* copy raw input line for later use (mainly debugging output) */
8546 strcpy(line_raw, line);
8548 if (read_continued_line)
8550 /* append new line to existing line, if there is enough space */
8551 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
8552 strcat(previous_line, line_ptr);
8554 strcpy(line, previous_line); /* copy storage buffer to line */
8556 read_continued_line = FALSE;
8559 /* if the last character is '\', continue at next line */
8560 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
8562 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
8563 strcpy(previous_line, line); /* copy line to storage buffer */
8565 read_continued_line = TRUE;
8570 /* skip empty lines */
8571 if (line[0] == '\0')
8574 /* extract comment text from comment line */
8577 for (line_ptr = line; *line_ptr; line_ptr++)
8578 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
8581 strcpy(last_comment, line_ptr);
8584 printf("::: found comment '%s' in line %d\n", last_comment, line_nr);
8590 /* extract level title text from line containing level title */
8591 if (line[0] == '\'')
8593 strcpy(level_name, &line[1]);
8595 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
8596 level_name[strlen(level_name) - 1] = '\0';
8599 printf("::: found level name '%s' in line %d\n", level_name, line_nr);
8605 /* skip lines containing only spaces (or empty lines) */
8606 for (line_ptr = line; *line_ptr; line_ptr++)
8607 if (*line_ptr != ' ')
8609 if (*line_ptr == '\0')
8612 /* at this point, we have found a line containing part of a playfield */
8615 printf("::: found playfield row in line %d\n", line_nr);
8618 got_valid_playfield_line = TRUE;
8620 if (!reading_playfield)
8622 reading_playfield = TRUE;
8623 invalid_playfield_char = FALSE;
8625 for (x = 0; x < MAX_LEV_FIELDX; x++)
8626 for (y = 0; y < MAX_LEV_FIELDY; y++)
8627 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
8632 /* start with topmost tile row */
8636 /* skip playfield line if larger row than allowed */
8637 if (y >= MAX_LEV_FIELDY)
8640 /* start with leftmost tile column */
8643 /* read playfield elements from line */
8644 for (line_ptr = line; *line_ptr; line_ptr++)
8646 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
8648 /* stop parsing playfield line if larger column than allowed */
8649 if (x >= MAX_LEV_FIELDX)
8652 if (mapped_sb_element == EL_UNDEFINED)
8654 invalid_playfield_char = TRUE;
8659 level->field[x][y] = mapped_sb_element;
8661 /* continue with next tile column */
8664 level->fieldx = MAX(x, level->fieldx);
8667 if (invalid_playfield_char)
8669 /* if first playfield line, treat invalid lines as comment lines */
8671 reading_playfield = FALSE;
8676 /* continue with next tile row */
8684 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
8685 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
8687 if (!reading_playfield)
8689 level->no_valid_file = TRUE;
8691 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
8696 if (*level_name != '\0')
8698 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
8699 level->name[MAX_LEVEL_NAME_LEN] = '\0';
8702 printf(":1: level name: '%s'\n", level->name);
8705 else if (*last_comment != '\0')
8707 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
8708 level->name[MAX_LEVEL_NAME_LEN] = '\0';
8711 printf(":2: level name: '%s'\n", level->name);
8716 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
8719 /* set all empty fields beyond the border walls to invisible steel wall */
8720 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8722 if ((x == 0 || x == level->fieldx - 1 ||
8723 y == 0 || y == level->fieldy - 1) &&
8724 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
8725 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
8726 level->field, level->fieldx, level->fieldy);
8729 /* set special level settings for Sokoban levels */
8732 level->use_step_counter = TRUE;
8734 if (load_xsb_to_ces)
8737 /* !!! special global settings can now be set in level template !!! */
8739 level->initial_player_stepsize[0] = STEPSIZE_SLOW;
8742 /* fill smaller playfields with padding "beyond border wall" elements */
8743 if (level->fieldx < SCR_FIELDX ||
8744 level->fieldy < SCR_FIELDY)
8746 short field[level->fieldx][level->fieldy];
8747 int new_fieldx = MAX(level->fieldx, SCR_FIELDX);
8748 int new_fieldy = MAX(level->fieldy, SCR_FIELDY);
8749 int pos_fieldx = (new_fieldx - level->fieldx) / 2;
8750 int pos_fieldy = (new_fieldy - level->fieldy) / 2;
8752 /* copy old playfield (which is smaller than the visible area) */
8753 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8754 field[x][y] = level->field[x][y];
8756 /* fill new, larger playfield with "beyond border wall" elements */
8757 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
8758 level->field[x][y] = getMappedElement_SB('_', load_xsb_to_ces);
8760 /* copy the old playfield to the middle of the new playfield */
8761 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8762 level->field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
8764 level->fieldx = new_fieldx;
8765 level->fieldy = new_fieldy;
8768 level->use_custom_template = TRUE;
8775 /* ------------------------------------------------------------------------- */
8776 /* functions for handling native levels */
8777 /* ------------------------------------------------------------------------- */
8779 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
8780 struct LevelFileInfo *level_file_info,
8781 boolean level_info_only)
8783 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
8784 level->no_valid_file = TRUE;
8787 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
8788 struct LevelFileInfo *level_file_info,
8789 boolean level_info_only)
8793 /* determine position of requested level inside level package */
8794 if (level_file_info->packed)
8795 pos = level_file_info->nr - leveldir_current->first_level;
8797 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
8798 level->no_valid_file = TRUE;
8801 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
8803 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
8804 CopyNativeLevel_RND_to_EM(level);
8805 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
8806 CopyNativeLevel_RND_to_SP(level);
8809 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
8811 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
8812 CopyNativeLevel_EM_to_RND(level);
8813 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
8814 CopyNativeLevel_SP_to_RND(level);
8817 void SaveNativeLevel(struct LevelInfo *level)
8819 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
8821 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
8822 char *filename = getLevelFilenameFromBasename(basename);
8824 CopyNativeLevel_RND_to_SP(level);
8825 CopyNativeTape_RND_to_SP(level);
8827 SaveNativeLevel_SP(filename);
8832 /* ------------------------------------------------------------------------- */
8833 /* functions for loading generic level */
8834 /* ------------------------------------------------------------------------- */
8836 static void LoadLevelFromFileInfo(struct LevelInfo *level,
8837 struct LevelFileInfo *level_file_info,
8838 boolean level_info_only)
8840 /* always start with reliable default values */
8841 setLevelInfoToDefaults(level, level_info_only);
8843 switch (level_file_info->type)
8845 case LEVEL_FILE_TYPE_RND:
8846 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
8849 case LEVEL_FILE_TYPE_EM:
8850 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
8851 level->game_engine_type = GAME_ENGINE_TYPE_EM;
8854 case LEVEL_FILE_TYPE_SP:
8855 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
8856 level->game_engine_type = GAME_ENGINE_TYPE_SP;
8859 case LEVEL_FILE_TYPE_DC:
8860 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
8863 case LEVEL_FILE_TYPE_SB:
8864 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
8868 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
8872 /* if level file is invalid, restore level structure to default values */
8873 if (level->no_valid_file)
8875 setLevelInfoToDefaults(level, level_info_only);
8877 level->no_valid_file = TRUE; /* but keep "no valid file" flag */
8880 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
8881 level->game_engine_type = GAME_ENGINE_TYPE_RND;
8883 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
8884 CopyNativeLevel_Native_to_RND(level);
8887 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
8889 static struct LevelFileInfo level_file_info;
8891 /* always start with reliable default values */
8892 setFileInfoToDefaults(&level_file_info);
8894 level_file_info.nr = 0; /* unknown level number */
8895 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
8896 level_file_info.filename = filename;
8898 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
8901 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
8905 if (leveldir_current == NULL) /* only when dumping level */
8908 /* all engine modifications also valid for levels which use latest engine */
8909 if (level->game_version < VERSION_IDENT(3,2,0,5))
8911 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
8912 level->score[SC_TIME_BONUS] /= 10;
8916 leveldir_current->latest_engine = TRUE; /* !!! TEST ONLY !!! */
8919 if (leveldir_current->latest_engine)
8921 /* ---------- use latest game engine ----------------------------------- */
8923 /* For all levels which are forced to use the latest game engine version
8924 (normally all but user contributed, private and undefined levels), set
8925 the game engine version to the actual version; this allows for actual
8926 corrections in the game engine to take effect for existing, converted
8927 levels (from "classic" or other existing games) to make the emulation
8928 of the corresponding game more accurate, while (hopefully) not breaking
8929 existing levels created from other players. */
8931 level->game_version = GAME_VERSION_ACTUAL;
8933 /* Set special EM style gems behaviour: EM style gems slip down from
8934 normal, steel and growing wall. As this is a more fundamental change,
8935 it seems better to set the default behaviour to "off" (as it is more
8936 natural) and make it configurable in the level editor (as a property
8937 of gem style elements). Already existing converted levels (neither
8938 private nor contributed levels) are changed to the new behaviour. */
8940 if (level->file_version < FILE_VERSION_2_0)
8941 level->em_slippery_gems = TRUE;
8946 /* ---------- use game engine the level was created with ----------------- */
8948 /* For all levels which are not forced to use the latest game engine
8949 version (normally user contributed, private and undefined levels),
8950 use the version of the game engine the levels were created for.
8952 Since 2.0.1, the game engine version is now directly stored
8953 in the level file (chunk "VERS"), so there is no need anymore
8954 to set the game version from the file version (except for old,
8955 pre-2.0 levels, where the game version is still taken from the
8956 file format version used to store the level -- see above). */
8958 /* player was faster than enemies in 1.0.0 and before */
8959 if (level->file_version == FILE_VERSION_1_0)
8960 for (i = 0; i < MAX_PLAYERS; i++)
8961 level->initial_player_stepsize[i] = STEPSIZE_FAST;
8963 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
8964 if (level->game_version == VERSION_IDENT(2,0,1,0))
8965 level->em_slippery_gems = TRUE;
8967 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
8968 if (level->game_version < VERSION_IDENT(2,2,0,0))
8969 level->use_spring_bug = TRUE;
8971 if (level->game_version < VERSION_IDENT(3,2,0,5))
8973 /* time orb caused limited time in endless time levels before 3.2.0-5 */
8974 level->use_time_orb_bug = TRUE;
8976 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
8977 level->block_snap_field = FALSE;
8979 /* extra time score was same value as time left score before 3.2.0-5 */
8980 level->extra_time_score = level->score[SC_TIME_BONUS];
8983 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
8984 level->score[SC_TIME_BONUS] /= 10;
8988 if (level->game_version < VERSION_IDENT(3,2,0,7))
8990 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
8991 level->continuous_snapping = FALSE;
8994 /* only few elements were able to actively move into acid before 3.1.0 */
8995 /* trigger settings did not exist before 3.1.0; set to default "any" */
8996 if (level->game_version < VERSION_IDENT(3,1,0,0))
8998 /* correct "can move into acid" settings (all zero in old levels) */
9000 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
9001 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
9003 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
9004 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
9005 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
9006 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
9008 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9009 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
9011 /* correct trigger settings (stored as zero == "none" in old levels) */
9013 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9015 int element = EL_CUSTOM_START + i;
9016 struct ElementInfo *ei = &element_info[element];
9018 for (j = 0; j < ei->num_change_pages; j++)
9020 struct ElementChangeInfo *change = &ei->change_page[j];
9022 change->trigger_player = CH_PLAYER_ANY;
9023 change->trigger_page = CH_PAGE_ANY;
9028 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
9030 int element = EL_CUSTOM_256;
9031 struct ElementInfo *ei = &element_info[element];
9032 struct ElementChangeInfo *change = &ei->change_page[0];
9034 /* This is needed to fix a problem that was caused by a bugfix in function
9035 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
9036 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
9037 not replace walkable elements, but instead just placed the player on it,
9038 without placing the Sokoban field under the player). Unfortunately, this
9039 breaks "Snake Bite" style levels when the snake is halfway through a door
9040 that just closes (the snake head is still alive and can be moved in this
9041 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
9042 player (without Sokoban element) which then gets killed as designed). */
9044 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
9045 strncmp(ei->description, "pause b4 death", 14) == 0) &&
9046 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
9047 change->target_element = EL_PLAYER_1;
9051 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
9052 if (level->game_version < VERSION_IDENT(3,2,5,0))
9054 /* This is needed to fix a problem that was caused by a bugfix in function
9055 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
9056 corrects the behaviour when a custom element changes to another custom
9057 element with a higher element number that has change actions defined.
9058 Normally, only one change per frame is allowed for custom elements.
9059 Therefore, it is checked if a custom element already changed in the
9060 current frame; if it did, subsequent changes are suppressed.
9061 Unfortunately, this is only checked for element changes, but not for
9062 change actions, which are still executed. As the function above loops
9063 through all custom elements from lower to higher, an element change
9064 resulting in a lower CE number won't be checked again, while a target
9065 element with a higher number will also be checked, and potential change
9066 actions will get executed for this CE, too (which is wrong), while
9067 further changes are ignored (which is correct). As this bugfix breaks
9068 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
9069 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
9070 behaviour for existing levels and tapes that make use of this bug */
9072 level->use_action_after_change_bug = TRUE;
9075 /* !!! THIS DOES NOT FIX "Zelda I" (GRAPHICALLY) AND "Alan's FMV" LEVELS */
9076 /* try to detect and fix "Zelda II" levels, which are broken with 3.2.5 */
9078 int element = EL_CUSTOM_16;
9079 struct ElementInfo *ei = &element_info[element];
9081 /* This is needed to fix a problem that was caused by a bugfix in function
9082 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
9083 corrects the behaviour when a custom element changes to another custom
9084 element with a higher element number that has change actions defined.
9085 Normally, only one change per frame is allowed for custom elements.
9086 Therefore, it is checked if a custom element already changed in the
9087 current frame; if it did, subsequent changes are suppressed.
9088 Unfortunately, this is only checked for element changes, but not for
9089 change actions, which are still executed. As the function above loops
9090 through all custom elements from lower to higher, an element change
9091 resulting in a lower CE number won't be checked again, while a target
9092 element with a higher number will also be checked, and potential change
9093 actions will get executed for this CE, too (which is wrong), while
9094 further changes are ignored (which is correct). As this bugfix breaks
9095 Zelda II (but no other levels), allow the previous, incorrect behaviour
9096 for this outstanding level set to not break the game or existing tapes */
9098 if (strncmp(leveldir_current->identifier, "zelda2", 6) == 0 ||
9099 strncmp(ei->description, "scanline - row 1", 16) == 0)
9100 level->use_action_after_change_bug = TRUE;
9104 /* not centering level after relocating player was default only in 3.2.3 */
9105 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
9106 level->shifted_relocation = TRUE;
9108 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
9109 if (level->game_version < VERSION_IDENT(3,2,6,0))
9110 level->em_explodes_by_fire = TRUE;
9113 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
9117 /* map custom element change events that have changed in newer versions
9118 (these following values were accidentally changed in version 3.0.1)
9119 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
9120 if (level->game_version <= VERSION_IDENT(3,0,0,0))
9122 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9124 int element = EL_CUSTOM_START + i;
9126 /* order of checking and copying events to be mapped is important */
9127 /* (do not change the start and end value -- they are constant) */
9128 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
9130 if (HAS_CHANGE_EVENT(element, j - 2))
9132 SET_CHANGE_EVENT(element, j - 2, FALSE);
9133 SET_CHANGE_EVENT(element, j, TRUE);
9137 /* order of checking and copying events to be mapped is important */
9138 /* (do not change the start and end value -- they are constant) */
9139 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
9141 if (HAS_CHANGE_EVENT(element, j - 1))
9143 SET_CHANGE_EVENT(element, j - 1, FALSE);
9144 SET_CHANGE_EVENT(element, j, TRUE);
9150 /* initialize "can_change" field for old levels with only one change page */
9151 if (level->game_version <= VERSION_IDENT(3,0,2,0))
9153 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9155 int element = EL_CUSTOM_START + i;
9157 if (CAN_CHANGE(element))
9158 element_info[element].change->can_change = TRUE;
9162 /* correct custom element values (for old levels without these options) */
9163 if (level->game_version < VERSION_IDENT(3,1,1,0))
9165 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9167 int element = EL_CUSTOM_START + i;
9168 struct ElementInfo *ei = &element_info[element];
9170 if (ei->access_direction == MV_NO_DIRECTION)
9171 ei->access_direction = MV_ALL_DIRECTIONS;
9175 /* correct custom element values (fix invalid values for all versions) */
9178 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9180 int element = EL_CUSTOM_START + i;
9181 struct ElementInfo *ei = &element_info[element];
9183 for (j = 0; j < ei->num_change_pages; j++)
9185 struct ElementChangeInfo *change = &ei->change_page[j];
9187 if (change->trigger_player == CH_PLAYER_NONE)
9188 change->trigger_player = CH_PLAYER_ANY;
9190 if (change->trigger_side == CH_SIDE_NONE)
9191 change->trigger_side = CH_SIDE_ANY;
9196 /* initialize "can_explode" field for old levels which did not store this */
9197 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
9198 if (level->game_version <= VERSION_IDENT(3,1,0,0))
9200 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9202 int element = EL_CUSTOM_START + i;
9204 if (EXPLODES_1X1_OLD(element))
9205 element_info[element].explosion_type = EXPLODES_1X1;
9207 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
9208 EXPLODES_SMASHED(element) ||
9209 EXPLODES_IMPACT(element)));
9213 /* correct previously hard-coded move delay values for maze runner style */
9214 if (level->game_version < VERSION_IDENT(3,1,1,0))
9216 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9218 int element = EL_CUSTOM_START + i;
9220 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
9222 /* previously hard-coded and therefore ignored */
9223 element_info[element].move_delay_fixed = 9;
9224 element_info[element].move_delay_random = 0;
9229 /* map elements that have changed in newer versions */
9230 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
9231 level->game_version);
9232 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
9233 for (x = 0; x < 3; x++)
9234 for (y = 0; y < 3; y++)
9235 level->yamyam_content[i].e[x][y] =
9236 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
9237 level->game_version);
9239 /* initialize element properties for level editor etc. */
9240 InitElementPropertiesEngine(level->game_version);
9241 InitElementPropertiesAfterLoading(level->game_version);
9242 InitElementPropertiesGfxElement();
9245 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
9249 /* map elements that have changed in newer versions */
9250 for (y = 0; y < level->fieldy; y++)
9251 for (x = 0; x < level->fieldx; x++)
9252 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
9253 level->game_version);
9255 /* copy elements to runtime playfield array */
9256 for (x = 0; x < MAX_LEV_FIELDX; x++)
9257 for (y = 0; y < MAX_LEV_FIELDY; y++)
9258 Feld[x][y] = level->field[x][y];
9260 /* initialize level size variables for faster access */
9261 lev_fieldx = level->fieldx;
9262 lev_fieldy = level->fieldy;
9264 /* determine border element for this level */
9265 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
9266 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
9271 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
9273 struct LevelFileInfo *level_file_info = &level->file_info;
9275 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
9276 CopyNativeLevel_RND_to_Native(level);
9279 void LoadLevelTemplate(int nr)
9283 setLevelFileInfo(&level_template.file_info, nr);
9284 filename = level_template.file_info.filename;
9286 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
9288 LoadLevel_InitVersion(&level_template, filename);
9289 LoadLevel_InitElements(&level_template, filename);
9291 ActivateLevelTemplate();
9294 void LoadLevel(int nr)
9298 setLevelFileInfo(&level.file_info, nr);
9299 filename = level.file_info.filename;
9301 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
9303 if (level.use_custom_template)
9304 LoadLevelTemplate(-1);
9306 LoadLevel_InitVersion(&level, filename);
9307 LoadLevel_InitElements(&level, filename);
9308 LoadLevel_InitPlayfield(&level, filename);
9310 LoadLevel_InitNativeEngines(&level, filename);
9313 void LoadLevelInfoOnly(int nr)
9319 setLevelFileInfo(&level.file_info, nr);
9321 filename = level.file_info.filename;
9324 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
9327 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
9331 chunk_size += putFileVersion(file, level->file_version);
9332 chunk_size += putFileVersion(file, level->game_version);
9337 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
9341 chunk_size += putFile16BitBE(file, level->creation_date.year);
9342 chunk_size += putFile8Bit(file, level->creation_date.month);
9343 chunk_size += putFile8Bit(file, level->creation_date.day);
9349 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
9353 putFile8Bit(file, level->fieldx);
9354 putFile8Bit(file, level->fieldy);
9356 putFile16BitBE(file, level->time);
9357 putFile16BitBE(file, level->gems_needed);
9359 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
9360 putFile8Bit(file, level->name[i]);
9362 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
9363 putFile8Bit(file, level->score[i]);
9365 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
9366 for (y = 0; y < 3; y++)
9367 for (x = 0; x < 3; x++)
9368 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
9369 level->yamyam_content[i].e[x][y]));
9370 putFile8Bit(file, level->amoeba_speed);
9371 putFile8Bit(file, level->time_magic_wall);
9372 putFile8Bit(file, level->time_wheel);
9373 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
9374 level->amoeba_content));
9375 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
9376 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
9377 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
9378 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
9380 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
9382 putFile8Bit(file, (level->block_last_field ? 1 : 0));
9383 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
9384 putFile32BitBE(file, level->can_move_into_acid_bits);
9385 putFile8Bit(file, level->dont_collide_with_bits);
9387 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
9388 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
9390 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
9391 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
9392 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
9394 putFile8Bit(file, level->game_engine_type);
9396 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
9400 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
9405 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
9406 chunk_size += putFile8Bit(file, level->name[i]);
9411 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
9416 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
9417 chunk_size += putFile8Bit(file, level->author[i]);
9423 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
9428 for (y = 0; y < level->fieldy; y++)
9429 for (x = 0; x < level->fieldx; x++)
9430 if (level->encoding_16bit_field)
9431 chunk_size += putFile16BitBE(file, level->field[x][y]);
9433 chunk_size += putFile8Bit(file, level->field[x][y]);
9439 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
9444 for (y = 0; y < level->fieldy; y++)
9445 for (x = 0; x < level->fieldx; x++)
9446 chunk_size += putFile16BitBE(file, level->field[x][y]);
9452 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
9456 putFile8Bit(file, EL_YAMYAM);
9457 putFile8Bit(file, level->num_yamyam_contents);
9458 putFile8Bit(file, 0);
9459 putFile8Bit(file, 0);
9461 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
9462 for (y = 0; y < 3; y++)
9463 for (x = 0; x < 3; x++)
9464 if (level->encoding_16bit_field)
9465 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
9467 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
9472 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
9475 int num_contents, content_xsize, content_ysize;
9476 int content_array[MAX_ELEMENT_CONTENTS][3][3];
9478 if (element == EL_YAMYAM)
9480 num_contents = level->num_yamyam_contents;
9484 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
9485 for (y = 0; y < 3; y++)
9486 for (x = 0; x < 3; x++)
9487 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
9489 else if (element == EL_BD_AMOEBA)
9495 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
9496 for (y = 0; y < 3; y++)
9497 for (x = 0; x < 3; x++)
9498 content_array[i][x][y] = EL_EMPTY;
9499 content_array[0][0][0] = level->amoeba_content;
9503 /* chunk header already written -- write empty chunk data */
9504 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
9506 Error(ERR_WARN, "cannot save content for element '%d'", element);
9510 putFile16BitBE(file, element);
9511 putFile8Bit(file, num_contents);
9512 putFile8Bit(file, content_xsize);
9513 putFile8Bit(file, content_ysize);
9515 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
9517 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
9518 for (y = 0; y < 3; y++)
9519 for (x = 0; x < 3; x++)
9520 putFile16BitBE(file, content_array[i][x][y]);
9525 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
9527 int envelope_nr = element - EL_ENVELOPE_1;
9528 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
9532 chunk_size += putFile16BitBE(file, element);
9533 chunk_size += putFile16BitBE(file, envelope_len);
9534 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
9535 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
9537 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
9538 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
9540 for (i = 0; i < envelope_len; i++)
9541 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
9548 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
9549 int num_changed_custom_elements)
9553 putFile16BitBE(file, num_changed_custom_elements);
9555 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9557 int element = EL_CUSTOM_START + i;
9559 struct ElementInfo *ei = &element_info[element];
9561 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
9563 if (check < num_changed_custom_elements)
9565 putFile16BitBE(file, element);
9566 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
9573 if (check != num_changed_custom_elements) /* should not happen */
9574 Error(ERR_WARN, "inconsistent number of custom element properties");
9579 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
9580 int num_changed_custom_elements)
9584 putFile16BitBE(file, num_changed_custom_elements);
9586 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9588 int element = EL_CUSTOM_START + i;
9590 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
9592 if (check < num_changed_custom_elements)
9594 putFile16BitBE(file, element);
9595 putFile16BitBE(file, element_info[element].change->target_element);
9602 if (check != num_changed_custom_elements) /* should not happen */
9603 Error(ERR_WARN, "inconsistent number of custom target elements");
9608 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
9609 int num_changed_custom_elements)
9611 int i, j, x, y, check = 0;
9613 putFile16BitBE(file, num_changed_custom_elements);
9615 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9617 int element = EL_CUSTOM_START + i;
9618 struct ElementInfo *ei = &element_info[element];
9620 if (ei->modified_settings)
9622 if (check < num_changed_custom_elements)
9624 putFile16BitBE(file, element);
9626 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
9627 putFile8Bit(file, ei->description[j]);
9629 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
9631 /* some free bytes for future properties and padding */
9632 WriteUnusedBytesToFile(file, 7);
9634 putFile8Bit(file, ei->use_gfx_element);
9635 putFile16BitBE(file, ei->gfx_element_initial);
9637 putFile8Bit(file, ei->collect_score_initial);
9638 putFile8Bit(file, ei->collect_count_initial);
9640 putFile16BitBE(file, ei->push_delay_fixed);
9641 putFile16BitBE(file, ei->push_delay_random);
9642 putFile16BitBE(file, ei->move_delay_fixed);
9643 putFile16BitBE(file, ei->move_delay_random);
9645 putFile16BitBE(file, ei->move_pattern);
9646 putFile8Bit(file, ei->move_direction_initial);
9647 putFile8Bit(file, ei->move_stepsize);
9649 for (y = 0; y < 3; y++)
9650 for (x = 0; x < 3; x++)
9651 putFile16BitBE(file, ei->content.e[x][y]);
9653 putFile32BitBE(file, ei->change->events);
9655 putFile16BitBE(file, ei->change->target_element);
9657 putFile16BitBE(file, ei->change->delay_fixed);
9658 putFile16BitBE(file, ei->change->delay_random);
9659 putFile16BitBE(file, ei->change->delay_frames);
9661 putFile16BitBE(file, ei->change->initial_trigger_element);
9663 putFile8Bit(file, ei->change->explode);
9664 putFile8Bit(file, ei->change->use_target_content);
9665 putFile8Bit(file, ei->change->only_if_complete);
9666 putFile8Bit(file, ei->change->use_random_replace);
9668 putFile8Bit(file, ei->change->random_percentage);
9669 putFile8Bit(file, ei->change->replace_when);
9671 for (y = 0; y < 3; y++)
9672 for (x = 0; x < 3; x++)
9673 putFile16BitBE(file, ei->change->content.e[x][y]);
9675 putFile8Bit(file, ei->slippery_type);
9677 /* some free bytes for future properties and padding */
9678 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
9685 if (check != num_changed_custom_elements) /* should not happen */
9686 Error(ERR_WARN, "inconsistent number of custom element properties");
9691 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
9693 struct ElementInfo *ei = &element_info[element];
9696 /* ---------- custom element base property values (96 bytes) ------------- */
9698 putFile16BitBE(file, element);
9700 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
9701 putFile8Bit(file, ei->description[i]);
9703 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
9705 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
9707 putFile8Bit(file, ei->num_change_pages);
9709 putFile16BitBE(file, ei->ce_value_fixed_initial);
9710 putFile16BitBE(file, ei->ce_value_random_initial);
9711 putFile8Bit(file, ei->use_last_ce_value);
9713 putFile8Bit(file, ei->use_gfx_element);
9714 putFile16BitBE(file, ei->gfx_element_initial);
9716 putFile8Bit(file, ei->collect_score_initial);
9717 putFile8Bit(file, ei->collect_count_initial);
9719 putFile8Bit(file, ei->drop_delay_fixed);
9720 putFile8Bit(file, ei->push_delay_fixed);
9721 putFile8Bit(file, ei->drop_delay_random);
9722 putFile8Bit(file, ei->push_delay_random);
9723 putFile16BitBE(file, ei->move_delay_fixed);
9724 putFile16BitBE(file, ei->move_delay_random);
9726 /* bits 0 - 15 of "move_pattern" ... */
9727 putFile16BitBE(file, ei->move_pattern & 0xffff);
9728 putFile8Bit(file, ei->move_direction_initial);
9729 putFile8Bit(file, ei->move_stepsize);
9731 putFile8Bit(file, ei->slippery_type);
9733 for (y = 0; y < 3; y++)
9734 for (x = 0; x < 3; x++)
9735 putFile16BitBE(file, ei->content.e[x][y]);
9737 putFile16BitBE(file, ei->move_enter_element);
9738 putFile16BitBE(file, ei->move_leave_element);
9739 putFile8Bit(file, ei->move_leave_type);
9741 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
9742 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
9744 putFile8Bit(file, ei->access_direction);
9746 putFile8Bit(file, ei->explosion_delay);
9747 putFile8Bit(file, ei->ignition_delay);
9748 putFile8Bit(file, ei->explosion_type);
9750 /* some free bytes for future custom property values and padding */
9751 WriteUnusedBytesToFile(file, 1);
9753 /* ---------- change page property values (48 bytes) --------------------- */
9755 for (i = 0; i < ei->num_change_pages; i++)
9757 struct ElementChangeInfo *change = &ei->change_page[i];
9758 unsigned int event_bits;
9760 /* bits 0 - 31 of "has_event[]" ... */
9762 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
9763 if (change->has_event[j])
9764 event_bits |= (1 << j);
9765 putFile32BitBE(file, event_bits);
9767 putFile16BitBE(file, change->target_element);
9769 putFile16BitBE(file, change->delay_fixed);
9770 putFile16BitBE(file, change->delay_random);
9771 putFile16BitBE(file, change->delay_frames);
9773 putFile16BitBE(file, change->initial_trigger_element);
9775 putFile8Bit(file, change->explode);
9776 putFile8Bit(file, change->use_target_content);
9777 putFile8Bit(file, change->only_if_complete);
9778 putFile8Bit(file, change->use_random_replace);
9780 putFile8Bit(file, change->random_percentage);
9781 putFile8Bit(file, change->replace_when);
9783 for (y = 0; y < 3; y++)
9784 for (x = 0; x < 3; x++)
9785 putFile16BitBE(file, change->target_content.e[x][y]);
9787 putFile8Bit(file, change->can_change);
9789 putFile8Bit(file, change->trigger_side);
9791 putFile8Bit(file, change->trigger_player);
9792 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
9793 log_2(change->trigger_page)));
9795 putFile8Bit(file, change->has_action);
9796 putFile8Bit(file, change->action_type);
9797 putFile8Bit(file, change->action_mode);
9798 putFile16BitBE(file, change->action_arg);
9800 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
9802 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
9803 if (change->has_event[j])
9804 event_bits |= (1 << (j - 32));
9805 putFile8Bit(file, event_bits);
9811 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
9813 struct ElementInfo *ei = &element_info[element];
9814 struct ElementGroupInfo *group = ei->group;
9817 putFile16BitBE(file, element);
9819 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
9820 putFile8Bit(file, ei->description[i]);
9822 putFile8Bit(file, group->num_elements);
9824 putFile8Bit(file, ei->use_gfx_element);
9825 putFile16BitBE(file, ei->gfx_element_initial);
9827 putFile8Bit(file, group->choice_mode);
9829 /* some free bytes for future values and padding */
9830 WriteUnusedBytesToFile(file, 3);
9832 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
9833 putFile16BitBE(file, group->element[i]);
9837 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
9838 boolean write_element)
9840 int save_type = entry->save_type;
9841 int data_type = entry->data_type;
9842 int conf_type = entry->conf_type;
9843 int byte_mask = conf_type & CONF_MASK_BYTES;
9844 int element = entry->element;
9845 int default_value = entry->default_value;
9847 boolean modified = FALSE;
9849 if (byte_mask != CONF_MASK_MULTI_BYTES)
9851 void *value_ptr = entry->value;
9852 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
9855 /* check if any settings have been modified before saving them */
9856 if (value != default_value)
9859 /* do not save if explicitly told or if unmodified default settings */
9860 if ((save_type == SAVE_CONF_NEVER) ||
9861 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
9865 num_bytes += putFile16BitBE(file, element);
9867 num_bytes += putFile8Bit(file, conf_type);
9868 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
9869 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
9870 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
9873 else if (data_type == TYPE_STRING)
9875 char *default_string = entry->default_string;
9876 char *string = (char *)(entry->value);
9877 int string_length = strlen(string);
9880 /* check if any settings have been modified before saving them */
9881 if (!strEqual(string, default_string))
9884 /* do not save if explicitly told or if unmodified default settings */
9885 if ((save_type == SAVE_CONF_NEVER) ||
9886 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
9890 num_bytes += putFile16BitBE(file, element);
9892 num_bytes += putFile8Bit(file, conf_type);
9893 num_bytes += putFile16BitBE(file, string_length);
9895 for (i = 0; i < string_length; i++)
9896 num_bytes += putFile8Bit(file, string[i]);
9898 else if (data_type == TYPE_ELEMENT_LIST)
9900 int *element_array = (int *)(entry->value);
9901 int num_elements = *(int *)(entry->num_entities);
9904 /* check if any settings have been modified before saving them */
9905 for (i = 0; i < num_elements; i++)
9906 if (element_array[i] != default_value)
9909 /* do not save if explicitly told or if unmodified default settings */
9910 if ((save_type == SAVE_CONF_NEVER) ||
9911 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
9915 num_bytes += putFile16BitBE(file, element);
9917 num_bytes += putFile8Bit(file, conf_type);
9918 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
9920 for (i = 0; i < num_elements; i++)
9921 num_bytes += putFile16BitBE(file, element_array[i]);
9923 else if (data_type == TYPE_CONTENT_LIST)
9925 struct Content *content = (struct Content *)(entry->value);
9926 int num_contents = *(int *)(entry->num_entities);
9929 /* check if any settings have been modified before saving them */
9930 for (i = 0; i < num_contents; i++)
9931 for (y = 0; y < 3; y++)
9932 for (x = 0; x < 3; x++)
9933 if (content[i].e[x][y] != default_value)
9936 /* do not save if explicitly told or if unmodified default settings */
9937 if ((save_type == SAVE_CONF_NEVER) ||
9938 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
9942 num_bytes += putFile16BitBE(file, element);
9944 num_bytes += putFile8Bit(file, conf_type);
9945 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
9947 for (i = 0; i < num_contents; i++)
9948 for (y = 0; y < 3; y++)
9949 for (x = 0; x < 3; x++)
9950 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
9956 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
9961 li = *level; /* copy level data into temporary buffer */
9963 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
9964 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
9969 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
9974 li = *level; /* copy level data into temporary buffer */
9976 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
9977 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
9982 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
9984 int envelope_nr = element - EL_ENVELOPE_1;
9988 chunk_size += putFile16BitBE(file, element);
9990 /* copy envelope data into temporary buffer */
9991 xx_envelope = level->envelope[envelope_nr];
9993 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
9994 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
9999 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
10001 struct ElementInfo *ei = &element_info[element];
10002 int chunk_size = 0;
10005 chunk_size += putFile16BitBE(file, element);
10007 xx_ei = *ei; /* copy element data into temporary buffer */
10009 /* set default description string for this specific element */
10010 strcpy(xx_default_description, getDefaultElementDescription(ei));
10013 /* set (fixed) number of content areas (may be wrong by broken level file) */
10014 /* (this is now directly corrected for broken level files after loading) */
10015 xx_num_contents = 1;
10018 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
10019 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
10021 for (i = 0; i < ei->num_change_pages; i++)
10023 struct ElementChangeInfo *change = &ei->change_page[i];
10025 xx_current_change_page = i;
10027 xx_change = *change; /* copy change data into temporary buffer */
10030 setEventBitsFromEventFlags(change);
10032 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
10033 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
10040 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
10042 struct ElementInfo *ei = &element_info[element];
10043 struct ElementGroupInfo *group = ei->group;
10044 int chunk_size = 0;
10047 chunk_size += putFile16BitBE(file, element);
10049 xx_ei = *ei; /* copy element data into temporary buffer */
10050 xx_group = *group; /* copy group data into temporary buffer */
10052 /* set default description string for this specific element */
10053 strcpy(xx_default_description, getDefaultElementDescription(ei));
10055 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
10056 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
10061 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
10067 if (!(file = fopen(filename, MODE_WRITE)))
10069 Error(ERR_WARN, "cannot save level file '%s'", filename);
10073 level->file_version = FILE_VERSION_ACTUAL;
10074 level->game_version = GAME_VERSION_ACTUAL;
10076 level->creation_date = getCurrentDate();
10078 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10079 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
10081 chunk_size = SaveLevel_VERS(NULL, level);
10082 putFileChunkBE(file, "VERS", chunk_size);
10083 SaveLevel_VERS(file, level);
10085 chunk_size = SaveLevel_DATE(NULL, level);
10086 putFileChunkBE(file, "DATE", chunk_size);
10087 SaveLevel_DATE(file, level);
10089 chunk_size = SaveLevel_NAME(NULL, level);
10090 putFileChunkBE(file, "NAME", chunk_size);
10091 SaveLevel_NAME(file, level);
10093 chunk_size = SaveLevel_AUTH(NULL, level);
10094 putFileChunkBE(file, "AUTH", chunk_size);
10095 SaveLevel_AUTH(file, level);
10097 chunk_size = SaveLevel_INFO(NULL, level);
10098 putFileChunkBE(file, "INFO", chunk_size);
10099 SaveLevel_INFO(file, level);
10101 chunk_size = SaveLevel_BODY(NULL, level);
10102 putFileChunkBE(file, "BODY", chunk_size);
10103 SaveLevel_BODY(file, level);
10105 chunk_size = SaveLevel_ELEM(NULL, level);
10106 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
10108 putFileChunkBE(file, "ELEM", chunk_size);
10109 SaveLevel_ELEM(file, level);
10112 for (i = 0; i < NUM_ENVELOPES; i++)
10114 int element = EL_ENVELOPE_1 + i;
10116 chunk_size = SaveLevel_NOTE(NULL, level, element);
10117 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
10119 putFileChunkBE(file, "NOTE", chunk_size);
10120 SaveLevel_NOTE(file, level, element);
10124 /* if not using template level, check for non-default custom/group elements */
10125 if (!level->use_custom_template)
10127 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10129 int element = EL_CUSTOM_START + i;
10131 chunk_size = SaveLevel_CUSX(NULL, level, element);
10132 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
10134 putFileChunkBE(file, "CUSX", chunk_size);
10135 SaveLevel_CUSX(file, level, element);
10139 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10141 int element = EL_GROUP_START + i;
10143 chunk_size = SaveLevel_GRPX(NULL, level, element);
10144 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
10146 putFileChunkBE(file, "GRPX", chunk_size);
10147 SaveLevel_GRPX(file, level, element);
10154 SetFilePermissions(filename, PERMS_PRIVATE);
10157 void SaveLevel(int nr)
10159 char *filename = getDefaultLevelFilename(nr);
10161 SaveLevelFromFilename(&level, filename);
10164 void SaveLevelTemplate()
10166 char *filename = getDefaultLevelFilename(-1);
10168 SaveLevelFromFilename(&level, filename);
10171 boolean SaveLevelChecked(int nr)
10173 char *filename = getDefaultLevelFilename(nr);
10174 boolean new_level = !fileExists(filename);
10175 boolean level_saved = FALSE;
10177 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
10182 Request("Level saved!", REQ_CONFIRM);
10184 level_saved = TRUE;
10187 return level_saved;
10190 void DumpLevel(struct LevelInfo *level)
10192 if (level->no_valid_file)
10194 Error(ERR_WARN, "cannot dump -- no valid level file found");
10199 printf_line("-", 79);
10200 printf("Level xxx (file version %08d, game version %08d)\n",
10201 level->file_version, level->game_version);
10202 printf_line("-", 79);
10204 printf("Level author: '%s'\n", level->author);
10205 printf("Level title: '%s'\n", level->name);
10207 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
10209 printf("Level time: %d seconds\n", level->time);
10210 printf("Gems needed: %d\n", level->gems_needed);
10212 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
10213 printf("Time for wheel: %d seconds\n", level->time_wheel);
10214 printf("Time for light: %d seconds\n", level->time_light);
10215 printf("Time for timegate: %d seconds\n", level->time_timegate);
10217 printf("Amoeba speed: %d\n", level->amoeba_speed);
10220 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
10221 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
10222 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
10223 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
10224 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
10226 printf_line("-", 79);
10230 /* ========================================================================= */
10231 /* tape file functions */
10232 /* ========================================================================= */
10234 static void setTapeInfoToDefaults()
10238 /* always start with reliable default values (empty tape) */
10241 /* default values (also for pre-1.2 tapes) with only the first player */
10242 tape.player_participates[0] = TRUE;
10243 for (i = 1; i < MAX_PLAYERS; i++)
10244 tape.player_participates[i] = FALSE;
10246 /* at least one (default: the first) player participates in every tape */
10247 tape.num_participating_players = 1;
10249 tape.level_nr = level_nr;
10251 tape.changed = FALSE;
10253 tape.recording = FALSE;
10254 tape.playing = FALSE;
10255 tape.pausing = FALSE;
10257 tape.no_valid_file = FALSE;
10262 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
10264 tape->file_version = getFileVersion(file);
10265 tape->game_version = getFileVersion(file);
10270 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
10274 tape->random_seed = getFile32BitBE(file);
10275 tape->date = getFile32BitBE(file);
10276 tape->length = getFile32BitBE(file);
10278 /* read header fields that are new since version 1.2 */
10279 if (tape->file_version >= FILE_VERSION_1_2)
10281 byte store_participating_players = getFile8Bit(file);
10282 int engine_version;
10284 /* since version 1.2, tapes store which players participate in the tape */
10285 tape->num_participating_players = 0;
10286 for (i = 0; i < MAX_PLAYERS; i++)
10288 tape->player_participates[i] = FALSE;
10290 if (store_participating_players & (1 << i))
10292 tape->player_participates[i] = TRUE;
10293 tape->num_participating_players++;
10297 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
10299 engine_version = getFileVersion(file);
10300 if (engine_version > 0)
10301 tape->engine_version = engine_version;
10303 tape->engine_version = tape->game_version;
10309 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
10311 int level_identifier_size;
10314 level_identifier_size = getFile16BitBE(file);
10316 tape->level_identifier =
10317 checked_realloc(tape->level_identifier, level_identifier_size);
10319 for (i = 0; i < level_identifier_size; i++)
10320 tape->level_identifier[i] = getFile8Bit(file);
10322 tape->level_nr = getFile16BitBE(file);
10324 chunk_size = 2 + level_identifier_size + 2;
10329 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
10332 int chunk_size_expected =
10333 (tape->num_participating_players + 1) * tape->length;
10335 if (chunk_size_expected != chunk_size)
10337 ReadUnusedBytesFromFile(file, chunk_size);
10338 return chunk_size_expected;
10341 for (i = 0; i < tape->length; i++)
10343 if (i >= MAX_TAPE_LEN)
10346 for (j = 0; j < MAX_PLAYERS; j++)
10348 tape->pos[i].action[j] = MV_NONE;
10350 if (tape->player_participates[j])
10351 tape->pos[i].action[j] = getFile8Bit(file);
10354 tape->pos[i].delay = getFile8Bit(file);
10356 if (tape->file_version == FILE_VERSION_1_0)
10358 /* eliminate possible diagonal moves in old tapes */
10359 /* this is only for backward compatibility */
10361 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
10362 byte action = tape->pos[i].action[0];
10363 int k, num_moves = 0;
10365 for (k = 0; k<4; k++)
10367 if (action & joy_dir[k])
10369 tape->pos[i + num_moves].action[0] = joy_dir[k];
10371 tape->pos[i + num_moves].delay = 0;
10380 tape->length += num_moves;
10383 else if (tape->file_version < FILE_VERSION_2_0)
10385 /* convert pre-2.0 tapes to new tape format */
10387 if (tape->pos[i].delay > 1)
10390 tape->pos[i + 1] = tape->pos[i];
10391 tape->pos[i + 1].delay = 1;
10394 for (j = 0; j < MAX_PLAYERS; j++)
10395 tape->pos[i].action[j] = MV_NONE;
10396 tape->pos[i].delay--;
10403 if (checkEndOfFile(file))
10407 if (i != tape->length)
10408 chunk_size = (tape->num_participating_players + 1) * i;
10415 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
10417 tape->file_version = getFileVersion(file);
10418 tape->game_version = getFileVersion(file);
10423 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
10427 tape->random_seed = getFile32BitBE(file);
10428 tape->date = getFile32BitBE(file);
10429 tape->length = getFile32BitBE(file);
10431 /* read header fields that are new since version 1.2 */
10432 if (tape->file_version >= FILE_VERSION_1_2)
10434 byte store_participating_players = getFile8Bit(file);
10435 int engine_version;
10437 /* since version 1.2, tapes store which players participate in the tape */
10438 tape->num_participating_players = 0;
10439 for (i = 0; i < MAX_PLAYERS; i++)
10441 tape->player_participates[i] = FALSE;
10443 if (store_participating_players & (1 << i))
10445 tape->player_participates[i] = TRUE;
10446 tape->num_participating_players++;
10450 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
10452 engine_version = getFileVersion(file);
10453 if (engine_version > 0)
10454 tape->engine_version = engine_version;
10456 tape->engine_version = tape->game_version;
10462 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
10464 int level_identifier_size;
10467 level_identifier_size = getFile16BitBE(file);
10469 tape->level_identifier =
10470 checked_realloc(tape->level_identifier, level_identifier_size);
10472 for (i = 0; i < level_identifier_size; i++)
10473 tape->level_identifier[i] = getFile8Bit(file);
10475 tape->level_nr = getFile16BitBE(file);
10477 chunk_size = 2 + level_identifier_size + 2;
10482 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
10485 int chunk_size_expected =
10486 (tape->num_participating_players + 1) * tape->length;
10488 if (chunk_size_expected != chunk_size)
10490 ReadUnusedBytesFromFile(file, chunk_size);
10491 return chunk_size_expected;
10494 for (i = 0; i < tape->length; i++)
10496 if (i >= MAX_TAPE_LEN)
10499 for (j = 0; j < MAX_PLAYERS; j++)
10501 tape->pos[i].action[j] = MV_NONE;
10503 if (tape->player_participates[j])
10504 tape->pos[i].action[j] = getFile8Bit(file);
10507 tape->pos[i].delay = getFile8Bit(file);
10509 if (tape->file_version == FILE_VERSION_1_0)
10511 /* eliminate possible diagonal moves in old tapes */
10512 /* this is only for backward compatibility */
10514 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
10515 byte action = tape->pos[i].action[0];
10516 int k, num_moves = 0;
10518 for (k = 0; k<4; k++)
10520 if (action & joy_dir[k])
10522 tape->pos[i + num_moves].action[0] = joy_dir[k];
10524 tape->pos[i + num_moves].delay = 0;
10533 tape->length += num_moves;
10536 else if (tape->file_version < FILE_VERSION_2_0)
10538 /* convert pre-2.0 tapes to new tape format */
10540 if (tape->pos[i].delay > 1)
10543 tape->pos[i + 1] = tape->pos[i];
10544 tape->pos[i + 1].delay = 1;
10547 for (j = 0; j < MAX_PLAYERS; j++)
10548 tape->pos[i].action[j] = MV_NONE;
10549 tape->pos[i].delay--;
10560 if (i != tape->length)
10561 chunk_size = (tape->num_participating_players + 1) * i;
10570 void LoadTape_SokobanSolution(char *filename)
10573 int move_delay = TILESIZE / level.initial_player_stepsize[0];
10575 if (!(file = openFile(filename, MODE_READ)))
10577 tape.no_valid_file = TRUE;
10582 while (!checkEndOfFile(file))
10584 unsigned char c = getByteFromFile(file);
10586 if (checkEndOfFile(file))
10593 tape.pos[tape.length].action[0] = MV_UP;
10594 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10600 tape.pos[tape.length].action[0] = MV_DOWN;
10601 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10607 tape.pos[tape.length].action[0] = MV_LEFT;
10608 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10614 tape.pos[tape.length].action[0] = MV_RIGHT;
10615 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10623 /* ignore white-space characters */
10627 tape.no_valid_file = TRUE;
10629 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
10637 if (tape.no_valid_file)
10640 tape.length_seconds = GetTapeLength();
10645 void LoadTape_SokobanSolution(char *filename)
10648 int move_delay = TILESIZE / level.initial_player_stepsize[0];
10650 if (!(file = fopen(filename, MODE_READ)))
10652 tape.no_valid_file = TRUE;
10657 while (!feof(file))
10659 unsigned char c = fgetc(file);
10668 tape.pos[tape.length].action[0] = MV_UP;
10669 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10675 tape.pos[tape.length].action[0] = MV_DOWN;
10676 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10682 tape.pos[tape.length].action[0] = MV_LEFT;
10683 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10689 tape.pos[tape.length].action[0] = MV_RIGHT;
10690 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10698 /* ignore white-space characters */
10702 tape.no_valid_file = TRUE;
10704 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
10712 if (tape.no_valid_file)
10715 tape.length_seconds = GetTapeLength();
10722 void LoadTapeFromFilename(char *filename)
10724 char cookie[MAX_LINE_LEN];
10725 char chunk_name[CHUNK_ID_LEN + 1];
10729 /* always start with reliable default values */
10730 setTapeInfoToDefaults();
10732 if (strSuffix(filename, ".sln"))
10734 LoadTape_SokobanSolution(filename);
10739 if (!(file = openFile(filename, MODE_READ)))
10741 tape.no_valid_file = TRUE;
10746 getFileChunkBE(file, chunk_name, NULL);
10747 if (strEqual(chunk_name, "RND1"))
10749 getFile32BitBE(file); /* not used */
10751 getFileChunkBE(file, chunk_name, NULL);
10752 if (!strEqual(chunk_name, "TAPE"))
10754 tape.no_valid_file = TRUE;
10756 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
10763 else /* check for pre-2.0 file format with cookie string */
10765 strcpy(cookie, chunk_name);
10766 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10768 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10769 cookie[strlen(cookie) - 1] = '\0';
10771 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
10773 tape.no_valid_file = TRUE;
10775 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
10782 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
10784 tape.no_valid_file = TRUE;
10786 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
10793 /* pre-2.0 tape files have no game version, so use file version here */
10794 tape.game_version = tape.file_version;
10797 if (tape.file_version < FILE_VERSION_1_2)
10799 /* tape files from versions before 1.2.0 without chunk structure */
10800 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
10801 LoadTape_BODY(file, 2 * tape.length, &tape);
10809 int (*loader)(File *, int, struct TapeInfo *);
10813 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
10814 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
10815 { "INFO", -1, LoadTape_INFO },
10816 { "BODY", -1, LoadTape_BODY },
10820 while (getFileChunkBE(file, chunk_name, &chunk_size))
10824 while (chunk_info[i].name != NULL &&
10825 !strEqual(chunk_name, chunk_info[i].name))
10828 if (chunk_info[i].name == NULL)
10830 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
10831 chunk_name, filename);
10832 ReadUnusedBytesFromFile(file, chunk_size);
10834 else if (chunk_info[i].size != -1 &&
10835 chunk_info[i].size != chunk_size)
10837 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
10838 chunk_size, chunk_name, filename);
10839 ReadUnusedBytesFromFile(file, chunk_size);
10843 /* call function to load this tape chunk */
10844 int chunk_size_expected =
10845 (chunk_info[i].loader)(file, chunk_size, &tape);
10847 /* the size of some chunks cannot be checked before reading other
10848 chunks first (like "HEAD" and "BODY") that contain some header
10849 information, so check them here */
10850 if (chunk_size_expected != chunk_size)
10852 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
10853 chunk_size, chunk_name, filename);
10861 tape.length_seconds = GetTapeLength();
10864 printf("::: tape file version: %d\n", tape.file_version);
10865 printf("::: tape game version: %d\n", tape.game_version);
10866 printf("::: tape engine version: %d\n", tape.engine_version);
10872 void LoadTapeFromFilename(char *filename)
10874 char cookie[MAX_LINE_LEN];
10875 char chunk_name[CHUNK_ID_LEN + 1];
10879 /* always start with reliable default values */
10880 setTapeInfoToDefaults();
10882 if (strSuffix(filename, ".sln"))
10884 LoadTape_SokobanSolution(filename);
10889 if (!(file = fopen(filename, MODE_READ)))
10891 tape.no_valid_file = TRUE;
10896 getFileChunkBE(file, chunk_name, NULL);
10897 if (strEqual(chunk_name, "RND1"))
10899 getFile32BitBE(file); /* not used */
10901 getFileChunkBE(file, chunk_name, NULL);
10902 if (!strEqual(chunk_name, "TAPE"))
10904 tape.no_valid_file = TRUE;
10906 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
10911 else /* check for pre-2.0 file format with cookie string */
10913 strcpy(cookie, chunk_name);
10914 if (fgets(&cookie[4], MAX_LINE_LEN - 4, file) == NULL)
10916 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10917 cookie[strlen(cookie) - 1] = '\0';
10919 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
10921 tape.no_valid_file = TRUE;
10923 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
10928 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
10930 tape.no_valid_file = TRUE;
10932 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
10938 /* pre-2.0 tape files have no game version, so use file version here */
10939 tape.game_version = tape.file_version;
10942 if (tape.file_version < FILE_VERSION_1_2)
10944 /* tape files from versions before 1.2.0 without chunk structure */
10945 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
10946 LoadTape_BODY(file, 2 * tape.length, &tape);
10954 int (*loader)(File *, int, struct TapeInfo *);
10958 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
10959 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
10960 { "INFO", -1, LoadTape_INFO },
10961 { "BODY", -1, LoadTape_BODY },
10965 while (getFileChunkBE(file, chunk_name, &chunk_size))
10969 while (chunk_info[i].name != NULL &&
10970 !strEqual(chunk_name, chunk_info[i].name))
10973 if (chunk_info[i].name == NULL)
10975 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
10976 chunk_name, filename);
10977 ReadUnusedBytesFromFile(file, chunk_size);
10979 else if (chunk_info[i].size != -1 &&
10980 chunk_info[i].size != chunk_size)
10982 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
10983 chunk_size, chunk_name, filename);
10984 ReadUnusedBytesFromFile(file, chunk_size);
10988 /* call function to load this tape chunk */
10989 int chunk_size_expected =
10990 (chunk_info[i].loader)(file, chunk_size, &tape);
10992 /* the size of some chunks cannot be checked before reading other
10993 chunks first (like "HEAD" and "BODY") that contain some header
10994 information, so check them here */
10995 if (chunk_size_expected != chunk_size)
10997 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
10998 chunk_size, chunk_name, filename);
11006 tape.length_seconds = GetTapeLength();
11009 printf("::: tape file version: %d\n", tape.file_version);
11010 printf("::: tape game version: %d\n", tape.game_version);
11011 printf("::: tape engine version: %d\n", tape.engine_version);
11017 void LoadTape(int nr)
11019 char *filename = getTapeFilename(nr);
11021 LoadTapeFromFilename(filename);
11024 void LoadSolutionTape(int nr)
11026 char *filename = getSolutionTapeFilename(nr);
11028 LoadTapeFromFilename(filename);
11031 if (TAPE_IS_EMPTY(tape) &&
11032 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
11033 level.native_sp_level->demo.is_available)
11034 CopyNativeTape_SP_to_RND(&level);
11038 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
11040 putFileVersion(file, tape->file_version);
11041 putFileVersion(file, tape->game_version);
11044 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
11047 byte store_participating_players = 0;
11049 /* set bits for participating players for compact storage */
11050 for (i = 0; i < MAX_PLAYERS; i++)
11051 if (tape->player_participates[i])
11052 store_participating_players |= (1 << i);
11054 putFile32BitBE(file, tape->random_seed);
11055 putFile32BitBE(file, tape->date);
11056 putFile32BitBE(file, tape->length);
11058 putFile8Bit(file, store_participating_players);
11060 /* unused bytes not at the end here for 4-byte alignment of engine_version */
11061 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
11063 putFileVersion(file, tape->engine_version);
11066 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
11068 int level_identifier_size = strlen(tape->level_identifier) + 1;
11071 putFile16BitBE(file, level_identifier_size);
11073 for (i = 0; i < level_identifier_size; i++)
11074 putFile8Bit(file, tape->level_identifier[i]);
11076 putFile16BitBE(file, tape->level_nr);
11079 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
11083 for (i = 0; i < tape->length; i++)
11085 for (j = 0; j < MAX_PLAYERS; j++)
11086 if (tape->player_participates[j])
11087 putFile8Bit(file, tape->pos[i].action[j]);
11089 putFile8Bit(file, tape->pos[i].delay);
11093 void SaveTape(int nr)
11095 char *filename = getTapeFilename(nr);
11098 boolean new_tape = TRUE;
11100 int num_participating_players = 0;
11101 int info_chunk_size;
11102 int body_chunk_size;
11105 InitTapeDirectory(leveldir_current->subdir);
11108 /* if a tape still exists, ask to overwrite it */
11109 if (fileExists(filename))
11112 if (!Request("Replace old tape?", REQ_ASK))
11117 if (!(file = fopen(filename, MODE_WRITE)))
11119 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
11123 tape.file_version = FILE_VERSION_ACTUAL;
11124 tape.game_version = GAME_VERSION_ACTUAL;
11126 /* count number of participating players */
11127 for (i = 0; i < MAX_PLAYERS; i++)
11128 if (tape.player_participates[i])
11129 num_participating_players++;
11131 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
11132 body_chunk_size = (num_participating_players + 1) * tape.length;
11134 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
11135 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
11137 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
11138 SaveTape_VERS(file, &tape);
11140 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
11141 SaveTape_HEAD(file, &tape);
11143 putFileChunkBE(file, "INFO", info_chunk_size);
11144 SaveTape_INFO(file, &tape);
11146 putFileChunkBE(file, "BODY", body_chunk_size);
11147 SaveTape_BODY(file, &tape);
11151 SetFilePermissions(filename, PERMS_PRIVATE);
11153 tape.changed = FALSE;
11157 Request("Tape saved!", REQ_CONFIRM);
11161 boolean SaveTapeChecked(int nr)
11163 char *filename = getTapeFilename(nr);
11164 boolean new_tape = !fileExists(filename);
11165 boolean tape_saved = FALSE;
11167 if (new_tape || Request("Replace old tape?", REQ_ASK))
11172 Request("Tape saved!", REQ_CONFIRM);
11180 void DumpTape(struct TapeInfo *tape)
11182 int tape_frame_counter;
11185 if (tape->no_valid_file)
11187 Error(ERR_WARN, "cannot dump -- no valid tape file found");
11192 printf_line("-", 79);
11193 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
11194 tape->level_nr, tape->file_version, tape->game_version);
11195 printf(" (effective engine version %08d)\n",
11196 tape->engine_version);
11197 printf("Level series identifier: '%s'\n", tape->level_identifier);
11198 printf_line("-", 79);
11200 tape_frame_counter = 0;
11202 for (i = 0; i < tape->length; i++)
11204 if (i >= MAX_TAPE_LEN)
11207 printf("%04d: ", i);
11209 for (j = 0; j < MAX_PLAYERS; j++)
11211 if (tape->player_participates[j])
11213 int action = tape->pos[i].action[j];
11215 printf("%d:%02x ", j, action);
11216 printf("[%c%c%c%c|%c%c] - ",
11217 (action & JOY_LEFT ? '<' : ' '),
11218 (action & JOY_RIGHT ? '>' : ' '),
11219 (action & JOY_UP ? '^' : ' '),
11220 (action & JOY_DOWN ? 'v' : ' '),
11221 (action & JOY_BUTTON_1 ? '1' : ' '),
11222 (action & JOY_BUTTON_2 ? '2' : ' '));
11226 printf("(%03d) ", tape->pos[i].delay);
11227 printf("[%05d]\n", tape_frame_counter);
11229 tape_frame_counter += tape->pos[i].delay;
11232 printf_line("-", 79);
11236 /* ========================================================================= */
11237 /* score file functions */
11238 /* ========================================================================= */
11240 void LoadScore(int nr)
11243 char *filename = getScoreFilename(nr);
11244 char cookie[MAX_LINE_LEN];
11245 char line[MAX_LINE_LEN];
11249 /* always start with reliable default values */
11250 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
11252 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
11253 highscore[i].Score = 0;
11256 if (!(file = fopen(filename, MODE_READ)))
11259 /* check file identifier */
11260 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
11262 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
11263 cookie[strlen(cookie) - 1] = '\0';
11265 if (!checkCookieString(cookie, SCORE_COOKIE))
11267 Error(ERR_WARN, "unknown format of score file '%s'", filename);
11272 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
11274 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
11275 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
11276 if (fgets(line, MAX_LINE_LEN, file) == NULL)
11279 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
11280 line[strlen(line) - 1] = '\0';
11282 for (line_ptr = line; *line_ptr; line_ptr++)
11284 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
11286 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
11287 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
11296 void SaveScore(int nr)
11299 char *filename = getScoreFilename(nr);
11302 InitScoreDirectory(leveldir_current->subdir);
11304 if (!(file = fopen(filename, MODE_WRITE)))
11306 Error(ERR_WARN, "cannot save score for level %d", nr);
11310 fprintf(file, "%s\n\n", SCORE_COOKIE);
11312 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
11313 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
11317 SetFilePermissions(filename, PERMS_PUBLIC);
11321 /* ========================================================================= */
11322 /* setup file functions */
11323 /* ========================================================================= */
11325 #define TOKEN_STR_PLAYER_PREFIX "player_"
11328 #define SETUP_TOKEN_PLAYER_NAME 0
11329 #define SETUP_TOKEN_SOUND 1
11330 #define SETUP_TOKEN_SOUND_LOOPS 2
11331 #define SETUP_TOKEN_SOUND_MUSIC 3
11332 #define SETUP_TOKEN_SOUND_SIMPLE 4
11333 #define SETUP_TOKEN_TOONS 5
11334 #define SETUP_TOKEN_SCROLL_DELAY 6
11335 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
11336 #define SETUP_TOKEN_SOFT_SCROLLING 8
11337 #define SETUP_TOKEN_FADE_SCREENS 9
11338 #define SETUP_TOKEN_AUTORECORD 10
11339 #define SETUP_TOKEN_SHOW_TITLESCREEN 11
11340 #define SETUP_TOKEN_QUICK_DOORS 12
11341 #define SETUP_TOKEN_TEAM_MODE 13
11342 #define SETUP_TOKEN_HANDICAP 14
11343 #define SETUP_TOKEN_SKIP_LEVELS 15
11344 #define SETUP_TOKEN_TIME_LIMIT 16
11345 #define SETUP_TOKEN_FULLSCREEN 17
11346 #define SETUP_TOKEN_FULLSCREEN_MODE 18
11347 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT 19
11348 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY 20
11349 #define SETUP_TOKEN_ASK_ON_ESCAPE 21
11350 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 22
11351 #define SETUP_TOKEN_QUICK_SWITCH 23
11352 #define SETUP_TOKEN_INPUT_ON_FOCUS 24
11353 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 25
11354 #define SETUP_TOKEN_GAME_FRAME_DELAY 26
11355 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 27
11356 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 28
11357 #define SETUP_TOKEN_GRAPHICS_SET 29
11358 #define SETUP_TOKEN_SOUNDS_SET 30
11359 #define SETUP_TOKEN_MUSIC_SET 31
11360 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 32
11361 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 33
11362 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 34
11363 #define SETUP_TOKEN_VOLUME_SIMPLE 35
11364 #define SETUP_TOKEN_VOLUME_LOOPS 36
11365 #define SETUP_TOKEN_VOLUME_MUSIC 37
11366 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE 38
11367 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 39
11368 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE 40
11370 #define NUM_GLOBAL_SETUP_TOKENS 41
11373 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
11374 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
11375 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
11376 #define SETUP_TOKEN_EDITOR_EL_MORE 3
11377 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 4
11378 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 5
11379 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 6
11380 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 7
11381 #define SETUP_TOKEN_EDITOR_EL_CHARS 8
11382 #define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS 9
11383 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 10
11384 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 11
11385 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 12
11386 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 13
11387 #define SETUP_TOKEN_EDITOR_EL_BY_GAME 14
11388 #define SETUP_TOKEN_EDITOR_EL_BY_TYPE 15
11389 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 16
11391 #define NUM_EDITOR_SETUP_TOKENS 17
11393 /* editor cascade setup */
11394 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
11395 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
11396 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
11397 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
11398 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
11399 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
11400 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
11401 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
11402 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
11403 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
11404 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
11405 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
11406 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
11407 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
11408 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
11410 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
11412 /* shortcut setup */
11413 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
11414 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
11415 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
11416 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
11417 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
11418 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
11419 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
11420 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
11421 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
11422 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
11423 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
11424 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
11425 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
11426 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
11427 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
11428 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
11429 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
11430 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
11431 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
11432 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
11433 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
11435 #define NUM_SHORTCUT_SETUP_TOKENS 21
11438 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
11439 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
11440 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
11441 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
11442 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
11443 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
11444 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
11445 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
11446 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
11447 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
11448 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
11449 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
11450 #define SETUP_TOKEN_PLAYER_KEY_UP 12
11451 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
11452 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
11453 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
11455 #define NUM_PLAYER_SETUP_TOKENS 16
11458 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
11459 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
11460 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
11462 #define NUM_SYSTEM_SETUP_TOKENS 3
11464 /* options setup */
11465 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
11467 #define NUM_OPTIONS_SETUP_TOKENS 1
11470 static struct SetupInfo si;
11471 static struct SetupEditorInfo sei;
11472 static struct SetupEditorCascadeInfo seci;
11473 static struct SetupShortcutInfo ssi;
11474 static struct SetupInputInfo sii;
11475 static struct SetupSystemInfo syi;
11476 static struct OptionInfo soi;
11478 static struct TokenInfo global_setup_tokens[] =
11480 { TYPE_STRING, &si.player_name, "player_name" },
11481 { TYPE_SWITCH, &si.sound, "sound" },
11482 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
11483 { TYPE_SWITCH, &si.sound_music, "background_music" },
11484 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
11485 { TYPE_SWITCH, &si.toons, "toons" },
11486 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
11487 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
11488 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
11489 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
11490 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
11491 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
11492 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
11493 { TYPE_SWITCH, &si.team_mode, "team_mode" },
11494 { TYPE_SWITCH, &si.handicap, "handicap" },
11495 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
11496 { TYPE_SWITCH, &si.time_limit, "time_limit" },
11497 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
11498 { TYPE_STRING, &si.fullscreen_mode, "fullscreen_mode" },
11499 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
11500 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
11501 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
11502 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
11503 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
11504 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
11505 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
11506 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
11507 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
11508 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
11509 { TYPE_STRING, &si.graphics_set, "graphics_set" },
11510 { TYPE_STRING, &si.sounds_set, "sounds_set" },
11511 { TYPE_STRING, &si.music_set, "music_set" },
11512 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
11513 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
11514 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
11515 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
11516 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
11517 { TYPE_INTEGER,&si.volume_music, "volume_music" },
11518 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
11519 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
11520 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
11523 static boolean not_used = FALSE;
11524 static struct TokenInfo editor_setup_tokens[] =
11527 { TYPE_SWITCH, ¬_used, "editor.el_boulderdash" },
11528 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine" },
11529 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine_club" },
11530 { TYPE_SWITCH, ¬_used, "editor.el_more" },
11531 { TYPE_SWITCH, ¬_used, "editor.el_sokoban" },
11532 { TYPE_SWITCH, ¬_used, "editor.el_supaplex" },
11533 { TYPE_SWITCH, ¬_used, "editor.el_diamond_caves" },
11534 { TYPE_SWITCH, ¬_used, "editor.el_dx_boulderdash" },
11536 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
11537 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
11538 { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
11539 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
11540 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
11541 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
11542 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
11543 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
11545 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
11546 { TYPE_SWITCH, &sei.el_steel_chars, "editor.el_steel_chars" },
11547 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
11549 { TYPE_SWITCH, ¬_used, "editor.el_headlines" },
11551 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
11553 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
11554 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
11555 { TYPE_SWITCH, &sei.el_by_game, "editor.el_by_game" },
11556 { TYPE_SWITCH, &sei.el_by_type, "editor.el_by_type" },
11557 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
11560 static struct TokenInfo editor_cascade_setup_tokens[] =
11562 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
11563 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
11564 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
11565 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
11566 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
11567 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
11568 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
11569 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
11570 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
11571 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
11572 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
11573 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
11574 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
11575 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
11576 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
11579 static struct TokenInfo shortcut_setup_tokens[] =
11581 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
11582 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
11583 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
11584 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
11585 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
11586 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
11587 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
11588 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
11589 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
11590 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
11591 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
11592 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
11593 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
11594 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
11595 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
11596 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
11597 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
11598 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
11599 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
11600 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
11601 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
11604 static struct TokenInfo player_setup_tokens[] =
11606 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
11607 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
11608 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
11609 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
11610 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
11611 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
11612 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
11613 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
11614 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
11615 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
11616 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
11617 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
11618 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
11619 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
11620 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
11621 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
11624 static struct TokenInfo system_setup_tokens[] =
11626 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
11627 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
11628 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
11631 static struct TokenInfo options_setup_tokens[] =
11633 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
11636 static char *get_corrected_login_name(char *login_name)
11638 /* needed because player name must be a fixed length string */
11639 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
11641 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
11642 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
11644 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
11645 if (strchr(login_name_new, ' '))
11646 *strchr(login_name_new, ' ') = '\0';
11648 return login_name_new;
11651 static void setSetupInfoToDefaults(struct SetupInfo *si)
11655 si->player_name = get_corrected_login_name(getLoginName());
11658 si->sound_loops = TRUE;
11659 si->sound_music = TRUE;
11660 si->sound_simple = TRUE;
11662 si->scroll_delay = TRUE;
11663 si->scroll_delay_value = STD_SCROLL_DELAY;
11664 si->soft_scrolling = TRUE;
11665 si->fade_screens = TRUE;
11666 si->autorecord = TRUE;
11667 si->show_titlescreen = TRUE;
11668 si->quick_doors = FALSE;
11669 si->team_mode = FALSE;
11670 si->handicap = TRUE;
11671 si->skip_levels = TRUE;
11672 si->time_limit = TRUE;
11673 si->fullscreen = FALSE;
11674 si->fullscreen_mode = getStringCopy(DEFAULT_FULLSCREEN_MODE);
11675 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11676 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11677 si->ask_on_escape = TRUE;
11678 si->ask_on_escape_editor = TRUE;
11679 si->quick_switch = FALSE;
11680 si->input_on_focus = FALSE;
11681 si->prefer_aga_graphics = TRUE;
11682 si->game_frame_delay = GAME_FRAME_DELAY;
11683 si->sp_show_border_elements = FALSE;
11684 si->small_game_graphics = FALSE;
11686 si->graphics_set = getStringCopy(GFX_DEFAULT_SUBDIR);
11687 si->sounds_set = getStringCopy(SND_DEFAULT_SUBDIR);
11688 si->music_set = getStringCopy(MUS_DEFAULT_SUBDIR);
11689 si->override_level_graphics = FALSE;
11690 si->override_level_sounds = FALSE;
11691 si->override_level_music = FALSE;
11693 si->volume_simple = 100; /* percent */
11694 si->volume_loops = 100; /* percent */
11695 si->volume_music = 100; /* percent */
11697 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11698 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
11699 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
11701 si->editor.el_boulderdash = TRUE;
11702 si->editor.el_emerald_mine = TRUE;
11703 si->editor.el_emerald_mine_club = TRUE;
11704 si->editor.el_more = TRUE;
11705 si->editor.el_sokoban = TRUE;
11706 si->editor.el_supaplex = TRUE;
11707 si->editor.el_diamond_caves = TRUE;
11708 si->editor.el_dx_boulderdash = TRUE;
11709 si->editor.el_chars = TRUE;
11710 si->editor.el_steel_chars = TRUE;
11711 si->editor.el_custom = TRUE;
11713 si->editor.el_headlines = TRUE;
11714 si->editor.el_user_defined = FALSE;
11715 si->editor.el_dynamic = TRUE;
11717 si->editor.show_element_token = FALSE;
11719 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11720 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11721 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11723 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11724 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11725 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11726 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11727 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11729 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11730 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11731 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11732 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11733 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11734 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11736 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11737 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11738 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11740 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11741 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11742 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11743 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11745 for (i = 0; i < MAX_PLAYERS; i++)
11747 si->input[i].use_joystick = FALSE;
11748 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
11749 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11750 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11751 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11752 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11753 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11754 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11755 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11756 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11757 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11758 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11759 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11760 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11761 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11762 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11765 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11766 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11767 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11769 si->options.verbose = FALSE;
11771 #if defined(CREATE_SPECIAL_EDITION_RND_JUE)
11773 si->handicap = FALSE;
11774 si->fullscreen = TRUE;
11775 si->override_level_graphics = AUTO;
11776 si->override_level_sounds = AUTO;
11777 si->override_level_music = AUTO;
11780 #if defined(PLATFORM_ANDROID)
11781 si->fullscreen = TRUE;
11785 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11787 si->editor_cascade.el_bd = TRUE;
11788 si->editor_cascade.el_em = TRUE;
11789 si->editor_cascade.el_emc = TRUE;
11790 si->editor_cascade.el_rnd = TRUE;
11791 si->editor_cascade.el_sb = TRUE;
11792 si->editor_cascade.el_sp = TRUE;
11793 si->editor_cascade.el_dc = TRUE;
11794 si->editor_cascade.el_dx = TRUE;
11796 si->editor_cascade.el_chars = FALSE;
11797 si->editor_cascade.el_steel_chars = FALSE;
11798 si->editor_cascade.el_ce = FALSE;
11799 si->editor_cascade.el_ge = FALSE;
11800 si->editor_cascade.el_ref = FALSE;
11801 si->editor_cascade.el_user = FALSE;
11802 si->editor_cascade.el_dynamic = FALSE;
11805 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
11809 if (!setup_file_hash)
11814 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
11815 setSetupInfo(global_setup_tokens, i,
11816 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
11820 sei = setup.editor;
11821 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
11822 setSetupInfo(editor_setup_tokens, i,
11823 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
11824 setup.editor = sei;
11826 /* shortcut setup */
11827 ssi = setup.shortcut;
11828 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
11829 setSetupInfo(shortcut_setup_tokens, i,
11830 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
11831 setup.shortcut = ssi;
11834 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11838 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11840 sii = setup.input[pnr];
11841 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
11843 char full_token[100];
11845 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11846 setSetupInfo(player_setup_tokens, i,
11847 getHashEntry(setup_file_hash, full_token));
11849 setup.input[pnr] = sii;
11853 syi = setup.system;
11854 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
11855 setSetupInfo(system_setup_tokens, i,
11856 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
11857 setup.system = syi;
11859 /* options setup */
11860 soi = setup.options;
11861 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
11862 setSetupInfo(options_setup_tokens, i,
11863 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
11864 setup.options = soi;
11867 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11871 if (!setup_file_hash)
11874 /* editor cascade setup */
11875 seci = setup.editor_cascade;
11876 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
11877 setSetupInfo(editor_cascade_setup_tokens, i,
11878 getHashEntry(setup_file_hash,
11879 editor_cascade_setup_tokens[i].text));
11880 setup.editor_cascade = seci;
11885 char *filename = getSetupFilename();
11886 SetupFileHash *setup_file_hash = NULL;
11888 /* always start with reliable default values */
11889 setSetupInfoToDefaults(&setup);
11891 setup_file_hash = loadSetupFileHash(filename);
11893 if (setup_file_hash)
11895 char *player_name_new;
11897 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
11898 decodeSetupFileHash(setup_file_hash);
11900 freeSetupFileHash(setup_file_hash);
11902 /* needed to work around problems with fixed length strings */
11903 player_name_new = get_corrected_login_name(setup.player_name);
11904 free(setup.player_name);
11905 setup.player_name = player_name_new;
11907 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
11908 if (setup.scroll_delay == FALSE)
11910 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11911 setup.scroll_delay = TRUE; /* now always "on" */
11914 /* make sure that scroll delay value stays inside valid range */
11915 setup.scroll_delay_value =
11916 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11919 Error(ERR_WARN, "using default setup values");
11922 void LoadSetup_EditorCascade()
11924 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11925 SetupFileHash *setup_file_hash = NULL;
11927 /* always start with reliable default values */
11928 setSetupInfoToDefaults_EditorCascade(&setup);
11930 setup_file_hash = loadSetupFileHash(filename);
11932 if (setup_file_hash)
11934 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
11935 decodeSetupFileHash_EditorCascade(setup_file_hash);
11937 freeSetupFileHash(setup_file_hash);
11945 char *filename = getSetupFilename();
11949 InitUserDataDirectory();
11951 if (!(file = fopen(filename, MODE_WRITE)))
11953 Error(ERR_WARN, "cannot write setup file '%s'", filename);
11957 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
11958 getCookie("SETUP")));
11959 fprintf(file, "\n");
11963 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
11965 /* just to make things nicer :) */
11966 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
11967 i == SETUP_TOKEN_GRAPHICS_SET ||
11968 i == SETUP_TOKEN_VOLUME_SIMPLE ||
11969 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
11970 fprintf(file, "\n");
11972 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11976 sei = setup.editor;
11977 fprintf(file, "\n");
11978 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
11979 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11981 /* shortcut setup */
11982 ssi = setup.shortcut;
11983 fprintf(file, "\n");
11984 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
11985 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11988 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11992 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11993 fprintf(file, "\n");
11995 sii = setup.input[pnr];
11996 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
11997 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12001 syi = setup.system;
12002 fprintf(file, "\n");
12003 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
12004 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12006 /* options setup */
12007 soi = setup.options;
12008 fprintf(file, "\n");
12009 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
12010 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12014 SetFilePermissions(filename, PERMS_PRIVATE);
12017 void SaveSetup_EditorCascade()
12019 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12023 InitUserDataDirectory();
12025 if (!(file = fopen(filename, MODE_WRITE)))
12027 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
12032 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
12033 getCookie("SETUP")));
12034 fprintf(file, "\n");
12036 seci = setup.editor_cascade;
12037 fprintf(file, "\n");
12038 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
12039 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12043 SetFilePermissions(filename, PERMS_PRIVATE);
12048 void LoadCustomElementDescriptions()
12050 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12051 SetupFileHash *setup_file_hash;
12054 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12056 if (element_info[i].custom_description != NULL)
12058 free(element_info[i].custom_description);
12059 element_info[i].custom_description = NULL;
12063 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12066 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12068 char *token = getStringCat2(element_info[i].token_name, ".name");
12069 char *value = getHashEntry(setup_file_hash, token);
12072 element_info[i].custom_description = getStringCopy(value);
12077 freeSetupFileHash(setup_file_hash);
12080 static int getElementFromToken(char *token)
12083 char *value = getHashEntry(element_token_hash, token);
12086 return atoi(value);
12090 /* !!! OPTIMIZE THIS BY USING HASH !!! */
12091 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
12092 if (strEqual(token, element_info[i].token_name))
12096 Error(ERR_WARN, "unknown element token '%s'", token);
12098 return EL_UNDEFINED;
12101 static int get_token_parameter_value(char *token, char *value_raw)
12105 if (token == NULL || value_raw == NULL)
12106 return ARG_UNDEFINED_VALUE;
12108 suffix = strrchr(token, '.');
12109 if (suffix == NULL)
12113 if (strEqual(suffix, ".element"))
12114 return getElementFromToken(value_raw);
12118 if (strncmp(suffix, ".font", 5) == 0)
12122 /* !!! OPTIMIZE THIS BY USING HASH !!! */
12123 for (i = 0; i < NUM_FONTS; i++)
12124 if (strEqual(value_raw, font_info[i].token_name))
12127 /* if font not found, use reliable default value */
12128 return FONT_INITIAL_1;
12132 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
12133 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12136 void InitMenuDesignSettings_Static()
12139 static SetupFileHash *image_config_hash = NULL;
12144 if (image_config_hash == NULL)
12146 image_config_hash = newSetupFileHash();
12148 for (i = 0; image_config[i].token != NULL; i++)
12149 setHashEntry(image_config_hash,
12150 image_config[i].token,
12151 image_config[i].value);
12156 /* always start with reliable default values from static default config */
12157 for (i = 0; image_config_vars[i].token != NULL; i++)
12159 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
12162 *image_config_vars[i].value =
12163 get_token_parameter_value(image_config_vars[i].token, value);
12170 /* always start with reliable default values from static default config */
12171 for (i = 0; image_config_vars[i].token != NULL; i++)
12172 for (j = 0; image_config[j].token != NULL; j++)
12173 if (strEqual(image_config_vars[i].token, image_config[j].token))
12174 *image_config_vars[i].value =
12175 get_token_parameter_value(image_config_vars[i].token,
12176 image_config[j].value);
12180 static void InitMenuDesignSettings_SpecialPreProcessing()
12184 /* the following initializes hierarchical values from static configuration */
12186 /* special case: initialize "ARG_DEFAULT" values in static default config */
12187 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
12188 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12189 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12190 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12191 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
12192 titlemessage_default.fade_mode = title_default.fade_mode;
12193 titlemessage_default.fade_delay = title_default.fade_delay;
12194 titlemessage_default.post_delay = title_default.post_delay;
12195 titlemessage_default.auto_delay = title_default.auto_delay;
12197 /* special case: initialize "ARG_DEFAULT" values in static default config */
12198 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
12199 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12201 titlemessage_initial[i] = titlemessage_initial_default;
12202 titlemessage[i] = titlemessage_default;
12205 /* special case: initialize "ARG_DEFAULT" values in static default config */
12206 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
12207 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12209 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12210 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12213 /* special case: initialize "ARG_DEFAULT" values in static default config */
12214 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
12215 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12217 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12218 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12219 if (i != GFX_SPECIAL_ARG_EDITOR) /* editor value already initialized */
12220 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12224 static void InitMenuDesignSettings_SpecialPostProcessing()
12226 /* special case: initialize later added SETUP list size from LEVELS value */
12227 if (menu.list_size[GAME_MODE_SETUP] == -1)
12228 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12231 static void LoadMenuDesignSettingsFromFilename(char *filename)
12233 static struct TitleMessageInfo tmi;
12234 static struct TokenInfo titlemessage_tokens[] =
12236 { TYPE_INTEGER, &tmi.x, ".x" },
12237 { TYPE_INTEGER, &tmi.y, ".y" },
12238 { TYPE_INTEGER, &tmi.width, ".width" },
12239 { TYPE_INTEGER, &tmi.height, ".height" },
12240 { TYPE_INTEGER, &tmi.chars, ".chars" },
12241 { TYPE_INTEGER, &tmi.lines, ".lines" },
12242 { TYPE_INTEGER, &tmi.align, ".align" },
12243 { TYPE_INTEGER, &tmi.valign, ".valign" },
12244 { TYPE_INTEGER, &tmi.font, ".font" },
12245 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12246 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12247 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12248 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12249 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12250 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12251 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12252 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12258 struct TitleMessageInfo *array;
12261 titlemessage_arrays[] =
12263 { titlemessage_initial, "[titlemessage_initial]" },
12264 { titlemessage, "[titlemessage]" },
12268 SetupFileHash *setup_file_hash;
12272 printf("LoadMenuDesignSettings from file '%s' ...\n", filename);
12275 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12278 /* the following initializes hierarchical values from dynamic configuration */
12280 /* special case: initialize with default values that may be overwritten */
12281 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
12282 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12284 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
12285 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
12286 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
12288 if (value_1 != NULL)
12289 menu.draw_xoffset[i] = get_integer_from_string(value_1);
12290 if (value_2 != NULL)
12291 menu.draw_yoffset[i] = get_integer_from_string(value_2);
12292 if (value_3 != NULL)
12293 menu.list_size[i] = get_integer_from_string(value_3);
12296 /* special case: initialize with default values that may be overwritten */
12297 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
12298 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12300 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
12301 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
12303 if (value_1 != NULL)
12304 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
12305 if (value_2 != NULL)
12306 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
12309 /* special case: initialize with default values that may be overwritten */
12310 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
12311 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12313 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
12314 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
12316 if (value_1 != NULL)
12317 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
12318 if (value_2 != NULL)
12319 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
12322 /* special case: initialize with default values that may be overwritten */
12323 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
12324 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12326 char *token_1 = "menu.enter_screen.fade_mode";
12327 char *token_2 = "menu.enter_screen.fade_delay";
12328 char *token_3 = "menu.enter_screen.post_delay";
12329 char *token_4 = "menu.leave_screen.fade_mode";
12330 char *token_5 = "menu.leave_screen.fade_delay";
12331 char *token_6 = "menu.leave_screen.post_delay";
12332 char *value_1 = getHashEntry(setup_file_hash, token_1);
12333 char *value_2 = getHashEntry(setup_file_hash, token_2);
12334 char *value_3 = getHashEntry(setup_file_hash, token_3);
12335 char *value_4 = getHashEntry(setup_file_hash, token_4);
12336 char *value_5 = getHashEntry(setup_file_hash, token_5);
12337 char *value_6 = getHashEntry(setup_file_hash, token_6);
12339 if (value_1 != NULL)
12340 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
12342 if (value_2 != NULL)
12343 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
12345 if (value_3 != NULL)
12346 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
12348 if (value_4 != NULL)
12349 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
12351 if (value_5 != NULL)
12352 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
12354 if (value_6 != NULL)
12355 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
12359 /* special case: initialize with default values that may be overwritten */
12360 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
12361 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12363 char *token_01 = "viewport.playfield.x";
12364 char *token_02 = "viewport.playfield.y";
12365 char *token_03 = "viewport.playfield.width";
12366 char *token_04 = "viewport.playfield.height";
12367 char *token_05 = "viewport.playfield.border_size";
12368 char *token_06 = "viewport.door_1.x";
12369 char *token_07 = "viewport.door_1.y";
12370 char *token_08 = "viewport.door_1.width";
12371 char *token_09 = "viewport.door_1.height";
12372 char *token_10 = "viewport.door_1.border_size";
12373 char *token_11 = "viewport.door_2.x";
12374 char *token_12 = "viewport.door_2.y";
12375 char *token_13 = "viewport.door_2.width";
12376 char *token_14 = "viewport.door_2.height";
12377 char *token_15 = "viewport.door_2.border_size";
12378 char *value_01 = getHashEntry(setup_file_hash, token_01);
12379 char *value_02 = getHashEntry(setup_file_hash, token_02);
12380 char *value_03 = getHashEntry(setup_file_hash, token_03);
12381 char *value_04 = getHashEntry(setup_file_hash, token_04);
12382 char *value_05 = getHashEntry(setup_file_hash, token_05);
12383 char *value_06 = getHashEntry(setup_file_hash, token_06);
12384 char *value_07 = getHashEntry(setup_file_hash, token_07);
12385 char *value_08 = getHashEntry(setup_file_hash, token_08);
12386 char *value_09 = getHashEntry(setup_file_hash, token_09);
12387 char *value_10 = getHashEntry(setup_file_hash, token_10);
12388 char *value_11 = getHashEntry(setup_file_hash, token_11);
12389 char *value_12 = getHashEntry(setup_file_hash, token_12);
12390 char *value_13 = getHashEntry(setup_file_hash, token_13);
12391 char *value_14 = getHashEntry(setup_file_hash, token_14);
12392 char *value_15 = getHashEntry(setup_file_hash, token_15);
12394 if (value_01 != NULL)
12395 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
12396 if (value_02 != NULL)
12397 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
12398 if (value_03 != NULL)
12399 viewport.playfield[i].width = get_token_parameter_value(token_03,
12401 if (value_04 != NULL)
12402 viewport.playfield[i].height = get_token_parameter_value(token_04,
12404 if (value_05 != NULL)
12405 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
12407 if (value_06 != NULL)
12408 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
12409 if (value_07 != NULL)
12410 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
12411 if (value_08 != NULL)
12412 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
12413 if (value_09 != NULL)
12414 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
12415 if (value_10 != NULL)
12416 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
12418 if (value_11 != NULL)
12419 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
12420 if (value_12 != NULL)
12421 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
12422 if (value_13 != NULL)
12423 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
12424 if (value_14 != NULL)
12425 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
12426 if (value_15 != NULL)
12427 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
12431 /* special case: initialize with default values that may be overwritten */
12432 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
12433 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12435 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12436 char *base_token = titlemessage_arrays[i].text;
12438 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12440 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12441 char *value = getHashEntry(setup_file_hash, token);
12445 int parameter_value = get_token_parameter_value(token, value);
12447 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12451 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12452 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12454 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12464 /* read (and overwrite with) values that may be specified in config file */
12465 for (i = 0; image_config_vars[i].token != NULL; i++)
12467 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12469 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
12470 if (value != NULL && !strEqual(value, ARG_DEFAULT))
12471 *image_config_vars[i].value =
12472 get_token_parameter_value(image_config_vars[i].token, value);
12475 freeSetupFileHash(setup_file_hash);
12478 void LoadMenuDesignSettings()
12480 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12482 InitMenuDesignSettings_Static();
12483 InitMenuDesignSettings_SpecialPreProcessing();
12486 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12488 if (!SETUP_OVERRIDE_ARTWORK(setup, ARTWORK_TYPE_GRAPHICS))
12491 /* first look for special settings configured in level series config */
12492 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12494 if (fileExists(filename_base))
12495 LoadMenuDesignSettingsFromFilename(filename_base);
12498 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12500 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12501 LoadMenuDesignSettingsFromFilename(filename_local);
12503 InitMenuDesignSettings_SpecialPostProcessing();
12506 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12508 char *filename = getEditorSetupFilename();
12509 SetupFileList *setup_file_list, *list;
12510 SetupFileHash *element_hash;
12511 int num_unknown_tokens = 0;
12514 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12517 element_hash = newSetupFileHash();
12519 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12520 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12522 /* determined size may be larger than needed (due to unknown elements) */
12524 for (list = setup_file_list; list != NULL; list = list->next)
12527 /* add space for up to 3 more elements for padding that may be needed */
12528 *num_elements += 3;
12530 /* free memory for old list of elements, if needed */
12531 checked_free(*elements);
12533 /* allocate memory for new list of elements */
12534 *elements = checked_malloc(*num_elements * sizeof(int));
12537 for (list = setup_file_list; list != NULL; list = list->next)
12539 char *value = getHashEntry(element_hash, list->token);
12541 if (value == NULL) /* try to find obsolete token mapping */
12543 char *mapped_token = get_mapped_token(list->token);
12545 if (mapped_token != NULL)
12547 value = getHashEntry(element_hash, mapped_token);
12549 free(mapped_token);
12555 (*elements)[(*num_elements)++] = atoi(value);
12559 if (num_unknown_tokens == 0)
12561 Error(ERR_INFO_LINE, "-");
12562 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
12563 Error(ERR_INFO, "- config file: '%s'", filename);
12565 num_unknown_tokens++;
12568 Error(ERR_INFO, "- token: '%s'", list->token);
12572 if (num_unknown_tokens > 0)
12573 Error(ERR_INFO_LINE, "-");
12575 while (*num_elements % 4) /* pad with empty elements, if needed */
12576 (*elements)[(*num_elements)++] = EL_EMPTY;
12578 freeSetupFileList(setup_file_list);
12579 freeSetupFileHash(element_hash);
12582 for (i = 0; i < *num_elements; i++)
12583 printf("editor: element '%s' [%d]\n",
12584 element_info[(*elements)[i]].token_name, (*elements)[i]);
12588 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12591 SetupFileHash *setup_file_hash = NULL;
12592 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12593 char *filename_music, *filename_prefix, *filename_info;
12599 token_to_value_ptr[] =
12601 { "title_header", &tmp_music_file_info.title_header },
12602 { "artist_header", &tmp_music_file_info.artist_header },
12603 { "album_header", &tmp_music_file_info.album_header },
12604 { "year_header", &tmp_music_file_info.year_header },
12606 { "title", &tmp_music_file_info.title },
12607 { "artist", &tmp_music_file_info.artist },
12608 { "album", &tmp_music_file_info.album },
12609 { "year", &tmp_music_file_info.year },
12615 filename_music = (is_sound ? getCustomSoundFilename(basename) :
12616 getCustomMusicFilename(basename));
12618 if (filename_music == NULL)
12621 /* ---------- try to replace file extension ---------- */
12623 filename_prefix = getStringCopy(filename_music);
12624 if (strrchr(filename_prefix, '.') != NULL)
12625 *strrchr(filename_prefix, '.') = '\0';
12626 filename_info = getStringCat2(filename_prefix, ".txt");
12629 printf("trying to load file '%s'...\n", filename_info);
12632 if (fileExists(filename_info))
12633 setup_file_hash = loadSetupFileHash(filename_info);
12635 free(filename_prefix);
12636 free(filename_info);
12638 if (setup_file_hash == NULL)
12640 /* ---------- try to add file extension ---------- */
12642 filename_prefix = getStringCopy(filename_music);
12643 filename_info = getStringCat2(filename_prefix, ".txt");
12646 printf("trying to load file '%s'...\n", filename_info);
12649 if (fileExists(filename_info))
12650 setup_file_hash = loadSetupFileHash(filename_info);
12652 free(filename_prefix);
12653 free(filename_info);
12656 if (setup_file_hash == NULL)
12659 /* ---------- music file info found ---------- */
12661 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
12663 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
12665 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
12667 *token_to_value_ptr[i].value_ptr =
12668 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
12671 tmp_music_file_info.basename = getStringCopy(basename);
12672 tmp_music_file_info.music = music;
12673 tmp_music_file_info.is_sound = is_sound;
12675 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
12676 *new_music_file_info = tmp_music_file_info;
12678 return new_music_file_info;
12681 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
12683 return get_music_file_info_ext(basename, music, FALSE);
12686 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
12688 return get_music_file_info_ext(basename, sound, TRUE);
12691 static boolean music_info_listed_ext(struct MusicFileInfo *list,
12692 char *basename, boolean is_sound)
12694 for (; list != NULL; list = list->next)
12695 if (list->is_sound == is_sound && strEqual(list->basename, basename))
12701 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
12703 return music_info_listed_ext(list, basename, FALSE);
12706 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
12708 return music_info_listed_ext(list, basename, TRUE);
12713 void LoadMusicInfo()
12715 char *music_directory = getCustomMusicDirectory();
12716 int num_music = getMusicListSize();
12717 int num_music_noconf = 0;
12718 int num_sounds = getSoundListSize();
12720 DirectoryEntry *dir_entry;
12721 struct FileInfo *music, *sound;
12722 struct MusicFileInfo *next, **new;
12725 while (music_file_info != NULL)
12727 next = music_file_info->next;
12729 checked_free(music_file_info->basename);
12731 checked_free(music_file_info->title_header);
12732 checked_free(music_file_info->artist_header);
12733 checked_free(music_file_info->album_header);
12734 checked_free(music_file_info->year_header);
12736 checked_free(music_file_info->title);
12737 checked_free(music_file_info->artist);
12738 checked_free(music_file_info->album);
12739 checked_free(music_file_info->year);
12741 free(music_file_info);
12743 music_file_info = next;
12746 new = &music_file_info;
12748 for (i = 0; i < num_music; i++)
12750 music = getMusicListEntry(i);
12752 if (music->filename == NULL)
12755 if (strEqual(music->filename, UNDEFINED_FILENAME))
12758 /* a configured file may be not recognized as music */
12759 if (!FileIsMusic(music->filename))
12763 printf("::: -> '%s' (configured)\n", music->filename);
12766 if (!music_info_listed(music_file_info, music->filename))
12768 *new = get_music_file_info(music->filename, i);
12771 printf(":1: adding '%s' ['%s'] ...\n", (*new)->title, music->filename);
12774 new = &(*new)->next;
12778 if ((dir = openDirectory(music_directory)) == NULL)
12780 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
12784 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
12786 char *basename = dir_entry->basename;
12787 boolean music_already_used = FALSE;
12790 /* skip all music files that are configured in music config file */
12791 for (i = 0; i < num_music; i++)
12793 music = getMusicListEntry(i);
12795 if (music->filename == NULL)
12798 if (strEqual(basename, music->filename))
12800 music_already_used = TRUE;
12805 if (music_already_used)
12808 if (!FileIsMusic(basename))
12812 printf("::: -> '%s' (found in directory)\n", basename);
12815 if (!music_info_listed(music_file_info, basename))
12817 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12820 printf(":2: adding '%s' ['%s'] ...\n", (*new)->title, basename);
12823 new = &(*new)->next;
12826 num_music_noconf++;
12829 closeDirectory(dir);
12831 for (i = 0; i < num_sounds; i++)
12833 sound = getSoundListEntry(i);
12835 if (sound->filename == NULL)
12838 if (strEqual(sound->filename, UNDEFINED_FILENAME))
12841 /* a configured file may be not recognized as sound */
12842 if (!FileIsSound(sound->filename))
12846 printf("::: -> '%s' (configured)\n", sound->filename);
12849 if (!sound_info_listed(music_file_info, sound->filename))
12851 *new = get_sound_file_info(sound->filename, i);
12853 new = &(*new)->next;
12858 for (next = music_file_info; next != NULL; next = next->next)
12859 printf("::: title == '%s'\n", next->title);
12865 void LoadMusicInfo()
12867 char *music_directory = getCustomMusicDirectory();
12868 int num_music = getMusicListSize();
12869 int num_music_noconf = 0;
12870 int num_sounds = getSoundListSize();
12872 struct dirent *dir_entry;
12873 struct FileInfo *music, *sound;
12874 struct MusicFileInfo *next, **new;
12877 while (music_file_info != NULL)
12879 next = music_file_info->next;
12881 checked_free(music_file_info->basename);
12883 checked_free(music_file_info->title_header);
12884 checked_free(music_file_info->artist_header);
12885 checked_free(music_file_info->album_header);
12886 checked_free(music_file_info->year_header);
12888 checked_free(music_file_info->title);
12889 checked_free(music_file_info->artist);
12890 checked_free(music_file_info->album);
12891 checked_free(music_file_info->year);
12893 free(music_file_info);
12895 music_file_info = next;
12898 new = &music_file_info;
12900 for (i = 0; i < num_music; i++)
12902 music = getMusicListEntry(i);
12904 if (music->filename == NULL)
12907 if (strEqual(music->filename, UNDEFINED_FILENAME))
12910 /* a configured file may be not recognized as music */
12911 if (!FileIsMusic(music->filename))
12915 printf("::: -> '%s' (configured)\n", music->filename);
12918 if (!music_info_listed(music_file_info, music->filename))
12920 *new = get_music_file_info(music->filename, i);
12923 printf(":1: adding '%s' ['%s'] ...\n", (*new)->title, music->filename);
12926 new = &(*new)->next;
12930 if ((dir = opendir(music_directory)) == NULL)
12932 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
12936 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
12938 char *basename = dir_entry->d_name;
12939 boolean music_already_used = FALSE;
12942 /* skip all music files that are configured in music config file */
12943 for (i = 0; i < num_music; i++)
12945 music = getMusicListEntry(i);
12947 if (music->filename == NULL)
12950 if (strEqual(basename, music->filename))
12952 music_already_used = TRUE;
12957 if (music_already_used)
12960 if (!FileIsMusic(basename))
12964 printf("::: -> '%s' (found in directory)\n", basename);
12967 if (!music_info_listed(music_file_info, basename))
12969 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12972 printf(":2: adding '%s' ['%s'] ...\n", (*new)->title, basename);
12975 new = &(*new)->next;
12978 num_music_noconf++;
12983 for (i = 0; i < num_sounds; i++)
12985 sound = getSoundListEntry(i);
12987 if (sound->filename == NULL)
12990 if (strEqual(sound->filename, UNDEFINED_FILENAME))
12993 /* a configured file may be not recognized as sound */
12994 if (!FileIsSound(sound->filename))
12998 printf("::: -> '%s' (configured)\n", sound->filename);
13001 if (!sound_info_listed(music_file_info, sound->filename))
13003 *new = get_sound_file_info(sound->filename, i);
13005 new = &(*new)->next;
13010 for (next = music_file_info; next != NULL; next = next->next)
13011 printf("::: title == '%s'\n", next->title);
13017 void add_helpanim_entry(int element, int action, int direction, int delay,
13018 int *num_list_entries)
13020 struct HelpAnimInfo *new_list_entry;
13021 (*num_list_entries)++;
13024 checked_realloc(helpanim_info,
13025 *num_list_entries * sizeof(struct HelpAnimInfo));
13026 new_list_entry = &helpanim_info[*num_list_entries - 1];
13028 new_list_entry->element = element;
13029 new_list_entry->action = action;
13030 new_list_entry->direction = direction;
13031 new_list_entry->delay = delay;
13034 void print_unknown_token(char *filename, char *token, int token_nr)
13038 Error(ERR_INFO_LINE, "-");
13039 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
13040 Error(ERR_INFO, "- config file: '%s'", filename);
13043 Error(ERR_INFO, "- token: '%s'", token);
13046 void print_unknown_token_end(int token_nr)
13049 Error(ERR_INFO_LINE, "-");
13052 void LoadHelpAnimInfo()
13054 char *filename = getHelpAnimFilename();
13055 SetupFileList *setup_file_list = NULL, *list;
13056 SetupFileHash *element_hash, *action_hash, *direction_hash;
13057 int num_list_entries = 0;
13058 int num_unknown_tokens = 0;
13061 if (fileExists(filename))
13062 setup_file_list = loadSetupFileList(filename);
13064 if (setup_file_list == NULL)
13066 /* use reliable default values from static configuration */
13067 SetupFileList *insert_ptr;
13069 insert_ptr = setup_file_list =
13070 newSetupFileList(helpanim_config[0].token,
13071 helpanim_config[0].value);
13073 for (i = 1; helpanim_config[i].token; i++)
13074 insert_ptr = addListEntry(insert_ptr,
13075 helpanim_config[i].token,
13076 helpanim_config[i].value);
13079 element_hash = newSetupFileHash();
13080 action_hash = newSetupFileHash();
13081 direction_hash = newSetupFileHash();
13083 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13084 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13086 for (i = 0; i < NUM_ACTIONS; i++)
13087 setHashEntry(action_hash, element_action_info[i].suffix,
13088 i_to_a(element_action_info[i].value));
13090 /* do not store direction index (bit) here, but direction value! */
13091 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13092 setHashEntry(direction_hash, element_direction_info[i].suffix,
13093 i_to_a(1 << element_direction_info[i].value));
13095 for (list = setup_file_list; list != NULL; list = list->next)
13097 char *element_token, *action_token, *direction_token;
13098 char *element_value, *action_value, *direction_value;
13099 int delay = atoi(list->value);
13101 if (strEqual(list->token, "end"))
13103 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13108 /* first try to break element into element/action/direction parts;
13109 if this does not work, also accept combined "element[.act][.dir]"
13110 elements (like "dynamite.active"), which are unique elements */
13112 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
13114 element_value = getHashEntry(element_hash, list->token);
13115 if (element_value != NULL) /* element found */
13116 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13117 &num_list_entries);
13120 /* no further suffixes found -- this is not an element */
13121 print_unknown_token(filename, list->token, num_unknown_tokens++);
13127 /* token has format "<prefix>.<something>" */
13129 action_token = strchr(list->token, '.'); /* suffix may be action ... */
13130 direction_token = action_token; /* ... or direction */
13132 element_token = getStringCopy(list->token);
13133 *strchr(element_token, '.') = '\0';
13135 element_value = getHashEntry(element_hash, element_token);
13137 if (element_value == NULL) /* this is no element */
13139 element_value = getHashEntry(element_hash, list->token);
13140 if (element_value != NULL) /* combined element found */
13141 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13142 &num_list_entries);
13144 print_unknown_token(filename, list->token, num_unknown_tokens++);
13146 free(element_token);
13151 action_value = getHashEntry(action_hash, action_token);
13153 if (action_value != NULL) /* action found */
13155 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13156 &num_list_entries);
13158 free(element_token);
13163 direction_value = getHashEntry(direction_hash, direction_token);
13165 if (direction_value != NULL) /* direction found */
13167 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13168 &num_list_entries);
13170 free(element_token);
13175 if (strchr(action_token + 1, '.') == NULL)
13177 /* no further suffixes found -- this is not an action nor direction */
13179 element_value = getHashEntry(element_hash, list->token);
13180 if (element_value != NULL) /* combined element found */
13181 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13182 &num_list_entries);
13184 print_unknown_token(filename, list->token, num_unknown_tokens++);
13186 free(element_token);
13191 /* token has format "<prefix>.<suffix>.<something>" */
13193 direction_token = strchr(action_token + 1, '.');
13195 action_token = getStringCopy(action_token);
13196 *strchr(action_token + 1, '.') = '\0';
13198 action_value = getHashEntry(action_hash, action_token);
13200 if (action_value == NULL) /* this is no action */
13202 element_value = getHashEntry(element_hash, list->token);
13203 if (element_value != NULL) /* combined element found */
13204 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13205 &num_list_entries);
13207 print_unknown_token(filename, list->token, num_unknown_tokens++);
13209 free(element_token);
13210 free(action_token);
13215 direction_value = getHashEntry(direction_hash, direction_token);
13217 if (direction_value != NULL) /* direction found */
13219 add_helpanim_entry(atoi(element_value), atoi(action_value),
13220 atoi(direction_value), delay, &num_list_entries);
13222 free(element_token);
13223 free(action_token);
13228 /* this is no direction */
13230 element_value = getHashEntry(element_hash, list->token);
13231 if (element_value != NULL) /* combined element found */
13232 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13233 &num_list_entries);
13235 print_unknown_token(filename, list->token, num_unknown_tokens++);
13237 free(element_token);
13238 free(action_token);
13241 print_unknown_token_end(num_unknown_tokens);
13243 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13244 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13246 freeSetupFileList(setup_file_list);
13247 freeSetupFileHash(element_hash);
13248 freeSetupFileHash(action_hash);
13249 freeSetupFileHash(direction_hash);
13252 for (i = 0; i < num_list_entries; i++)
13253 printf("::: '%s': %d, %d, %d => %d\n",
13254 EL_NAME(helpanim_info[i].element),
13255 helpanim_info[i].element,
13256 helpanim_info[i].action,
13257 helpanim_info[i].direction,
13258 helpanim_info[i].delay);
13262 void LoadHelpTextInfo()
13264 char *filename = getHelpTextFilename();
13267 if (helptext_info != NULL)
13269 freeSetupFileHash(helptext_info);
13270 helptext_info = NULL;
13273 if (fileExists(filename))
13274 helptext_info = loadSetupFileHash(filename);
13276 if (helptext_info == NULL)
13278 /* use reliable default values from static configuration */
13279 helptext_info = newSetupFileHash();
13281 for (i = 0; helptext_config[i].token; i++)
13282 setHashEntry(helptext_info,
13283 helptext_config[i].token,
13284 helptext_config[i].value);
13288 BEGIN_HASH_ITERATION(helptext_info, itr)
13290 printf("::: '%s' => '%s'\n",
13291 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13293 END_HASH_ITERATION(hash, itr)
13298 /* ------------------------------------------------------------------------- */
13299 /* convert levels */
13300 /* ------------------------------------------------------------------------- */
13302 #define MAX_NUM_CONVERT_LEVELS 1000
13304 void ConvertLevels()
13306 static LevelDirTree *convert_leveldir = NULL;
13307 static int convert_level_nr = -1;
13308 static int num_levels_handled = 0;
13309 static int num_levels_converted = 0;
13310 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13313 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13314 global.convert_leveldir);
13316 if (convert_leveldir == NULL)
13317 Error(ERR_EXIT, "no such level identifier: '%s'",
13318 global.convert_leveldir);
13320 leveldir_current = convert_leveldir;
13322 if (global.convert_level_nr != -1)
13324 convert_leveldir->first_level = global.convert_level_nr;
13325 convert_leveldir->last_level = global.convert_level_nr;
13328 convert_level_nr = convert_leveldir->first_level;
13330 printf_line("=", 79);
13331 printf("Converting levels\n");
13332 printf_line("-", 79);
13333 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
13334 printf("Level series name: '%s'\n", convert_leveldir->name);
13335 printf("Level series author: '%s'\n", convert_leveldir->author);
13336 printf("Number of levels: %d\n", convert_leveldir->levels);
13337 printf_line("=", 79);
13340 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13341 levels_failed[i] = FALSE;
13343 while (convert_level_nr <= convert_leveldir->last_level)
13345 char *level_filename;
13348 level_nr = convert_level_nr++;
13350 printf("Level %03d: ", level_nr);
13352 LoadLevel(level_nr);
13353 if (level.no_valid_file)
13355 printf("(no level)\n");
13359 printf("converting level ... ");
13361 level_filename = getDefaultLevelFilename(level_nr);
13362 new_level = !fileExists(level_filename);
13366 SaveLevel(level_nr);
13368 num_levels_converted++;
13370 printf("converted.\n");
13374 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13375 levels_failed[level_nr] = TRUE;
13377 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13380 num_levels_handled++;
13384 printf_line("=", 79);
13385 printf("Number of levels handled: %d\n", num_levels_handled);
13386 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13387 (num_levels_handled ?
13388 num_levels_converted * 100 / num_levels_handled : 0));
13389 printf_line("-", 79);
13390 printf("Summary (for automatic parsing by scripts):\n");
13391 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13392 convert_leveldir->identifier, num_levels_converted,
13393 num_levels_handled,
13394 (num_levels_handled ?
13395 num_levels_converted * 100 / num_levels_handled : 0));
13397 if (num_levels_handled != num_levels_converted)
13399 printf(", FAILED:");
13400 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13401 if (levels_failed[i])
13402 printf(" %03d", i);
13406 printf_line("=", 79);
13408 CloseAllAndExit(0);
13412 /* ------------------------------------------------------------------------- */
13413 /* create and save images for use in level sketches (raw BMP format) */
13414 /* ------------------------------------------------------------------------- */
13416 void CreateLevelSketchImages()
13418 #if defined(TARGET_SDL)
13423 InitElementPropertiesGfxElement();
13425 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13426 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13428 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13430 Bitmap *src_bitmap;
13432 int element = getMappedElement(i);
13433 int graphic = el2edimg(element);
13434 char basename1[16];
13435 char basename2[16];
13439 sprintf(basename1, "%03d.bmp", i);
13440 sprintf(basename2, "%03ds.bmp", i);
13442 filename1 = getPath2(global.create_images_dir, basename1);
13443 filename2 = getPath2(global.create_images_dir, basename2);
13445 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13446 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
13449 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13450 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
13452 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
13453 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
13455 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13456 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
13462 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13465 FreeBitmap(bitmap1);
13466 FreeBitmap(bitmap2);
13471 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
13473 CloseAllAndExit(0);
13478 /* ------------------------------------------------------------------------- */
13479 /* create and save images for custom and group elements (raw BMP format) */
13480 /* ------------------------------------------------------------------------- */
13482 void CreateCustomElementImages()
13484 #if defined(TARGET_SDL)
13485 char *filename = "graphics.classic/RocksCE.bmp";
13487 Bitmap *src_bitmap;
13488 int dummy_graphic = IMG_CUSTOM_99;
13489 int yoffset_ce = 0;
13490 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13494 bitmap = CreateBitmap(TILEX * 16 * 2,
13495 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13498 getFixedGraphicSource(dummy_graphic, 0, &src_bitmap, &src_x, &src_y);
13500 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13507 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13508 TILEX * x, TILEY * y + yoffset_ce);
13510 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13512 TILEX * x + TILEX * 16,
13513 TILEY * y + yoffset_ce);
13515 for (j = 2; j >= 0; j--)
13519 BlitBitmap(src_bitmap, bitmap,
13520 TILEX + c * 7, 0, 6, 10,
13521 TILEX * x + 6 + j * 7,
13522 TILEY * y + 11 + yoffset_ce);
13524 BlitBitmap(src_bitmap, bitmap,
13525 TILEX + c * 8, TILEY, 6, 10,
13526 TILEX * 16 + TILEX * x + 6 + j * 8,
13527 TILEY * y + 10 + yoffset_ce);
13533 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13540 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13541 TILEX * x, TILEY * y + yoffset_ge);
13543 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13545 TILEX * x + TILEX * 16,
13546 TILEY * y + yoffset_ge);
13548 for (j = 1; j >= 0; j--)
13552 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13553 TILEX * x + 6 + j * 10,
13554 TILEY * y + 11 + yoffset_ge);
13556 BlitBitmap(src_bitmap, bitmap,
13557 TILEX + c * 8, TILEY + 12, 6, 10,
13558 TILEX * 16 + TILEX * x + 10 + j * 8,
13559 TILEY * y + 10 + yoffset_ge);
13565 if (SDL_SaveBMP(bitmap->surface, filename) != 0)
13566 Error(ERR_EXIT, "cannot save CE graphics file '%s'", filename);
13568 FreeBitmap(bitmap);
13570 CloseAllAndExit(0);
13575 void CreateLevelSketchImages_TEST()
13577 void CreateCustomElementImages()