1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
24 #define ENABLE_UNUSED_CODE 0 /* currently unused functions */
25 #define ENABLE_HISTORIC_CHUNKS 0 /* only for historic reference */
26 #define ENABLE_RESERVED_CODE 0 /* reserved for later use */
28 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
29 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
30 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
32 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
33 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
35 #define LEVEL_CHUNK_VERS_SIZE 8 /* size of file version chunk */
36 #define LEVEL_CHUNK_DATE_SIZE 4 /* size of file date chunk */
37 #define LEVEL_CHUNK_HEAD_SIZE 80 /* size of level file header */
38 #define LEVEL_CHUNK_HEAD_UNUSED 0 /* unused level header bytes */
39 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
40 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
41 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
42 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
43 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
44 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
45 #define LEVEL_CHUNK_GRP1_SIZE 74 /* size of level GRP1 chunk */
47 /* (element number, number of change pages, change page number) */
48 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
50 /* (element number only) */
51 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
52 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
54 /* (nothing at all if unchanged) */
55 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
57 #define TAPE_CHUNK_VERS_SIZE 8 /* size of file version chunk */
58 #define TAPE_CHUNK_HEAD_SIZE 20 /* size of tape file header */
59 #define TAPE_CHUNK_HEAD_UNUSED 3 /* unused tape header bytes */
61 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
62 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
63 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
65 /* file identifier strings */
66 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
67 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
68 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
70 /* values for deciding when (not) to save configuration data */
71 #define SAVE_CONF_NEVER 0
72 #define SAVE_CONF_ALWAYS 1
73 #define SAVE_CONF_WHEN_CHANGED -1
75 /* values for chunks using micro chunks */
76 #define CONF_MASK_1_BYTE 0x00
77 #define CONF_MASK_2_BYTE 0x40
78 #define CONF_MASK_4_BYTE 0x80
79 #define CONF_MASK_MULTI_BYTES 0xc0
81 #define CONF_MASK_BYTES 0xc0
82 #define CONF_MASK_TOKEN 0x3f
84 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
85 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
86 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
87 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
89 /* these definitions are just for convenience of use and readability */
90 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
91 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
92 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
93 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
95 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
96 (x) == CONF_MASK_2_BYTE ? 2 : \
97 (x) == CONF_MASK_4_BYTE ? 4 : 0)
99 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
100 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
101 #define CONF_ELEMENT_NUM_BYTES (2)
103 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
104 (t) == TYPE_ELEMENT_LIST ? \
105 CONF_ELEMENT_NUM_BYTES : \
106 (t) == TYPE_CONTENT || \
107 (t) == TYPE_CONTENT_LIST ? \
108 CONF_CONTENT_NUM_BYTES : 1)
110 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
111 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
112 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
114 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
116 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
117 CONF_ELEMENT_NUM_BYTES)
118 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
119 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
121 /* temporary variables used to store pointers to structure members */
122 static struct LevelInfo li;
123 static struct ElementInfo xx_ei, yy_ei;
124 static struct ElementChangeInfo xx_change;
125 static struct ElementGroupInfo xx_group;
126 static struct EnvelopeInfo xx_envelope;
127 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
128 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
129 static int xx_num_contents;
130 static int xx_current_change_page;
131 static char xx_default_string_empty[1] = "";
132 static int xx_string_length_unused;
134 struct LevelFileConfigInfo
136 int element; /* element for which data is to be stored */
137 int save_type; /* save data always, never or when changed */
138 int data_type; /* data type (used internally, not stored) */
139 int conf_type; /* micro chunk identifier (stored in file) */
142 void *value; /* variable that holds the data to be stored */
143 int default_value; /* initial default value for this variable */
146 void *value_copy; /* variable that holds the data to be copied */
147 void *num_entities; /* number of entities for multi-byte data */
148 int default_num_entities; /* default number of entities for this data */
149 int max_num_entities; /* maximal number of entities for this data */
150 char *default_string; /* optional default string for string data */
153 static struct LevelFileConfigInfo chunk_config_INFO[] =
155 /* ---------- values not related to single elements ----------------------- */
158 -1, SAVE_CONF_ALWAYS,
159 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
160 &li.game_engine_type, GAME_ENGINE_TYPE_RND
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
166 &li.fieldx, STD_LEV_FIELDX
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
171 &li.fieldy, STD_LEV_FIELDY
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
181 -1, SAVE_CONF_ALWAYS,
182 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
188 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
194 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
195 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
206 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
207 &li.em_slippery_gems, FALSE
212 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
213 &li.use_custom_template, FALSE
218 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
219 &li.can_move_into_acid_bits, ~0 /* default: everything can */
224 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
225 &li.dont_collide_with_bits, ~0 /* default: always deadly */
230 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
231 &li.em_explodes_by_fire, FALSE
236 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
237 &li.score[SC_TIME_BONUS], 1
242 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
243 &li.auto_exit_sokoban, FALSE
253 static struct LevelFileConfigInfo chunk_config_ELEM[] =
255 /* (these values are the same for each player) */
258 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
259 &li.block_last_field, FALSE /* default case for EM levels */
263 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
264 &li.sp_block_last_field, TRUE /* default case for SP levels */
268 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
269 &li.instant_relocation, FALSE
273 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
274 &li.can_pass_to_walkable, FALSE
278 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
279 &li.block_snap_field, TRUE
283 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
284 &li.continuous_snapping, TRUE
288 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
289 &li.shifted_relocation, FALSE
292 /* (these values are different for each player) */
295 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
296 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
301 &li.initial_player_gravity[0], FALSE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
306 &li.use_start_element[0], FALSE
310 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
311 &li.start_element[0], EL_PLAYER_1
315 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
316 &li.use_artwork_element[0], FALSE
320 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
321 &li.artwork_element[0], EL_PLAYER_1
325 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
326 &li.use_explosion_element[0], FALSE
330 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
331 &li.explosion_element[0], EL_PLAYER_1
335 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
336 &li.use_initial_inventory[0], FALSE
340 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
341 &li.initial_inventory_size[0], 1
345 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
346 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
347 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
352 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
353 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
357 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
358 &li.initial_player_gravity[1], FALSE
362 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
363 &li.use_start_element[1], FALSE
367 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
368 &li.start_element[1], EL_PLAYER_2
372 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
373 &li.use_artwork_element[1], FALSE
377 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
378 &li.artwork_element[1], EL_PLAYER_2
382 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
383 &li.use_explosion_element[1], FALSE
387 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
388 &li.explosion_element[1], EL_PLAYER_2
392 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
393 &li.use_initial_inventory[1], FALSE
397 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
398 &li.initial_inventory_size[1], 1
402 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
403 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
404 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
409 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
410 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
414 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
415 &li.initial_player_gravity[2], FALSE
419 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
420 &li.use_start_element[2], FALSE
424 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
425 &li.start_element[2], EL_PLAYER_3
429 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
430 &li.use_artwork_element[2], FALSE
434 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
435 &li.artwork_element[2], EL_PLAYER_3
439 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
440 &li.use_explosion_element[2], FALSE
444 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
445 &li.explosion_element[2], EL_PLAYER_3
449 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
450 &li.use_initial_inventory[2], FALSE
454 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
455 &li.initial_inventory_size[2], 1
459 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
460 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
461 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
466 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
467 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
471 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
472 &li.initial_player_gravity[3], FALSE
476 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
477 &li.use_start_element[3], FALSE
481 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
482 &li.start_element[3], EL_PLAYER_4
486 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
487 &li.use_artwork_element[3], FALSE
491 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
492 &li.artwork_element[3], EL_PLAYER_4
496 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
497 &li.use_explosion_element[3], FALSE
501 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
502 &li.explosion_element[3], EL_PLAYER_4
506 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
507 &li.use_initial_inventory[3], FALSE
511 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
512 &li.initial_inventory_size[3], 1
516 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
517 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
518 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
523 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
524 &li.score[SC_EMERALD], 10
529 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
530 &li.score[SC_DIAMOND], 10
535 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
536 &li.score[SC_BUG], 10
541 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
542 &li.score[SC_SPACESHIP], 10
547 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
548 &li.score[SC_PACMAN], 10
553 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
554 &li.score[SC_NUT], 10
559 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
560 &li.score[SC_DYNAMITE], 10
565 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
566 &li.score[SC_KEY], 10
571 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
572 &li.score[SC_PEARL], 10
577 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
578 &li.score[SC_CRYSTAL], 10
583 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
584 &li.amoeba_content, EL_DIAMOND
588 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
593 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
594 &li.grow_into_diggable, TRUE
599 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
600 &li.yamyam_content, EL_ROCK, NULL,
601 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
605 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
606 &li.score[SC_YAMYAM], 10
611 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
612 &li.score[SC_ROBOT], 10
616 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
622 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
628 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
629 &li.time_magic_wall, 10
634 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
635 &li.game_of_life[0], 2
639 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
640 &li.game_of_life[1], 3
644 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
645 &li.game_of_life[2], 3
649 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
650 &li.game_of_life[3], 3
655 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
660 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
665 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
670 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
675 EL_TIMEGATE_SWITCH, -1,
676 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
677 &li.time_timegate, 10
681 EL_LIGHT_SWITCH_ACTIVE, -1,
682 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
687 EL_SHIELD_NORMAL, -1,
688 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
689 &li.shield_normal_time, 10
692 EL_SHIELD_NORMAL, -1,
693 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
694 &li.score[SC_SHIELD], 10
698 EL_SHIELD_DEADLY, -1,
699 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
700 &li.shield_deadly_time, 10
703 EL_SHIELD_DEADLY, -1,
704 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
705 &li.score[SC_SHIELD], 10
710 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
715 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
716 &li.extra_time_score, 10
720 EL_TIME_ORB_FULL, -1,
721 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
722 &li.time_orb_time, 10
725 EL_TIME_ORB_FULL, -1,
726 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
727 &li.use_time_orb_bug, FALSE
732 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
733 &li.use_spring_bug, FALSE
738 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
739 &li.android_move_time, 10
743 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
744 &li.android_clone_time, 10
748 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
749 &li.android_clone_element[0], EL_EMPTY, NULL,
750 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
755 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
760 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
765 EL_EMC_MAGNIFIER, -1,
766 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
767 &li.magnify_score, 10
770 EL_EMC_MAGNIFIER, -1,
771 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
776 EL_EMC_MAGIC_BALL, -1,
777 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
781 EL_EMC_MAGIC_BALL, -1,
782 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
783 &li.ball_random, FALSE
786 EL_EMC_MAGIC_BALL, -1,
787 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
788 &li.ball_state_initial, FALSE
791 EL_EMC_MAGIC_BALL, -1,
792 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
793 &li.ball_content, EL_EMPTY, NULL,
794 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
797 /* ---------- unused values ----------------------------------------------- */
800 EL_UNKNOWN, SAVE_CONF_NEVER,
801 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
802 &li.score[SC_UNKNOWN_14], 10
805 EL_UNKNOWN, SAVE_CONF_NEVER,
806 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
807 &li.score[SC_UNKNOWN_15], 10
817 static struct LevelFileConfigInfo chunk_config_NOTE[] =
821 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
822 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
826 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
827 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
832 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
833 &xx_envelope.autowrap, FALSE
837 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
838 &xx_envelope.centered, FALSE
843 TYPE_STRING, CONF_VALUE_BYTES(1),
844 &xx_envelope.text, -1, NULL,
845 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
846 &xx_default_string_empty[0]
856 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
860 TYPE_STRING, CONF_VALUE_BYTES(1),
861 &xx_ei.description[0], -1,
862 &yy_ei.description[0],
863 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
864 &xx_default_description[0]
869 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
870 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
871 &yy_ei.properties[EP_BITFIELD_BASE_NR]
873 #if ENABLE_RESERVED_CODE
874 /* (reserved for later use) */
877 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
878 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
879 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
885 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
886 &xx_ei.use_gfx_element, FALSE,
887 &yy_ei.use_gfx_element
891 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
892 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
893 &yy_ei.gfx_element_initial
898 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
899 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
900 &yy_ei.access_direction
905 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
906 &xx_ei.collect_score_initial, 10,
907 &yy_ei.collect_score_initial
911 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
912 &xx_ei.collect_count_initial, 1,
913 &yy_ei.collect_count_initial
918 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
919 &xx_ei.ce_value_fixed_initial, 0,
920 &yy_ei.ce_value_fixed_initial
924 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
925 &xx_ei.ce_value_random_initial, 0,
926 &yy_ei.ce_value_random_initial
930 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
931 &xx_ei.use_last_ce_value, FALSE,
932 &yy_ei.use_last_ce_value
937 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
938 &xx_ei.push_delay_fixed, 8,
939 &yy_ei.push_delay_fixed
943 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
944 &xx_ei.push_delay_random, 8,
945 &yy_ei.push_delay_random
949 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
950 &xx_ei.drop_delay_fixed, 0,
951 &yy_ei.drop_delay_fixed
955 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
956 &xx_ei.drop_delay_random, 0,
957 &yy_ei.drop_delay_random
961 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
962 &xx_ei.move_delay_fixed, 0,
963 &yy_ei.move_delay_fixed
967 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
968 &xx_ei.move_delay_random, 0,
969 &yy_ei.move_delay_random
974 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
975 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
980 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
981 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
982 &yy_ei.move_direction_initial
986 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
987 &xx_ei.move_stepsize, TILEX / 8,
993 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
994 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
995 &yy_ei.move_enter_element
999 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1000 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1001 &yy_ei.move_leave_element
1005 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1006 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1007 &yy_ei.move_leave_type
1012 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1013 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1014 &yy_ei.slippery_type
1019 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1020 &xx_ei.explosion_type, EXPLODES_3X3,
1021 &yy_ei.explosion_type
1025 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1026 &xx_ei.explosion_delay, 16,
1027 &yy_ei.explosion_delay
1031 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1032 &xx_ei.ignition_delay, 8,
1033 &yy_ei.ignition_delay
1038 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1039 &xx_ei.content, EL_EMPTY_SPACE,
1041 &xx_num_contents, 1, 1
1044 /* ---------- "num_change_pages" must be the last entry ------------------- */
1047 -1, SAVE_CONF_ALWAYS,
1048 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1049 &xx_ei.num_change_pages, 1,
1050 &yy_ei.num_change_pages
1061 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1063 /* ---------- "current_change_page" must be the first entry --------------- */
1066 -1, SAVE_CONF_ALWAYS,
1067 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1068 &xx_current_change_page, -1
1071 /* ---------- (the remaining entries can be in any order) ----------------- */
1075 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1076 &xx_change.can_change, FALSE
1081 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1082 &xx_event_bits[0], 0
1086 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1087 &xx_event_bits[1], 0
1092 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1093 &xx_change.trigger_player, CH_PLAYER_ANY
1097 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1098 &xx_change.trigger_side, CH_SIDE_ANY
1102 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1103 &xx_change.trigger_page, CH_PAGE_ANY
1108 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1109 &xx_change.target_element, EL_EMPTY_SPACE
1114 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1115 &xx_change.delay_fixed, 0
1119 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1120 &xx_change.delay_random, 0
1124 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1125 &xx_change.delay_frames, FRAMES_PER_SECOND
1130 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1131 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1136 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1137 &xx_change.explode, FALSE
1141 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1142 &xx_change.use_target_content, FALSE
1146 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1147 &xx_change.only_if_complete, FALSE
1151 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1152 &xx_change.use_random_replace, FALSE
1156 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1157 &xx_change.random_percentage, 100
1161 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1162 &xx_change.replace_when, CP_WHEN_EMPTY
1167 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1168 &xx_change.has_action, FALSE
1172 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1173 &xx_change.action_type, CA_NO_ACTION
1177 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1178 &xx_change.action_mode, CA_MODE_UNDEFINED
1182 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1183 &xx_change.action_arg, CA_ARG_UNDEFINED
1188 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1189 &xx_change.action_element, EL_EMPTY_SPACE
1194 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1195 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1196 &xx_num_contents, 1, 1
1206 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1210 TYPE_STRING, CONF_VALUE_BYTES(1),
1211 &xx_ei.description[0], -1, NULL,
1212 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1213 &xx_default_description[0]
1218 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1219 &xx_ei.use_gfx_element, FALSE
1223 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1224 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1229 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1230 &xx_group.choice_mode, ANIM_RANDOM
1235 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1236 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1237 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1247 static struct LevelFileConfigInfo chunk_config_CONF[] = /* (OBSOLETE) */
1251 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1252 &li.block_snap_field, TRUE
1256 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1257 &li.continuous_snapping, TRUE
1261 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1262 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1266 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1267 &li.use_start_element[0], FALSE
1271 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1272 &li.start_element[0], EL_PLAYER_1
1276 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1277 &li.use_artwork_element[0], FALSE
1281 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1282 &li.artwork_element[0], EL_PLAYER_1
1286 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1287 &li.use_explosion_element[0], FALSE
1291 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1292 &li.explosion_element[0], EL_PLAYER_1
1307 filetype_id_list[] =
1309 { LEVEL_FILE_TYPE_RND, "RND" },
1310 { LEVEL_FILE_TYPE_BD, "BD" },
1311 { LEVEL_FILE_TYPE_EM, "EM" },
1312 { LEVEL_FILE_TYPE_SP, "SP" },
1313 { LEVEL_FILE_TYPE_DX, "DX" },
1314 { LEVEL_FILE_TYPE_SB, "SB" },
1315 { LEVEL_FILE_TYPE_DC, "DC" },
1320 /* ========================================================================= */
1321 /* level file functions */
1322 /* ========================================================================= */
1324 static boolean check_special_flags(char *flag)
1326 if (strEqual(options.special_flags, flag) ||
1327 strEqual(leveldir_current->special_flags, flag))
1333 static struct DateInfo getCurrentDate()
1335 time_t epoch_seconds = time(NULL);
1336 struct tm *now = localtime(&epoch_seconds);
1337 struct DateInfo date;
1339 date.year = now->tm_year + 1900;
1340 date.month = now->tm_mon + 1;
1341 date.day = now->tm_mday;
1343 date.src = DATE_SRC_CLOCK;
1348 static void resetEventFlags(struct ElementChangeInfo *change)
1352 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1353 change->has_event[i] = FALSE;
1356 static void resetEventBits()
1360 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1361 xx_event_bits[i] = 0;
1364 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1368 /* important: only change event flag if corresponding event bit is set
1369 (this is because all xx_event_bits[] values are loaded separately,
1370 and all xx_event_bits[] values are set back to zero before loading
1371 another value xx_event_bits[x] (each value representing 32 flags)) */
1373 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1374 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1375 change->has_event[i] = TRUE;
1378 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1382 /* in contrast to the above function setEventFlagsFromEventBits(), it
1383 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1384 depending on the corresponding change->has_event[i] values here, as
1385 all xx_event_bits[] values are reset in resetEventBits() before */
1387 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1388 if (change->has_event[i])
1389 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1392 static char *getDefaultElementDescription(struct ElementInfo *ei)
1394 static char description[MAX_ELEMENT_NAME_LEN + 1];
1395 char *default_description = (ei->custom_description != NULL ?
1396 ei->custom_description :
1397 ei->editor_description);
1400 /* always start with reliable default values */
1401 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1402 description[i] = '\0';
1404 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1405 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1407 return &description[0];
1410 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1412 char *default_description = getDefaultElementDescription(ei);
1415 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1416 ei->description[i] = default_description[i];
1419 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1423 for (i = 0; conf[i].data_type != -1; i++)
1425 int default_value = conf[i].default_value;
1426 int data_type = conf[i].data_type;
1427 int conf_type = conf[i].conf_type;
1428 int byte_mask = conf_type & CONF_MASK_BYTES;
1430 if (byte_mask == CONF_MASK_MULTI_BYTES)
1432 int default_num_entities = conf[i].default_num_entities;
1433 int max_num_entities = conf[i].max_num_entities;
1435 *(int *)(conf[i].num_entities) = default_num_entities;
1437 if (data_type == TYPE_STRING)
1439 char *default_string = conf[i].default_string;
1440 char *string = (char *)(conf[i].value);
1442 strncpy(string, default_string, max_num_entities);
1444 else if (data_type == TYPE_ELEMENT_LIST)
1446 int *element_array = (int *)(conf[i].value);
1449 for (j = 0; j < max_num_entities; j++)
1450 element_array[j] = default_value;
1452 else if (data_type == TYPE_CONTENT_LIST)
1454 struct Content *content = (struct Content *)(conf[i].value);
1457 for (c = 0; c < max_num_entities; c++)
1458 for (y = 0; y < 3; y++)
1459 for (x = 0; x < 3; x++)
1460 content[c].e[x][y] = default_value;
1463 else /* constant size configuration data (1, 2 or 4 bytes) */
1465 if (data_type == TYPE_BOOLEAN)
1466 *(boolean *)(conf[i].value) = default_value;
1468 *(int *) (conf[i].value) = default_value;
1473 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1477 for (i = 0; conf[i].data_type != -1; i++)
1479 int data_type = conf[i].data_type;
1480 int conf_type = conf[i].conf_type;
1481 int byte_mask = conf_type & CONF_MASK_BYTES;
1483 if (byte_mask == CONF_MASK_MULTI_BYTES)
1485 int max_num_entities = conf[i].max_num_entities;
1487 if (data_type == TYPE_STRING)
1489 char *string = (char *)(conf[i].value);
1490 char *string_copy = (char *)(conf[i].value_copy);
1492 strncpy(string_copy, string, max_num_entities);
1494 else if (data_type == TYPE_ELEMENT_LIST)
1496 int *element_array = (int *)(conf[i].value);
1497 int *element_array_copy = (int *)(conf[i].value_copy);
1500 for (j = 0; j < max_num_entities; j++)
1501 element_array_copy[j] = element_array[j];
1503 else if (data_type == TYPE_CONTENT_LIST)
1505 struct Content *content = (struct Content *)(conf[i].value);
1506 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1509 for (c = 0; c < max_num_entities; c++)
1510 for (y = 0; y < 3; y++)
1511 for (x = 0; x < 3; x++)
1512 content_copy[c].e[x][y] = content[c].e[x][y];
1515 else /* constant size configuration data (1, 2 or 4 bytes) */
1517 if (data_type == TYPE_BOOLEAN)
1518 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1520 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1525 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1529 xx_ei = *ei_from; /* copy element data into temporary buffer */
1530 yy_ei = *ei_to; /* copy element data into temporary buffer */
1532 copyConfigFromConfigList(chunk_config_CUSX_base);
1537 /* ---------- reinitialize and copy change pages ---------- */
1539 ei_to->num_change_pages = ei_from->num_change_pages;
1540 ei_to->current_change_page = ei_from->current_change_page;
1542 setElementChangePages(ei_to, ei_to->num_change_pages);
1544 for (i = 0; i < ei_to->num_change_pages; i++)
1545 ei_to->change_page[i] = ei_from->change_page[i];
1547 /* ---------- copy group element info ---------- */
1548 if (ei_from->group != NULL && ei_to->group != NULL) /* group or internal */
1549 *ei_to->group = *ei_from->group;
1551 /* mark this custom element as modified */
1552 ei_to->modified_settings = TRUE;
1555 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1557 int change_page_size = sizeof(struct ElementChangeInfo);
1559 ei->num_change_pages = MAX(1, change_pages);
1562 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1564 if (ei->current_change_page >= ei->num_change_pages)
1565 ei->current_change_page = ei->num_change_pages - 1;
1567 ei->change = &ei->change_page[ei->current_change_page];
1570 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1572 xx_change = *change; /* copy change data into temporary buffer */
1574 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1576 *change = xx_change;
1578 resetEventFlags(change);
1580 change->direct_action = 0;
1581 change->other_action = 0;
1583 change->pre_change_function = NULL;
1584 change->change_function = NULL;
1585 change->post_change_function = NULL;
1588 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1592 li = *level; /* copy level data into temporary buffer */
1593 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1594 *level = li; /* copy temporary buffer back to level data */
1596 setLevelInfoToDefaults_EM();
1597 setLevelInfoToDefaults_SP();
1599 level->native_em_level = &native_em_level;
1600 level->native_sp_level = &native_sp_level;
1602 level->file_version = FILE_VERSION_ACTUAL;
1603 level->game_version = GAME_VERSION_ACTUAL;
1605 level->creation_date = getCurrentDate();
1607 level->encoding_16bit_field = TRUE;
1608 level->encoding_16bit_yamyam = TRUE;
1609 level->encoding_16bit_amoeba = TRUE;
1611 /* clear level name and level author string buffers */
1612 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1613 level->name[i] = '\0';
1614 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1615 level->author[i] = '\0';
1617 /* set level name and level author to default values */
1618 strcpy(level->name, NAMELESS_LEVEL_NAME);
1619 strcpy(level->author, ANONYMOUS_NAME);
1621 /* set level playfield to playable default level with player and exit */
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 level->field[0][0] = EL_PLAYER_1;
1627 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1629 BorderElement = EL_STEELWALL;
1631 /* set all bug compatibility flags to "false" => do not emulate this bug */
1632 level->use_action_after_change_bug = FALSE;
1634 if (leveldir_current)
1636 /* try to determine better author name than 'anonymous' */
1637 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1639 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1640 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1644 switch (LEVELCLASS(leveldir_current))
1646 case LEVELCLASS_TUTORIAL:
1647 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1650 case LEVELCLASS_CONTRIB:
1651 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1652 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1655 case LEVELCLASS_PRIVATE:
1656 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1657 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1661 /* keep default value */
1668 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1670 static boolean clipboard_elements_initialized = FALSE;
1673 InitElementPropertiesStatic();
1675 li = *level; /* copy level data into temporary buffer */
1676 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1677 *level = li; /* copy temporary buffer back to level data */
1679 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1682 struct ElementInfo *ei = &element_info[element];
1684 /* never initialize clipboard elements after the very first time */
1685 /* (to be able to use clipboard elements between several levels) */
1686 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1689 if (IS_ENVELOPE(element))
1691 int envelope_nr = element - EL_ENVELOPE_1;
1693 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1695 level->envelope[envelope_nr] = xx_envelope;
1698 if (IS_CUSTOM_ELEMENT(element) ||
1699 IS_GROUP_ELEMENT(element) ||
1700 IS_INTERNAL_ELEMENT(element))
1702 xx_ei = *ei; /* copy element data into temporary buffer */
1704 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1709 setElementChangePages(ei, 1);
1710 setElementChangeInfoToDefaults(ei->change);
1712 if (IS_CUSTOM_ELEMENT(element) ||
1713 IS_GROUP_ELEMENT(element) ||
1714 IS_INTERNAL_ELEMENT(element))
1716 setElementDescriptionToDefault(ei);
1718 ei->modified_settings = FALSE;
1721 if (IS_CUSTOM_ELEMENT(element) ||
1722 IS_INTERNAL_ELEMENT(element))
1724 /* internal values used in level editor */
1726 ei->access_type = 0;
1727 ei->access_layer = 0;
1728 ei->access_protected = 0;
1729 ei->walk_to_action = 0;
1730 ei->smash_targets = 0;
1733 ei->can_explode_by_fire = FALSE;
1734 ei->can_explode_smashed = FALSE;
1735 ei->can_explode_impact = FALSE;
1737 ei->current_change_page = 0;
1740 if (IS_GROUP_ELEMENT(element) ||
1741 IS_INTERNAL_ELEMENT(element))
1743 struct ElementGroupInfo *group;
1745 /* initialize memory for list of elements in group */
1746 if (ei->group == NULL)
1747 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1751 xx_group = *group; /* copy group data into temporary buffer */
1753 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1759 clipboard_elements_initialized = TRUE;
1762 static void setLevelInfoToDefaults(struct LevelInfo *level,
1763 boolean level_info_only)
1765 setLevelInfoToDefaults_Level(level);
1767 if (!level_info_only)
1768 setLevelInfoToDefaults_Elements(level);
1770 level->no_valid_file = FALSE;
1772 level->changed = FALSE;
1775 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1777 level_file_info->nr = 0;
1778 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1779 level_file_info->packed = FALSE;
1780 level_file_info->basename = NULL;
1781 level_file_info->filename = NULL;
1784 static void ActivateLevelTemplate()
1788 /* Currently there is no special action needed to activate the template
1789 data, because 'element_info' property settings overwrite the original
1790 level data, while all other variables do not change. */
1792 /* Exception: 'from_level_template' elements in the original level playfield
1793 are overwritten with the corresponding elements at the same position in
1794 playfield from the level template. */
1796 for (x = 0; x < level.fieldx; x++)
1797 for (y = 0; y < level.fieldy; y++)
1798 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1799 level.field[x][y] = level_template.field[x][y];
1801 if (check_special_flags("load_xsb_to_ces"))
1803 struct LevelInfo level_backup = level;
1805 /* overwrite all individual level settings from template level settings */
1806 level = level_template;
1808 /* restore playfield size */
1809 level.fieldx = level_backup.fieldx;
1810 level.fieldy = level_backup.fieldy;
1812 /* restore playfield content */
1813 for (x = 0; x < level.fieldx; x++)
1814 for (y = 0; y < level.fieldy; y++)
1815 level.field[x][y] = level_backup.field[x][y];
1817 /* restore name and author from individual level */
1818 strcpy(level.name, level_backup.name);
1819 strcpy(level.author, level_backup.author);
1821 /* restore flag "use_custom_template" */
1822 level.use_custom_template = level_backup.use_custom_template;
1826 static char *getLevelFilenameFromBasename(char *basename)
1828 static char *filename = NULL;
1830 checked_free(filename);
1832 filename = getPath2(getCurrentLevelDir(), basename);
1837 static int getFileTypeFromBasename(char *basename)
1839 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1841 static char *filename = NULL;
1842 struct stat file_status;
1844 /* ---------- try to determine file type from filename ---------- */
1846 /* check for typical filename of a Supaplex level package file */
1847 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1848 return LEVEL_FILE_TYPE_SP;
1850 /* check for typical filename of a Diamond Caves II level package file */
1851 if (strSuffixLower(basename, ".dc") ||
1852 strSuffixLower(basename, ".dc2"))
1853 return LEVEL_FILE_TYPE_DC;
1855 /* check for typical filename of a Sokoban level package file */
1856 if (strSuffixLower(basename, ".xsb") &&
1857 strchr(basename, '%') == NULL)
1858 return LEVEL_FILE_TYPE_SB;
1860 /* ---------- try to determine file type from filesize ---------- */
1862 checked_free(filename);
1863 filename = getPath2(getCurrentLevelDir(), basename);
1865 if (stat(filename, &file_status) == 0)
1867 /* check for typical filesize of a Supaplex level package file */
1868 if (file_status.st_size == 170496)
1869 return LEVEL_FILE_TYPE_SP;
1872 return LEVEL_FILE_TYPE_UNKNOWN;
1875 static boolean checkForPackageFromBasename(char *basename)
1877 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
1878 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
1880 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
1883 static char *getSingleLevelBasenameExt(int nr, char *extension)
1885 static char basename[MAX_FILENAME_LEN];
1888 sprintf(basename, "template.%s", extension);
1890 sprintf(basename, "%03d.%s", nr, extension);
1895 static char *getSingleLevelBasename(int nr)
1897 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
1900 static char *getPackedLevelBasename(int type)
1902 static char basename[MAX_FILENAME_LEN];
1903 char *directory = getCurrentLevelDir();
1905 DirectoryEntry *dir_entry;
1907 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
1909 if ((dir = openDirectory(directory)) == NULL)
1911 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
1916 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
1918 char *entry_basename = dir_entry->basename;
1919 int entry_type = getFileTypeFromBasename(entry_basename);
1921 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
1923 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
1926 strcpy(basename, entry_basename);
1933 closeDirectory(dir);
1938 static char *getSingleLevelFilename(int nr)
1940 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
1943 #if ENABLE_UNUSED_CODE
1944 static char *getPackedLevelFilename(int type)
1946 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
1950 char *getDefaultLevelFilename(int nr)
1952 return getSingleLevelFilename(nr);
1955 #if ENABLE_UNUSED_CODE
1956 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
1960 lfi->packed = FALSE;
1961 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
1962 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1966 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
1967 int type, char *format, ...)
1969 static char basename[MAX_FILENAME_LEN];
1972 va_start(ap, format);
1973 vsprintf(basename, format, ap);
1977 lfi->packed = FALSE;
1978 lfi->basename = basename;
1979 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1982 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
1987 lfi->basename = getPackedLevelBasename(lfi->type);
1988 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1991 static int getFiletypeFromID(char *filetype_id)
1993 char *filetype_id_lower;
1994 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
1997 if (filetype_id == NULL)
1998 return LEVEL_FILE_TYPE_UNKNOWN;
2000 filetype_id_lower = getStringToLower(filetype_id);
2002 for (i = 0; filetype_id_list[i].id != NULL; i++)
2004 char *id_lower = getStringToLower(filetype_id_list[i].id);
2006 if (strEqual(filetype_id_lower, id_lower))
2007 filetype = filetype_id_list[i].filetype;
2011 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2015 free(filetype_id_lower);
2020 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2024 /* special case: level number is negative => check for level template file */
2027 /* global variable "leveldir_current" must be modified in the loop below */
2028 LevelDirTree *leveldir_current_last = leveldir_current;
2030 /* check for template level in path from current to topmost tree node */
2032 while (leveldir_current != NULL)
2034 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2035 "template.%s", LEVELFILE_EXTENSION);
2037 if (fileExists(lfi->filename))
2040 leveldir_current = leveldir_current->node_parent;
2043 /* restore global variable "leveldir_current" modified in above loop */
2044 leveldir_current = leveldir_current_last;
2046 /* no fallback if template file not existing */
2050 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2051 if (leveldir_current->level_filename != NULL)
2053 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2055 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2056 leveldir_current->level_filename, nr);
2058 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2060 if (fileExists(lfi->filename))
2064 /* check for native Rocks'n'Diamonds level file */
2065 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2066 "%03d.%s", nr, LEVELFILE_EXTENSION);
2067 if (fileExists(lfi->filename))
2070 /* check for Emerald Mine level file (V1) */
2071 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2072 'a' + (nr / 10) % 26, '0' + nr % 10);
2073 if (fileExists(lfi->filename))
2075 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2076 'A' + (nr / 10) % 26, '0' + nr % 10);
2077 if (fileExists(lfi->filename))
2080 /* check for Emerald Mine level file (V2 to V5) */
2081 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2082 if (fileExists(lfi->filename))
2085 /* check for Emerald Mine level file (V6 / single mode) */
2086 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2087 if (fileExists(lfi->filename))
2089 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2090 if (fileExists(lfi->filename))
2093 /* check for Emerald Mine level file (V6 / teamwork mode) */
2094 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2095 if (fileExists(lfi->filename))
2097 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2098 if (fileExists(lfi->filename))
2101 /* check for various packed level file formats */
2102 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2103 if (fileExists(lfi->filename))
2106 /* no known level file found -- use default values (and fail later) */
2107 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2108 "%03d.%s", nr, LEVELFILE_EXTENSION);
2111 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2113 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2114 lfi->type = getFileTypeFromBasename(lfi->basename);
2117 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2119 /* always start with reliable default values */
2120 setFileInfoToDefaults(level_file_info);
2122 level_file_info->nr = nr; /* set requested level number */
2124 determineLevelFileInfo_Filename(level_file_info);
2125 determineLevelFileInfo_Filetype(level_file_info);
2128 /* ------------------------------------------------------------------------- */
2129 /* functions for loading R'n'D level */
2130 /* ------------------------------------------------------------------------- */
2132 int getMappedElement(int element)
2134 /* remap some (historic, now obsolete) elements */
2138 case EL_PLAYER_OBSOLETE:
2139 element = EL_PLAYER_1;
2142 case EL_KEY_OBSOLETE:
2146 case EL_EM_KEY_1_FILE_OBSOLETE:
2147 element = EL_EM_KEY_1;
2150 case EL_EM_KEY_2_FILE_OBSOLETE:
2151 element = EL_EM_KEY_2;
2154 case EL_EM_KEY_3_FILE_OBSOLETE:
2155 element = EL_EM_KEY_3;
2158 case EL_EM_KEY_4_FILE_OBSOLETE:
2159 element = EL_EM_KEY_4;
2162 case EL_ENVELOPE_OBSOLETE:
2163 element = EL_ENVELOPE_1;
2171 if (element >= NUM_FILE_ELEMENTS)
2173 Error(ERR_WARN, "invalid level element %d", element);
2175 element = EL_UNKNOWN;
2183 int getMappedElementByVersion(int element, int game_version)
2185 /* remap some elements due to certain game version */
2187 if (game_version <= VERSION_IDENT(2,2,0,0))
2189 /* map game font elements */
2190 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2191 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2192 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2193 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2196 if (game_version < VERSION_IDENT(3,0,0,0))
2198 /* map Supaplex gravity tube elements */
2199 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2200 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2201 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2202 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2209 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2211 level->file_version = getFileVersion(file);
2212 level->game_version = getFileVersion(file);
2217 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2219 level->creation_date.year = getFile16BitBE(file);
2220 level->creation_date.month = getFile8Bit(file);
2221 level->creation_date.day = getFile8Bit(file);
2223 level->creation_date.src = DATE_SRC_LEVELFILE;
2228 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2230 int initial_player_stepsize;
2231 int initial_player_gravity;
2234 level->fieldx = getFile8Bit(file);
2235 level->fieldy = getFile8Bit(file);
2237 level->time = getFile16BitBE(file);
2238 level->gems_needed = getFile16BitBE(file);
2240 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2241 level->name[i] = getFile8Bit(file);
2242 level->name[MAX_LEVEL_NAME_LEN] = 0;
2244 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2245 level->score[i] = getFile8Bit(file);
2247 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2248 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2249 for (y = 0; y < 3; y++)
2250 for (x = 0; x < 3; x++)
2251 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2253 level->amoeba_speed = getFile8Bit(file);
2254 level->time_magic_wall = getFile8Bit(file);
2255 level->time_wheel = getFile8Bit(file);
2256 level->amoeba_content = getMappedElement(getFile8Bit(file));
2258 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2261 for (i = 0; i < MAX_PLAYERS; i++)
2262 level->initial_player_stepsize[i] = initial_player_stepsize;
2264 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2266 for (i = 0; i < MAX_PLAYERS; i++)
2267 level->initial_player_gravity[i] = initial_player_gravity;
2269 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2270 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2272 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2274 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2275 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2276 level->can_move_into_acid_bits = getFile32BitBE(file);
2277 level->dont_collide_with_bits = getFile8Bit(file);
2279 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2280 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2282 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2283 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2284 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2286 level->game_engine_type = getFile8Bit(file);
2288 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2293 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2297 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2298 level->name[i] = getFile8Bit(file);
2299 level->name[MAX_LEVEL_NAME_LEN] = 0;
2304 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2308 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2309 level->author[i] = getFile8Bit(file);
2310 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2315 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2318 int chunk_size_expected = level->fieldx * level->fieldy;
2320 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2321 stored with 16-bit encoding (and should be twice as big then).
2322 Even worse, playfield data was stored 16-bit when only yamyam content
2323 contained 16-bit elements and vice versa. */
2325 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2326 chunk_size_expected *= 2;
2328 if (chunk_size_expected != chunk_size)
2330 ReadUnusedBytesFromFile(file, chunk_size);
2331 return chunk_size_expected;
2334 for (y = 0; y < level->fieldy; y++)
2335 for (x = 0; x < level->fieldx; x++)
2336 level->field[x][y] =
2337 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2342 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2345 int header_size = 4;
2346 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2347 int chunk_size_expected = header_size + content_size;
2349 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2350 stored with 16-bit encoding (and should be twice as big then).
2351 Even worse, playfield data was stored 16-bit when only yamyam content
2352 contained 16-bit elements and vice versa. */
2354 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2355 chunk_size_expected += content_size;
2357 if (chunk_size_expected != chunk_size)
2359 ReadUnusedBytesFromFile(file, chunk_size);
2360 return chunk_size_expected;
2364 level->num_yamyam_contents = getFile8Bit(file);
2368 /* correct invalid number of content fields -- should never happen */
2369 if (level->num_yamyam_contents < 1 ||
2370 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2371 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2373 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2374 for (y = 0; y < 3; y++)
2375 for (x = 0; x < 3; x++)
2376 level->yamyam_content[i].e[x][y] =
2377 getMappedElement(level->encoding_16bit_field ?
2378 getFile16BitBE(file) : getFile8Bit(file));
2382 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2387 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2389 element = getMappedElement(getFile16BitBE(file));
2390 num_contents = getFile8Bit(file);
2392 getFile8Bit(file); /* content x size (unused) */
2393 getFile8Bit(file); /* content y size (unused) */
2395 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2397 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2398 for (y = 0; y < 3; y++)
2399 for (x = 0; x < 3; x++)
2400 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2402 /* correct invalid number of content fields -- should never happen */
2403 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2404 num_contents = STD_ELEMENT_CONTENTS;
2406 if (element == EL_YAMYAM)
2408 level->num_yamyam_contents = num_contents;
2410 for (i = 0; i < num_contents; i++)
2411 for (y = 0; y < 3; y++)
2412 for (x = 0; x < 3; x++)
2413 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2415 else if (element == EL_BD_AMOEBA)
2417 level->amoeba_content = content_array[0][0][0];
2421 Error(ERR_WARN, "cannot load content for element '%d'", element);
2427 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2433 int chunk_size_expected;
2435 element = getMappedElement(getFile16BitBE(file));
2436 if (!IS_ENVELOPE(element))
2437 element = EL_ENVELOPE_1;
2439 envelope_nr = element - EL_ENVELOPE_1;
2441 envelope_len = getFile16BitBE(file);
2443 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2444 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2446 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2448 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2449 if (chunk_size_expected != chunk_size)
2451 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2452 return chunk_size_expected;
2455 for (i = 0; i < envelope_len; i++)
2456 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2461 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2463 int num_changed_custom_elements = getFile16BitBE(file);
2464 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2467 if (chunk_size_expected != chunk_size)
2469 ReadUnusedBytesFromFile(file, chunk_size - 2);
2470 return chunk_size_expected;
2473 for (i = 0; i < num_changed_custom_elements; i++)
2475 int element = getMappedElement(getFile16BitBE(file));
2476 int properties = getFile32BitBE(file);
2478 if (IS_CUSTOM_ELEMENT(element))
2479 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2481 Error(ERR_WARN, "invalid custom element number %d", element);
2483 /* older game versions that wrote level files with CUS1 chunks used
2484 different default push delay values (not yet stored in level file) */
2485 element_info[element].push_delay_fixed = 2;
2486 element_info[element].push_delay_random = 8;
2492 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2494 int num_changed_custom_elements = getFile16BitBE(file);
2495 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2498 if (chunk_size_expected != chunk_size)
2500 ReadUnusedBytesFromFile(file, chunk_size - 2);
2501 return chunk_size_expected;
2504 for (i = 0; i < num_changed_custom_elements; i++)
2506 int element = getMappedElement(getFile16BitBE(file));
2507 int custom_target_element = getMappedElement(getFile16BitBE(file));
2509 if (IS_CUSTOM_ELEMENT(element))
2510 element_info[element].change->target_element = custom_target_element;
2512 Error(ERR_WARN, "invalid custom element number %d", element);
2518 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2520 int num_changed_custom_elements = getFile16BitBE(file);
2521 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2524 if (chunk_size_expected != chunk_size)
2526 ReadUnusedBytesFromFile(file, chunk_size - 2);
2527 return chunk_size_expected;
2530 for (i = 0; i < num_changed_custom_elements; i++)
2532 int element = getMappedElement(getFile16BitBE(file));
2533 struct ElementInfo *ei = &element_info[element];
2534 unsigned int event_bits;
2536 if (!IS_CUSTOM_ELEMENT(element))
2538 Error(ERR_WARN, "invalid custom element number %d", element);
2540 element = EL_INTERNAL_DUMMY;
2543 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2544 ei->description[j] = getFile8Bit(file);
2545 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2547 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2549 /* some free bytes for future properties and padding */
2550 ReadUnusedBytesFromFile(file, 7);
2552 ei->use_gfx_element = getFile8Bit(file);
2553 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2555 ei->collect_score_initial = getFile8Bit(file);
2556 ei->collect_count_initial = getFile8Bit(file);
2558 ei->push_delay_fixed = getFile16BitBE(file);
2559 ei->push_delay_random = getFile16BitBE(file);
2560 ei->move_delay_fixed = getFile16BitBE(file);
2561 ei->move_delay_random = getFile16BitBE(file);
2563 ei->move_pattern = getFile16BitBE(file);
2564 ei->move_direction_initial = getFile8Bit(file);
2565 ei->move_stepsize = getFile8Bit(file);
2567 for (y = 0; y < 3; y++)
2568 for (x = 0; x < 3; x++)
2569 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2571 event_bits = getFile32BitBE(file);
2572 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2573 if (event_bits & (1 << j))
2574 ei->change->has_event[j] = TRUE;
2576 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2578 ei->change->delay_fixed = getFile16BitBE(file);
2579 ei->change->delay_random = getFile16BitBE(file);
2580 ei->change->delay_frames = getFile16BitBE(file);
2582 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2584 ei->change->explode = getFile8Bit(file);
2585 ei->change->use_target_content = getFile8Bit(file);
2586 ei->change->only_if_complete = getFile8Bit(file);
2587 ei->change->use_random_replace = getFile8Bit(file);
2589 ei->change->random_percentage = getFile8Bit(file);
2590 ei->change->replace_when = getFile8Bit(file);
2592 for (y = 0; y < 3; y++)
2593 for (x = 0; x < 3; x++)
2594 ei->change->target_content.e[x][y] =
2595 getMappedElement(getFile16BitBE(file));
2597 ei->slippery_type = getFile8Bit(file);
2599 /* some free bytes for future properties and padding */
2600 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2602 /* mark that this custom element has been modified */
2603 ei->modified_settings = TRUE;
2609 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2611 struct ElementInfo *ei;
2612 int chunk_size_expected;
2616 /* ---------- custom element base property values (96 bytes) ------------- */
2618 element = getMappedElement(getFile16BitBE(file));
2620 if (!IS_CUSTOM_ELEMENT(element))
2622 Error(ERR_WARN, "invalid custom element number %d", element);
2624 ReadUnusedBytesFromFile(file, chunk_size - 2);
2628 ei = &element_info[element];
2630 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2631 ei->description[i] = getFile8Bit(file);
2632 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2634 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2636 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2638 ei->num_change_pages = getFile8Bit(file);
2640 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2641 if (chunk_size_expected != chunk_size)
2643 ReadUnusedBytesFromFile(file, chunk_size - 43);
2644 return chunk_size_expected;
2647 ei->ce_value_fixed_initial = getFile16BitBE(file);
2648 ei->ce_value_random_initial = getFile16BitBE(file);
2649 ei->use_last_ce_value = getFile8Bit(file);
2651 ei->use_gfx_element = getFile8Bit(file);
2652 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2654 ei->collect_score_initial = getFile8Bit(file);
2655 ei->collect_count_initial = getFile8Bit(file);
2657 ei->drop_delay_fixed = getFile8Bit(file);
2658 ei->push_delay_fixed = getFile8Bit(file);
2659 ei->drop_delay_random = getFile8Bit(file);
2660 ei->push_delay_random = getFile8Bit(file);
2661 ei->move_delay_fixed = getFile16BitBE(file);
2662 ei->move_delay_random = getFile16BitBE(file);
2664 /* bits 0 - 15 of "move_pattern" ... */
2665 ei->move_pattern = getFile16BitBE(file);
2666 ei->move_direction_initial = getFile8Bit(file);
2667 ei->move_stepsize = getFile8Bit(file);
2669 ei->slippery_type = getFile8Bit(file);
2671 for (y = 0; y < 3; y++)
2672 for (x = 0; x < 3; x++)
2673 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2675 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2676 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2677 ei->move_leave_type = getFile8Bit(file);
2679 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2680 ei->move_pattern |= (getFile16BitBE(file) << 16);
2682 ei->access_direction = getFile8Bit(file);
2684 ei->explosion_delay = getFile8Bit(file);
2685 ei->ignition_delay = getFile8Bit(file);
2686 ei->explosion_type = getFile8Bit(file);
2688 /* some free bytes for future custom property values and padding */
2689 ReadUnusedBytesFromFile(file, 1);
2691 /* ---------- change page property values (48 bytes) --------------------- */
2693 setElementChangePages(ei, ei->num_change_pages);
2695 for (i = 0; i < ei->num_change_pages; i++)
2697 struct ElementChangeInfo *change = &ei->change_page[i];
2698 unsigned int event_bits;
2700 /* always start with reliable default values */
2701 setElementChangeInfoToDefaults(change);
2703 /* bits 0 - 31 of "has_event[]" ... */
2704 event_bits = getFile32BitBE(file);
2705 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2706 if (event_bits & (1 << j))
2707 change->has_event[j] = TRUE;
2709 change->target_element = getMappedElement(getFile16BitBE(file));
2711 change->delay_fixed = getFile16BitBE(file);
2712 change->delay_random = getFile16BitBE(file);
2713 change->delay_frames = getFile16BitBE(file);
2715 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2717 change->explode = getFile8Bit(file);
2718 change->use_target_content = getFile8Bit(file);
2719 change->only_if_complete = getFile8Bit(file);
2720 change->use_random_replace = getFile8Bit(file);
2722 change->random_percentage = getFile8Bit(file);
2723 change->replace_when = getFile8Bit(file);
2725 for (y = 0; y < 3; y++)
2726 for (x = 0; x < 3; x++)
2727 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2729 change->can_change = getFile8Bit(file);
2731 change->trigger_side = getFile8Bit(file);
2733 change->trigger_player = getFile8Bit(file);
2734 change->trigger_page = getFile8Bit(file);
2736 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2737 CH_PAGE_ANY : (1 << change->trigger_page));
2739 change->has_action = getFile8Bit(file);
2740 change->action_type = getFile8Bit(file);
2741 change->action_mode = getFile8Bit(file);
2742 change->action_arg = getFile16BitBE(file);
2744 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2745 event_bits = getFile8Bit(file);
2746 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2747 if (event_bits & (1 << (j - 32)))
2748 change->has_event[j] = TRUE;
2751 /* mark this custom element as modified */
2752 ei->modified_settings = TRUE;
2757 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2759 struct ElementInfo *ei;
2760 struct ElementGroupInfo *group;
2764 element = getMappedElement(getFile16BitBE(file));
2766 if (!IS_GROUP_ELEMENT(element))
2768 Error(ERR_WARN, "invalid group element number %d", element);
2770 ReadUnusedBytesFromFile(file, chunk_size - 2);
2774 ei = &element_info[element];
2776 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2777 ei->description[i] = getFile8Bit(file);
2778 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2780 group = element_info[element].group;
2782 group->num_elements = getFile8Bit(file);
2784 ei->use_gfx_element = getFile8Bit(file);
2785 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2787 group->choice_mode = getFile8Bit(file);
2789 /* some free bytes for future values and padding */
2790 ReadUnusedBytesFromFile(file, 3);
2792 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2793 group->element[i] = getMappedElement(getFile16BitBE(file));
2795 /* mark this group element as modified */
2796 element_info[element].modified_settings = TRUE;
2801 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2802 int element, int real_element)
2804 int micro_chunk_size = 0;
2805 int conf_type = getFile8Bit(file);
2806 int byte_mask = conf_type & CONF_MASK_BYTES;
2807 boolean element_found = FALSE;
2810 micro_chunk_size += 1;
2812 if (byte_mask == CONF_MASK_MULTI_BYTES)
2814 int num_bytes = getFile16BitBE(file);
2815 byte *buffer = checked_malloc(num_bytes);
2817 ReadBytesFromFile(file, buffer, num_bytes);
2819 for (i = 0; conf[i].data_type != -1; i++)
2821 if (conf[i].element == element &&
2822 conf[i].conf_type == conf_type)
2824 int data_type = conf[i].data_type;
2825 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2826 int max_num_entities = conf[i].max_num_entities;
2828 if (num_entities > max_num_entities)
2831 "truncating number of entities for element %d from %d to %d",
2832 element, num_entities, max_num_entities);
2834 num_entities = max_num_entities;
2837 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
2838 data_type == TYPE_CONTENT_LIST))
2840 /* for element and content lists, zero entities are not allowed */
2841 Error(ERR_WARN, "found empty list of entities for element %d",
2844 /* do not set "num_entities" here to prevent reading behind buffer */
2846 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
2850 *(int *)(conf[i].num_entities) = num_entities;
2853 element_found = TRUE;
2855 if (data_type == TYPE_STRING)
2857 char *string = (char *)(conf[i].value);
2860 for (j = 0; j < max_num_entities; j++)
2861 string[j] = (j < num_entities ? buffer[j] : '\0');
2863 else if (data_type == TYPE_ELEMENT_LIST)
2865 int *element_array = (int *)(conf[i].value);
2868 for (j = 0; j < num_entities; j++)
2870 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
2872 else if (data_type == TYPE_CONTENT_LIST)
2874 struct Content *content= (struct Content *)(conf[i].value);
2877 for (c = 0; c < num_entities; c++)
2878 for (y = 0; y < 3; y++)
2879 for (x = 0; x < 3; x++)
2880 content[c].e[x][y] =
2881 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
2884 element_found = FALSE;
2890 checked_free(buffer);
2892 micro_chunk_size += 2 + num_bytes;
2894 else /* constant size configuration data (1, 2 or 4 bytes) */
2896 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
2897 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
2898 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
2900 for (i = 0; conf[i].data_type != -1; i++)
2902 if (conf[i].element == element &&
2903 conf[i].conf_type == conf_type)
2905 int data_type = conf[i].data_type;
2907 if (data_type == TYPE_ELEMENT)
2908 value = getMappedElement(value);
2910 if (data_type == TYPE_BOOLEAN)
2911 *(boolean *)(conf[i].value) = value;
2913 *(int *) (conf[i].value) = value;
2915 element_found = TRUE;
2921 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
2926 char *error_conf_chunk_bytes =
2927 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
2928 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
2929 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
2930 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
2931 int error_element = real_element;
2933 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
2934 error_conf_chunk_bytes, error_conf_chunk_token,
2935 error_element, EL_NAME(error_element));
2938 return micro_chunk_size;
2941 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
2943 int real_chunk_size = 0;
2945 li = *level; /* copy level data into temporary buffer */
2947 while (!checkEndOfFile(file))
2949 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
2951 if (real_chunk_size >= chunk_size)
2955 *level = li; /* copy temporary buffer back to level data */
2957 return real_chunk_size;
2960 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
2962 int real_chunk_size = 0;
2964 li = *level; /* copy level data into temporary buffer */
2966 while (!checkEndOfFile(file))
2968 int element = getMappedElement(getFile16BitBE(file));
2970 real_chunk_size += 2;
2971 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
2973 if (real_chunk_size >= chunk_size)
2977 *level = li; /* copy temporary buffer back to level data */
2979 return real_chunk_size;
2982 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
2984 int real_chunk_size = 0;
2986 li = *level; /* copy level data into temporary buffer */
2988 while (!checkEndOfFile(file))
2990 int element = getMappedElement(getFile16BitBE(file));
2992 real_chunk_size += 2;
2993 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
2995 if (real_chunk_size >= chunk_size)
2999 *level = li; /* copy temporary buffer back to level data */
3001 return real_chunk_size;
3004 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3006 int element = getMappedElement(getFile16BitBE(file));
3007 int envelope_nr = element - EL_ENVELOPE_1;
3008 int real_chunk_size = 2;
3010 while (!checkEndOfFile(file))
3012 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3015 if (real_chunk_size >= chunk_size)
3019 level->envelope[envelope_nr] = xx_envelope;
3021 return real_chunk_size;
3024 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3026 int element = getMappedElement(getFile16BitBE(file));
3027 int real_chunk_size = 2;
3028 struct ElementInfo *ei = &element_info[element];
3031 xx_ei = *ei; /* copy element data into temporary buffer */
3033 xx_ei.num_change_pages = -1;
3035 while (!checkEndOfFile(file))
3037 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3039 if (xx_ei.num_change_pages != -1)
3042 if (real_chunk_size >= chunk_size)
3048 if (ei->num_change_pages == -1)
3050 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3053 ei->num_change_pages = 1;
3055 setElementChangePages(ei, 1);
3056 setElementChangeInfoToDefaults(ei->change);
3058 return real_chunk_size;
3061 /* initialize number of change pages stored for this custom element */
3062 setElementChangePages(ei, ei->num_change_pages);
3063 for (i = 0; i < ei->num_change_pages; i++)
3064 setElementChangeInfoToDefaults(&ei->change_page[i]);
3066 /* start with reading properties for the first change page */
3067 xx_current_change_page = 0;
3069 while (!checkEndOfFile(file))
3071 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3073 xx_change = *change; /* copy change data into temporary buffer */
3075 resetEventBits(); /* reset bits; change page might have changed */
3077 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3080 *change = xx_change;
3082 setEventFlagsFromEventBits(change);
3084 if (real_chunk_size >= chunk_size)
3088 return real_chunk_size;
3091 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3093 int element = getMappedElement(getFile16BitBE(file));
3094 int real_chunk_size = 2;
3095 struct ElementInfo *ei = &element_info[element];
3096 struct ElementGroupInfo *group = ei->group;
3098 xx_ei = *ei; /* copy element data into temporary buffer */
3099 xx_group = *group; /* copy group data into temporary buffer */
3101 while (!checkEndOfFile(file))
3103 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3106 if (real_chunk_size >= chunk_size)
3113 return real_chunk_size;
3116 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3117 struct LevelFileInfo *level_file_info,
3118 boolean level_info_only)
3120 char *filename = level_file_info->filename;
3121 char cookie[MAX_LINE_LEN];
3122 char chunk_name[CHUNK_ID_LEN + 1];
3126 if (!(file = openFile(filename, MODE_READ)))
3128 level->no_valid_file = TRUE;
3130 if (!level_info_only)
3131 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3136 getFileChunkBE(file, chunk_name, NULL);
3137 if (strEqual(chunk_name, "RND1"))
3139 getFile32BitBE(file); /* not used */
3141 getFileChunkBE(file, chunk_name, NULL);
3142 if (!strEqual(chunk_name, "CAVE"))
3144 level->no_valid_file = TRUE;
3146 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3153 else /* check for pre-2.0 file format with cookie string */
3155 strcpy(cookie, chunk_name);
3156 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3158 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3159 cookie[strlen(cookie) - 1] = '\0';
3161 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3163 level->no_valid_file = TRUE;
3165 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3172 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3174 level->no_valid_file = TRUE;
3176 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3183 /* pre-2.0 level files have no game version, so use file version here */
3184 level->game_version = level->file_version;
3187 if (level->file_version < FILE_VERSION_1_2)
3189 /* level files from versions before 1.2.0 without chunk structure */
3190 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3191 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3199 int (*loader)(File *, int, struct LevelInfo *);
3203 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3204 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3205 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3206 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3207 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3208 { "INFO", -1, LoadLevel_INFO },
3209 { "BODY", -1, LoadLevel_BODY },
3210 { "CONT", -1, LoadLevel_CONT },
3211 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3212 { "CNT3", -1, LoadLevel_CNT3 },
3213 { "CUS1", -1, LoadLevel_CUS1 },
3214 { "CUS2", -1, LoadLevel_CUS2 },
3215 { "CUS3", -1, LoadLevel_CUS3 },
3216 { "CUS4", -1, LoadLevel_CUS4 },
3217 { "GRP1", -1, LoadLevel_GRP1 },
3218 { "CONF", -1, LoadLevel_CONF },
3219 { "ELEM", -1, LoadLevel_ELEM },
3220 { "NOTE", -1, LoadLevel_NOTE },
3221 { "CUSX", -1, LoadLevel_CUSX },
3222 { "GRPX", -1, LoadLevel_GRPX },
3227 while (getFileChunkBE(file, chunk_name, &chunk_size))
3231 while (chunk_info[i].name != NULL &&
3232 !strEqual(chunk_name, chunk_info[i].name))
3235 if (chunk_info[i].name == NULL)
3237 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3238 chunk_name, filename);
3239 ReadUnusedBytesFromFile(file, chunk_size);
3241 else if (chunk_info[i].size != -1 &&
3242 chunk_info[i].size != chunk_size)
3244 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3245 chunk_size, chunk_name, filename);
3246 ReadUnusedBytesFromFile(file, chunk_size);
3250 /* call function to load this level chunk */
3251 int chunk_size_expected =
3252 (chunk_info[i].loader)(file, chunk_size, level);
3254 /* the size of some chunks cannot be checked before reading other
3255 chunks first (like "HEAD" and "BODY") that contain some header
3256 information, so check them here */
3257 if (chunk_size_expected != chunk_size)
3259 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3260 chunk_size, chunk_name, filename);
3270 /* ------------------------------------------------------------------------- */
3271 /* functions for loading EM level */
3272 /* ------------------------------------------------------------------------- */
3274 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3276 static int ball_xy[8][2] =
3287 struct LevelInfo_EM *level_em = level->native_em_level;
3288 struct LEVEL *lev = level_em->lev;
3289 struct PLAYER **ply = level_em->ply;
3292 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3293 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3295 lev->time_seconds = level->time;
3296 lev->required_initial = level->gems_needed;
3298 lev->emerald_score = level->score[SC_EMERALD];
3299 lev->diamond_score = level->score[SC_DIAMOND];
3300 lev->alien_score = level->score[SC_ROBOT];
3301 lev->tank_score = level->score[SC_SPACESHIP];
3302 lev->bug_score = level->score[SC_BUG];
3303 lev->eater_score = level->score[SC_YAMYAM];
3304 lev->nut_score = level->score[SC_NUT];
3305 lev->dynamite_score = level->score[SC_DYNAMITE];
3306 lev->key_score = level->score[SC_KEY];
3307 lev->exit_score = level->score[SC_TIME_BONUS];
3309 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3310 for (y = 0; y < 3; y++)
3311 for (x = 0; x < 3; x++)
3312 lev->eater_array[i][y * 3 + x] =
3313 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3315 lev->amoeba_time = level->amoeba_speed;
3316 lev->wonderwall_time_initial = level->time_magic_wall;
3317 lev->wheel_time = level->time_wheel;
3319 lev->android_move_time = level->android_move_time;
3320 lev->android_clone_time = level->android_clone_time;
3321 lev->ball_random = level->ball_random;
3322 lev->ball_state_initial = level->ball_state_initial;
3323 lev->ball_time = level->ball_time;
3324 lev->num_ball_arrays = level->num_ball_contents;
3326 lev->lenses_score = level->lenses_score;
3327 lev->magnify_score = level->magnify_score;
3328 lev->slurp_score = level->slurp_score;
3330 lev->lenses_time = level->lenses_time;
3331 lev->magnify_time = level->magnify_time;
3333 lev->wind_direction_initial =
3334 map_direction_RND_to_EM(level->wind_direction_initial);
3335 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3336 lev->wind_time : 0);
3338 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3339 for (j = 0; j < 8; j++)
3340 lev->ball_array[i][j] =
3341 map_element_RND_to_EM(level->
3342 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3344 map_android_clone_elements_RND_to_EM(level);
3346 /* first fill the complete playfield with the default border element */
3347 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3348 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3349 level_em->cave[x][y] = ZBORDER;
3351 if (BorderElement == EL_STEELWALL)
3353 for (y = 0; y < lev->height + 2; y++)
3354 for (x = 0; x < lev->width + 2; x++)
3355 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3358 /* then copy the real level contents from level file into the playfield */
3359 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3361 int new_element = map_element_RND_to_EM(level->field[x][y]);
3362 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3363 int xx = x + 1 + offset;
3364 int yy = y + 1 + offset;
3366 if (level->field[x][y] == EL_AMOEBA_DEAD)
3367 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3369 level_em->cave[xx][yy] = new_element;
3372 for (i = 0; i < MAX_PLAYERS; i++)
3374 ply[i]->x_initial = 0;
3375 ply[i]->y_initial = 0;
3378 /* initialize player positions and delete players from the playfield */
3379 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3381 if (ELEM_IS_PLAYER(level->field[x][y]))
3383 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3384 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3385 int xx = x + 1 + offset;
3386 int yy = y + 1 + offset;
3388 ply[player_nr]->x_initial = xx;
3389 ply[player_nr]->y_initial = yy;
3391 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3395 if (BorderElement == EL_STEELWALL)
3402 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3404 static int ball_xy[8][2] =
3415 struct LevelInfo_EM *level_em = level->native_em_level;
3416 struct LEVEL *lev = level_em->lev;
3417 struct PLAYER **ply = level_em->ply;
3420 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3421 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3423 level->time = lev->time_seconds;
3424 level->gems_needed = lev->required_initial;
3426 sprintf(level->name, "Level %d", level->file_info.nr);
3428 level->score[SC_EMERALD] = lev->emerald_score;
3429 level->score[SC_DIAMOND] = lev->diamond_score;
3430 level->score[SC_ROBOT] = lev->alien_score;
3431 level->score[SC_SPACESHIP] = lev->tank_score;
3432 level->score[SC_BUG] = lev->bug_score;
3433 level->score[SC_YAMYAM] = lev->eater_score;
3434 level->score[SC_NUT] = lev->nut_score;
3435 level->score[SC_DYNAMITE] = lev->dynamite_score;
3436 level->score[SC_KEY] = lev->key_score;
3437 level->score[SC_TIME_BONUS] = lev->exit_score;
3439 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3441 for (i = 0; i < level->num_yamyam_contents; i++)
3442 for (y = 0; y < 3; y++)
3443 for (x = 0; x < 3; x++)
3444 level->yamyam_content[i].e[x][y] =
3445 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3447 level->amoeba_speed = lev->amoeba_time;
3448 level->time_magic_wall = lev->wonderwall_time_initial;
3449 level->time_wheel = lev->wheel_time;
3451 level->android_move_time = lev->android_move_time;
3452 level->android_clone_time = lev->android_clone_time;
3453 level->ball_random = lev->ball_random;
3454 level->ball_state_initial = lev->ball_state_initial;
3455 level->ball_time = lev->ball_time;
3456 level->num_ball_contents = lev->num_ball_arrays;
3458 level->lenses_score = lev->lenses_score;
3459 level->magnify_score = lev->magnify_score;
3460 level->slurp_score = lev->slurp_score;
3462 level->lenses_time = lev->lenses_time;
3463 level->magnify_time = lev->magnify_time;
3465 level->wind_direction_initial =
3466 map_direction_EM_to_RND(lev->wind_direction_initial);
3468 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3469 for (j = 0; j < 8; j++)
3470 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3471 map_element_EM_to_RND(lev->ball_array[i][j]);
3473 map_android_clone_elements_EM_to_RND(level);
3475 /* convert the playfield (some elements need special treatment) */
3476 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3478 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3480 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3481 new_element = EL_AMOEBA_DEAD;
3483 level->field[x][y] = new_element;
3486 for (i = 0; i < MAX_PLAYERS; i++)
3488 /* in case of all players set to the same field, use the first player */
3489 int nr = MAX_PLAYERS - i - 1;
3490 int jx = ply[nr]->x_initial - 1;
3491 int jy = ply[nr]->y_initial - 1;
3493 if (jx != -1 && jy != -1)
3494 level->field[jx][jy] = EL_PLAYER_1 + nr;
3499 /* ------------------------------------------------------------------------- */
3500 /* functions for loading SP level */
3501 /* ------------------------------------------------------------------------- */
3503 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3505 struct LevelInfo_SP *level_sp = level->native_sp_level;
3506 LevelInfoType *header = &level_sp->header;
3509 level_sp->width = level->fieldx;
3510 level_sp->height = level->fieldy;
3512 for (x = 0; x < level->fieldx; x++)
3513 for (y = 0; y < level->fieldy; y++)
3514 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3516 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3518 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3519 header->LevelTitle[i] = level->name[i];
3520 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3522 header->InfotronsNeeded = level->gems_needed;
3524 header->SpecialPortCount = 0;
3526 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3528 boolean gravity_port_found = FALSE;
3529 boolean gravity_port_valid = FALSE;
3530 int gravity_port_flag;
3531 int gravity_port_base_element;
3532 int element = level->field[x][y];
3534 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3535 element <= EL_SP_GRAVITY_ON_PORT_UP)
3537 gravity_port_found = TRUE;
3538 gravity_port_valid = TRUE;
3539 gravity_port_flag = 1;
3540 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3542 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3543 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3545 gravity_port_found = TRUE;
3546 gravity_port_valid = TRUE;
3547 gravity_port_flag = 0;
3548 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3550 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3551 element <= EL_SP_GRAVITY_PORT_UP)
3553 /* change R'n'D style gravity inverting special port to normal port
3554 (there are no gravity inverting ports in native Supaplex engine) */
3556 gravity_port_found = TRUE;
3557 gravity_port_valid = FALSE;
3558 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3561 if (gravity_port_found)
3563 if (gravity_port_valid &&
3564 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3566 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3568 port->PortLocation = (y * level->fieldx + x) * 2;
3569 port->Gravity = gravity_port_flag;
3571 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3573 header->SpecialPortCount++;
3577 /* change special gravity port to normal port */
3579 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3582 level_sp->playfield[x][y] = element - EL_SP_START;
3587 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3589 struct LevelInfo_SP *level_sp = level->native_sp_level;
3590 LevelInfoType *header = &level_sp->header;
3593 level->fieldx = level_sp->width;
3594 level->fieldy = level_sp->height;
3596 for (x = 0; x < level->fieldx; x++)
3598 for (y = 0; y < level->fieldy; y++)
3600 int element_old = level_sp->playfield[x][y];
3601 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3603 if (element_new == EL_UNKNOWN)
3604 Error(ERR_WARN, "invalid element %d at position %d, %d",
3607 level->field[x][y] = element_new;
3611 for (i = 0; i < MAX_PLAYERS; i++)
3612 level->initial_player_gravity[i] =
3613 (header->InitialGravity == 1 ? TRUE : FALSE);
3615 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3616 level->name[i] = header->LevelTitle[i];
3617 level->name[SP_LEVEL_NAME_LEN] = '\0';
3619 level->gems_needed = header->InfotronsNeeded;
3621 for (i = 0; i < header->SpecialPortCount; i++)
3623 SpecialPortType *port = &header->SpecialPort[i];
3624 int port_location = port->PortLocation;
3625 int gravity = port->Gravity;
3626 int port_x, port_y, port_element;
3628 port_x = (port_location / 2) % level->fieldx;
3629 port_y = (port_location / 2) / level->fieldx;
3631 if (port_x < 0 || port_x >= level->fieldx ||
3632 port_y < 0 || port_y >= level->fieldy)
3634 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3640 port_element = level->field[port_x][port_y];
3642 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3643 port_element > EL_SP_GRAVITY_PORT_UP)
3645 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3650 /* change previous (wrong) gravity inverting special port to either
3651 gravity enabling special port or gravity disabling special port */
3652 level->field[port_x][port_y] +=
3653 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3654 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3657 /* change special gravity ports without database entries to normal ports */
3658 for (x = 0; x < level->fieldx; x++)
3659 for (y = 0; y < level->fieldy; y++)
3660 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3661 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3662 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3664 level->time = 0; /* no time limit */
3665 level->amoeba_speed = 0;
3666 level->time_magic_wall = 0;
3667 level->time_wheel = 0;
3668 level->amoeba_content = EL_EMPTY;
3671 /* original Supaplex does not use score values -- use default values */
3673 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3674 level->score[i] = 0;
3677 /* there are no yamyams in supaplex levels */
3678 for (i = 0; i < level->num_yamyam_contents; i++)
3679 for (x = 0; x < 3; x++)
3680 for (y = 0; y < 3; y++)
3681 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3684 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3686 struct LevelInfo_SP *level_sp = level->native_sp_level;
3687 struct DemoInfo_SP *demo = &level_sp->demo;
3690 /* always start with reliable default values */
3691 demo->is_available = FALSE;
3694 if (TAPE_IS_EMPTY(tape))
3697 demo->level_nr = tape.level_nr; /* (currently not used) */
3699 level_sp->header.DemoRandomSeed = tape.random_seed;
3702 for (i = 0; i < tape.length; i++)
3704 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3705 int demo_repeat = tape.pos[i].delay;
3707 for (j = 0; j < demo_repeat / 16; j++)
3708 demo->data[demo->length++] = 0xf0 | demo_action;
3710 if (demo_repeat % 16)
3711 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3714 demo->data[demo->length++] = 0xff;
3716 demo->is_available = TRUE;
3719 static void setTapeInfoToDefaults();
3721 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3723 struct LevelInfo_SP *level_sp = level->native_sp_level;
3724 struct DemoInfo_SP *demo = &level_sp->demo;
3725 char *filename = level->file_info.filename;
3728 /* always start with reliable default values */
3729 setTapeInfoToDefaults();
3731 if (!demo->is_available)
3734 tape.level_nr = demo->level_nr; /* (currently not used) */
3735 tape.length = demo->length - 1; /* without "end of demo" byte */
3736 tape.random_seed = level_sp->header.DemoRandomSeed;
3738 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3740 for (i = 0; i < demo->length - 1; i++)
3742 int demo_action = demo->data[i] & 0x0f;
3743 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3745 tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
3746 tape.pos[i].delay = demo_repeat + 1;
3749 tape.length_frames = GetTapeLengthFrames();
3750 tape.length_seconds = GetTapeLengthSeconds();
3754 /* ------------------------------------------------------------------------- */
3755 /* functions for loading DC level */
3756 /* ------------------------------------------------------------------------- */
3758 #define DC_LEVEL_HEADER_SIZE 344
3760 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
3762 static int last_data_encoded;
3766 int diff_hi, diff_lo;
3767 int data_hi, data_lo;
3768 unsigned short data_decoded;
3772 last_data_encoded = 0;
3779 diff = data_encoded - last_data_encoded;
3780 diff_hi = diff & ~0xff;
3781 diff_lo = diff & 0xff;
3785 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
3786 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
3787 data_hi = data_hi & 0xff00;
3789 data_decoded = data_hi | data_lo;
3791 last_data_encoded = data_encoded;
3793 offset1 = (offset1 + 1) % 31;
3794 offset2 = offset2 & 0xff;
3796 return data_decoded;
3799 int getMappedElement_DC(int element)
3807 /* 0x0117 - 0x036e: (?) */
3810 /* 0x042d - 0x0684: (?) */
3826 element = EL_CRYSTAL;
3829 case 0x0e77: /* quicksand (boulder) */
3830 element = EL_QUICKSAND_FAST_FULL;
3833 case 0x0e99: /* slow quicksand (boulder) */
3834 element = EL_QUICKSAND_FULL;
3838 element = EL_EM_EXIT_OPEN;
3842 element = EL_EM_EXIT_CLOSED;
3846 element = EL_EM_STEEL_EXIT_OPEN;
3850 element = EL_EM_STEEL_EXIT_CLOSED;
3853 case 0x0f4f: /* dynamite (lit 1) */
3854 element = EL_EM_DYNAMITE_ACTIVE;
3857 case 0x0f57: /* dynamite (lit 2) */
3858 element = EL_EM_DYNAMITE_ACTIVE;
3861 case 0x0f5f: /* dynamite (lit 3) */
3862 element = EL_EM_DYNAMITE_ACTIVE;
3865 case 0x0f67: /* dynamite (lit 4) */
3866 element = EL_EM_DYNAMITE_ACTIVE;
3873 element = EL_AMOEBA_WET;
3877 element = EL_AMOEBA_DROP;
3881 element = EL_DC_MAGIC_WALL;
3885 element = EL_SPACESHIP_UP;
3889 element = EL_SPACESHIP_DOWN;
3893 element = EL_SPACESHIP_LEFT;
3897 element = EL_SPACESHIP_RIGHT;
3901 element = EL_BUG_UP;
3905 element = EL_BUG_DOWN;
3909 element = EL_BUG_LEFT;
3913 element = EL_BUG_RIGHT;
3917 element = EL_MOLE_UP;
3921 element = EL_MOLE_DOWN;
3925 element = EL_MOLE_LEFT;
3929 element = EL_MOLE_RIGHT;
3937 element = EL_YAMYAM;
3941 element = EL_SWITCHGATE_OPEN;
3945 element = EL_SWITCHGATE_CLOSED;
3949 element = EL_DC_SWITCHGATE_SWITCH_UP;
3953 element = EL_TIMEGATE_CLOSED;
3956 case 0x144c: /* conveyor belt switch (green) */
3957 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
3960 case 0x144f: /* conveyor belt switch (red) */
3961 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
3964 case 0x1452: /* conveyor belt switch (blue) */
3965 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
3969 element = EL_CONVEYOR_BELT_3_MIDDLE;
3973 element = EL_CONVEYOR_BELT_3_LEFT;
3977 element = EL_CONVEYOR_BELT_3_RIGHT;
3981 element = EL_CONVEYOR_BELT_1_MIDDLE;
3985 element = EL_CONVEYOR_BELT_1_LEFT;
3989 element = EL_CONVEYOR_BELT_1_RIGHT;
3993 element = EL_CONVEYOR_BELT_4_MIDDLE;
3997 element = EL_CONVEYOR_BELT_4_LEFT;
4001 element = EL_CONVEYOR_BELT_4_RIGHT;
4005 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4009 element = EL_EXPANDABLE_WALL_VERTICAL;
4013 element = EL_EXPANDABLE_WALL_ANY;
4016 case 0x14ce: /* growing steel wall (left/right) */
4017 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4020 case 0x14df: /* growing steel wall (up/down) */
4021 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4024 case 0x14e8: /* growing steel wall (up/down/left/right) */
4025 element = EL_EXPANDABLE_STEELWALL_ANY;
4029 element = EL_SHIELD_DEADLY;
4033 element = EL_EXTRA_TIME;
4041 element = EL_EMPTY_SPACE;
4044 case 0x1578: /* quicksand (empty) */
4045 element = EL_QUICKSAND_FAST_EMPTY;
4048 case 0x1579: /* slow quicksand (empty) */
4049 element = EL_QUICKSAND_EMPTY;
4052 /* 0x157c - 0x158b: */
4055 /* 0x1590 - 0x159f: */
4056 /* EL_DC_LANDMINE */
4059 element = EL_EM_DYNAMITE;
4062 case 0x15a1: /* key (red) */
4063 element = EL_EM_KEY_1;
4066 case 0x15a2: /* key (yellow) */
4067 element = EL_EM_KEY_2;
4070 case 0x15a3: /* key (blue) */
4071 element = EL_EM_KEY_4;
4074 case 0x15a4: /* key (green) */
4075 element = EL_EM_KEY_3;
4078 case 0x15a5: /* key (white) */
4079 element = EL_DC_KEY_WHITE;
4083 element = EL_WALL_SLIPPERY;
4090 case 0x15a8: /* wall (not round) */
4094 case 0x15a9: /* (blue) */
4095 element = EL_CHAR_A;
4098 case 0x15aa: /* (blue) */
4099 element = EL_CHAR_B;
4102 case 0x15ab: /* (blue) */
4103 element = EL_CHAR_C;
4106 case 0x15ac: /* (blue) */
4107 element = EL_CHAR_D;
4110 case 0x15ad: /* (blue) */
4111 element = EL_CHAR_E;
4114 case 0x15ae: /* (blue) */
4115 element = EL_CHAR_F;
4118 case 0x15af: /* (blue) */
4119 element = EL_CHAR_G;
4122 case 0x15b0: /* (blue) */
4123 element = EL_CHAR_H;
4126 case 0x15b1: /* (blue) */
4127 element = EL_CHAR_I;
4130 case 0x15b2: /* (blue) */
4131 element = EL_CHAR_J;
4134 case 0x15b3: /* (blue) */
4135 element = EL_CHAR_K;
4138 case 0x15b4: /* (blue) */
4139 element = EL_CHAR_L;
4142 case 0x15b5: /* (blue) */
4143 element = EL_CHAR_M;
4146 case 0x15b6: /* (blue) */
4147 element = EL_CHAR_N;
4150 case 0x15b7: /* (blue) */
4151 element = EL_CHAR_O;
4154 case 0x15b8: /* (blue) */
4155 element = EL_CHAR_P;
4158 case 0x15b9: /* (blue) */
4159 element = EL_CHAR_Q;
4162 case 0x15ba: /* (blue) */
4163 element = EL_CHAR_R;
4166 case 0x15bb: /* (blue) */
4167 element = EL_CHAR_S;
4170 case 0x15bc: /* (blue) */
4171 element = EL_CHAR_T;
4174 case 0x15bd: /* (blue) */
4175 element = EL_CHAR_U;
4178 case 0x15be: /* (blue) */
4179 element = EL_CHAR_V;
4182 case 0x15bf: /* (blue) */
4183 element = EL_CHAR_W;
4186 case 0x15c0: /* (blue) */
4187 element = EL_CHAR_X;
4190 case 0x15c1: /* (blue) */
4191 element = EL_CHAR_Y;
4194 case 0x15c2: /* (blue) */
4195 element = EL_CHAR_Z;
4198 case 0x15c3: /* (blue) */
4199 element = EL_CHAR_AUMLAUT;
4202 case 0x15c4: /* (blue) */
4203 element = EL_CHAR_OUMLAUT;
4206 case 0x15c5: /* (blue) */
4207 element = EL_CHAR_UUMLAUT;
4210 case 0x15c6: /* (blue) */
4211 element = EL_CHAR_0;
4214 case 0x15c7: /* (blue) */
4215 element = EL_CHAR_1;
4218 case 0x15c8: /* (blue) */
4219 element = EL_CHAR_2;
4222 case 0x15c9: /* (blue) */
4223 element = EL_CHAR_3;
4226 case 0x15ca: /* (blue) */
4227 element = EL_CHAR_4;
4230 case 0x15cb: /* (blue) */
4231 element = EL_CHAR_5;
4234 case 0x15cc: /* (blue) */
4235 element = EL_CHAR_6;
4238 case 0x15cd: /* (blue) */
4239 element = EL_CHAR_7;
4242 case 0x15ce: /* (blue) */
4243 element = EL_CHAR_8;
4246 case 0x15cf: /* (blue) */
4247 element = EL_CHAR_9;
4250 case 0x15d0: /* (blue) */
4251 element = EL_CHAR_PERIOD;
4254 case 0x15d1: /* (blue) */
4255 element = EL_CHAR_EXCLAM;
4258 case 0x15d2: /* (blue) */
4259 element = EL_CHAR_COLON;
4262 case 0x15d3: /* (blue) */
4263 element = EL_CHAR_LESS;
4266 case 0x15d4: /* (blue) */
4267 element = EL_CHAR_GREATER;
4270 case 0x15d5: /* (blue) */
4271 element = EL_CHAR_QUESTION;
4274 case 0x15d6: /* (blue) */
4275 element = EL_CHAR_COPYRIGHT;
4278 case 0x15d7: /* (blue) */
4279 element = EL_CHAR_UP;
4282 case 0x15d8: /* (blue) */
4283 element = EL_CHAR_DOWN;
4286 case 0x15d9: /* (blue) */
4287 element = EL_CHAR_BUTTON;
4290 case 0x15da: /* (blue) */
4291 element = EL_CHAR_PLUS;
4294 case 0x15db: /* (blue) */
4295 element = EL_CHAR_MINUS;
4298 case 0x15dc: /* (blue) */
4299 element = EL_CHAR_APOSTROPHE;
4302 case 0x15dd: /* (blue) */
4303 element = EL_CHAR_PARENLEFT;
4306 case 0x15de: /* (blue) */
4307 element = EL_CHAR_PARENRIGHT;
4310 case 0x15df: /* (green) */
4311 element = EL_CHAR_A;
4314 case 0x15e0: /* (green) */
4315 element = EL_CHAR_B;
4318 case 0x15e1: /* (green) */
4319 element = EL_CHAR_C;
4322 case 0x15e2: /* (green) */
4323 element = EL_CHAR_D;
4326 case 0x15e3: /* (green) */
4327 element = EL_CHAR_E;
4330 case 0x15e4: /* (green) */
4331 element = EL_CHAR_F;
4334 case 0x15e5: /* (green) */
4335 element = EL_CHAR_G;
4338 case 0x15e6: /* (green) */
4339 element = EL_CHAR_H;
4342 case 0x15e7: /* (green) */
4343 element = EL_CHAR_I;
4346 case 0x15e8: /* (green) */
4347 element = EL_CHAR_J;
4350 case 0x15e9: /* (green) */
4351 element = EL_CHAR_K;
4354 case 0x15ea: /* (green) */
4355 element = EL_CHAR_L;
4358 case 0x15eb: /* (green) */
4359 element = EL_CHAR_M;
4362 case 0x15ec: /* (green) */
4363 element = EL_CHAR_N;
4366 case 0x15ed: /* (green) */
4367 element = EL_CHAR_O;
4370 case 0x15ee: /* (green) */
4371 element = EL_CHAR_P;
4374 case 0x15ef: /* (green) */
4375 element = EL_CHAR_Q;
4378 case 0x15f0: /* (green) */
4379 element = EL_CHAR_R;
4382 case 0x15f1: /* (green) */
4383 element = EL_CHAR_S;
4386 case 0x15f2: /* (green) */
4387 element = EL_CHAR_T;
4390 case 0x15f3: /* (green) */
4391 element = EL_CHAR_U;
4394 case 0x15f4: /* (green) */
4395 element = EL_CHAR_V;
4398 case 0x15f5: /* (green) */
4399 element = EL_CHAR_W;
4402 case 0x15f6: /* (green) */
4403 element = EL_CHAR_X;
4406 case 0x15f7: /* (green) */
4407 element = EL_CHAR_Y;
4410 case 0x15f8: /* (green) */
4411 element = EL_CHAR_Z;
4414 case 0x15f9: /* (green) */
4415 element = EL_CHAR_AUMLAUT;
4418 case 0x15fa: /* (green) */
4419 element = EL_CHAR_OUMLAUT;
4422 case 0x15fb: /* (green) */
4423 element = EL_CHAR_UUMLAUT;
4426 case 0x15fc: /* (green) */
4427 element = EL_CHAR_0;
4430 case 0x15fd: /* (green) */
4431 element = EL_CHAR_1;
4434 case 0x15fe: /* (green) */
4435 element = EL_CHAR_2;
4438 case 0x15ff: /* (green) */
4439 element = EL_CHAR_3;
4442 case 0x1600: /* (green) */
4443 element = EL_CHAR_4;
4446 case 0x1601: /* (green) */
4447 element = EL_CHAR_5;
4450 case 0x1602: /* (green) */
4451 element = EL_CHAR_6;
4454 case 0x1603: /* (green) */
4455 element = EL_CHAR_7;
4458 case 0x1604: /* (green) */
4459 element = EL_CHAR_8;
4462 case 0x1605: /* (green) */
4463 element = EL_CHAR_9;
4466 case 0x1606: /* (green) */
4467 element = EL_CHAR_PERIOD;
4470 case 0x1607: /* (green) */
4471 element = EL_CHAR_EXCLAM;
4474 case 0x1608: /* (green) */
4475 element = EL_CHAR_COLON;
4478 case 0x1609: /* (green) */
4479 element = EL_CHAR_LESS;
4482 case 0x160a: /* (green) */
4483 element = EL_CHAR_GREATER;
4486 case 0x160b: /* (green) */
4487 element = EL_CHAR_QUESTION;
4490 case 0x160c: /* (green) */
4491 element = EL_CHAR_COPYRIGHT;
4494 case 0x160d: /* (green) */
4495 element = EL_CHAR_UP;
4498 case 0x160e: /* (green) */
4499 element = EL_CHAR_DOWN;
4502 case 0x160f: /* (green) */
4503 element = EL_CHAR_BUTTON;
4506 case 0x1610: /* (green) */
4507 element = EL_CHAR_PLUS;
4510 case 0x1611: /* (green) */
4511 element = EL_CHAR_MINUS;
4514 case 0x1612: /* (green) */
4515 element = EL_CHAR_APOSTROPHE;
4518 case 0x1613: /* (green) */
4519 element = EL_CHAR_PARENLEFT;
4522 case 0x1614: /* (green) */
4523 element = EL_CHAR_PARENRIGHT;
4526 case 0x1615: /* (blue steel) */
4527 element = EL_STEEL_CHAR_A;
4530 case 0x1616: /* (blue steel) */
4531 element = EL_STEEL_CHAR_B;
4534 case 0x1617: /* (blue steel) */
4535 element = EL_STEEL_CHAR_C;
4538 case 0x1618: /* (blue steel) */
4539 element = EL_STEEL_CHAR_D;
4542 case 0x1619: /* (blue steel) */
4543 element = EL_STEEL_CHAR_E;
4546 case 0x161a: /* (blue steel) */
4547 element = EL_STEEL_CHAR_F;
4550 case 0x161b: /* (blue steel) */
4551 element = EL_STEEL_CHAR_G;
4554 case 0x161c: /* (blue steel) */
4555 element = EL_STEEL_CHAR_H;
4558 case 0x161d: /* (blue steel) */
4559 element = EL_STEEL_CHAR_I;
4562 case 0x161e: /* (blue steel) */
4563 element = EL_STEEL_CHAR_J;
4566 case 0x161f: /* (blue steel) */
4567 element = EL_STEEL_CHAR_K;
4570 case 0x1620: /* (blue steel) */
4571 element = EL_STEEL_CHAR_L;
4574 case 0x1621: /* (blue steel) */
4575 element = EL_STEEL_CHAR_M;
4578 case 0x1622: /* (blue steel) */
4579 element = EL_STEEL_CHAR_N;
4582 case 0x1623: /* (blue steel) */
4583 element = EL_STEEL_CHAR_O;
4586 case 0x1624: /* (blue steel) */
4587 element = EL_STEEL_CHAR_P;
4590 case 0x1625: /* (blue steel) */
4591 element = EL_STEEL_CHAR_Q;
4594 case 0x1626: /* (blue steel) */
4595 element = EL_STEEL_CHAR_R;
4598 case 0x1627: /* (blue steel) */
4599 element = EL_STEEL_CHAR_S;
4602 case 0x1628: /* (blue steel) */
4603 element = EL_STEEL_CHAR_T;
4606 case 0x1629: /* (blue steel) */
4607 element = EL_STEEL_CHAR_U;
4610 case 0x162a: /* (blue steel) */
4611 element = EL_STEEL_CHAR_V;
4614 case 0x162b: /* (blue steel) */
4615 element = EL_STEEL_CHAR_W;
4618 case 0x162c: /* (blue steel) */
4619 element = EL_STEEL_CHAR_X;
4622 case 0x162d: /* (blue steel) */
4623 element = EL_STEEL_CHAR_Y;
4626 case 0x162e: /* (blue steel) */
4627 element = EL_STEEL_CHAR_Z;
4630 case 0x162f: /* (blue steel) */
4631 element = EL_STEEL_CHAR_AUMLAUT;
4634 case 0x1630: /* (blue steel) */
4635 element = EL_STEEL_CHAR_OUMLAUT;
4638 case 0x1631: /* (blue steel) */
4639 element = EL_STEEL_CHAR_UUMLAUT;
4642 case 0x1632: /* (blue steel) */
4643 element = EL_STEEL_CHAR_0;
4646 case 0x1633: /* (blue steel) */
4647 element = EL_STEEL_CHAR_1;
4650 case 0x1634: /* (blue steel) */
4651 element = EL_STEEL_CHAR_2;
4654 case 0x1635: /* (blue steel) */
4655 element = EL_STEEL_CHAR_3;
4658 case 0x1636: /* (blue steel) */
4659 element = EL_STEEL_CHAR_4;
4662 case 0x1637: /* (blue steel) */
4663 element = EL_STEEL_CHAR_5;
4666 case 0x1638: /* (blue steel) */
4667 element = EL_STEEL_CHAR_6;
4670 case 0x1639: /* (blue steel) */
4671 element = EL_STEEL_CHAR_7;
4674 case 0x163a: /* (blue steel) */
4675 element = EL_STEEL_CHAR_8;
4678 case 0x163b: /* (blue steel) */
4679 element = EL_STEEL_CHAR_9;
4682 case 0x163c: /* (blue steel) */
4683 element = EL_STEEL_CHAR_PERIOD;
4686 case 0x163d: /* (blue steel) */
4687 element = EL_STEEL_CHAR_EXCLAM;
4690 case 0x163e: /* (blue steel) */
4691 element = EL_STEEL_CHAR_COLON;
4694 case 0x163f: /* (blue steel) */
4695 element = EL_STEEL_CHAR_LESS;
4698 case 0x1640: /* (blue steel) */
4699 element = EL_STEEL_CHAR_GREATER;
4702 case 0x1641: /* (blue steel) */
4703 element = EL_STEEL_CHAR_QUESTION;
4706 case 0x1642: /* (blue steel) */
4707 element = EL_STEEL_CHAR_COPYRIGHT;
4710 case 0x1643: /* (blue steel) */
4711 element = EL_STEEL_CHAR_UP;
4714 case 0x1644: /* (blue steel) */
4715 element = EL_STEEL_CHAR_DOWN;
4718 case 0x1645: /* (blue steel) */
4719 element = EL_STEEL_CHAR_BUTTON;
4722 case 0x1646: /* (blue steel) */
4723 element = EL_STEEL_CHAR_PLUS;
4726 case 0x1647: /* (blue steel) */
4727 element = EL_STEEL_CHAR_MINUS;
4730 case 0x1648: /* (blue steel) */
4731 element = EL_STEEL_CHAR_APOSTROPHE;
4734 case 0x1649: /* (blue steel) */
4735 element = EL_STEEL_CHAR_PARENLEFT;
4738 case 0x164a: /* (blue steel) */
4739 element = EL_STEEL_CHAR_PARENRIGHT;
4742 case 0x164b: /* (green steel) */
4743 element = EL_STEEL_CHAR_A;
4746 case 0x164c: /* (green steel) */
4747 element = EL_STEEL_CHAR_B;
4750 case 0x164d: /* (green steel) */
4751 element = EL_STEEL_CHAR_C;
4754 case 0x164e: /* (green steel) */
4755 element = EL_STEEL_CHAR_D;
4758 case 0x164f: /* (green steel) */
4759 element = EL_STEEL_CHAR_E;
4762 case 0x1650: /* (green steel) */
4763 element = EL_STEEL_CHAR_F;
4766 case 0x1651: /* (green steel) */
4767 element = EL_STEEL_CHAR_G;
4770 case 0x1652: /* (green steel) */
4771 element = EL_STEEL_CHAR_H;
4774 case 0x1653: /* (green steel) */
4775 element = EL_STEEL_CHAR_I;
4778 case 0x1654: /* (green steel) */
4779 element = EL_STEEL_CHAR_J;
4782 case 0x1655: /* (green steel) */
4783 element = EL_STEEL_CHAR_K;
4786 case 0x1656: /* (green steel) */
4787 element = EL_STEEL_CHAR_L;
4790 case 0x1657: /* (green steel) */
4791 element = EL_STEEL_CHAR_M;
4794 case 0x1658: /* (green steel) */
4795 element = EL_STEEL_CHAR_N;
4798 case 0x1659: /* (green steel) */
4799 element = EL_STEEL_CHAR_O;
4802 case 0x165a: /* (green steel) */
4803 element = EL_STEEL_CHAR_P;
4806 case 0x165b: /* (green steel) */
4807 element = EL_STEEL_CHAR_Q;
4810 case 0x165c: /* (green steel) */
4811 element = EL_STEEL_CHAR_R;
4814 case 0x165d: /* (green steel) */
4815 element = EL_STEEL_CHAR_S;
4818 case 0x165e: /* (green steel) */
4819 element = EL_STEEL_CHAR_T;
4822 case 0x165f: /* (green steel) */
4823 element = EL_STEEL_CHAR_U;
4826 case 0x1660: /* (green steel) */
4827 element = EL_STEEL_CHAR_V;
4830 case 0x1661: /* (green steel) */
4831 element = EL_STEEL_CHAR_W;
4834 case 0x1662: /* (green steel) */
4835 element = EL_STEEL_CHAR_X;
4838 case 0x1663: /* (green steel) */
4839 element = EL_STEEL_CHAR_Y;
4842 case 0x1664: /* (green steel) */
4843 element = EL_STEEL_CHAR_Z;
4846 case 0x1665: /* (green steel) */
4847 element = EL_STEEL_CHAR_AUMLAUT;
4850 case 0x1666: /* (green steel) */
4851 element = EL_STEEL_CHAR_OUMLAUT;
4854 case 0x1667: /* (green steel) */
4855 element = EL_STEEL_CHAR_UUMLAUT;
4858 case 0x1668: /* (green steel) */
4859 element = EL_STEEL_CHAR_0;
4862 case 0x1669: /* (green steel) */
4863 element = EL_STEEL_CHAR_1;
4866 case 0x166a: /* (green steel) */
4867 element = EL_STEEL_CHAR_2;
4870 case 0x166b: /* (green steel) */
4871 element = EL_STEEL_CHAR_3;
4874 case 0x166c: /* (green steel) */
4875 element = EL_STEEL_CHAR_4;
4878 case 0x166d: /* (green steel) */
4879 element = EL_STEEL_CHAR_5;
4882 case 0x166e: /* (green steel) */
4883 element = EL_STEEL_CHAR_6;
4886 case 0x166f: /* (green steel) */
4887 element = EL_STEEL_CHAR_7;
4890 case 0x1670: /* (green steel) */
4891 element = EL_STEEL_CHAR_8;
4894 case 0x1671: /* (green steel) */
4895 element = EL_STEEL_CHAR_9;
4898 case 0x1672: /* (green steel) */
4899 element = EL_STEEL_CHAR_PERIOD;
4902 case 0x1673: /* (green steel) */
4903 element = EL_STEEL_CHAR_EXCLAM;
4906 case 0x1674: /* (green steel) */
4907 element = EL_STEEL_CHAR_COLON;
4910 case 0x1675: /* (green steel) */
4911 element = EL_STEEL_CHAR_LESS;
4914 case 0x1676: /* (green steel) */
4915 element = EL_STEEL_CHAR_GREATER;
4918 case 0x1677: /* (green steel) */
4919 element = EL_STEEL_CHAR_QUESTION;
4922 case 0x1678: /* (green steel) */
4923 element = EL_STEEL_CHAR_COPYRIGHT;
4926 case 0x1679: /* (green steel) */
4927 element = EL_STEEL_CHAR_UP;
4930 case 0x167a: /* (green steel) */
4931 element = EL_STEEL_CHAR_DOWN;
4934 case 0x167b: /* (green steel) */
4935 element = EL_STEEL_CHAR_BUTTON;
4938 case 0x167c: /* (green steel) */
4939 element = EL_STEEL_CHAR_PLUS;
4942 case 0x167d: /* (green steel) */
4943 element = EL_STEEL_CHAR_MINUS;
4946 case 0x167e: /* (green steel) */
4947 element = EL_STEEL_CHAR_APOSTROPHE;
4950 case 0x167f: /* (green steel) */
4951 element = EL_STEEL_CHAR_PARENLEFT;
4954 case 0x1680: /* (green steel) */
4955 element = EL_STEEL_CHAR_PARENRIGHT;
4958 case 0x1681: /* gate (red) */
4959 element = EL_EM_GATE_1;
4962 case 0x1682: /* secret gate (red) */
4963 element = EL_GATE_1_GRAY;
4966 case 0x1683: /* gate (yellow) */
4967 element = EL_EM_GATE_2;
4970 case 0x1684: /* secret gate (yellow) */
4971 element = EL_GATE_2_GRAY;
4974 case 0x1685: /* gate (blue) */
4975 element = EL_EM_GATE_4;
4978 case 0x1686: /* secret gate (blue) */
4979 element = EL_GATE_4_GRAY;
4982 case 0x1687: /* gate (green) */
4983 element = EL_EM_GATE_3;
4986 case 0x1688: /* secret gate (green) */
4987 element = EL_GATE_3_GRAY;
4990 case 0x1689: /* gate (white) */
4991 element = EL_DC_GATE_WHITE;
4994 case 0x168a: /* secret gate (white) */
4995 element = EL_DC_GATE_WHITE_GRAY;
4998 case 0x168b: /* secret gate (no key) */
4999 element = EL_DC_GATE_FAKE_GRAY;
5003 element = EL_ROBOT_WHEEL;
5007 element = EL_DC_TIMEGATE_SWITCH;
5011 element = EL_ACID_POOL_BOTTOM;
5015 element = EL_ACID_POOL_TOPLEFT;
5019 element = EL_ACID_POOL_TOPRIGHT;
5023 element = EL_ACID_POOL_BOTTOMLEFT;
5027 element = EL_ACID_POOL_BOTTOMRIGHT;
5031 element = EL_STEELWALL;
5035 element = EL_STEELWALL_SLIPPERY;
5038 case 0x1695: /* steel wall (not round) */
5039 element = EL_STEELWALL;
5042 case 0x1696: /* steel wall (left) */
5043 element = EL_DC_STEELWALL_1_LEFT;
5046 case 0x1697: /* steel wall (bottom) */
5047 element = EL_DC_STEELWALL_1_BOTTOM;
5050 case 0x1698: /* steel wall (right) */
5051 element = EL_DC_STEELWALL_1_RIGHT;
5054 case 0x1699: /* steel wall (top) */
5055 element = EL_DC_STEELWALL_1_TOP;
5058 case 0x169a: /* steel wall (left/bottom) */
5059 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5062 case 0x169b: /* steel wall (right/bottom) */
5063 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5066 case 0x169c: /* steel wall (right/top) */
5067 element = EL_DC_STEELWALL_1_TOPRIGHT;
5070 case 0x169d: /* steel wall (left/top) */
5071 element = EL_DC_STEELWALL_1_TOPLEFT;
5074 case 0x169e: /* steel wall (right/bottom small) */
5075 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5078 case 0x169f: /* steel wall (left/bottom small) */
5079 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5082 case 0x16a0: /* steel wall (right/top small) */
5083 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5086 case 0x16a1: /* steel wall (left/top small) */
5087 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5090 case 0x16a2: /* steel wall (left/right) */
5091 element = EL_DC_STEELWALL_1_VERTICAL;
5094 case 0x16a3: /* steel wall (top/bottom) */
5095 element = EL_DC_STEELWALL_1_HORIZONTAL;
5098 case 0x16a4: /* steel wall 2 (left end) */
5099 element = EL_DC_STEELWALL_2_LEFT;
5102 case 0x16a5: /* steel wall 2 (right end) */
5103 element = EL_DC_STEELWALL_2_RIGHT;
5106 case 0x16a6: /* steel wall 2 (top end) */
5107 element = EL_DC_STEELWALL_2_TOP;
5110 case 0x16a7: /* steel wall 2 (bottom end) */
5111 element = EL_DC_STEELWALL_2_BOTTOM;
5114 case 0x16a8: /* steel wall 2 (left/right) */
5115 element = EL_DC_STEELWALL_2_HORIZONTAL;
5118 case 0x16a9: /* steel wall 2 (up/down) */
5119 element = EL_DC_STEELWALL_2_VERTICAL;
5122 case 0x16aa: /* steel wall 2 (mid) */
5123 element = EL_DC_STEELWALL_2_MIDDLE;
5127 element = EL_SIGN_EXCLAMATION;
5131 element = EL_SIGN_RADIOACTIVITY;
5135 element = EL_SIGN_STOP;
5139 element = EL_SIGN_WHEELCHAIR;
5143 element = EL_SIGN_PARKING;
5147 element = EL_SIGN_NO_ENTRY;
5151 element = EL_SIGN_HEART;
5155 element = EL_SIGN_GIVE_WAY;
5159 element = EL_SIGN_ENTRY_FORBIDDEN;
5163 element = EL_SIGN_EMERGENCY_EXIT;
5167 element = EL_SIGN_YIN_YANG;
5171 element = EL_WALL_EMERALD;
5175 element = EL_WALL_DIAMOND;
5179 element = EL_WALL_PEARL;
5183 element = EL_WALL_CRYSTAL;
5187 element = EL_INVISIBLE_WALL;
5191 element = EL_INVISIBLE_STEELWALL;
5194 /* 0x16bc - 0x16cb: */
5195 /* EL_INVISIBLE_SAND */
5198 element = EL_LIGHT_SWITCH;
5202 element = EL_ENVELOPE_1;
5206 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5207 element = EL_DIAMOND;
5208 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5209 element = EL_EMERALD;
5210 else if (element >= 0x157c && element <= 0x158b)
5212 else if (element >= 0x1590 && element <= 0x159f)
5213 element = EL_DC_LANDMINE;
5214 else if (element >= 0x16bc && element <= 0x16cb)
5215 element = EL_INVISIBLE_SAND;
5218 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5219 element = EL_UNKNOWN;
5224 return getMappedElement(element);
5227 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5230 byte header[DC_LEVEL_HEADER_SIZE];
5232 int envelope_header_pos = 62;
5233 int envelope_content_pos = 94;
5234 int level_name_pos = 251;
5235 int level_author_pos = 292;
5236 int envelope_header_len;
5237 int envelope_content_len;
5239 int level_author_len;
5241 int num_yamyam_contents;
5244 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5246 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5248 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5250 header[i * 2 + 0] = header_word >> 8;
5251 header[i * 2 + 1] = header_word & 0xff;
5254 /* read some values from level header to check level decoding integrity */
5255 fieldx = header[6] | (header[7] << 8);
5256 fieldy = header[8] | (header[9] << 8);
5257 num_yamyam_contents = header[60] | (header[61] << 8);
5259 /* do some simple sanity checks to ensure that level was correctly decoded */
5260 if (fieldx < 1 || fieldx > 256 ||
5261 fieldy < 1 || fieldy > 256 ||
5262 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5264 level->no_valid_file = TRUE;
5266 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5271 /* maximum envelope header size is 31 bytes */
5272 envelope_header_len = header[envelope_header_pos];
5273 /* maximum envelope content size is 110 (156?) bytes */
5274 envelope_content_len = header[envelope_content_pos];
5276 /* maximum level title size is 40 bytes */
5277 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5278 /* maximum level author size is 30 (51?) bytes */
5279 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5283 for (i = 0; i < envelope_header_len; i++)
5284 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5285 level->envelope[0].text[envelope_size++] =
5286 header[envelope_header_pos + 1 + i];
5288 if (envelope_header_len > 0 && envelope_content_len > 0)
5290 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5291 level->envelope[0].text[envelope_size++] = '\n';
5292 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5293 level->envelope[0].text[envelope_size++] = '\n';
5296 for (i = 0; i < envelope_content_len; i++)
5297 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5298 level->envelope[0].text[envelope_size++] =
5299 header[envelope_content_pos + 1 + i];
5301 level->envelope[0].text[envelope_size] = '\0';
5303 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5304 level->envelope[0].ysize = 10;
5305 level->envelope[0].autowrap = TRUE;
5306 level->envelope[0].centered = TRUE;
5308 for (i = 0; i < level_name_len; i++)
5309 level->name[i] = header[level_name_pos + 1 + i];
5310 level->name[level_name_len] = '\0';
5312 for (i = 0; i < level_author_len; i++)
5313 level->author[i] = header[level_author_pos + 1 + i];
5314 level->author[level_author_len] = '\0';
5316 num_yamyam_contents = header[60] | (header[61] << 8);
5317 level->num_yamyam_contents =
5318 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5320 for (i = 0; i < num_yamyam_contents; i++)
5322 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5324 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5325 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5327 if (i < MAX_ELEMENT_CONTENTS)
5328 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5332 fieldx = header[6] | (header[7] << 8);
5333 fieldy = header[8] | (header[9] << 8);
5334 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5335 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5337 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5339 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5340 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5342 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5343 level->field[x][y] = getMappedElement_DC(element_dc);
5346 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5347 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5348 level->field[x][y] = EL_PLAYER_1;
5350 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5351 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5352 level->field[x][y] = EL_PLAYER_2;
5354 level->gems_needed = header[18] | (header[19] << 8);
5356 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5357 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5358 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5359 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5360 level->score[SC_NUT] = header[28] | (header[29] << 8);
5361 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5362 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5363 level->score[SC_BUG] = header[34] | (header[35] << 8);
5364 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5365 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5366 level->score[SC_KEY] = header[40] | (header[41] << 8);
5367 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5369 level->time = header[44] | (header[45] << 8);
5371 level->amoeba_speed = header[46] | (header[47] << 8);
5372 level->time_light = header[48] | (header[49] << 8);
5373 level->time_timegate = header[50] | (header[51] << 8);
5374 level->time_wheel = header[52] | (header[53] << 8);
5375 level->time_magic_wall = header[54] | (header[55] << 8);
5376 level->extra_time = header[56] | (header[57] << 8);
5377 level->shield_normal_time = header[58] | (header[59] << 8);
5379 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5380 can slip down from flat walls, like normal walls and steel walls */
5381 level->em_slippery_gems = TRUE;
5384 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5385 struct LevelFileInfo *level_file_info,
5386 boolean level_info_only)
5388 char *filename = level_file_info->filename;
5390 int num_magic_bytes = 8;
5391 char magic_bytes[num_magic_bytes + 1];
5392 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5394 if (!(file = openFile(filename, MODE_READ)))
5396 level->no_valid_file = TRUE;
5398 if (!level_info_only)
5399 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5404 // fseek(file, 0x0000, SEEK_SET);
5406 if (level_file_info->packed)
5408 /* read "magic bytes" from start of file */
5409 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5410 magic_bytes[0] = '\0';
5412 /* check "magic bytes" for correct file format */
5413 if (!strPrefix(magic_bytes, "DC2"))
5415 level->no_valid_file = TRUE;
5417 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5423 if (strPrefix(magic_bytes, "DC2Win95") ||
5424 strPrefix(magic_bytes, "DC2Win98"))
5426 int position_first_level = 0x00fa;
5427 int extra_bytes = 4;
5430 /* advance file stream to first level inside the level package */
5431 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5433 /* each block of level data is followed by block of non-level data */
5434 num_levels_to_skip *= 2;
5436 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5437 while (num_levels_to_skip >= 0)
5439 /* advance file stream to next level inside the level package */
5440 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5442 level->no_valid_file = TRUE;
5444 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5450 /* skip apparently unused extra bytes following each level */
5451 ReadUnusedBytesFromFile(file, extra_bytes);
5453 /* read size of next level in level package */
5454 skip_bytes = getFile32BitLE(file);
5456 num_levels_to_skip--;
5461 level->no_valid_file = TRUE;
5463 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5470 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5476 /* ------------------------------------------------------------------------- */
5477 /* functions for loading SB level */
5478 /* ------------------------------------------------------------------------- */
5480 int getMappedElement_SB(int element_ascii, boolean use_ces)
5488 sb_element_mapping[] =
5490 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5491 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5492 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5493 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5494 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5495 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5496 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5497 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5504 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5505 if (element_ascii == sb_element_mapping[i].ascii)
5506 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5508 return EL_UNDEFINED;
5511 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5512 struct LevelFileInfo *level_file_info,
5513 boolean level_info_only)
5515 char *filename = level_file_info->filename;
5516 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5517 char last_comment[MAX_LINE_LEN];
5518 char level_name[MAX_LINE_LEN];
5521 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5522 boolean read_continued_line = FALSE;
5523 boolean reading_playfield = FALSE;
5524 boolean got_valid_playfield_line = FALSE;
5525 boolean invalid_playfield_char = FALSE;
5526 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5527 int file_level_nr = 0;
5529 int x = 0, y = 0; /* initialized to make compilers happy */
5531 last_comment[0] = '\0';
5532 level_name[0] = '\0';
5534 if (!(file = openFile(filename, MODE_READ)))
5536 level->no_valid_file = TRUE;
5538 if (!level_info_only)
5539 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5544 while (!checkEndOfFile(file))
5546 /* level successfully read, but next level may follow here */
5547 if (!got_valid_playfield_line && reading_playfield)
5549 /* read playfield from single level file -- skip remaining file */
5550 if (!level_file_info->packed)
5553 if (file_level_nr >= num_levels_to_skip)
5558 last_comment[0] = '\0';
5559 level_name[0] = '\0';
5561 reading_playfield = FALSE;
5564 got_valid_playfield_line = FALSE;
5566 /* read next line of input file */
5567 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5570 /* check if line was completely read and is terminated by line break */
5571 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5574 /* cut trailing line break (this can be newline and/or carriage return) */
5575 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5576 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5579 /* copy raw input line for later use (mainly debugging output) */
5580 strcpy(line_raw, line);
5582 if (read_continued_line)
5584 /* append new line to existing line, if there is enough space */
5585 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5586 strcat(previous_line, line_ptr);
5588 strcpy(line, previous_line); /* copy storage buffer to line */
5590 read_continued_line = FALSE;
5593 /* if the last character is '\', continue at next line */
5594 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5596 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5597 strcpy(previous_line, line); /* copy line to storage buffer */
5599 read_continued_line = TRUE;
5604 /* skip empty lines */
5605 if (line[0] == '\0')
5608 /* extract comment text from comment line */
5611 for (line_ptr = line; *line_ptr; line_ptr++)
5612 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5615 strcpy(last_comment, line_ptr);
5620 /* extract level title text from line containing level title */
5621 if (line[0] == '\'')
5623 strcpy(level_name, &line[1]);
5625 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5626 level_name[strlen(level_name) - 1] = '\0';
5631 /* skip lines containing only spaces (or empty lines) */
5632 for (line_ptr = line; *line_ptr; line_ptr++)
5633 if (*line_ptr != ' ')
5635 if (*line_ptr == '\0')
5638 /* at this point, we have found a line containing part of a playfield */
5640 got_valid_playfield_line = TRUE;
5642 if (!reading_playfield)
5644 reading_playfield = TRUE;
5645 invalid_playfield_char = FALSE;
5647 for (x = 0; x < MAX_LEV_FIELDX; x++)
5648 for (y = 0; y < MAX_LEV_FIELDY; y++)
5649 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5654 /* start with topmost tile row */
5658 /* skip playfield line if larger row than allowed */
5659 if (y >= MAX_LEV_FIELDY)
5662 /* start with leftmost tile column */
5665 /* read playfield elements from line */
5666 for (line_ptr = line; *line_ptr; line_ptr++)
5668 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5670 /* stop parsing playfield line if larger column than allowed */
5671 if (x >= MAX_LEV_FIELDX)
5674 if (mapped_sb_element == EL_UNDEFINED)
5676 invalid_playfield_char = TRUE;
5681 level->field[x][y] = mapped_sb_element;
5683 /* continue with next tile column */
5686 level->fieldx = MAX(x, level->fieldx);
5689 if (invalid_playfield_char)
5691 /* if first playfield line, treat invalid lines as comment lines */
5693 reading_playfield = FALSE;
5698 /* continue with next tile row */
5706 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
5707 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
5709 if (!reading_playfield)
5711 level->no_valid_file = TRUE;
5713 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5718 if (*level_name != '\0')
5720 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
5721 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5723 else if (*last_comment != '\0')
5725 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
5726 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5730 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
5733 /* set all empty fields beyond the border walls to invisible steel wall */
5734 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5736 if ((x == 0 || x == level->fieldx - 1 ||
5737 y == 0 || y == level->fieldy - 1) &&
5738 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
5739 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
5740 level->field, level->fieldx, level->fieldy);
5743 /* set special level settings for Sokoban levels */
5746 level->use_step_counter = TRUE;
5748 if (load_xsb_to_ces)
5750 /* special global settings can now be set in level template */
5752 /* fill smaller playfields with padding "beyond border wall" elements */
5753 if (level->fieldx < SCR_FIELDX ||
5754 level->fieldy < SCR_FIELDY)
5756 short field[level->fieldx][level->fieldy];
5757 int new_fieldx = MAX(level->fieldx, SCR_FIELDX);
5758 int new_fieldy = MAX(level->fieldy, SCR_FIELDY);
5759 int pos_fieldx = (new_fieldx - level->fieldx) / 2;
5760 int pos_fieldy = (new_fieldy - level->fieldy) / 2;
5762 /* copy old playfield (which is smaller than the visible area) */
5763 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5764 field[x][y] = level->field[x][y];
5766 /* fill new, larger playfield with "beyond border wall" elements */
5767 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
5768 level->field[x][y] = getMappedElement_SB('_', load_xsb_to_ces);
5770 /* copy the old playfield to the middle of the new playfield */
5771 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5772 level->field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
5774 level->fieldx = new_fieldx;
5775 level->fieldy = new_fieldy;
5778 level->use_custom_template = TRUE;
5783 /* ------------------------------------------------------------------------- */
5784 /* functions for handling native levels */
5785 /* ------------------------------------------------------------------------- */
5787 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
5788 struct LevelFileInfo *level_file_info,
5789 boolean level_info_only)
5791 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
5792 level->no_valid_file = TRUE;
5795 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
5796 struct LevelFileInfo *level_file_info,
5797 boolean level_info_only)
5801 /* determine position of requested level inside level package */
5802 if (level_file_info->packed)
5803 pos = level_file_info->nr - leveldir_current->first_level;
5805 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
5806 level->no_valid_file = TRUE;
5809 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
5811 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5812 CopyNativeLevel_RND_to_EM(level);
5813 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5814 CopyNativeLevel_RND_to_SP(level);
5817 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
5819 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5820 CopyNativeLevel_EM_to_RND(level);
5821 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5822 CopyNativeLevel_SP_to_RND(level);
5825 void SaveNativeLevel(struct LevelInfo *level)
5827 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5829 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
5830 char *filename = getLevelFilenameFromBasename(basename);
5832 CopyNativeLevel_RND_to_SP(level);
5833 CopyNativeTape_RND_to_SP(level);
5835 SaveNativeLevel_SP(filename);
5840 /* ------------------------------------------------------------------------- */
5841 /* functions for loading generic level */
5842 /* ------------------------------------------------------------------------- */
5844 static void LoadLevelFromFileInfo(struct LevelInfo *level,
5845 struct LevelFileInfo *level_file_info,
5846 boolean level_info_only)
5848 /* always start with reliable default values */
5849 setLevelInfoToDefaults(level, level_info_only);
5851 switch (level_file_info->type)
5853 case LEVEL_FILE_TYPE_RND:
5854 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5857 case LEVEL_FILE_TYPE_EM:
5858 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
5859 level->game_engine_type = GAME_ENGINE_TYPE_EM;
5862 case LEVEL_FILE_TYPE_SP:
5863 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
5864 level->game_engine_type = GAME_ENGINE_TYPE_SP;
5867 case LEVEL_FILE_TYPE_DC:
5868 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
5871 case LEVEL_FILE_TYPE_SB:
5872 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
5876 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5880 /* if level file is invalid, restore level structure to default values */
5881 if (level->no_valid_file)
5883 setLevelInfoToDefaults(level, level_info_only);
5885 level->no_valid_file = TRUE; /* but keep "no valid file" flag */
5888 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
5889 level->game_engine_type = GAME_ENGINE_TYPE_RND;
5891 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
5892 CopyNativeLevel_Native_to_RND(level);
5895 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
5897 static struct LevelFileInfo level_file_info;
5899 /* always start with reliable default values */
5900 setFileInfoToDefaults(&level_file_info);
5902 level_file_info.nr = 0; /* unknown level number */
5903 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
5904 level_file_info.filename = filename;
5906 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
5909 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
5913 if (leveldir_current == NULL) /* only when dumping level */
5916 /* all engine modifications also valid for levels which use latest engine */
5917 if (level->game_version < VERSION_IDENT(3,2,0,5))
5919 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
5920 level->score[SC_TIME_BONUS] /= 10;
5923 if (leveldir_current->latest_engine)
5925 /* ---------- use latest game engine ----------------------------------- */
5927 /* For all levels which are forced to use the latest game engine version
5928 (normally all but user contributed, private and undefined levels), set
5929 the game engine version to the actual version; this allows for actual
5930 corrections in the game engine to take effect for existing, converted
5931 levels (from "classic" or other existing games) to make the emulation
5932 of the corresponding game more accurate, while (hopefully) not breaking
5933 existing levels created from other players. */
5935 level->game_version = GAME_VERSION_ACTUAL;
5937 /* Set special EM style gems behaviour: EM style gems slip down from
5938 normal, steel and growing wall. As this is a more fundamental change,
5939 it seems better to set the default behaviour to "off" (as it is more
5940 natural) and make it configurable in the level editor (as a property
5941 of gem style elements). Already existing converted levels (neither
5942 private nor contributed levels) are changed to the new behaviour. */
5944 if (level->file_version < FILE_VERSION_2_0)
5945 level->em_slippery_gems = TRUE;
5950 /* ---------- use game engine the level was created with ----------------- */
5952 /* For all levels which are not forced to use the latest game engine
5953 version (normally user contributed, private and undefined levels),
5954 use the version of the game engine the levels were created for.
5956 Since 2.0.1, the game engine version is now directly stored
5957 in the level file (chunk "VERS"), so there is no need anymore
5958 to set the game version from the file version (except for old,
5959 pre-2.0 levels, where the game version is still taken from the
5960 file format version used to store the level -- see above). */
5962 /* player was faster than enemies in 1.0.0 and before */
5963 if (level->file_version == FILE_VERSION_1_0)
5964 for (i = 0; i < MAX_PLAYERS; i++)
5965 level->initial_player_stepsize[i] = STEPSIZE_FAST;
5967 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
5968 if (level->game_version == VERSION_IDENT(2,0,1,0))
5969 level->em_slippery_gems = TRUE;
5971 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
5972 if (level->game_version < VERSION_IDENT(2,2,0,0))
5973 level->use_spring_bug = TRUE;
5975 if (level->game_version < VERSION_IDENT(3,2,0,5))
5977 /* time orb caused limited time in endless time levels before 3.2.0-5 */
5978 level->use_time_orb_bug = TRUE;
5980 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
5981 level->block_snap_field = FALSE;
5983 /* extra time score was same value as time left score before 3.2.0-5 */
5984 level->extra_time_score = level->score[SC_TIME_BONUS];
5987 if (level->game_version < VERSION_IDENT(3,2,0,7))
5989 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
5990 level->continuous_snapping = FALSE;
5993 /* only few elements were able to actively move into acid before 3.1.0 */
5994 /* trigger settings did not exist before 3.1.0; set to default "any" */
5995 if (level->game_version < VERSION_IDENT(3,1,0,0))
5997 /* correct "can move into acid" settings (all zero in old levels) */
5999 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6000 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6002 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6003 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6004 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6005 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6007 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6008 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6010 /* correct trigger settings (stored as zero == "none" in old levels) */
6012 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6014 int element = EL_CUSTOM_START + i;
6015 struct ElementInfo *ei = &element_info[element];
6017 for (j = 0; j < ei->num_change_pages; j++)
6019 struct ElementChangeInfo *change = &ei->change_page[j];
6021 change->trigger_player = CH_PLAYER_ANY;
6022 change->trigger_page = CH_PAGE_ANY;
6027 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6029 int element = EL_CUSTOM_256;
6030 struct ElementInfo *ei = &element_info[element];
6031 struct ElementChangeInfo *change = &ei->change_page[0];
6033 /* This is needed to fix a problem that was caused by a bugfix in function
6034 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6035 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6036 not replace walkable elements, but instead just placed the player on it,
6037 without placing the Sokoban field under the player). Unfortunately, this
6038 breaks "Snake Bite" style levels when the snake is halfway through a door
6039 that just closes (the snake head is still alive and can be moved in this
6040 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6041 player (without Sokoban element) which then gets killed as designed). */
6043 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6044 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6045 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6046 change->target_element = EL_PLAYER_1;
6049 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6050 if (level->game_version < VERSION_IDENT(3,2,5,0))
6052 /* This is needed to fix a problem that was caused by a bugfix in function
6053 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6054 corrects the behaviour when a custom element changes to another custom
6055 element with a higher element number that has change actions defined.
6056 Normally, only one change per frame is allowed for custom elements.
6057 Therefore, it is checked if a custom element already changed in the
6058 current frame; if it did, subsequent changes are suppressed.
6059 Unfortunately, this is only checked for element changes, but not for
6060 change actions, which are still executed. As the function above loops
6061 through all custom elements from lower to higher, an element change
6062 resulting in a lower CE number won't be checked again, while a target
6063 element with a higher number will also be checked, and potential change
6064 actions will get executed for this CE, too (which is wrong), while
6065 further changes are ignored (which is correct). As this bugfix breaks
6066 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6067 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6068 behaviour for existing levels and tapes that make use of this bug */
6070 level->use_action_after_change_bug = TRUE;
6073 /* not centering level after relocating player was default only in 3.2.3 */
6074 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6075 level->shifted_relocation = TRUE;
6077 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6078 if (level->game_version < VERSION_IDENT(3,2,6,0))
6079 level->em_explodes_by_fire = TRUE;
6082 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6086 /* map custom element change events that have changed in newer versions
6087 (these following values were accidentally changed in version 3.0.1)
6088 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6089 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6091 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6093 int element = EL_CUSTOM_START + i;
6095 /* order of checking and copying events to be mapped is important */
6096 /* (do not change the start and end value -- they are constant) */
6097 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6099 if (HAS_CHANGE_EVENT(element, j - 2))
6101 SET_CHANGE_EVENT(element, j - 2, FALSE);
6102 SET_CHANGE_EVENT(element, j, TRUE);
6106 /* order of checking and copying events to be mapped is important */
6107 /* (do not change the start and end value -- they are constant) */
6108 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6110 if (HAS_CHANGE_EVENT(element, j - 1))
6112 SET_CHANGE_EVENT(element, j - 1, FALSE);
6113 SET_CHANGE_EVENT(element, j, TRUE);
6119 /* initialize "can_change" field for old levels with only one change page */
6120 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6122 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6124 int element = EL_CUSTOM_START + i;
6126 if (CAN_CHANGE(element))
6127 element_info[element].change->can_change = TRUE;
6131 /* correct custom element values (for old levels without these options) */
6132 if (level->game_version < VERSION_IDENT(3,1,1,0))
6134 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6136 int element = EL_CUSTOM_START + i;
6137 struct ElementInfo *ei = &element_info[element];
6139 if (ei->access_direction == MV_NO_DIRECTION)
6140 ei->access_direction = MV_ALL_DIRECTIONS;
6144 /* correct custom element values (fix invalid values for all versions) */
6147 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6149 int element = EL_CUSTOM_START + i;
6150 struct ElementInfo *ei = &element_info[element];
6152 for (j = 0; j < ei->num_change_pages; j++)
6154 struct ElementChangeInfo *change = &ei->change_page[j];
6156 if (change->trigger_player == CH_PLAYER_NONE)
6157 change->trigger_player = CH_PLAYER_ANY;
6159 if (change->trigger_side == CH_SIDE_NONE)
6160 change->trigger_side = CH_SIDE_ANY;
6165 /* initialize "can_explode" field for old levels which did not store this */
6166 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6167 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6169 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6171 int element = EL_CUSTOM_START + i;
6173 if (EXPLODES_1X1_OLD(element))
6174 element_info[element].explosion_type = EXPLODES_1X1;
6176 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6177 EXPLODES_SMASHED(element) ||
6178 EXPLODES_IMPACT(element)));
6182 /* correct previously hard-coded move delay values for maze runner style */
6183 if (level->game_version < VERSION_IDENT(3,1,1,0))
6185 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6187 int element = EL_CUSTOM_START + i;
6189 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6191 /* previously hard-coded and therefore ignored */
6192 element_info[element].move_delay_fixed = 9;
6193 element_info[element].move_delay_random = 0;
6198 /* map elements that have changed in newer versions */
6199 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6200 level->game_version);
6201 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6202 for (x = 0; x < 3; x++)
6203 for (y = 0; y < 3; y++)
6204 level->yamyam_content[i].e[x][y] =
6205 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6206 level->game_version);
6208 /* initialize element properties for level editor etc. */
6209 InitElementPropertiesEngine(level->game_version);
6210 InitElementPropertiesAfterLoading(level->game_version);
6211 InitElementPropertiesGfxElement();
6214 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6218 /* map elements that have changed in newer versions */
6219 for (y = 0; y < level->fieldy; y++)
6220 for (x = 0; x < level->fieldx; x++)
6221 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6222 level->game_version);
6224 /* clear unused playfield data (nicer if level gets resized in editor) */
6225 for (x = 0; x < MAX_LEV_FIELDX; x++)
6226 for (y = 0; y < MAX_LEV_FIELDY; y++)
6227 if (x >= level->fieldx || y >= level->fieldy)
6228 level->field[x][y] = EL_EMPTY;
6230 /* copy elements to runtime playfield array */
6231 for (x = 0; x < MAX_LEV_FIELDX; x++)
6232 for (y = 0; y < MAX_LEV_FIELDY; y++)
6233 Feld[x][y] = level->field[x][y];
6235 /* initialize level size variables for faster access */
6236 lev_fieldx = level->fieldx;
6237 lev_fieldy = level->fieldy;
6239 /* determine border element for this level */
6240 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6241 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6246 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6248 struct LevelFileInfo *level_file_info = &level->file_info;
6250 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6251 CopyNativeLevel_RND_to_Native(level);
6254 void LoadLevelTemplate(int nr)
6258 setLevelFileInfo(&level_template.file_info, nr);
6259 filename = level_template.file_info.filename;
6261 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6263 LoadLevel_InitVersion(&level_template, filename);
6264 LoadLevel_InitElements(&level_template, filename);
6266 ActivateLevelTemplate();
6269 void LoadLevel(int nr)
6273 setLevelFileInfo(&level.file_info, nr);
6274 filename = level.file_info.filename;
6276 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6278 if (level.use_custom_template)
6279 LoadLevelTemplate(-1);
6281 LoadLevel_InitVersion(&level, filename);
6282 LoadLevel_InitElements(&level, filename);
6283 LoadLevel_InitPlayfield(&level, filename);
6285 LoadLevel_InitNativeEngines(&level, filename);
6288 void LoadLevelInfoOnly(int nr)
6290 setLevelFileInfo(&level.file_info, nr);
6292 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6295 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6299 chunk_size += putFileVersion(file, level->file_version);
6300 chunk_size += putFileVersion(file, level->game_version);
6305 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6309 chunk_size += putFile16BitBE(file, level->creation_date.year);
6310 chunk_size += putFile8Bit(file, level->creation_date.month);
6311 chunk_size += putFile8Bit(file, level->creation_date.day);
6316 #if ENABLE_HISTORIC_CHUNKS
6317 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6321 putFile8Bit(file, level->fieldx);
6322 putFile8Bit(file, level->fieldy);
6324 putFile16BitBE(file, level->time);
6325 putFile16BitBE(file, level->gems_needed);
6327 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6328 putFile8Bit(file, level->name[i]);
6330 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6331 putFile8Bit(file, level->score[i]);
6333 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6334 for (y = 0; y < 3; y++)
6335 for (x = 0; x < 3; x++)
6336 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6337 level->yamyam_content[i].e[x][y]));
6338 putFile8Bit(file, level->amoeba_speed);
6339 putFile8Bit(file, level->time_magic_wall);
6340 putFile8Bit(file, level->time_wheel);
6341 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6342 level->amoeba_content));
6343 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6344 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6345 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6346 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6348 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6350 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6351 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6352 putFile32BitBE(file, level->can_move_into_acid_bits);
6353 putFile8Bit(file, level->dont_collide_with_bits);
6355 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6356 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6358 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6359 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6360 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6362 putFile8Bit(file, level->game_engine_type);
6364 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6368 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6373 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6374 chunk_size += putFile8Bit(file, level->name[i]);
6379 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6384 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6385 chunk_size += putFile8Bit(file, level->author[i]);
6390 #if ENABLE_HISTORIC_CHUNKS
6391 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6396 for (y = 0; y < level->fieldy; y++)
6397 for (x = 0; x < level->fieldx; x++)
6398 if (level->encoding_16bit_field)
6399 chunk_size += putFile16BitBE(file, level->field[x][y]);
6401 chunk_size += putFile8Bit(file, level->field[x][y]);
6407 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6412 for (y = 0; y < level->fieldy; y++)
6413 for (x = 0; x < level->fieldx; x++)
6414 chunk_size += putFile16BitBE(file, level->field[x][y]);
6419 #if ENABLE_HISTORIC_CHUNKS
6420 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6424 putFile8Bit(file, EL_YAMYAM);
6425 putFile8Bit(file, level->num_yamyam_contents);
6426 putFile8Bit(file, 0);
6427 putFile8Bit(file, 0);
6429 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6430 for (y = 0; y < 3; y++)
6431 for (x = 0; x < 3; x++)
6432 if (level->encoding_16bit_field)
6433 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6435 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6439 #if ENABLE_HISTORIC_CHUNKS
6440 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6443 int num_contents, content_xsize, content_ysize;
6444 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6446 if (element == EL_YAMYAM)
6448 num_contents = level->num_yamyam_contents;
6452 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6453 for (y = 0; y < 3; y++)
6454 for (x = 0; x < 3; x++)
6455 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6457 else if (element == EL_BD_AMOEBA)
6463 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6464 for (y = 0; y < 3; y++)
6465 for (x = 0; x < 3; x++)
6466 content_array[i][x][y] = EL_EMPTY;
6467 content_array[0][0][0] = level->amoeba_content;
6471 /* chunk header already written -- write empty chunk data */
6472 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6474 Error(ERR_WARN, "cannot save content for element '%d'", element);
6478 putFile16BitBE(file, element);
6479 putFile8Bit(file, num_contents);
6480 putFile8Bit(file, content_xsize);
6481 putFile8Bit(file, content_ysize);
6483 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6485 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6486 for (y = 0; y < 3; y++)
6487 for (x = 0; x < 3; x++)
6488 putFile16BitBE(file, content_array[i][x][y]);
6492 #if ENABLE_HISTORIC_CHUNKS
6493 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6495 int envelope_nr = element - EL_ENVELOPE_1;
6496 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6500 chunk_size += putFile16BitBE(file, element);
6501 chunk_size += putFile16BitBE(file, envelope_len);
6502 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6503 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6505 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6506 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6508 for (i = 0; i < envelope_len; i++)
6509 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6515 #if ENABLE_HISTORIC_CHUNKS
6516 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6517 int num_changed_custom_elements)
6521 putFile16BitBE(file, num_changed_custom_elements);
6523 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6525 int element = EL_CUSTOM_START + i;
6527 struct ElementInfo *ei = &element_info[element];
6529 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6531 if (check < num_changed_custom_elements)
6533 putFile16BitBE(file, element);
6534 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6541 if (check != num_changed_custom_elements) /* should not happen */
6542 Error(ERR_WARN, "inconsistent number of custom element properties");
6546 #if ENABLE_HISTORIC_CHUNKS
6547 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6548 int num_changed_custom_elements)
6552 putFile16BitBE(file, num_changed_custom_elements);
6554 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6556 int element = EL_CUSTOM_START + i;
6558 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6560 if (check < num_changed_custom_elements)
6562 putFile16BitBE(file, element);
6563 putFile16BitBE(file, element_info[element].change->target_element);
6570 if (check != num_changed_custom_elements) /* should not happen */
6571 Error(ERR_WARN, "inconsistent number of custom target elements");
6575 #if ENABLE_HISTORIC_CHUNKS
6576 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6577 int num_changed_custom_elements)
6579 int i, j, x, y, check = 0;
6581 putFile16BitBE(file, num_changed_custom_elements);
6583 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6585 int element = EL_CUSTOM_START + i;
6586 struct ElementInfo *ei = &element_info[element];
6588 if (ei->modified_settings)
6590 if (check < num_changed_custom_elements)
6592 putFile16BitBE(file, element);
6594 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6595 putFile8Bit(file, ei->description[j]);
6597 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6599 /* some free bytes for future properties and padding */
6600 WriteUnusedBytesToFile(file, 7);
6602 putFile8Bit(file, ei->use_gfx_element);
6603 putFile16BitBE(file, ei->gfx_element_initial);
6605 putFile8Bit(file, ei->collect_score_initial);
6606 putFile8Bit(file, ei->collect_count_initial);
6608 putFile16BitBE(file, ei->push_delay_fixed);
6609 putFile16BitBE(file, ei->push_delay_random);
6610 putFile16BitBE(file, ei->move_delay_fixed);
6611 putFile16BitBE(file, ei->move_delay_random);
6613 putFile16BitBE(file, ei->move_pattern);
6614 putFile8Bit(file, ei->move_direction_initial);
6615 putFile8Bit(file, ei->move_stepsize);
6617 for (y = 0; y < 3; y++)
6618 for (x = 0; x < 3; x++)
6619 putFile16BitBE(file, ei->content.e[x][y]);
6621 putFile32BitBE(file, ei->change->events);
6623 putFile16BitBE(file, ei->change->target_element);
6625 putFile16BitBE(file, ei->change->delay_fixed);
6626 putFile16BitBE(file, ei->change->delay_random);
6627 putFile16BitBE(file, ei->change->delay_frames);
6629 putFile16BitBE(file, ei->change->initial_trigger_element);
6631 putFile8Bit(file, ei->change->explode);
6632 putFile8Bit(file, ei->change->use_target_content);
6633 putFile8Bit(file, ei->change->only_if_complete);
6634 putFile8Bit(file, ei->change->use_random_replace);
6636 putFile8Bit(file, ei->change->random_percentage);
6637 putFile8Bit(file, ei->change->replace_when);
6639 for (y = 0; y < 3; y++)
6640 for (x = 0; x < 3; x++)
6641 putFile16BitBE(file, ei->change->content.e[x][y]);
6643 putFile8Bit(file, ei->slippery_type);
6645 /* some free bytes for future properties and padding */
6646 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6653 if (check != num_changed_custom_elements) /* should not happen */
6654 Error(ERR_WARN, "inconsistent number of custom element properties");
6658 #if ENABLE_HISTORIC_CHUNKS
6659 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6661 struct ElementInfo *ei = &element_info[element];
6664 /* ---------- custom element base property values (96 bytes) ------------- */
6666 putFile16BitBE(file, element);
6668 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6669 putFile8Bit(file, ei->description[i]);
6671 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6673 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
6675 putFile8Bit(file, ei->num_change_pages);
6677 putFile16BitBE(file, ei->ce_value_fixed_initial);
6678 putFile16BitBE(file, ei->ce_value_random_initial);
6679 putFile8Bit(file, ei->use_last_ce_value);
6681 putFile8Bit(file, ei->use_gfx_element);
6682 putFile16BitBE(file, ei->gfx_element_initial);
6684 putFile8Bit(file, ei->collect_score_initial);
6685 putFile8Bit(file, ei->collect_count_initial);
6687 putFile8Bit(file, ei->drop_delay_fixed);
6688 putFile8Bit(file, ei->push_delay_fixed);
6689 putFile8Bit(file, ei->drop_delay_random);
6690 putFile8Bit(file, ei->push_delay_random);
6691 putFile16BitBE(file, ei->move_delay_fixed);
6692 putFile16BitBE(file, ei->move_delay_random);
6694 /* bits 0 - 15 of "move_pattern" ... */
6695 putFile16BitBE(file, ei->move_pattern & 0xffff);
6696 putFile8Bit(file, ei->move_direction_initial);
6697 putFile8Bit(file, ei->move_stepsize);
6699 putFile8Bit(file, ei->slippery_type);
6701 for (y = 0; y < 3; y++)
6702 for (x = 0; x < 3; x++)
6703 putFile16BitBE(file, ei->content.e[x][y]);
6705 putFile16BitBE(file, ei->move_enter_element);
6706 putFile16BitBE(file, ei->move_leave_element);
6707 putFile8Bit(file, ei->move_leave_type);
6709 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
6710 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
6712 putFile8Bit(file, ei->access_direction);
6714 putFile8Bit(file, ei->explosion_delay);
6715 putFile8Bit(file, ei->ignition_delay);
6716 putFile8Bit(file, ei->explosion_type);
6718 /* some free bytes for future custom property values and padding */
6719 WriteUnusedBytesToFile(file, 1);
6721 /* ---------- change page property values (48 bytes) --------------------- */
6723 for (i = 0; i < ei->num_change_pages; i++)
6725 struct ElementChangeInfo *change = &ei->change_page[i];
6726 unsigned int event_bits;
6728 /* bits 0 - 31 of "has_event[]" ... */
6730 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
6731 if (change->has_event[j])
6732 event_bits |= (1 << j);
6733 putFile32BitBE(file, event_bits);
6735 putFile16BitBE(file, change->target_element);
6737 putFile16BitBE(file, change->delay_fixed);
6738 putFile16BitBE(file, change->delay_random);
6739 putFile16BitBE(file, change->delay_frames);
6741 putFile16BitBE(file, change->initial_trigger_element);
6743 putFile8Bit(file, change->explode);
6744 putFile8Bit(file, change->use_target_content);
6745 putFile8Bit(file, change->only_if_complete);
6746 putFile8Bit(file, change->use_random_replace);
6748 putFile8Bit(file, change->random_percentage);
6749 putFile8Bit(file, change->replace_when);
6751 for (y = 0; y < 3; y++)
6752 for (x = 0; x < 3; x++)
6753 putFile16BitBE(file, change->target_content.e[x][y]);
6755 putFile8Bit(file, change->can_change);
6757 putFile8Bit(file, change->trigger_side);
6759 putFile8Bit(file, change->trigger_player);
6760 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
6761 log_2(change->trigger_page)));
6763 putFile8Bit(file, change->has_action);
6764 putFile8Bit(file, change->action_type);
6765 putFile8Bit(file, change->action_mode);
6766 putFile16BitBE(file, change->action_arg);
6768 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
6770 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
6771 if (change->has_event[j])
6772 event_bits |= (1 << (j - 32));
6773 putFile8Bit(file, event_bits);
6778 #if ENABLE_HISTORIC_CHUNKS
6779 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
6781 struct ElementInfo *ei = &element_info[element];
6782 struct ElementGroupInfo *group = ei->group;
6785 putFile16BitBE(file, element);
6787 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6788 putFile8Bit(file, ei->description[i]);
6790 putFile8Bit(file, group->num_elements);
6792 putFile8Bit(file, ei->use_gfx_element);
6793 putFile16BitBE(file, ei->gfx_element_initial);
6795 putFile8Bit(file, group->choice_mode);
6797 /* some free bytes for future values and padding */
6798 WriteUnusedBytesToFile(file, 3);
6800 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
6801 putFile16BitBE(file, group->element[i]);
6805 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
6806 boolean write_element)
6808 int save_type = entry->save_type;
6809 int data_type = entry->data_type;
6810 int conf_type = entry->conf_type;
6811 int byte_mask = conf_type & CONF_MASK_BYTES;
6812 int element = entry->element;
6813 int default_value = entry->default_value;
6815 boolean modified = FALSE;
6817 if (byte_mask != CONF_MASK_MULTI_BYTES)
6819 void *value_ptr = entry->value;
6820 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
6823 /* check if any settings have been modified before saving them */
6824 if (value != default_value)
6827 /* do not save if explicitly told or if unmodified default settings */
6828 if ((save_type == SAVE_CONF_NEVER) ||
6829 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6833 num_bytes += putFile16BitBE(file, element);
6835 num_bytes += putFile8Bit(file, conf_type);
6836 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
6837 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
6838 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
6841 else if (data_type == TYPE_STRING)
6843 char *default_string = entry->default_string;
6844 char *string = (char *)(entry->value);
6845 int string_length = strlen(string);
6848 /* check if any settings have been modified before saving them */
6849 if (!strEqual(string, default_string))
6852 /* do not save if explicitly told or if unmodified default settings */
6853 if ((save_type == SAVE_CONF_NEVER) ||
6854 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6858 num_bytes += putFile16BitBE(file, element);
6860 num_bytes += putFile8Bit(file, conf_type);
6861 num_bytes += putFile16BitBE(file, string_length);
6863 for (i = 0; i < string_length; i++)
6864 num_bytes += putFile8Bit(file, string[i]);
6866 else if (data_type == TYPE_ELEMENT_LIST)
6868 int *element_array = (int *)(entry->value);
6869 int num_elements = *(int *)(entry->num_entities);
6872 /* check if any settings have been modified before saving them */
6873 for (i = 0; i < num_elements; i++)
6874 if (element_array[i] != default_value)
6877 /* do not save if explicitly told or if unmodified default settings */
6878 if ((save_type == SAVE_CONF_NEVER) ||
6879 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6883 num_bytes += putFile16BitBE(file, element);
6885 num_bytes += putFile8Bit(file, conf_type);
6886 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
6888 for (i = 0; i < num_elements; i++)
6889 num_bytes += putFile16BitBE(file, element_array[i]);
6891 else if (data_type == TYPE_CONTENT_LIST)
6893 struct Content *content = (struct Content *)(entry->value);
6894 int num_contents = *(int *)(entry->num_entities);
6897 /* check if any settings have been modified before saving them */
6898 for (i = 0; i < num_contents; i++)
6899 for (y = 0; y < 3; y++)
6900 for (x = 0; x < 3; x++)
6901 if (content[i].e[x][y] != default_value)
6904 /* do not save if explicitly told or if unmodified default settings */
6905 if ((save_type == SAVE_CONF_NEVER) ||
6906 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6910 num_bytes += putFile16BitBE(file, element);
6912 num_bytes += putFile8Bit(file, conf_type);
6913 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
6915 for (i = 0; i < num_contents; i++)
6916 for (y = 0; y < 3; y++)
6917 for (x = 0; x < 3; x++)
6918 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
6924 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
6929 li = *level; /* copy level data into temporary buffer */
6931 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
6932 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
6937 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
6942 li = *level; /* copy level data into temporary buffer */
6944 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
6945 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
6950 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
6952 int envelope_nr = element - EL_ENVELOPE_1;
6956 chunk_size += putFile16BitBE(file, element);
6958 /* copy envelope data into temporary buffer */
6959 xx_envelope = level->envelope[envelope_nr];
6961 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
6962 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
6967 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
6969 struct ElementInfo *ei = &element_info[element];
6973 chunk_size += putFile16BitBE(file, element);
6975 xx_ei = *ei; /* copy element data into temporary buffer */
6977 /* set default description string for this specific element */
6978 strcpy(xx_default_description, getDefaultElementDescription(ei));
6980 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
6981 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
6983 for (i = 0; i < ei->num_change_pages; i++)
6985 struct ElementChangeInfo *change = &ei->change_page[i];
6987 xx_current_change_page = i;
6989 xx_change = *change; /* copy change data into temporary buffer */
6992 setEventBitsFromEventFlags(change);
6994 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
6995 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7002 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7004 struct ElementInfo *ei = &element_info[element];
7005 struct ElementGroupInfo *group = ei->group;
7009 chunk_size += putFile16BitBE(file, element);
7011 xx_ei = *ei; /* copy element data into temporary buffer */
7012 xx_group = *group; /* copy group data into temporary buffer */
7014 /* set default description string for this specific element */
7015 strcpy(xx_default_description, getDefaultElementDescription(ei));
7017 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7018 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7023 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
7029 if (!(file = fopen(filename, MODE_WRITE)))
7031 Error(ERR_WARN, "cannot save level file '%s'", filename);
7035 level->file_version = FILE_VERSION_ACTUAL;
7036 level->game_version = GAME_VERSION_ACTUAL;
7038 level->creation_date = getCurrentDate();
7040 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7041 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7043 chunk_size = SaveLevel_VERS(NULL, level);
7044 putFileChunkBE(file, "VERS", chunk_size);
7045 SaveLevel_VERS(file, level);
7047 chunk_size = SaveLevel_DATE(NULL, level);
7048 putFileChunkBE(file, "DATE", chunk_size);
7049 SaveLevel_DATE(file, level);
7051 chunk_size = SaveLevel_NAME(NULL, level);
7052 putFileChunkBE(file, "NAME", chunk_size);
7053 SaveLevel_NAME(file, level);
7055 chunk_size = SaveLevel_AUTH(NULL, level);
7056 putFileChunkBE(file, "AUTH", chunk_size);
7057 SaveLevel_AUTH(file, level);
7059 chunk_size = SaveLevel_INFO(NULL, level);
7060 putFileChunkBE(file, "INFO", chunk_size);
7061 SaveLevel_INFO(file, level);
7063 chunk_size = SaveLevel_BODY(NULL, level);
7064 putFileChunkBE(file, "BODY", chunk_size);
7065 SaveLevel_BODY(file, level);
7067 chunk_size = SaveLevel_ELEM(NULL, level);
7068 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7070 putFileChunkBE(file, "ELEM", chunk_size);
7071 SaveLevel_ELEM(file, level);
7074 for (i = 0; i < NUM_ENVELOPES; i++)
7076 int element = EL_ENVELOPE_1 + i;
7078 chunk_size = SaveLevel_NOTE(NULL, level, element);
7079 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7081 putFileChunkBE(file, "NOTE", chunk_size);
7082 SaveLevel_NOTE(file, level, element);
7086 /* if not using template level, check for non-default custom/group elements */
7087 if (!level->use_custom_template)
7089 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7091 int element = EL_CUSTOM_START + i;
7093 chunk_size = SaveLevel_CUSX(NULL, level, element);
7094 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7096 putFileChunkBE(file, "CUSX", chunk_size);
7097 SaveLevel_CUSX(file, level, element);
7101 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7103 int element = EL_GROUP_START + i;
7105 chunk_size = SaveLevel_GRPX(NULL, level, element);
7106 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7108 putFileChunkBE(file, "GRPX", chunk_size);
7109 SaveLevel_GRPX(file, level, element);
7116 SetFilePermissions(filename, PERMS_PRIVATE);
7119 void SaveLevel(int nr)
7121 char *filename = getDefaultLevelFilename(nr);
7123 SaveLevelFromFilename(&level, filename);
7126 void SaveLevelTemplate()
7128 char *filename = getDefaultLevelFilename(-1);
7130 SaveLevelFromFilename(&level, filename);
7133 boolean SaveLevelChecked(int nr)
7135 char *filename = getDefaultLevelFilename(nr);
7136 boolean new_level = !fileExists(filename);
7137 boolean level_saved = FALSE;
7139 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7144 Request("Level saved!", REQ_CONFIRM);
7152 void DumpLevel(struct LevelInfo *level)
7154 if (level->no_valid_file)
7156 Error(ERR_WARN, "cannot dump -- no valid level file found");
7161 printf_line("-", 79);
7162 printf("Level xxx (file version %08d, game version %08d)\n",
7163 level->file_version, level->game_version);
7164 printf_line("-", 79);
7166 printf("Level author: '%s'\n", level->author);
7167 printf("Level title: '%s'\n", level->name);
7169 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7171 printf("Level time: %d seconds\n", level->time);
7172 printf("Gems needed: %d\n", level->gems_needed);
7174 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
7175 printf("Time for wheel: %d seconds\n", level->time_wheel);
7176 printf("Time for light: %d seconds\n", level->time_light);
7177 printf("Time for timegate: %d seconds\n", level->time_timegate);
7179 printf("Amoeba speed: %d\n", level->amoeba_speed);
7182 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7183 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7184 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7185 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7186 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7188 printf_line("-", 79);
7192 /* ========================================================================= */
7193 /* tape file functions */
7194 /* ========================================================================= */
7196 static void setTapeInfoToDefaults()
7200 /* always start with reliable default values (empty tape) */
7203 /* default values (also for pre-1.2 tapes) with only the first player */
7204 tape.player_participates[0] = TRUE;
7205 for (i = 1; i < MAX_PLAYERS; i++)
7206 tape.player_participates[i] = FALSE;
7208 /* at least one (default: the first) player participates in every tape */
7209 tape.num_participating_players = 1;
7211 tape.level_nr = level_nr;
7213 tape.changed = FALSE;
7215 tape.recording = FALSE;
7216 tape.playing = FALSE;
7217 tape.pausing = FALSE;
7219 tape.no_valid_file = FALSE;
7222 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7224 tape->file_version = getFileVersion(file);
7225 tape->game_version = getFileVersion(file);
7230 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7234 tape->random_seed = getFile32BitBE(file);
7235 tape->date = getFile32BitBE(file);
7236 tape->length = getFile32BitBE(file);
7238 /* read header fields that are new since version 1.2 */
7239 if (tape->file_version >= FILE_VERSION_1_2)
7241 byte store_participating_players = getFile8Bit(file);
7244 /* since version 1.2, tapes store which players participate in the tape */
7245 tape->num_participating_players = 0;
7246 for (i = 0; i < MAX_PLAYERS; i++)
7248 tape->player_participates[i] = FALSE;
7250 if (store_participating_players & (1 << i))
7252 tape->player_participates[i] = TRUE;
7253 tape->num_participating_players++;
7257 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7259 engine_version = getFileVersion(file);
7260 if (engine_version > 0)
7261 tape->engine_version = engine_version;
7263 tape->engine_version = tape->game_version;
7269 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7271 int level_identifier_size;
7274 level_identifier_size = getFile16BitBE(file);
7276 tape->level_identifier =
7277 checked_realloc(tape->level_identifier, level_identifier_size);
7279 for (i = 0; i < level_identifier_size; i++)
7280 tape->level_identifier[i] = getFile8Bit(file);
7282 tape->level_nr = getFile16BitBE(file);
7284 chunk_size = 2 + level_identifier_size + 2;
7289 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7292 int chunk_size_expected =
7293 (tape->num_participating_players + 1) * tape->length;
7295 if (chunk_size_expected != chunk_size)
7297 ReadUnusedBytesFromFile(file, chunk_size);
7298 return chunk_size_expected;
7301 for (i = 0; i < tape->length; i++)
7303 if (i >= MAX_TAPE_LEN)
7306 for (j = 0; j < MAX_PLAYERS; j++)
7308 tape->pos[i].action[j] = MV_NONE;
7310 if (tape->player_participates[j])
7311 tape->pos[i].action[j] = getFile8Bit(file);
7314 tape->pos[i].delay = getFile8Bit(file);
7316 if (tape->file_version == FILE_VERSION_1_0)
7318 /* eliminate possible diagonal moves in old tapes */
7319 /* this is only for backward compatibility */
7321 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7322 byte action = tape->pos[i].action[0];
7323 int k, num_moves = 0;
7325 for (k = 0; k<4; k++)
7327 if (action & joy_dir[k])
7329 tape->pos[i + num_moves].action[0] = joy_dir[k];
7331 tape->pos[i + num_moves].delay = 0;
7340 tape->length += num_moves;
7343 else if (tape->file_version < FILE_VERSION_2_0)
7345 /* convert pre-2.0 tapes to new tape format */
7347 if (tape->pos[i].delay > 1)
7350 tape->pos[i + 1] = tape->pos[i];
7351 tape->pos[i + 1].delay = 1;
7354 for (j = 0; j < MAX_PLAYERS; j++)
7355 tape->pos[i].action[j] = MV_NONE;
7356 tape->pos[i].delay--;
7363 if (checkEndOfFile(file))
7367 if (i != tape->length)
7368 chunk_size = (tape->num_participating_players + 1) * i;
7373 void LoadTape_SokobanSolution(char *filename)
7376 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7378 if (!(file = openFile(filename, MODE_READ)))
7380 tape.no_valid_file = TRUE;
7385 while (!checkEndOfFile(file))
7387 unsigned char c = getByteFromFile(file);
7389 if (checkEndOfFile(file))
7396 tape.pos[tape.length].action[0] = MV_UP;
7397 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7403 tape.pos[tape.length].action[0] = MV_DOWN;
7404 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7410 tape.pos[tape.length].action[0] = MV_LEFT;
7411 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7417 tape.pos[tape.length].action[0] = MV_RIGHT;
7418 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7426 /* ignore white-space characters */
7430 tape.no_valid_file = TRUE;
7432 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7440 if (tape.no_valid_file)
7443 tape.length_frames = GetTapeLengthFrames();
7444 tape.length_seconds = GetTapeLengthSeconds();
7447 void LoadTapeFromFilename(char *filename)
7449 char cookie[MAX_LINE_LEN];
7450 char chunk_name[CHUNK_ID_LEN + 1];
7454 /* always start with reliable default values */
7455 setTapeInfoToDefaults();
7457 if (strSuffix(filename, ".sln"))
7459 LoadTape_SokobanSolution(filename);
7464 if (!(file = openFile(filename, MODE_READ)))
7466 tape.no_valid_file = TRUE;
7471 getFileChunkBE(file, chunk_name, NULL);
7472 if (strEqual(chunk_name, "RND1"))
7474 getFile32BitBE(file); /* not used */
7476 getFileChunkBE(file, chunk_name, NULL);
7477 if (!strEqual(chunk_name, "TAPE"))
7479 tape.no_valid_file = TRUE;
7481 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7488 else /* check for pre-2.0 file format with cookie string */
7490 strcpy(cookie, chunk_name);
7491 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7493 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7494 cookie[strlen(cookie) - 1] = '\0';
7496 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7498 tape.no_valid_file = TRUE;
7500 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7507 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7509 tape.no_valid_file = TRUE;
7511 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7518 /* pre-2.0 tape files have no game version, so use file version here */
7519 tape.game_version = tape.file_version;
7522 if (tape.file_version < FILE_VERSION_1_2)
7524 /* tape files from versions before 1.2.0 without chunk structure */
7525 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7526 LoadTape_BODY(file, 2 * tape.length, &tape);
7534 int (*loader)(File *, int, struct TapeInfo *);
7538 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7539 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7540 { "INFO", -1, LoadTape_INFO },
7541 { "BODY", -1, LoadTape_BODY },
7545 while (getFileChunkBE(file, chunk_name, &chunk_size))
7549 while (chunk_info[i].name != NULL &&
7550 !strEqual(chunk_name, chunk_info[i].name))
7553 if (chunk_info[i].name == NULL)
7555 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7556 chunk_name, filename);
7557 ReadUnusedBytesFromFile(file, chunk_size);
7559 else if (chunk_info[i].size != -1 &&
7560 chunk_info[i].size != chunk_size)
7562 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7563 chunk_size, chunk_name, filename);
7564 ReadUnusedBytesFromFile(file, chunk_size);
7568 /* call function to load this tape chunk */
7569 int chunk_size_expected =
7570 (chunk_info[i].loader)(file, chunk_size, &tape);
7572 /* the size of some chunks cannot be checked before reading other
7573 chunks first (like "HEAD" and "BODY") that contain some header
7574 information, so check them here */
7575 if (chunk_size_expected != chunk_size)
7577 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7578 chunk_size, chunk_name, filename);
7586 tape.length_frames = GetTapeLengthFrames();
7587 tape.length_seconds = GetTapeLengthSeconds();
7590 printf("::: tape file version: %d\n", tape.file_version);
7591 printf("::: tape game version: %d\n", tape.game_version);
7592 printf("::: tape engine version: %d\n", tape.engine_version);
7596 void LoadTape(int nr)
7598 char *filename = getTapeFilename(nr);
7600 LoadTapeFromFilename(filename);
7603 void LoadSolutionTape(int nr)
7605 char *filename = getSolutionTapeFilename(nr);
7607 LoadTapeFromFilename(filename);
7609 if (TAPE_IS_EMPTY(tape) &&
7610 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7611 level.native_sp_level->demo.is_available)
7612 CopyNativeTape_SP_to_RND(&level);
7615 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7617 putFileVersion(file, tape->file_version);
7618 putFileVersion(file, tape->game_version);
7621 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7624 byte store_participating_players = 0;
7626 /* set bits for participating players for compact storage */
7627 for (i = 0; i < MAX_PLAYERS; i++)
7628 if (tape->player_participates[i])
7629 store_participating_players |= (1 << i);
7631 putFile32BitBE(file, tape->random_seed);
7632 putFile32BitBE(file, tape->date);
7633 putFile32BitBE(file, tape->length);
7635 putFile8Bit(file, store_participating_players);
7637 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7638 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7640 putFileVersion(file, tape->engine_version);
7643 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7645 int level_identifier_size = strlen(tape->level_identifier) + 1;
7648 putFile16BitBE(file, level_identifier_size);
7650 for (i = 0; i < level_identifier_size; i++)
7651 putFile8Bit(file, tape->level_identifier[i]);
7653 putFile16BitBE(file, tape->level_nr);
7656 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7660 for (i = 0; i < tape->length; i++)
7662 for (j = 0; j < MAX_PLAYERS; j++)
7663 if (tape->player_participates[j])
7664 putFile8Bit(file, tape->pos[i].action[j]);
7666 putFile8Bit(file, tape->pos[i].delay);
7670 void SaveTape(int nr)
7672 char *filename = getTapeFilename(nr);
7674 int num_participating_players = 0;
7675 int info_chunk_size;
7676 int body_chunk_size;
7679 InitTapeDirectory(leveldir_current->subdir);
7681 if (!(file = fopen(filename, MODE_WRITE)))
7683 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7687 tape.file_version = FILE_VERSION_ACTUAL;
7688 tape.game_version = GAME_VERSION_ACTUAL;
7690 /* count number of participating players */
7691 for (i = 0; i < MAX_PLAYERS; i++)
7692 if (tape.player_participates[i])
7693 num_participating_players++;
7695 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7696 body_chunk_size = (num_participating_players + 1) * tape.length;
7698 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7699 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7701 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7702 SaveTape_VERS(file, &tape);
7704 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7705 SaveTape_HEAD(file, &tape);
7707 putFileChunkBE(file, "INFO", info_chunk_size);
7708 SaveTape_INFO(file, &tape);
7710 putFileChunkBE(file, "BODY", body_chunk_size);
7711 SaveTape_BODY(file, &tape);
7715 SetFilePermissions(filename, PERMS_PRIVATE);
7717 tape.changed = FALSE;
7720 boolean SaveTapeChecked(int nr)
7722 char *filename = getTapeFilename(nr);
7723 boolean new_tape = !fileExists(filename);
7724 boolean tape_saved = FALSE;
7726 if (new_tape || Request("Replace old tape?", REQ_ASK))
7731 Request("Tape saved!", REQ_CONFIRM);
7739 void DumpTape(struct TapeInfo *tape)
7741 int tape_frame_counter;
7744 if (tape->no_valid_file)
7746 Error(ERR_WARN, "cannot dump -- no valid tape file found");
7751 printf_line("-", 79);
7752 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
7753 tape->level_nr, tape->file_version, tape->game_version);
7754 printf(" (effective engine version %08d)\n",
7755 tape->engine_version);
7756 printf("Level series identifier: '%s'\n", tape->level_identifier);
7757 printf_line("-", 79);
7759 tape_frame_counter = 0;
7761 for (i = 0; i < tape->length; i++)
7763 if (i >= MAX_TAPE_LEN)
7766 printf("%04d: ", i);
7768 for (j = 0; j < MAX_PLAYERS; j++)
7770 if (tape->player_participates[j])
7772 int action = tape->pos[i].action[j];
7774 printf("%d:%02x ", j, action);
7775 printf("[%c%c%c%c|%c%c] - ",
7776 (action & JOY_LEFT ? '<' : ' '),
7777 (action & JOY_RIGHT ? '>' : ' '),
7778 (action & JOY_UP ? '^' : ' '),
7779 (action & JOY_DOWN ? 'v' : ' '),
7780 (action & JOY_BUTTON_1 ? '1' : ' '),
7781 (action & JOY_BUTTON_2 ? '2' : ' '));
7785 printf("(%03d) ", tape->pos[i].delay);
7786 printf("[%05d]\n", tape_frame_counter);
7788 tape_frame_counter += tape->pos[i].delay;
7791 printf_line("-", 79);
7795 /* ========================================================================= */
7796 /* score file functions */
7797 /* ========================================================================= */
7799 void LoadScore(int nr)
7802 char *filename = getScoreFilename(nr);
7803 char cookie[MAX_LINE_LEN];
7804 char line[MAX_LINE_LEN];
7808 /* always start with reliable default values */
7809 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7811 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
7812 highscore[i].Score = 0;
7815 if (!(file = fopen(filename, MODE_READ)))
7818 /* check file identifier */
7819 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
7821 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7822 cookie[strlen(cookie) - 1] = '\0';
7824 if (!checkCookieString(cookie, SCORE_COOKIE))
7826 Error(ERR_WARN, "unknown format of score file '%s'", filename);
7831 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7833 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
7834 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
7835 if (fgets(line, MAX_LINE_LEN, file) == NULL)
7838 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
7839 line[strlen(line) - 1] = '\0';
7841 for (line_ptr = line; *line_ptr; line_ptr++)
7843 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
7845 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
7846 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
7855 void SaveScore(int nr)
7858 char *filename = getScoreFilename(nr);
7861 InitScoreDirectory(leveldir_current->subdir);
7863 if (!(file = fopen(filename, MODE_WRITE)))
7865 Error(ERR_WARN, "cannot save score for level %d", nr);
7869 fprintf(file, "%s\n\n", SCORE_COOKIE);
7871 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7872 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
7876 SetFilePermissions(filename, PERMS_PUBLIC);
7880 /* ========================================================================= */
7881 /* setup file functions */
7882 /* ========================================================================= */
7884 #define TOKEN_STR_PLAYER_PREFIX "player_"
7887 #define SETUP_TOKEN_PLAYER_NAME 0
7888 #define SETUP_TOKEN_SOUND 1
7889 #define SETUP_TOKEN_SOUND_LOOPS 2
7890 #define SETUP_TOKEN_SOUND_MUSIC 3
7891 #define SETUP_TOKEN_SOUND_SIMPLE 4
7892 #define SETUP_TOKEN_TOONS 5
7893 #define SETUP_TOKEN_SCROLL_DELAY 6
7894 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
7895 #define SETUP_TOKEN_SOFT_SCROLLING 8
7896 #define SETUP_TOKEN_FADE_SCREENS 9
7897 #define SETUP_TOKEN_AUTORECORD 10
7898 #define SETUP_TOKEN_SHOW_TITLESCREEN 11
7899 #define SETUP_TOKEN_QUICK_DOORS 12
7900 #define SETUP_TOKEN_TEAM_MODE 13
7901 #define SETUP_TOKEN_HANDICAP 14
7902 #define SETUP_TOKEN_SKIP_LEVELS 15
7903 #define SETUP_TOKEN_TIME_LIMIT 16
7904 #define SETUP_TOKEN_FULLSCREEN 17
7905 #define SETUP_TOKEN_FULLSCREEN_MODE 18
7906 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT 19
7907 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY 20
7908 #define SETUP_TOKEN_ASK_ON_ESCAPE 21
7909 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 22
7910 #define SETUP_TOKEN_QUICK_SWITCH 23
7911 #define SETUP_TOKEN_INPUT_ON_FOCUS 24
7912 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 25
7913 #define SETUP_TOKEN_GAME_FRAME_DELAY 26
7914 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 27
7915 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 28
7916 #define SETUP_TOKEN_GRAPHICS_SET 29
7917 #define SETUP_TOKEN_SOUNDS_SET 30
7918 #define SETUP_TOKEN_MUSIC_SET 31
7919 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 32
7920 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 33
7921 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 34
7922 #define SETUP_TOKEN_VOLUME_SIMPLE 35
7923 #define SETUP_TOKEN_VOLUME_LOOPS 36
7924 #define SETUP_TOKEN_VOLUME_MUSIC 37
7925 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE 38
7926 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 39
7927 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE 40
7929 #define NUM_GLOBAL_SETUP_TOKENS 41
7932 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
7933 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
7934 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
7935 #define SETUP_TOKEN_EDITOR_EL_MORE 3
7936 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 4
7937 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 5
7938 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 6
7939 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 7
7940 #define SETUP_TOKEN_EDITOR_EL_CHARS 8
7941 #define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS 9
7942 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 10
7943 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 11
7944 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 12
7945 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 13
7946 #define SETUP_TOKEN_EDITOR_EL_BY_GAME 14
7947 #define SETUP_TOKEN_EDITOR_EL_BY_TYPE 15
7948 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 16
7950 #define NUM_EDITOR_SETUP_TOKENS 17
7952 /* editor cascade setup */
7953 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
7954 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
7955 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
7956 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
7957 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
7958 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
7959 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
7960 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
7961 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
7962 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
7963 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
7964 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
7965 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
7966 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
7967 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
7969 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
7971 /* shortcut setup */
7972 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
7973 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
7974 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
7975 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
7976 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
7977 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
7978 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
7979 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
7980 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
7981 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
7982 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
7983 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
7984 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
7985 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
7986 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
7987 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
7988 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
7989 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
7990 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
7991 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
7992 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
7994 #define NUM_SHORTCUT_SETUP_TOKENS 21
7997 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
7998 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
7999 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
8000 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
8001 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
8002 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
8003 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
8004 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
8005 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
8006 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
8007 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
8008 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
8009 #define SETUP_TOKEN_PLAYER_KEY_UP 12
8010 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
8011 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8012 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8014 #define NUM_PLAYER_SETUP_TOKENS 16
8017 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8018 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8019 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8021 #define NUM_SYSTEM_SETUP_TOKENS 3
8024 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8026 #define NUM_OPTIONS_SETUP_TOKENS 1
8029 static struct SetupInfo si;
8030 static struct SetupEditorInfo sei;
8031 static struct SetupEditorCascadeInfo seci;
8032 static struct SetupShortcutInfo ssi;
8033 static struct SetupInputInfo sii;
8034 static struct SetupSystemInfo syi;
8035 static struct OptionInfo soi;
8037 static struct TokenInfo global_setup_tokens[] =
8039 { TYPE_STRING, &si.player_name, "player_name" },
8040 { TYPE_SWITCH, &si.sound, "sound" },
8041 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8042 { TYPE_SWITCH, &si.sound_music, "background_music" },
8043 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8044 { TYPE_SWITCH, &si.toons, "toons" },
8045 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8046 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8047 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
8048 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8049 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8050 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8051 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8052 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8053 { TYPE_SWITCH, &si.handicap, "handicap" },
8054 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8055 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8056 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8057 { TYPE_STRING, &si.fullscreen_mode, "fullscreen_mode" },
8058 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8059 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8060 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8061 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8062 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8063 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8064 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8065 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8066 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8067 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8068 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8069 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8070 { TYPE_STRING, &si.music_set, "music_set" },
8071 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8072 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8073 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8074 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8075 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8076 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8077 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8078 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8079 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8082 static struct TokenInfo editor_setup_tokens[] =
8084 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
8085 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
8086 { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
8087 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
8088 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
8089 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
8090 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
8091 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
8092 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
8093 { TYPE_SWITCH, &sei.el_steel_chars, "editor.el_steel_chars" },
8094 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8095 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8096 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8097 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8098 { TYPE_SWITCH, &sei.el_by_game, "editor.el_by_game" },
8099 { TYPE_SWITCH, &sei.el_by_type, "editor.el_by_type" },
8100 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8103 static struct TokenInfo editor_cascade_setup_tokens[] =
8105 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8106 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8107 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8108 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8109 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8110 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8111 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8112 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8113 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8114 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8115 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8116 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8117 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8118 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8119 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8122 static struct TokenInfo shortcut_setup_tokens[] =
8124 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8125 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8126 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8127 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8128 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8129 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8130 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8131 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8132 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8133 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8134 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8135 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8136 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8137 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8138 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8139 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8140 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8141 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8142 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8143 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8144 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8147 static struct TokenInfo player_setup_tokens[] =
8149 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8150 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8151 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8152 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8153 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8154 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8155 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8156 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8157 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8158 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8159 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8160 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8161 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8162 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8163 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8164 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8167 static struct TokenInfo system_setup_tokens[] =
8169 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8170 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8171 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8174 static struct TokenInfo options_setup_tokens[] =
8176 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8179 static char *get_corrected_login_name(char *login_name)
8181 /* needed because player name must be a fixed length string */
8182 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8184 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8185 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8187 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8188 if (strchr(login_name_new, ' '))
8189 *strchr(login_name_new, ' ') = '\0';
8191 return login_name_new;
8194 static void setSetupInfoToDefaults(struct SetupInfo *si)
8198 si->player_name = get_corrected_login_name(getLoginName());
8201 si->sound_loops = TRUE;
8202 si->sound_music = TRUE;
8203 si->sound_simple = TRUE;
8205 si->scroll_delay = TRUE;
8206 si->scroll_delay_value = STD_SCROLL_DELAY;
8207 si->soft_scrolling = TRUE;
8208 si->fade_screens = TRUE;
8209 si->autorecord = TRUE;
8210 si->show_titlescreen = TRUE;
8211 si->quick_doors = FALSE;
8212 si->team_mode = FALSE;
8213 si->handicap = TRUE;
8214 si->skip_levels = TRUE;
8215 si->time_limit = TRUE;
8216 si->fullscreen = FALSE;
8217 si->fullscreen_mode = getStringCopy(DEFAULT_FULLSCREEN_MODE);
8218 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8219 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8220 si->ask_on_escape = TRUE;
8221 si->ask_on_escape_editor = TRUE;
8222 si->quick_switch = FALSE;
8223 si->input_on_focus = FALSE;
8224 si->prefer_aga_graphics = TRUE;
8225 si->game_frame_delay = GAME_FRAME_DELAY;
8226 si->sp_show_border_elements = FALSE;
8227 si->small_game_graphics = FALSE;
8229 si->graphics_set = getStringCopy(GFX_DEFAULT_SUBDIR);
8230 si->sounds_set = getStringCopy(SND_DEFAULT_SUBDIR);
8231 si->music_set = getStringCopy(MUS_DEFAULT_SUBDIR);
8232 si->override_level_graphics = FALSE;
8233 si->override_level_sounds = FALSE;
8234 si->override_level_music = FALSE;
8236 si->volume_simple = 100; /* percent */
8237 si->volume_loops = 100; /* percent */
8238 si->volume_music = 100; /* percent */
8240 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8241 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8242 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8244 si->editor.el_boulderdash = TRUE;
8245 si->editor.el_emerald_mine = TRUE;
8246 si->editor.el_emerald_mine_club = TRUE;
8247 si->editor.el_more = TRUE;
8248 si->editor.el_sokoban = TRUE;
8249 si->editor.el_supaplex = TRUE;
8250 si->editor.el_diamond_caves = TRUE;
8251 si->editor.el_dx_boulderdash = TRUE;
8252 si->editor.el_chars = TRUE;
8253 si->editor.el_steel_chars = TRUE;
8254 si->editor.el_custom = TRUE;
8256 si->editor.el_headlines = TRUE;
8257 si->editor.el_user_defined = FALSE;
8258 si->editor.el_dynamic = TRUE;
8260 si->editor.show_element_token = FALSE;
8262 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8263 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8264 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8266 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8267 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8268 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8269 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8270 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8272 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8273 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8274 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8275 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8276 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8277 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8279 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8280 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8281 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8283 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8284 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8285 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8286 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8288 for (i = 0; i < MAX_PLAYERS; i++)
8290 si->input[i].use_joystick = FALSE;
8291 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8292 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8293 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8294 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8295 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8296 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8297 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8298 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8299 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8300 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8301 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8302 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8303 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8304 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8305 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8308 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8309 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8310 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8312 si->options.verbose = FALSE;
8314 #if defined(CREATE_SPECIAL_EDITION_RND_JUE)
8316 si->handicap = FALSE;
8317 si->fullscreen = TRUE;
8318 si->override_level_graphics = AUTO;
8319 si->override_level_sounds = AUTO;
8320 si->override_level_music = AUTO;
8323 #if defined(PLATFORM_ANDROID)
8324 si->fullscreen = TRUE;
8328 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8330 si->editor_cascade.el_bd = TRUE;
8331 si->editor_cascade.el_em = TRUE;
8332 si->editor_cascade.el_emc = TRUE;
8333 si->editor_cascade.el_rnd = TRUE;
8334 si->editor_cascade.el_sb = TRUE;
8335 si->editor_cascade.el_sp = TRUE;
8336 si->editor_cascade.el_dc = TRUE;
8337 si->editor_cascade.el_dx = TRUE;
8339 si->editor_cascade.el_chars = FALSE;
8340 si->editor_cascade.el_steel_chars = FALSE;
8341 si->editor_cascade.el_ce = FALSE;
8342 si->editor_cascade.el_ge = FALSE;
8343 si->editor_cascade.el_ref = FALSE;
8344 si->editor_cascade.el_user = FALSE;
8345 si->editor_cascade.el_dynamic = FALSE;
8348 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8352 if (!setup_file_hash)
8357 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8358 setSetupInfo(global_setup_tokens, i,
8359 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
8364 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8365 setSetupInfo(editor_setup_tokens, i,
8366 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
8369 /* shortcut setup */
8370 ssi = setup.shortcut;
8371 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8372 setSetupInfo(shortcut_setup_tokens, i,
8373 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
8374 setup.shortcut = ssi;
8377 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8381 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8383 sii = setup.input[pnr];
8384 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8386 char full_token[100];
8388 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8389 setSetupInfo(player_setup_tokens, i,
8390 getHashEntry(setup_file_hash, full_token));
8392 setup.input[pnr] = sii;
8397 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8398 setSetupInfo(system_setup_tokens, i,
8399 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
8403 soi = setup.options;
8404 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8405 setSetupInfo(options_setup_tokens, i,
8406 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
8407 setup.options = soi;
8410 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8414 if (!setup_file_hash)
8417 /* editor cascade setup */
8418 seci = setup.editor_cascade;
8419 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8420 setSetupInfo(editor_cascade_setup_tokens, i,
8421 getHashEntry(setup_file_hash,
8422 editor_cascade_setup_tokens[i].text));
8423 setup.editor_cascade = seci;
8428 char *filename = getSetupFilename();
8429 SetupFileHash *setup_file_hash = NULL;
8431 /* always start with reliable default values */
8432 setSetupInfoToDefaults(&setup);
8434 setup_file_hash = loadSetupFileHash(filename);
8436 if (setup_file_hash)
8438 char *player_name_new;
8440 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
8441 decodeSetupFileHash(setup_file_hash);
8443 freeSetupFileHash(setup_file_hash);
8445 /* needed to work around problems with fixed length strings */
8446 player_name_new = get_corrected_login_name(setup.player_name);
8447 free(setup.player_name);
8448 setup.player_name = player_name_new;
8450 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
8451 if (setup.scroll_delay == FALSE)
8453 setup.scroll_delay_value = MIN_SCROLL_DELAY;
8454 setup.scroll_delay = TRUE; /* now always "on" */
8457 /* make sure that scroll delay value stays inside valid range */
8458 setup.scroll_delay_value =
8459 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
8462 Error(ERR_WARN, "using default setup values");
8465 void LoadSetup_EditorCascade()
8467 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8468 SetupFileHash *setup_file_hash = NULL;
8470 /* always start with reliable default values */
8471 setSetupInfoToDefaults_EditorCascade(&setup);
8473 setup_file_hash = loadSetupFileHash(filename);
8475 if (setup_file_hash)
8477 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
8478 decodeSetupFileHash_EditorCascade(setup_file_hash);
8480 freeSetupFileHash(setup_file_hash);
8488 char *filename = getSetupFilename();
8492 InitUserDataDirectory();
8494 if (!(file = fopen(filename, MODE_WRITE)))
8496 Error(ERR_WARN, "cannot write setup file '%s'", filename);
8500 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
8501 getCookie("SETUP")));
8502 fprintf(file, "\n");
8506 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8508 /* just to make things nicer :) */
8509 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
8510 i == SETUP_TOKEN_GRAPHICS_SET ||
8511 i == SETUP_TOKEN_VOLUME_SIMPLE ||
8512 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
8513 fprintf(file, "\n");
8515 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
8520 fprintf(file, "\n");
8521 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8522 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
8524 /* shortcut setup */
8525 ssi = setup.shortcut;
8526 fprintf(file, "\n");
8527 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8528 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
8531 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8535 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8536 fprintf(file, "\n");
8538 sii = setup.input[pnr];
8539 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8540 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
8545 fprintf(file, "\n");
8546 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8547 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
8550 soi = setup.options;
8551 fprintf(file, "\n");
8552 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8553 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
8557 SetFilePermissions(filename, PERMS_PRIVATE);
8560 void SaveSetup_EditorCascade()
8562 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8566 InitUserDataDirectory();
8568 if (!(file = fopen(filename, MODE_WRITE)))
8570 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
8575 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
8576 getCookie("SETUP")));
8577 fprintf(file, "\n");
8579 seci = setup.editor_cascade;
8580 fprintf(file, "\n");
8581 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8582 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
8586 SetFilePermissions(filename, PERMS_PRIVATE);
8591 void LoadCustomElementDescriptions()
8593 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8594 SetupFileHash *setup_file_hash;
8597 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8599 if (element_info[i].custom_description != NULL)
8601 free(element_info[i].custom_description);
8602 element_info[i].custom_description = NULL;
8606 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8609 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8611 char *token = getStringCat2(element_info[i].token_name, ".name");
8612 char *value = getHashEntry(setup_file_hash, token);
8615 element_info[i].custom_description = getStringCopy(value);
8620 freeSetupFileHash(setup_file_hash);
8623 static int getElementFromToken(char *token)
8625 char *value = getHashEntry(element_token_hash, token);
8630 Error(ERR_WARN, "unknown element token '%s'", token);
8632 return EL_UNDEFINED;
8635 static int get_token_parameter_value(char *token, char *value_raw)
8639 if (token == NULL || value_raw == NULL)
8640 return ARG_UNDEFINED_VALUE;
8642 suffix = strrchr(token, '.');
8646 if (strEqual(suffix, ".element"))
8647 return getElementFromToken(value_raw);
8649 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
8650 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
8653 void InitMenuDesignSettings_Static()
8657 /* always start with reliable default values from static default config */
8658 for (i = 0; image_config_vars[i].token != NULL; i++)
8660 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
8663 *image_config_vars[i].value =
8664 get_token_parameter_value(image_config_vars[i].token, value);
8668 static void InitMenuDesignSettings_SpecialPreProcessing()
8672 /* the following initializes hierarchical values from static configuration */
8674 /* special case: initialize "ARG_DEFAULT" values in static default config */
8675 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
8676 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
8677 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
8678 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
8679 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
8680 titlemessage_default.fade_mode = title_default.fade_mode;
8681 titlemessage_default.fade_delay = title_default.fade_delay;
8682 titlemessage_default.post_delay = title_default.post_delay;
8683 titlemessage_default.auto_delay = title_default.auto_delay;
8685 /* special case: initialize "ARG_DEFAULT" values in static default config */
8686 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
8687 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
8689 titlemessage_initial[i] = titlemessage_initial_default;
8690 titlemessage[i] = titlemessage_default;
8693 /* special case: initialize "ARG_DEFAULT" values in static default config */
8694 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
8695 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8697 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
8698 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
8701 /* special case: initialize "ARG_DEFAULT" values in static default config */
8702 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
8703 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8705 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
8706 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
8707 if (i != GFX_SPECIAL_ARG_EDITOR) /* editor value already initialized */
8708 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
8712 static void InitMenuDesignSettings_SpecialPostProcessing()
8714 /* special case: initialize later added SETUP list size from LEVELS value */
8715 if (menu.list_size[GAME_MODE_SETUP] == -1)
8716 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
8719 static void LoadMenuDesignSettingsFromFilename(char *filename)
8721 static struct TitleMessageInfo tmi;
8722 static struct TokenInfo titlemessage_tokens[] =
8724 { TYPE_INTEGER, &tmi.x, ".x" },
8725 { TYPE_INTEGER, &tmi.y, ".y" },
8726 { TYPE_INTEGER, &tmi.width, ".width" },
8727 { TYPE_INTEGER, &tmi.height, ".height" },
8728 { TYPE_INTEGER, &tmi.chars, ".chars" },
8729 { TYPE_INTEGER, &tmi.lines, ".lines" },
8730 { TYPE_INTEGER, &tmi.align, ".align" },
8731 { TYPE_INTEGER, &tmi.valign, ".valign" },
8732 { TYPE_INTEGER, &tmi.font, ".font" },
8733 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
8734 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
8735 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
8736 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
8737 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
8738 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
8739 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
8740 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
8746 struct TitleMessageInfo *array;
8749 titlemessage_arrays[] =
8751 { titlemessage_initial, "[titlemessage_initial]" },
8752 { titlemessage, "[titlemessage]" },
8756 SetupFileHash *setup_file_hash;
8759 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8762 /* the following initializes hierarchical values from dynamic configuration */
8764 /* special case: initialize with default values that may be overwritten */
8765 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
8766 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8768 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
8769 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
8770 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
8772 if (value_1 != NULL)
8773 menu.draw_xoffset[i] = get_integer_from_string(value_1);
8774 if (value_2 != NULL)
8775 menu.draw_yoffset[i] = get_integer_from_string(value_2);
8776 if (value_3 != NULL)
8777 menu.list_size[i] = get_integer_from_string(value_3);
8780 /* special case: initialize with default values that may be overwritten */
8781 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
8782 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
8784 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
8785 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
8787 if (value_1 != NULL)
8788 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
8789 if (value_2 != NULL)
8790 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
8793 /* special case: initialize with default values that may be overwritten */
8794 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
8795 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
8797 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
8798 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
8800 if (value_1 != NULL)
8801 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
8802 if (value_2 != NULL)
8803 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
8806 /* special case: initialize with default values that may be overwritten */
8807 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
8808 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8810 char *token_1 = "menu.enter_screen.fade_mode";
8811 char *token_2 = "menu.enter_screen.fade_delay";
8812 char *token_3 = "menu.enter_screen.post_delay";
8813 char *token_4 = "menu.leave_screen.fade_mode";
8814 char *token_5 = "menu.leave_screen.fade_delay";
8815 char *token_6 = "menu.leave_screen.post_delay";
8816 char *value_1 = getHashEntry(setup_file_hash, token_1);
8817 char *value_2 = getHashEntry(setup_file_hash, token_2);
8818 char *value_3 = getHashEntry(setup_file_hash, token_3);
8819 char *value_4 = getHashEntry(setup_file_hash, token_4);
8820 char *value_5 = getHashEntry(setup_file_hash, token_5);
8821 char *value_6 = getHashEntry(setup_file_hash, token_6);
8823 if (value_1 != NULL)
8824 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
8826 if (value_2 != NULL)
8827 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
8829 if (value_3 != NULL)
8830 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
8832 if (value_4 != NULL)
8833 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
8835 if (value_5 != NULL)
8836 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
8838 if (value_6 != NULL)
8839 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
8843 /* special case: initialize with default values that may be overwritten */
8844 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
8845 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8847 char *token_01 = "viewport.playfield.x";
8848 char *token_02 = "viewport.playfield.y";
8849 char *token_03 = "viewport.playfield.width";
8850 char *token_04 = "viewport.playfield.height";
8851 char *token_05 = "viewport.playfield.border_size";
8852 char *token_06 = "viewport.door_1.x";
8853 char *token_07 = "viewport.door_1.y";
8854 char *token_08 = "viewport.door_1.width";
8855 char *token_09 = "viewport.door_1.height";
8856 char *token_10 = "viewport.door_1.border_size";
8857 char *token_11 = "viewport.door_2.x";
8858 char *token_12 = "viewport.door_2.y";
8859 char *token_13 = "viewport.door_2.width";
8860 char *token_14 = "viewport.door_2.height";
8861 char *token_15 = "viewport.door_2.border_size";
8862 char *value_01 = getHashEntry(setup_file_hash, token_01);
8863 char *value_02 = getHashEntry(setup_file_hash, token_02);
8864 char *value_03 = getHashEntry(setup_file_hash, token_03);
8865 char *value_04 = getHashEntry(setup_file_hash, token_04);
8866 char *value_05 = getHashEntry(setup_file_hash, token_05);
8867 char *value_06 = getHashEntry(setup_file_hash, token_06);
8868 char *value_07 = getHashEntry(setup_file_hash, token_07);
8869 char *value_08 = getHashEntry(setup_file_hash, token_08);
8870 char *value_09 = getHashEntry(setup_file_hash, token_09);
8871 char *value_10 = getHashEntry(setup_file_hash, token_10);
8872 char *value_11 = getHashEntry(setup_file_hash, token_11);
8873 char *value_12 = getHashEntry(setup_file_hash, token_12);
8874 char *value_13 = getHashEntry(setup_file_hash, token_13);
8875 char *value_14 = getHashEntry(setup_file_hash, token_14);
8876 char *value_15 = getHashEntry(setup_file_hash, token_15);
8878 if (value_01 != NULL)
8879 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
8880 if (value_02 != NULL)
8881 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
8882 if (value_03 != NULL)
8883 viewport.playfield[i].width = get_token_parameter_value(token_03,
8885 if (value_04 != NULL)
8886 viewport.playfield[i].height = get_token_parameter_value(token_04,
8888 if (value_05 != NULL)
8889 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
8891 if (value_06 != NULL)
8892 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
8893 if (value_07 != NULL)
8894 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
8895 if (value_08 != NULL)
8896 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
8897 if (value_09 != NULL)
8898 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
8899 if (value_10 != NULL)
8900 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
8902 if (value_11 != NULL)
8903 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
8904 if (value_12 != NULL)
8905 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
8906 if (value_13 != NULL)
8907 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
8908 if (value_14 != NULL)
8909 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
8910 if (value_15 != NULL)
8911 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
8915 /* special case: initialize with default values that may be overwritten */
8916 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
8917 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
8919 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
8920 char *base_token = titlemessage_arrays[i].text;
8922 for (j = 0; titlemessage_tokens[j].type != -1; j++)
8924 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
8925 char *value = getHashEntry(setup_file_hash, token);
8929 int parameter_value = get_token_parameter_value(token, value);
8931 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
8935 if (titlemessage_tokens[j].type == TYPE_INTEGER)
8936 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
8938 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
8948 /* read (and overwrite with) values that may be specified in config file */
8949 for (i = 0; image_config_vars[i].token != NULL; i++)
8951 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
8953 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
8954 if (value != NULL && !strEqual(value, ARG_DEFAULT))
8955 *image_config_vars[i].value =
8956 get_token_parameter_value(image_config_vars[i].token, value);
8959 freeSetupFileHash(setup_file_hash);
8962 void LoadMenuDesignSettings()
8964 char *filename_base = UNDEFINED_FILENAME, *filename_local;
8966 InitMenuDesignSettings_Static();
8967 InitMenuDesignSettings_SpecialPreProcessing();
8969 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
8971 /* first look for special settings configured in level series config */
8972 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
8974 if (fileExists(filename_base))
8975 LoadMenuDesignSettingsFromFilename(filename_base);
8978 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8980 if (filename_local != NULL && !strEqual(filename_base, filename_local))
8981 LoadMenuDesignSettingsFromFilename(filename_local);
8983 InitMenuDesignSettings_SpecialPostProcessing();
8986 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
8988 char *filename = getEditorSetupFilename();
8989 SetupFileList *setup_file_list, *list;
8990 SetupFileHash *element_hash;
8991 int num_unknown_tokens = 0;
8994 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
8997 element_hash = newSetupFileHash();
8999 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9000 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9002 /* determined size may be larger than needed (due to unknown elements) */
9004 for (list = setup_file_list; list != NULL; list = list->next)
9007 /* add space for up to 3 more elements for padding that may be needed */
9010 /* free memory for old list of elements, if needed */
9011 checked_free(*elements);
9013 /* allocate memory for new list of elements */
9014 *elements = checked_malloc(*num_elements * sizeof(int));
9017 for (list = setup_file_list; list != NULL; list = list->next)
9019 char *value = getHashEntry(element_hash, list->token);
9021 if (value == NULL) /* try to find obsolete token mapping */
9023 char *mapped_token = get_mapped_token(list->token);
9025 if (mapped_token != NULL)
9027 value = getHashEntry(element_hash, mapped_token);
9035 (*elements)[(*num_elements)++] = atoi(value);
9039 if (num_unknown_tokens == 0)
9041 Error(ERR_INFO_LINE, "-");
9042 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9043 Error(ERR_INFO, "- config file: '%s'", filename);
9045 num_unknown_tokens++;
9048 Error(ERR_INFO, "- token: '%s'", list->token);
9052 if (num_unknown_tokens > 0)
9053 Error(ERR_INFO_LINE, "-");
9055 while (*num_elements % 4) /* pad with empty elements, if needed */
9056 (*elements)[(*num_elements)++] = EL_EMPTY;
9058 freeSetupFileList(setup_file_list);
9059 freeSetupFileHash(element_hash);
9062 for (i = 0; i < *num_elements; i++)
9063 printf("editor: element '%s' [%d]\n",
9064 element_info[(*elements)[i]].token_name, (*elements)[i]);
9068 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9071 SetupFileHash *setup_file_hash = NULL;
9072 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9073 char *filename_music, *filename_prefix, *filename_info;
9079 token_to_value_ptr[] =
9081 { "title_header", &tmp_music_file_info.title_header },
9082 { "artist_header", &tmp_music_file_info.artist_header },
9083 { "album_header", &tmp_music_file_info.album_header },
9084 { "year_header", &tmp_music_file_info.year_header },
9086 { "title", &tmp_music_file_info.title },
9087 { "artist", &tmp_music_file_info.artist },
9088 { "album", &tmp_music_file_info.album },
9089 { "year", &tmp_music_file_info.year },
9095 filename_music = (is_sound ? getCustomSoundFilename(basename) :
9096 getCustomMusicFilename(basename));
9098 if (filename_music == NULL)
9101 /* ---------- try to replace file extension ---------- */
9103 filename_prefix = getStringCopy(filename_music);
9104 if (strrchr(filename_prefix, '.') != NULL)
9105 *strrchr(filename_prefix, '.') = '\0';
9106 filename_info = getStringCat2(filename_prefix, ".txt");
9108 if (fileExists(filename_info))
9109 setup_file_hash = loadSetupFileHash(filename_info);
9111 free(filename_prefix);
9112 free(filename_info);
9114 if (setup_file_hash == NULL)
9116 /* ---------- try to add file extension ---------- */
9118 filename_prefix = getStringCopy(filename_music);
9119 filename_info = getStringCat2(filename_prefix, ".txt");
9121 if (fileExists(filename_info))
9122 setup_file_hash = loadSetupFileHash(filename_info);
9124 free(filename_prefix);
9125 free(filename_info);
9128 if (setup_file_hash == NULL)
9131 /* ---------- music file info found ---------- */
9133 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
9135 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
9137 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
9139 *token_to_value_ptr[i].value_ptr =
9140 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
9143 tmp_music_file_info.basename = getStringCopy(basename);
9144 tmp_music_file_info.music = music;
9145 tmp_music_file_info.is_sound = is_sound;
9147 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
9148 *new_music_file_info = tmp_music_file_info;
9150 return new_music_file_info;
9153 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
9155 return get_music_file_info_ext(basename, music, FALSE);
9158 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
9160 return get_music_file_info_ext(basename, sound, TRUE);
9163 static boolean music_info_listed_ext(struct MusicFileInfo *list,
9164 char *basename, boolean is_sound)
9166 for (; list != NULL; list = list->next)
9167 if (list->is_sound == is_sound && strEqual(list->basename, basename))
9173 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
9175 return music_info_listed_ext(list, basename, FALSE);
9178 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
9180 return music_info_listed_ext(list, basename, TRUE);
9183 void LoadMusicInfo()
9185 char *music_directory = getCustomMusicDirectory();
9186 int num_music = getMusicListSize();
9187 int num_music_noconf = 0;
9188 int num_sounds = getSoundListSize();
9190 DirectoryEntry *dir_entry;
9191 struct FileInfo *music, *sound;
9192 struct MusicFileInfo *next, **new;
9195 while (music_file_info != NULL)
9197 next = music_file_info->next;
9199 checked_free(music_file_info->basename);
9201 checked_free(music_file_info->title_header);
9202 checked_free(music_file_info->artist_header);
9203 checked_free(music_file_info->album_header);
9204 checked_free(music_file_info->year_header);
9206 checked_free(music_file_info->title);
9207 checked_free(music_file_info->artist);
9208 checked_free(music_file_info->album);
9209 checked_free(music_file_info->year);
9211 free(music_file_info);
9213 music_file_info = next;
9216 new = &music_file_info;
9218 for (i = 0; i < num_music; i++)
9220 music = getMusicListEntry(i);
9222 if (music->filename == NULL)
9225 if (strEqual(music->filename, UNDEFINED_FILENAME))
9228 /* a configured file may be not recognized as music */
9229 if (!FileIsMusic(music->filename))
9232 if (!music_info_listed(music_file_info, music->filename))
9234 *new = get_music_file_info(music->filename, i);
9237 new = &(*new)->next;
9241 if ((dir = openDirectory(music_directory)) == NULL)
9243 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
9247 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
9249 char *basename = dir_entry->basename;
9250 boolean music_already_used = FALSE;
9253 /* skip all music files that are configured in music config file */
9254 for (i = 0; i < num_music; i++)
9256 music = getMusicListEntry(i);
9258 if (music->filename == NULL)
9261 if (strEqual(basename, music->filename))
9263 music_already_used = TRUE;
9268 if (music_already_used)
9271 if (!FileIsMusic(dir_entry->filename))
9274 if (!music_info_listed(music_file_info, basename))
9276 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
9279 new = &(*new)->next;
9285 closeDirectory(dir);
9287 for (i = 0; i < num_sounds; i++)
9289 sound = getSoundListEntry(i);
9291 if (sound->filename == NULL)
9294 if (strEqual(sound->filename, UNDEFINED_FILENAME))
9297 /* a configured file may be not recognized as sound */
9298 if (!FileIsSound(sound->filename))
9301 if (!sound_info_listed(music_file_info, sound->filename))
9303 *new = get_sound_file_info(sound->filename, i);
9305 new = &(*new)->next;
9310 void add_helpanim_entry(int element, int action, int direction, int delay,
9311 int *num_list_entries)
9313 struct HelpAnimInfo *new_list_entry;
9314 (*num_list_entries)++;
9317 checked_realloc(helpanim_info,
9318 *num_list_entries * sizeof(struct HelpAnimInfo));
9319 new_list_entry = &helpanim_info[*num_list_entries - 1];
9321 new_list_entry->element = element;
9322 new_list_entry->action = action;
9323 new_list_entry->direction = direction;
9324 new_list_entry->delay = delay;
9327 void print_unknown_token(char *filename, char *token, int token_nr)
9331 Error(ERR_INFO_LINE, "-");
9332 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9333 Error(ERR_INFO, "- config file: '%s'", filename);
9336 Error(ERR_INFO, "- token: '%s'", token);
9339 void print_unknown_token_end(int token_nr)
9342 Error(ERR_INFO_LINE, "-");
9345 void LoadHelpAnimInfo()
9347 char *filename = getHelpAnimFilename();
9348 SetupFileList *setup_file_list = NULL, *list;
9349 SetupFileHash *element_hash, *action_hash, *direction_hash;
9350 int num_list_entries = 0;
9351 int num_unknown_tokens = 0;
9354 if (fileExists(filename))
9355 setup_file_list = loadSetupFileList(filename);
9357 if (setup_file_list == NULL)
9359 /* use reliable default values from static configuration */
9360 SetupFileList *insert_ptr;
9362 insert_ptr = setup_file_list =
9363 newSetupFileList(helpanim_config[0].token,
9364 helpanim_config[0].value);
9366 for (i = 1; helpanim_config[i].token; i++)
9367 insert_ptr = addListEntry(insert_ptr,
9368 helpanim_config[i].token,
9369 helpanim_config[i].value);
9372 element_hash = newSetupFileHash();
9373 action_hash = newSetupFileHash();
9374 direction_hash = newSetupFileHash();
9376 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
9377 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9379 for (i = 0; i < NUM_ACTIONS; i++)
9380 setHashEntry(action_hash, element_action_info[i].suffix,
9381 i_to_a(element_action_info[i].value));
9383 /* do not store direction index (bit) here, but direction value! */
9384 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
9385 setHashEntry(direction_hash, element_direction_info[i].suffix,
9386 i_to_a(1 << element_direction_info[i].value));
9388 for (list = setup_file_list; list != NULL; list = list->next)
9390 char *element_token, *action_token, *direction_token;
9391 char *element_value, *action_value, *direction_value;
9392 int delay = atoi(list->value);
9394 if (strEqual(list->token, "end"))
9396 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
9401 /* first try to break element into element/action/direction parts;
9402 if this does not work, also accept combined "element[.act][.dir]"
9403 elements (like "dynamite.active"), which are unique elements */
9405 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
9407 element_value = getHashEntry(element_hash, list->token);
9408 if (element_value != NULL) /* element found */
9409 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9413 /* no further suffixes found -- this is not an element */
9414 print_unknown_token(filename, list->token, num_unknown_tokens++);
9420 /* token has format "<prefix>.<something>" */
9422 action_token = strchr(list->token, '.'); /* suffix may be action ... */
9423 direction_token = action_token; /* ... or direction */
9425 element_token = getStringCopy(list->token);
9426 *strchr(element_token, '.') = '\0';
9428 element_value = getHashEntry(element_hash, element_token);
9430 if (element_value == NULL) /* this is no element */
9432 element_value = getHashEntry(element_hash, list->token);
9433 if (element_value != NULL) /* combined element found */
9434 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9437 print_unknown_token(filename, list->token, num_unknown_tokens++);
9439 free(element_token);
9444 action_value = getHashEntry(action_hash, action_token);
9446 if (action_value != NULL) /* action found */
9448 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
9451 free(element_token);
9456 direction_value = getHashEntry(direction_hash, direction_token);
9458 if (direction_value != NULL) /* direction found */
9460 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
9463 free(element_token);
9468 if (strchr(action_token + 1, '.') == NULL)
9470 /* no further suffixes found -- this is not an action nor direction */
9472 element_value = getHashEntry(element_hash, list->token);
9473 if (element_value != NULL) /* combined element found */
9474 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9477 print_unknown_token(filename, list->token, num_unknown_tokens++);
9479 free(element_token);
9484 /* token has format "<prefix>.<suffix>.<something>" */
9486 direction_token = strchr(action_token + 1, '.');
9488 action_token = getStringCopy(action_token);
9489 *strchr(action_token + 1, '.') = '\0';
9491 action_value = getHashEntry(action_hash, action_token);
9493 if (action_value == NULL) /* this is no action */
9495 element_value = getHashEntry(element_hash, list->token);
9496 if (element_value != NULL) /* combined element found */
9497 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9500 print_unknown_token(filename, list->token, num_unknown_tokens++);
9502 free(element_token);
9508 direction_value = getHashEntry(direction_hash, direction_token);
9510 if (direction_value != NULL) /* direction found */
9512 add_helpanim_entry(atoi(element_value), atoi(action_value),
9513 atoi(direction_value), delay, &num_list_entries);
9515 free(element_token);
9521 /* this is no direction */
9523 element_value = getHashEntry(element_hash, list->token);
9524 if (element_value != NULL) /* combined element found */
9525 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9528 print_unknown_token(filename, list->token, num_unknown_tokens++);
9530 free(element_token);
9534 print_unknown_token_end(num_unknown_tokens);
9536 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
9537 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
9539 freeSetupFileList(setup_file_list);
9540 freeSetupFileHash(element_hash);
9541 freeSetupFileHash(action_hash);
9542 freeSetupFileHash(direction_hash);
9545 for (i = 0; i < num_list_entries; i++)
9546 printf("::: '%s': %d, %d, %d => %d\n",
9547 EL_NAME(helpanim_info[i].element),
9548 helpanim_info[i].element,
9549 helpanim_info[i].action,
9550 helpanim_info[i].direction,
9551 helpanim_info[i].delay);
9555 void LoadHelpTextInfo()
9557 char *filename = getHelpTextFilename();
9560 if (helptext_info != NULL)
9562 freeSetupFileHash(helptext_info);
9563 helptext_info = NULL;
9566 if (fileExists(filename))
9567 helptext_info = loadSetupFileHash(filename);
9569 if (helptext_info == NULL)
9571 /* use reliable default values from static configuration */
9572 helptext_info = newSetupFileHash();
9574 for (i = 0; helptext_config[i].token; i++)
9575 setHashEntry(helptext_info,
9576 helptext_config[i].token,
9577 helptext_config[i].value);
9581 BEGIN_HASH_ITERATION(helptext_info, itr)
9583 printf("::: '%s' => '%s'\n",
9584 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
9586 END_HASH_ITERATION(hash, itr)
9591 /* ------------------------------------------------------------------------- */
9592 /* convert levels */
9593 /* ------------------------------------------------------------------------- */
9595 #define MAX_NUM_CONVERT_LEVELS 1000
9597 void ConvertLevels()
9599 static LevelDirTree *convert_leveldir = NULL;
9600 static int convert_level_nr = -1;
9601 static int num_levels_handled = 0;
9602 static int num_levels_converted = 0;
9603 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
9606 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9607 global.convert_leveldir);
9609 if (convert_leveldir == NULL)
9610 Error(ERR_EXIT, "no such level identifier: '%s'",
9611 global.convert_leveldir);
9613 leveldir_current = convert_leveldir;
9615 if (global.convert_level_nr != -1)
9617 convert_leveldir->first_level = global.convert_level_nr;
9618 convert_leveldir->last_level = global.convert_level_nr;
9621 convert_level_nr = convert_leveldir->first_level;
9623 printf_line("=", 79);
9624 printf("Converting levels\n");
9625 printf_line("-", 79);
9626 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
9627 printf("Level series name: '%s'\n", convert_leveldir->name);
9628 printf("Level series author: '%s'\n", convert_leveldir->author);
9629 printf("Number of levels: %d\n", convert_leveldir->levels);
9630 printf_line("=", 79);
9633 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
9634 levels_failed[i] = FALSE;
9636 while (convert_level_nr <= convert_leveldir->last_level)
9638 char *level_filename;
9641 level_nr = convert_level_nr++;
9643 printf("Level %03d: ", level_nr);
9645 LoadLevel(level_nr);
9646 if (level.no_valid_file)
9648 printf("(no level)\n");
9652 printf("converting level ... ");
9654 level_filename = getDefaultLevelFilename(level_nr);
9655 new_level = !fileExists(level_filename);
9659 SaveLevel(level_nr);
9661 num_levels_converted++;
9663 printf("converted.\n");
9667 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
9668 levels_failed[level_nr] = TRUE;
9670 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
9673 num_levels_handled++;
9677 printf_line("=", 79);
9678 printf("Number of levels handled: %d\n", num_levels_handled);
9679 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
9680 (num_levels_handled ?
9681 num_levels_converted * 100 / num_levels_handled : 0));
9682 printf_line("-", 79);
9683 printf("Summary (for automatic parsing by scripts):\n");
9684 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
9685 convert_leveldir->identifier, num_levels_converted,
9687 (num_levels_handled ?
9688 num_levels_converted * 100 / num_levels_handled : 0));
9690 if (num_levels_handled != num_levels_converted)
9692 printf(", FAILED:");
9693 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
9694 if (levels_failed[i])
9699 printf_line("=", 79);
9705 /* ------------------------------------------------------------------------- */
9706 /* create and save images for use in level sketches (raw BMP format) */
9707 /* ------------------------------------------------------------------------- */
9709 void CreateLevelSketchImages()
9711 #if defined(TARGET_SDL)
9716 InitElementPropertiesGfxElement();
9718 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
9719 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
9721 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9725 int element = getMappedElement(i);
9726 int graphic = el2edimg(element);
9732 sprintf(basename1, "%03d.bmp", i);
9733 sprintf(basename2, "%03ds.bmp", i);
9735 filename1 = getPath2(global.create_images_dir, basename1);
9736 filename2 = getPath2(global.create_images_dir, basename2);
9738 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
9739 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
9742 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
9743 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
9745 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
9746 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
9748 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
9749 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
9755 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
9758 FreeBitmap(bitmap1);
9759 FreeBitmap(bitmap2);
9764 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
9771 /* ------------------------------------------------------------------------- */
9772 /* create and save images for custom and group elements (raw BMP format) */
9773 /* ------------------------------------------------------------------------- */
9775 void CreateCustomElementImages(char *directory)
9777 #if defined(TARGET_SDL)
9778 char *src_basename = "RocksCE-template.ilbm";
9779 char *dst_basename = "RocksCE.bmp";
9780 char *src_filename = getPath2(directory, src_basename);
9781 char *dst_filename = getPath2(directory, dst_basename);
9785 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
9788 SDLInitVideoDisplay();
9790 src_bitmap = LoadImage(src_filename);
9792 bitmap = CreateBitmap(TILEX * 16 * 2,
9793 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
9796 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9803 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
9804 TILEX * x, TILEY * y + yoffset_ce);
9806 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
9808 TILEX * x + TILEX * 16,
9809 TILEY * y + yoffset_ce);
9811 for (j = 2; j >= 0; j--)
9815 BlitBitmap(src_bitmap, bitmap,
9816 TILEX + c * 7, 0, 6, 10,
9817 TILEX * x + 6 + j * 7,
9818 TILEY * y + 11 + yoffset_ce);
9820 BlitBitmap(src_bitmap, bitmap,
9821 TILEX + c * 8, TILEY, 6, 10,
9822 TILEX * 16 + TILEX * x + 6 + j * 8,
9823 TILEY * y + 10 + yoffset_ce);
9829 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
9836 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
9837 TILEX * x, TILEY * y + yoffset_ge);
9839 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
9841 TILEX * x + TILEX * 16,
9842 TILEY * y + yoffset_ge);
9844 for (j = 1; j >= 0; j--)
9848 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
9849 TILEX * x + 6 + j * 10,
9850 TILEY * y + 11 + yoffset_ge);
9852 BlitBitmap(src_bitmap, bitmap,
9853 TILEX + c * 8, TILEY + 12, 6, 10,
9854 TILEX * 16 + TILEX * x + 10 + j * 8,
9855 TILEY * y + 10 + yoffset_ge);
9861 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
9862 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);