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 for (x = 0; x < MAX_LEV_FIELDX; x++)
1612 for (y = 0; y < MAX_LEV_FIELDY; y++)
1613 level->field[x][y] = EL_SAND;
1615 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1616 level->name[i] = '\0';
1617 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1618 level->author[i] = '\0';
1620 strcpy(level->name, NAMELESS_LEVEL_NAME);
1621 strcpy(level->author, ANONYMOUS_NAME);
1623 level->field[0][0] = EL_PLAYER_1;
1624 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1626 BorderElement = EL_STEELWALL;
1628 /* set all bug compatibility flags to "false" => do not emulate this bug */
1629 level->use_action_after_change_bug = FALSE;
1631 if (leveldir_current)
1633 /* try to determine better author name than 'anonymous' */
1634 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1636 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1637 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1641 switch (LEVELCLASS(leveldir_current))
1643 case LEVELCLASS_TUTORIAL:
1644 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1647 case LEVELCLASS_CONTRIB:
1648 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1649 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1652 case LEVELCLASS_PRIVATE:
1653 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1654 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1658 /* keep default value */
1665 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1667 static boolean clipboard_elements_initialized = FALSE;
1670 InitElementPropertiesStatic();
1672 li = *level; /* copy level data into temporary buffer */
1673 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1674 *level = li; /* copy temporary buffer back to level data */
1676 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1679 struct ElementInfo *ei = &element_info[element];
1681 /* never initialize clipboard elements after the very first time */
1682 /* (to be able to use clipboard elements between several levels) */
1683 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1686 if (IS_ENVELOPE(element))
1688 int envelope_nr = element - EL_ENVELOPE_1;
1690 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1692 level->envelope[envelope_nr] = xx_envelope;
1695 if (IS_CUSTOM_ELEMENT(element) ||
1696 IS_GROUP_ELEMENT(element) ||
1697 IS_INTERNAL_ELEMENT(element))
1699 xx_ei = *ei; /* copy element data into temporary buffer */
1701 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1706 setElementChangePages(ei, 1);
1707 setElementChangeInfoToDefaults(ei->change);
1709 if (IS_CUSTOM_ELEMENT(element) ||
1710 IS_GROUP_ELEMENT(element) ||
1711 IS_INTERNAL_ELEMENT(element))
1713 setElementDescriptionToDefault(ei);
1715 ei->modified_settings = FALSE;
1718 if (IS_CUSTOM_ELEMENT(element) ||
1719 IS_INTERNAL_ELEMENT(element))
1721 /* internal values used in level editor */
1723 ei->access_type = 0;
1724 ei->access_layer = 0;
1725 ei->access_protected = 0;
1726 ei->walk_to_action = 0;
1727 ei->smash_targets = 0;
1730 ei->can_explode_by_fire = FALSE;
1731 ei->can_explode_smashed = FALSE;
1732 ei->can_explode_impact = FALSE;
1734 ei->current_change_page = 0;
1737 if (IS_GROUP_ELEMENT(element) ||
1738 IS_INTERNAL_ELEMENT(element))
1740 struct ElementGroupInfo *group;
1742 /* initialize memory for list of elements in group */
1743 if (ei->group == NULL)
1744 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1748 xx_group = *group; /* copy group data into temporary buffer */
1750 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1756 clipboard_elements_initialized = TRUE;
1759 static void setLevelInfoToDefaults(struct LevelInfo *level,
1760 boolean level_info_only)
1762 setLevelInfoToDefaults_Level(level);
1764 if (!level_info_only)
1765 setLevelInfoToDefaults_Elements(level);
1767 level->no_valid_file = FALSE;
1769 level->changed = FALSE;
1772 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1774 level_file_info->nr = 0;
1775 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1776 level_file_info->packed = FALSE;
1777 level_file_info->basename = NULL;
1778 level_file_info->filename = NULL;
1781 static void ActivateLevelTemplate()
1785 /* Currently there is no special action needed to activate the template
1786 data, because 'element_info' property settings overwrite the original
1787 level data, while all other variables do not change. */
1789 /* Exception: 'from_level_template' elements in the original level playfield
1790 are overwritten with the corresponding elements at the same position in
1791 playfield from the level template. */
1793 for (x = 0; x < level.fieldx; x++)
1794 for (y = 0; y < level.fieldy; y++)
1795 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1796 level.field[x][y] = level_template.field[x][y];
1798 if (check_special_flags("load_xsb_to_ces"))
1800 struct LevelInfo level_backup = level;
1802 /* overwrite all individual level settings from template level settings */
1803 level = level_template;
1805 /* restore playfield size */
1806 level.fieldx = level_backup.fieldx;
1807 level.fieldy = level_backup.fieldy;
1809 /* restore playfield content */
1810 for (x = 0; x < level.fieldx; x++)
1811 for (y = 0; y < level.fieldy; y++)
1812 level.field[x][y] = level_backup.field[x][y];
1814 /* restore name and author from individual level */
1815 strcpy(level.name, level_backup.name);
1816 strcpy(level.author, level_backup.author);
1818 /* restore flag "use_custom_template" */
1819 level.use_custom_template = level_backup.use_custom_template;
1823 static char *getLevelFilenameFromBasename(char *basename)
1825 static char *filename = NULL;
1827 checked_free(filename);
1829 filename = getPath2(getCurrentLevelDir(), basename);
1834 static int getFileTypeFromBasename(char *basename)
1836 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1838 static char *filename = NULL;
1839 struct stat file_status;
1841 /* ---------- try to determine file type from filename ---------- */
1843 /* check for typical filename of a Supaplex level package file */
1844 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1845 return LEVEL_FILE_TYPE_SP;
1847 /* check for typical filename of a Diamond Caves II level package file */
1848 if (strSuffixLower(basename, ".dc") ||
1849 strSuffixLower(basename, ".dc2"))
1850 return LEVEL_FILE_TYPE_DC;
1852 /* check for typical filename of a Sokoban level package file */
1853 if (strSuffixLower(basename, ".xsb") &&
1854 strchr(basename, '%') == NULL)
1855 return LEVEL_FILE_TYPE_SB;
1857 /* ---------- try to determine file type from filesize ---------- */
1859 checked_free(filename);
1860 filename = getPath2(getCurrentLevelDir(), basename);
1862 if (stat(filename, &file_status) == 0)
1864 /* check for typical filesize of a Supaplex level package file */
1865 if (file_status.st_size == 170496)
1866 return LEVEL_FILE_TYPE_SP;
1869 return LEVEL_FILE_TYPE_UNKNOWN;
1872 static boolean checkForPackageFromBasename(char *basename)
1874 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
1875 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
1877 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
1880 static char *getSingleLevelBasenameExt(int nr, char *extension)
1882 static char basename[MAX_FILENAME_LEN];
1885 sprintf(basename, "template.%s", extension);
1887 sprintf(basename, "%03d.%s", nr, extension);
1892 static char *getSingleLevelBasename(int nr)
1894 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
1897 static char *getPackedLevelBasename(int type)
1899 static char basename[MAX_FILENAME_LEN];
1900 char *directory = getCurrentLevelDir();
1902 DirectoryEntry *dir_entry;
1904 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
1906 if ((dir = openDirectory(directory)) == NULL)
1908 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
1913 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
1915 char *entry_basename = dir_entry->basename;
1916 int entry_type = getFileTypeFromBasename(entry_basename);
1918 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
1920 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
1923 strcpy(basename, entry_basename);
1930 closeDirectory(dir);
1935 static char *getSingleLevelFilename(int nr)
1937 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
1940 #if ENABLE_UNUSED_CODE
1941 static char *getPackedLevelFilename(int type)
1943 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
1947 char *getDefaultLevelFilename(int nr)
1949 return getSingleLevelFilename(nr);
1952 #if ENABLE_UNUSED_CODE
1953 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
1957 lfi->packed = FALSE;
1958 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
1959 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1963 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
1964 int type, char *format, ...)
1966 static char basename[MAX_FILENAME_LEN];
1969 va_start(ap, format);
1970 vsprintf(basename, format, ap);
1974 lfi->packed = FALSE;
1975 lfi->basename = basename;
1976 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1979 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
1984 lfi->basename = getPackedLevelBasename(lfi->type);
1985 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1988 static int getFiletypeFromID(char *filetype_id)
1990 char *filetype_id_lower;
1991 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
1994 if (filetype_id == NULL)
1995 return LEVEL_FILE_TYPE_UNKNOWN;
1997 filetype_id_lower = getStringToLower(filetype_id);
1999 for (i = 0; filetype_id_list[i].id != NULL; i++)
2001 char *id_lower = getStringToLower(filetype_id_list[i].id);
2003 if (strEqual(filetype_id_lower, id_lower))
2004 filetype = filetype_id_list[i].filetype;
2008 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2012 free(filetype_id_lower);
2017 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2021 /* special case: level number is negative => check for level template file */
2024 /* global variable "leveldir_current" must be modified in the loop below */
2025 LevelDirTree *leveldir_current_last = leveldir_current;
2027 /* check for template level in path from current to topmost tree node */
2029 while (leveldir_current != NULL)
2031 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2032 "template.%s", LEVELFILE_EXTENSION);
2034 if (fileExists(lfi->filename))
2037 leveldir_current = leveldir_current->node_parent;
2040 /* restore global variable "leveldir_current" modified in above loop */
2041 leveldir_current = leveldir_current_last;
2043 /* no fallback if template file not existing */
2047 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2048 if (leveldir_current->level_filename != NULL)
2050 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2052 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2053 leveldir_current->level_filename, nr);
2055 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2057 if (fileExists(lfi->filename))
2061 /* check for native Rocks'n'Diamonds level file */
2062 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2063 "%03d.%s", nr, LEVELFILE_EXTENSION);
2064 if (fileExists(lfi->filename))
2067 /* check for Emerald Mine level file (V1) */
2068 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2069 'a' + (nr / 10) % 26, '0' + nr % 10);
2070 if (fileExists(lfi->filename))
2072 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2073 'A' + (nr / 10) % 26, '0' + nr % 10);
2074 if (fileExists(lfi->filename))
2077 /* check for Emerald Mine level file (V2 to V5) */
2078 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2079 if (fileExists(lfi->filename))
2082 /* check for Emerald Mine level file (V6 / single mode) */
2083 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2084 if (fileExists(lfi->filename))
2086 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2087 if (fileExists(lfi->filename))
2090 /* check for Emerald Mine level file (V6 / teamwork mode) */
2091 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2092 if (fileExists(lfi->filename))
2094 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2095 if (fileExists(lfi->filename))
2098 /* check for various packed level file formats */
2099 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2100 if (fileExists(lfi->filename))
2103 /* no known level file found -- use default values (and fail later) */
2104 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2105 "%03d.%s", nr, LEVELFILE_EXTENSION);
2108 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2110 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2111 lfi->type = getFileTypeFromBasename(lfi->basename);
2114 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2116 /* always start with reliable default values */
2117 setFileInfoToDefaults(level_file_info);
2119 level_file_info->nr = nr; /* set requested level number */
2121 determineLevelFileInfo_Filename(level_file_info);
2122 determineLevelFileInfo_Filetype(level_file_info);
2125 /* ------------------------------------------------------------------------- */
2126 /* functions for loading R'n'D level */
2127 /* ------------------------------------------------------------------------- */
2129 int getMappedElement(int element)
2131 /* remap some (historic, now obsolete) elements */
2135 case EL_PLAYER_OBSOLETE:
2136 element = EL_PLAYER_1;
2139 case EL_KEY_OBSOLETE:
2143 case EL_EM_KEY_1_FILE_OBSOLETE:
2144 element = EL_EM_KEY_1;
2147 case EL_EM_KEY_2_FILE_OBSOLETE:
2148 element = EL_EM_KEY_2;
2151 case EL_EM_KEY_3_FILE_OBSOLETE:
2152 element = EL_EM_KEY_3;
2155 case EL_EM_KEY_4_FILE_OBSOLETE:
2156 element = EL_EM_KEY_4;
2159 case EL_ENVELOPE_OBSOLETE:
2160 element = EL_ENVELOPE_1;
2168 if (element >= NUM_FILE_ELEMENTS)
2170 Error(ERR_WARN, "invalid level element %d", element);
2172 element = EL_UNKNOWN;
2180 int getMappedElementByVersion(int element, int game_version)
2182 /* remap some elements due to certain game version */
2184 if (game_version <= VERSION_IDENT(2,2,0,0))
2186 /* map game font elements */
2187 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2188 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2189 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2190 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2193 if (game_version < VERSION_IDENT(3,0,0,0))
2195 /* map Supaplex gravity tube elements */
2196 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2197 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2198 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2199 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2206 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2208 level->file_version = getFileVersion(file);
2209 level->game_version = getFileVersion(file);
2214 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2216 level->creation_date.year = getFile16BitBE(file);
2217 level->creation_date.month = getFile8Bit(file);
2218 level->creation_date.day = getFile8Bit(file);
2220 level->creation_date.src = DATE_SRC_LEVELFILE;
2225 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2227 int initial_player_stepsize;
2228 int initial_player_gravity;
2231 level->fieldx = getFile8Bit(file);
2232 level->fieldy = getFile8Bit(file);
2234 level->time = getFile16BitBE(file);
2235 level->gems_needed = getFile16BitBE(file);
2237 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2238 level->name[i] = getFile8Bit(file);
2239 level->name[MAX_LEVEL_NAME_LEN] = 0;
2241 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2242 level->score[i] = getFile8Bit(file);
2244 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2245 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2246 for (y = 0; y < 3; y++)
2247 for (x = 0; x < 3; x++)
2248 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2250 level->amoeba_speed = getFile8Bit(file);
2251 level->time_magic_wall = getFile8Bit(file);
2252 level->time_wheel = getFile8Bit(file);
2253 level->amoeba_content = getMappedElement(getFile8Bit(file));
2255 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2258 for (i = 0; i < MAX_PLAYERS; i++)
2259 level->initial_player_stepsize[i] = initial_player_stepsize;
2261 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2263 for (i = 0; i < MAX_PLAYERS; i++)
2264 level->initial_player_gravity[i] = initial_player_gravity;
2266 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2267 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2269 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2271 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2272 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2273 level->can_move_into_acid_bits = getFile32BitBE(file);
2274 level->dont_collide_with_bits = getFile8Bit(file);
2276 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2277 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2279 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2280 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2281 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2283 level->game_engine_type = getFile8Bit(file);
2285 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2290 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2294 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2295 level->name[i] = getFile8Bit(file);
2296 level->name[MAX_LEVEL_NAME_LEN] = 0;
2301 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2305 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2306 level->author[i] = getFile8Bit(file);
2307 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2312 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2315 int chunk_size_expected = level->fieldx * level->fieldy;
2317 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2318 stored with 16-bit encoding (and should be twice as big then).
2319 Even worse, playfield data was stored 16-bit when only yamyam content
2320 contained 16-bit elements and vice versa. */
2322 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2323 chunk_size_expected *= 2;
2325 if (chunk_size_expected != chunk_size)
2327 ReadUnusedBytesFromFile(file, chunk_size);
2328 return chunk_size_expected;
2331 for (y = 0; y < level->fieldy; y++)
2332 for (x = 0; x < level->fieldx; x++)
2333 level->field[x][y] =
2334 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2339 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2342 int header_size = 4;
2343 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2344 int chunk_size_expected = header_size + content_size;
2346 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2347 stored with 16-bit encoding (and should be twice as big then).
2348 Even worse, playfield data was stored 16-bit when only yamyam content
2349 contained 16-bit elements and vice versa. */
2351 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2352 chunk_size_expected += content_size;
2354 if (chunk_size_expected != chunk_size)
2356 ReadUnusedBytesFromFile(file, chunk_size);
2357 return chunk_size_expected;
2361 level->num_yamyam_contents = getFile8Bit(file);
2365 /* correct invalid number of content fields -- should never happen */
2366 if (level->num_yamyam_contents < 1 ||
2367 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2368 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2370 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2371 for (y = 0; y < 3; y++)
2372 for (x = 0; x < 3; x++)
2373 level->yamyam_content[i].e[x][y] =
2374 getMappedElement(level->encoding_16bit_field ?
2375 getFile16BitBE(file) : getFile8Bit(file));
2379 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2384 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2386 element = getMappedElement(getFile16BitBE(file));
2387 num_contents = getFile8Bit(file);
2389 getFile8Bit(file); /* content x size (unused) */
2390 getFile8Bit(file); /* content y size (unused) */
2392 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2394 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2395 for (y = 0; y < 3; y++)
2396 for (x = 0; x < 3; x++)
2397 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2399 /* correct invalid number of content fields -- should never happen */
2400 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2401 num_contents = STD_ELEMENT_CONTENTS;
2403 if (element == EL_YAMYAM)
2405 level->num_yamyam_contents = num_contents;
2407 for (i = 0; i < num_contents; i++)
2408 for (y = 0; y < 3; y++)
2409 for (x = 0; x < 3; x++)
2410 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2412 else if (element == EL_BD_AMOEBA)
2414 level->amoeba_content = content_array[0][0][0];
2418 Error(ERR_WARN, "cannot load content for element '%d'", element);
2424 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2430 int chunk_size_expected;
2432 element = getMappedElement(getFile16BitBE(file));
2433 if (!IS_ENVELOPE(element))
2434 element = EL_ENVELOPE_1;
2436 envelope_nr = element - EL_ENVELOPE_1;
2438 envelope_len = getFile16BitBE(file);
2440 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2441 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2443 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2445 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2446 if (chunk_size_expected != chunk_size)
2448 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2449 return chunk_size_expected;
2452 for (i = 0; i < envelope_len; i++)
2453 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2458 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2460 int num_changed_custom_elements = getFile16BitBE(file);
2461 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2464 if (chunk_size_expected != chunk_size)
2466 ReadUnusedBytesFromFile(file, chunk_size - 2);
2467 return chunk_size_expected;
2470 for (i = 0; i < num_changed_custom_elements; i++)
2472 int element = getMappedElement(getFile16BitBE(file));
2473 int properties = getFile32BitBE(file);
2475 if (IS_CUSTOM_ELEMENT(element))
2476 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2478 Error(ERR_WARN, "invalid custom element number %d", element);
2480 /* older game versions that wrote level files with CUS1 chunks used
2481 different default push delay values (not yet stored in level file) */
2482 element_info[element].push_delay_fixed = 2;
2483 element_info[element].push_delay_random = 8;
2489 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2491 int num_changed_custom_elements = getFile16BitBE(file);
2492 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2495 if (chunk_size_expected != chunk_size)
2497 ReadUnusedBytesFromFile(file, chunk_size - 2);
2498 return chunk_size_expected;
2501 for (i = 0; i < num_changed_custom_elements; i++)
2503 int element = getMappedElement(getFile16BitBE(file));
2504 int custom_target_element = getMappedElement(getFile16BitBE(file));
2506 if (IS_CUSTOM_ELEMENT(element))
2507 element_info[element].change->target_element = custom_target_element;
2509 Error(ERR_WARN, "invalid custom element number %d", element);
2515 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2517 int num_changed_custom_elements = getFile16BitBE(file);
2518 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2521 if (chunk_size_expected != chunk_size)
2523 ReadUnusedBytesFromFile(file, chunk_size - 2);
2524 return chunk_size_expected;
2527 for (i = 0; i < num_changed_custom_elements; i++)
2529 int element = getMappedElement(getFile16BitBE(file));
2530 struct ElementInfo *ei = &element_info[element];
2531 unsigned int event_bits;
2533 if (!IS_CUSTOM_ELEMENT(element))
2535 Error(ERR_WARN, "invalid custom element number %d", element);
2537 element = EL_INTERNAL_DUMMY;
2540 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2541 ei->description[j] = getFile8Bit(file);
2542 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2544 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2546 /* some free bytes for future properties and padding */
2547 ReadUnusedBytesFromFile(file, 7);
2549 ei->use_gfx_element = getFile8Bit(file);
2550 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2552 ei->collect_score_initial = getFile8Bit(file);
2553 ei->collect_count_initial = getFile8Bit(file);
2555 ei->push_delay_fixed = getFile16BitBE(file);
2556 ei->push_delay_random = getFile16BitBE(file);
2557 ei->move_delay_fixed = getFile16BitBE(file);
2558 ei->move_delay_random = getFile16BitBE(file);
2560 ei->move_pattern = getFile16BitBE(file);
2561 ei->move_direction_initial = getFile8Bit(file);
2562 ei->move_stepsize = getFile8Bit(file);
2564 for (y = 0; y < 3; y++)
2565 for (x = 0; x < 3; x++)
2566 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2568 event_bits = getFile32BitBE(file);
2569 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2570 if (event_bits & (1 << j))
2571 ei->change->has_event[j] = TRUE;
2573 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2575 ei->change->delay_fixed = getFile16BitBE(file);
2576 ei->change->delay_random = getFile16BitBE(file);
2577 ei->change->delay_frames = getFile16BitBE(file);
2579 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2581 ei->change->explode = getFile8Bit(file);
2582 ei->change->use_target_content = getFile8Bit(file);
2583 ei->change->only_if_complete = getFile8Bit(file);
2584 ei->change->use_random_replace = getFile8Bit(file);
2586 ei->change->random_percentage = getFile8Bit(file);
2587 ei->change->replace_when = getFile8Bit(file);
2589 for (y = 0; y < 3; y++)
2590 for (x = 0; x < 3; x++)
2591 ei->change->target_content.e[x][y] =
2592 getMappedElement(getFile16BitBE(file));
2594 ei->slippery_type = getFile8Bit(file);
2596 /* some free bytes for future properties and padding */
2597 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2599 /* mark that this custom element has been modified */
2600 ei->modified_settings = TRUE;
2606 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2608 struct ElementInfo *ei;
2609 int chunk_size_expected;
2613 /* ---------- custom element base property values (96 bytes) ------------- */
2615 element = getMappedElement(getFile16BitBE(file));
2617 if (!IS_CUSTOM_ELEMENT(element))
2619 Error(ERR_WARN, "invalid custom element number %d", element);
2621 ReadUnusedBytesFromFile(file, chunk_size - 2);
2625 ei = &element_info[element];
2627 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2628 ei->description[i] = getFile8Bit(file);
2629 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2631 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2633 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2635 ei->num_change_pages = getFile8Bit(file);
2637 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2638 if (chunk_size_expected != chunk_size)
2640 ReadUnusedBytesFromFile(file, chunk_size - 43);
2641 return chunk_size_expected;
2644 ei->ce_value_fixed_initial = getFile16BitBE(file);
2645 ei->ce_value_random_initial = getFile16BitBE(file);
2646 ei->use_last_ce_value = getFile8Bit(file);
2648 ei->use_gfx_element = getFile8Bit(file);
2649 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2651 ei->collect_score_initial = getFile8Bit(file);
2652 ei->collect_count_initial = getFile8Bit(file);
2654 ei->drop_delay_fixed = getFile8Bit(file);
2655 ei->push_delay_fixed = getFile8Bit(file);
2656 ei->drop_delay_random = getFile8Bit(file);
2657 ei->push_delay_random = getFile8Bit(file);
2658 ei->move_delay_fixed = getFile16BitBE(file);
2659 ei->move_delay_random = getFile16BitBE(file);
2661 /* bits 0 - 15 of "move_pattern" ... */
2662 ei->move_pattern = getFile16BitBE(file);
2663 ei->move_direction_initial = getFile8Bit(file);
2664 ei->move_stepsize = getFile8Bit(file);
2666 ei->slippery_type = getFile8Bit(file);
2668 for (y = 0; y < 3; y++)
2669 for (x = 0; x < 3; x++)
2670 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2672 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2673 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2674 ei->move_leave_type = getFile8Bit(file);
2676 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2677 ei->move_pattern |= (getFile16BitBE(file) << 16);
2679 ei->access_direction = getFile8Bit(file);
2681 ei->explosion_delay = getFile8Bit(file);
2682 ei->ignition_delay = getFile8Bit(file);
2683 ei->explosion_type = getFile8Bit(file);
2685 /* some free bytes for future custom property values and padding */
2686 ReadUnusedBytesFromFile(file, 1);
2688 /* ---------- change page property values (48 bytes) --------------------- */
2690 setElementChangePages(ei, ei->num_change_pages);
2692 for (i = 0; i < ei->num_change_pages; i++)
2694 struct ElementChangeInfo *change = &ei->change_page[i];
2695 unsigned int event_bits;
2697 /* always start with reliable default values */
2698 setElementChangeInfoToDefaults(change);
2700 /* bits 0 - 31 of "has_event[]" ... */
2701 event_bits = getFile32BitBE(file);
2702 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2703 if (event_bits & (1 << j))
2704 change->has_event[j] = TRUE;
2706 change->target_element = getMappedElement(getFile16BitBE(file));
2708 change->delay_fixed = getFile16BitBE(file);
2709 change->delay_random = getFile16BitBE(file);
2710 change->delay_frames = getFile16BitBE(file);
2712 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2714 change->explode = getFile8Bit(file);
2715 change->use_target_content = getFile8Bit(file);
2716 change->only_if_complete = getFile8Bit(file);
2717 change->use_random_replace = getFile8Bit(file);
2719 change->random_percentage = getFile8Bit(file);
2720 change->replace_when = getFile8Bit(file);
2722 for (y = 0; y < 3; y++)
2723 for (x = 0; x < 3; x++)
2724 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2726 change->can_change = getFile8Bit(file);
2728 change->trigger_side = getFile8Bit(file);
2730 change->trigger_player = getFile8Bit(file);
2731 change->trigger_page = getFile8Bit(file);
2733 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2734 CH_PAGE_ANY : (1 << change->trigger_page));
2736 change->has_action = getFile8Bit(file);
2737 change->action_type = getFile8Bit(file);
2738 change->action_mode = getFile8Bit(file);
2739 change->action_arg = getFile16BitBE(file);
2741 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2742 event_bits = getFile8Bit(file);
2743 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2744 if (event_bits & (1 << (j - 32)))
2745 change->has_event[j] = TRUE;
2748 /* mark this custom element as modified */
2749 ei->modified_settings = TRUE;
2754 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2756 struct ElementInfo *ei;
2757 struct ElementGroupInfo *group;
2761 element = getMappedElement(getFile16BitBE(file));
2763 if (!IS_GROUP_ELEMENT(element))
2765 Error(ERR_WARN, "invalid group element number %d", element);
2767 ReadUnusedBytesFromFile(file, chunk_size - 2);
2771 ei = &element_info[element];
2773 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2774 ei->description[i] = getFile8Bit(file);
2775 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2777 group = element_info[element].group;
2779 group->num_elements = getFile8Bit(file);
2781 ei->use_gfx_element = getFile8Bit(file);
2782 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2784 group->choice_mode = getFile8Bit(file);
2786 /* some free bytes for future values and padding */
2787 ReadUnusedBytesFromFile(file, 3);
2789 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2790 group->element[i] = getMappedElement(getFile16BitBE(file));
2792 /* mark this group element as modified */
2793 element_info[element].modified_settings = TRUE;
2798 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2799 int element, int real_element)
2801 int micro_chunk_size = 0;
2802 int conf_type = getFile8Bit(file);
2803 int byte_mask = conf_type & CONF_MASK_BYTES;
2804 boolean element_found = FALSE;
2807 micro_chunk_size += 1;
2809 if (byte_mask == CONF_MASK_MULTI_BYTES)
2811 int num_bytes = getFile16BitBE(file);
2812 byte *buffer = checked_malloc(num_bytes);
2814 ReadBytesFromFile(file, buffer, num_bytes);
2816 for (i = 0; conf[i].data_type != -1; i++)
2818 if (conf[i].element == element &&
2819 conf[i].conf_type == conf_type)
2821 int data_type = conf[i].data_type;
2822 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2823 int max_num_entities = conf[i].max_num_entities;
2825 if (num_entities > max_num_entities)
2828 "truncating number of entities for element %d from %d to %d",
2829 element, num_entities, max_num_entities);
2831 num_entities = max_num_entities;
2834 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
2835 data_type == TYPE_CONTENT_LIST))
2837 /* for element and content lists, zero entities are not allowed */
2838 Error(ERR_WARN, "found empty list of entities for element %d",
2841 /* do not set "num_entities" here to prevent reading behind buffer */
2843 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
2847 *(int *)(conf[i].num_entities) = num_entities;
2850 element_found = TRUE;
2852 if (data_type == TYPE_STRING)
2854 char *string = (char *)(conf[i].value);
2857 for (j = 0; j < max_num_entities; j++)
2858 string[j] = (j < num_entities ? buffer[j] : '\0');
2860 else if (data_type == TYPE_ELEMENT_LIST)
2862 int *element_array = (int *)(conf[i].value);
2865 for (j = 0; j < num_entities; j++)
2867 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
2869 else if (data_type == TYPE_CONTENT_LIST)
2871 struct Content *content= (struct Content *)(conf[i].value);
2874 for (c = 0; c < num_entities; c++)
2875 for (y = 0; y < 3; y++)
2876 for (x = 0; x < 3; x++)
2877 content[c].e[x][y] =
2878 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
2881 element_found = FALSE;
2887 checked_free(buffer);
2889 micro_chunk_size += 2 + num_bytes;
2891 else /* constant size configuration data (1, 2 or 4 bytes) */
2893 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
2894 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
2895 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
2897 for (i = 0; conf[i].data_type != -1; i++)
2899 if (conf[i].element == element &&
2900 conf[i].conf_type == conf_type)
2902 int data_type = conf[i].data_type;
2904 if (data_type == TYPE_ELEMENT)
2905 value = getMappedElement(value);
2907 if (data_type == TYPE_BOOLEAN)
2908 *(boolean *)(conf[i].value) = value;
2910 *(int *) (conf[i].value) = value;
2912 element_found = TRUE;
2918 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
2923 char *error_conf_chunk_bytes =
2924 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
2925 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
2926 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
2927 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
2928 int error_element = real_element;
2930 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
2931 error_conf_chunk_bytes, error_conf_chunk_token,
2932 error_element, EL_NAME(error_element));
2935 return micro_chunk_size;
2938 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
2940 int real_chunk_size = 0;
2942 li = *level; /* copy level data into temporary buffer */
2944 while (!checkEndOfFile(file))
2946 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
2948 if (real_chunk_size >= chunk_size)
2952 *level = li; /* copy temporary buffer back to level data */
2954 return real_chunk_size;
2957 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
2959 int real_chunk_size = 0;
2961 li = *level; /* copy level data into temporary buffer */
2963 while (!checkEndOfFile(file))
2965 int element = getMappedElement(getFile16BitBE(file));
2967 real_chunk_size += 2;
2968 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
2970 if (real_chunk_size >= chunk_size)
2974 *level = li; /* copy temporary buffer back to level data */
2976 return real_chunk_size;
2979 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
2981 int real_chunk_size = 0;
2983 li = *level; /* copy level data into temporary buffer */
2985 while (!checkEndOfFile(file))
2987 int element = getMappedElement(getFile16BitBE(file));
2989 real_chunk_size += 2;
2990 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
2992 if (real_chunk_size >= chunk_size)
2996 *level = li; /* copy temporary buffer back to level data */
2998 return real_chunk_size;
3001 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3003 int element = getMappedElement(getFile16BitBE(file));
3004 int envelope_nr = element - EL_ENVELOPE_1;
3005 int real_chunk_size = 2;
3007 while (!checkEndOfFile(file))
3009 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3012 if (real_chunk_size >= chunk_size)
3016 level->envelope[envelope_nr] = xx_envelope;
3018 return real_chunk_size;
3021 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3023 int element = getMappedElement(getFile16BitBE(file));
3024 int real_chunk_size = 2;
3025 struct ElementInfo *ei = &element_info[element];
3028 xx_ei = *ei; /* copy element data into temporary buffer */
3030 xx_ei.num_change_pages = -1;
3032 while (!checkEndOfFile(file))
3034 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3036 if (xx_ei.num_change_pages != -1)
3039 if (real_chunk_size >= chunk_size)
3045 if (ei->num_change_pages == -1)
3047 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3050 ei->num_change_pages = 1;
3052 setElementChangePages(ei, 1);
3053 setElementChangeInfoToDefaults(ei->change);
3055 return real_chunk_size;
3058 /* initialize number of change pages stored for this custom element */
3059 setElementChangePages(ei, ei->num_change_pages);
3060 for (i = 0; i < ei->num_change_pages; i++)
3061 setElementChangeInfoToDefaults(&ei->change_page[i]);
3063 /* start with reading properties for the first change page */
3064 xx_current_change_page = 0;
3066 while (!checkEndOfFile(file))
3068 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3070 xx_change = *change; /* copy change data into temporary buffer */
3072 resetEventBits(); /* reset bits; change page might have changed */
3074 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3077 *change = xx_change;
3079 setEventFlagsFromEventBits(change);
3081 if (real_chunk_size >= chunk_size)
3085 return real_chunk_size;
3088 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3090 int element = getMappedElement(getFile16BitBE(file));
3091 int real_chunk_size = 2;
3092 struct ElementInfo *ei = &element_info[element];
3093 struct ElementGroupInfo *group = ei->group;
3095 xx_ei = *ei; /* copy element data into temporary buffer */
3096 xx_group = *group; /* copy group data into temporary buffer */
3098 while (!checkEndOfFile(file))
3100 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3103 if (real_chunk_size >= chunk_size)
3110 return real_chunk_size;
3113 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3114 struct LevelFileInfo *level_file_info,
3115 boolean level_info_only)
3117 char *filename = level_file_info->filename;
3118 char cookie[MAX_LINE_LEN];
3119 char chunk_name[CHUNK_ID_LEN + 1];
3123 if (!(file = openFile(filename, MODE_READ)))
3125 level->no_valid_file = TRUE;
3127 if (!level_info_only)
3128 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3133 getFileChunkBE(file, chunk_name, NULL);
3134 if (strEqual(chunk_name, "RND1"))
3136 getFile32BitBE(file); /* not used */
3138 getFileChunkBE(file, chunk_name, NULL);
3139 if (!strEqual(chunk_name, "CAVE"))
3141 level->no_valid_file = TRUE;
3143 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3150 else /* check for pre-2.0 file format with cookie string */
3152 strcpy(cookie, chunk_name);
3153 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3155 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3156 cookie[strlen(cookie) - 1] = '\0';
3158 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3160 level->no_valid_file = TRUE;
3162 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3169 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3171 level->no_valid_file = TRUE;
3173 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3180 /* pre-2.0 level files have no game version, so use file version here */
3181 level->game_version = level->file_version;
3184 if (level->file_version < FILE_VERSION_1_2)
3186 /* level files from versions before 1.2.0 without chunk structure */
3187 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3188 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3196 int (*loader)(File *, int, struct LevelInfo *);
3200 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3201 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3202 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3203 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3204 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3205 { "INFO", -1, LoadLevel_INFO },
3206 { "BODY", -1, LoadLevel_BODY },
3207 { "CONT", -1, LoadLevel_CONT },
3208 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3209 { "CNT3", -1, LoadLevel_CNT3 },
3210 { "CUS1", -1, LoadLevel_CUS1 },
3211 { "CUS2", -1, LoadLevel_CUS2 },
3212 { "CUS3", -1, LoadLevel_CUS3 },
3213 { "CUS4", -1, LoadLevel_CUS4 },
3214 { "GRP1", -1, LoadLevel_GRP1 },
3215 { "CONF", -1, LoadLevel_CONF },
3216 { "ELEM", -1, LoadLevel_ELEM },
3217 { "NOTE", -1, LoadLevel_NOTE },
3218 { "CUSX", -1, LoadLevel_CUSX },
3219 { "GRPX", -1, LoadLevel_GRPX },
3224 while (getFileChunkBE(file, chunk_name, &chunk_size))
3228 while (chunk_info[i].name != NULL &&
3229 !strEqual(chunk_name, chunk_info[i].name))
3232 if (chunk_info[i].name == NULL)
3234 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3235 chunk_name, filename);
3236 ReadUnusedBytesFromFile(file, chunk_size);
3238 else if (chunk_info[i].size != -1 &&
3239 chunk_info[i].size != chunk_size)
3241 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3242 chunk_size, chunk_name, filename);
3243 ReadUnusedBytesFromFile(file, chunk_size);
3247 /* call function to load this level chunk */
3248 int chunk_size_expected =
3249 (chunk_info[i].loader)(file, chunk_size, level);
3251 /* the size of some chunks cannot be checked before reading other
3252 chunks first (like "HEAD" and "BODY") that contain some header
3253 information, so check them here */
3254 if (chunk_size_expected != chunk_size)
3256 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3257 chunk_size, chunk_name, filename);
3267 /* ------------------------------------------------------------------------- */
3268 /* functions for loading EM level */
3269 /* ------------------------------------------------------------------------- */
3271 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3273 static int ball_xy[8][2] =
3284 struct LevelInfo_EM *level_em = level->native_em_level;
3285 struct LEVEL *lev = level_em->lev;
3286 struct PLAYER **ply = level_em->ply;
3289 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3290 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3292 lev->time_seconds = level->time;
3293 lev->required_initial = level->gems_needed;
3295 lev->emerald_score = level->score[SC_EMERALD];
3296 lev->diamond_score = level->score[SC_DIAMOND];
3297 lev->alien_score = level->score[SC_ROBOT];
3298 lev->tank_score = level->score[SC_SPACESHIP];
3299 lev->bug_score = level->score[SC_BUG];
3300 lev->eater_score = level->score[SC_YAMYAM];
3301 lev->nut_score = level->score[SC_NUT];
3302 lev->dynamite_score = level->score[SC_DYNAMITE];
3303 lev->key_score = level->score[SC_KEY];
3304 lev->exit_score = level->score[SC_TIME_BONUS];
3306 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3307 for (y = 0; y < 3; y++)
3308 for (x = 0; x < 3; x++)
3309 lev->eater_array[i][y * 3 + x] =
3310 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3312 lev->amoeba_time = level->amoeba_speed;
3313 lev->wonderwall_time_initial = level->time_magic_wall;
3314 lev->wheel_time = level->time_wheel;
3316 lev->android_move_time = level->android_move_time;
3317 lev->android_clone_time = level->android_clone_time;
3318 lev->ball_random = level->ball_random;
3319 lev->ball_state_initial = level->ball_state_initial;
3320 lev->ball_time = level->ball_time;
3321 lev->num_ball_arrays = level->num_ball_contents;
3323 lev->lenses_score = level->lenses_score;
3324 lev->magnify_score = level->magnify_score;
3325 lev->slurp_score = level->slurp_score;
3327 lev->lenses_time = level->lenses_time;
3328 lev->magnify_time = level->magnify_time;
3330 lev->wind_direction_initial =
3331 map_direction_RND_to_EM(level->wind_direction_initial);
3332 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3333 lev->wind_time : 0);
3335 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3336 for (j = 0; j < 8; j++)
3337 lev->ball_array[i][j] =
3338 map_element_RND_to_EM(level->
3339 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3341 map_android_clone_elements_RND_to_EM(level);
3343 /* first fill the complete playfield with the default border element */
3344 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3345 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3346 level_em->cave[x][y] = ZBORDER;
3348 if (BorderElement == EL_STEELWALL)
3350 for (y = 0; y < lev->height + 2; y++)
3351 for (x = 0; x < lev->width + 2; x++)
3352 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3355 /* then copy the real level contents from level file into the playfield */
3356 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3358 int new_element = map_element_RND_to_EM(level->field[x][y]);
3359 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3360 int xx = x + 1 + offset;
3361 int yy = y + 1 + offset;
3363 if (level->field[x][y] == EL_AMOEBA_DEAD)
3364 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3366 level_em->cave[xx][yy] = new_element;
3369 for (i = 0; i < MAX_PLAYERS; i++)
3371 ply[i]->x_initial = 0;
3372 ply[i]->y_initial = 0;
3375 /* initialize player positions and delete players from the playfield */
3376 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3378 if (ELEM_IS_PLAYER(level->field[x][y]))
3380 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3381 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3382 int xx = x + 1 + offset;
3383 int yy = y + 1 + offset;
3385 ply[player_nr]->x_initial = xx;
3386 ply[player_nr]->y_initial = yy;
3388 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3392 if (BorderElement == EL_STEELWALL)
3399 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3401 static int ball_xy[8][2] =
3412 struct LevelInfo_EM *level_em = level->native_em_level;
3413 struct LEVEL *lev = level_em->lev;
3414 struct PLAYER **ply = level_em->ply;
3417 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3418 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3420 level->time = lev->time_seconds;
3421 level->gems_needed = lev->required_initial;
3423 sprintf(level->name, "Level %d", level->file_info.nr);
3425 level->score[SC_EMERALD] = lev->emerald_score;
3426 level->score[SC_DIAMOND] = lev->diamond_score;
3427 level->score[SC_ROBOT] = lev->alien_score;
3428 level->score[SC_SPACESHIP] = lev->tank_score;
3429 level->score[SC_BUG] = lev->bug_score;
3430 level->score[SC_YAMYAM] = lev->eater_score;
3431 level->score[SC_NUT] = lev->nut_score;
3432 level->score[SC_DYNAMITE] = lev->dynamite_score;
3433 level->score[SC_KEY] = lev->key_score;
3434 level->score[SC_TIME_BONUS] = lev->exit_score;
3436 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3438 for (i = 0; i < level->num_yamyam_contents; i++)
3439 for (y = 0; y < 3; y++)
3440 for (x = 0; x < 3; x++)
3441 level->yamyam_content[i].e[x][y] =
3442 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3444 level->amoeba_speed = lev->amoeba_time;
3445 level->time_magic_wall = lev->wonderwall_time_initial;
3446 level->time_wheel = lev->wheel_time;
3448 level->android_move_time = lev->android_move_time;
3449 level->android_clone_time = lev->android_clone_time;
3450 level->ball_random = lev->ball_random;
3451 level->ball_state_initial = lev->ball_state_initial;
3452 level->ball_time = lev->ball_time;
3453 level->num_ball_contents = lev->num_ball_arrays;
3455 level->lenses_score = lev->lenses_score;
3456 level->magnify_score = lev->magnify_score;
3457 level->slurp_score = lev->slurp_score;
3459 level->lenses_time = lev->lenses_time;
3460 level->magnify_time = lev->magnify_time;
3462 level->wind_direction_initial =
3463 map_direction_EM_to_RND(lev->wind_direction_initial);
3465 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3466 for (j = 0; j < 8; j++)
3467 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3468 map_element_EM_to_RND(lev->ball_array[i][j]);
3470 map_android_clone_elements_EM_to_RND(level);
3472 /* convert the playfield (some elements need special treatment) */
3473 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3475 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3477 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3478 new_element = EL_AMOEBA_DEAD;
3480 level->field[x][y] = new_element;
3483 for (i = 0; i < MAX_PLAYERS; i++)
3485 /* in case of all players set to the same field, use the first player */
3486 int nr = MAX_PLAYERS - i - 1;
3487 int jx = ply[nr]->x_initial - 1;
3488 int jy = ply[nr]->y_initial - 1;
3490 if (jx != -1 && jy != -1)
3491 level->field[jx][jy] = EL_PLAYER_1 + nr;
3496 /* ------------------------------------------------------------------------- */
3497 /* functions for loading SP level */
3498 /* ------------------------------------------------------------------------- */
3500 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3502 struct LevelInfo_SP *level_sp = level->native_sp_level;
3503 LevelInfoType *header = &level_sp->header;
3506 level_sp->width = level->fieldx;
3507 level_sp->height = level->fieldy;
3509 for (x = 0; x < level->fieldx; x++)
3510 for (y = 0; y < level->fieldy; y++)
3511 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3513 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3515 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3516 header->LevelTitle[i] = level->name[i];
3517 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3519 header->InfotronsNeeded = level->gems_needed;
3521 header->SpecialPortCount = 0;
3523 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3525 boolean gravity_port_found = FALSE;
3526 boolean gravity_port_valid = FALSE;
3527 int gravity_port_flag;
3528 int gravity_port_base_element;
3529 int element = level->field[x][y];
3531 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3532 element <= EL_SP_GRAVITY_ON_PORT_UP)
3534 gravity_port_found = TRUE;
3535 gravity_port_valid = TRUE;
3536 gravity_port_flag = 1;
3537 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3539 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3540 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3542 gravity_port_found = TRUE;
3543 gravity_port_valid = TRUE;
3544 gravity_port_flag = 0;
3545 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3547 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3548 element <= EL_SP_GRAVITY_PORT_UP)
3550 /* change R'n'D style gravity inverting special port to normal port
3551 (there are no gravity inverting ports in native Supaplex engine) */
3553 gravity_port_found = TRUE;
3554 gravity_port_valid = FALSE;
3555 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3558 if (gravity_port_found)
3560 if (gravity_port_valid &&
3561 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3563 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3565 port->PortLocation = (y * level->fieldx + x) * 2;
3566 port->Gravity = gravity_port_flag;
3568 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3570 header->SpecialPortCount++;
3574 /* change special gravity port to normal port */
3576 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3579 level_sp->playfield[x][y] = element - EL_SP_START;
3584 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3586 struct LevelInfo_SP *level_sp = level->native_sp_level;
3587 LevelInfoType *header = &level_sp->header;
3590 level->fieldx = level_sp->width;
3591 level->fieldy = level_sp->height;
3593 for (x = 0; x < level->fieldx; x++)
3595 for (y = 0; y < level->fieldy; y++)
3597 int element_old = level_sp->playfield[x][y];
3598 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3600 if (element_new == EL_UNKNOWN)
3601 Error(ERR_WARN, "invalid element %d at position %d, %d",
3604 level->field[x][y] = element_new;
3608 for (i = 0; i < MAX_PLAYERS; i++)
3609 level->initial_player_gravity[i] =
3610 (header->InitialGravity == 1 ? TRUE : FALSE);
3612 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3613 level->name[i] = header->LevelTitle[i];
3614 level->name[SP_LEVEL_NAME_LEN] = '\0';
3616 level->gems_needed = header->InfotronsNeeded;
3618 for (i = 0; i < header->SpecialPortCount; i++)
3620 SpecialPortType *port = &header->SpecialPort[i];
3621 int port_location = port->PortLocation;
3622 int gravity = port->Gravity;
3623 int port_x, port_y, port_element;
3625 port_x = (port_location / 2) % level->fieldx;
3626 port_y = (port_location / 2) / level->fieldx;
3628 if (port_x < 0 || port_x >= level->fieldx ||
3629 port_y < 0 || port_y >= level->fieldy)
3631 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3637 port_element = level->field[port_x][port_y];
3639 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3640 port_element > EL_SP_GRAVITY_PORT_UP)
3642 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3647 /* change previous (wrong) gravity inverting special port to either
3648 gravity enabling special port or gravity disabling special port */
3649 level->field[port_x][port_y] +=
3650 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3651 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3654 /* change special gravity ports without database entries to normal ports */
3655 for (x = 0; x < level->fieldx; x++)
3656 for (y = 0; y < level->fieldy; y++)
3657 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3658 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3659 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3661 level->time = 0; /* no time limit */
3662 level->amoeba_speed = 0;
3663 level->time_magic_wall = 0;
3664 level->time_wheel = 0;
3665 level->amoeba_content = EL_EMPTY;
3668 /* original Supaplex does not use score values -- use default values */
3670 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3671 level->score[i] = 0;
3674 /* there are no yamyams in supaplex levels */
3675 for (i = 0; i < level->num_yamyam_contents; i++)
3676 for (x = 0; x < 3; x++)
3677 for (y = 0; y < 3; y++)
3678 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3681 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3683 struct LevelInfo_SP *level_sp = level->native_sp_level;
3684 struct DemoInfo_SP *demo = &level_sp->demo;
3687 /* always start with reliable default values */
3688 demo->is_available = FALSE;
3691 if (TAPE_IS_EMPTY(tape))
3694 demo->level_nr = tape.level_nr; /* (currently not used) */
3696 level_sp->header.DemoRandomSeed = tape.random_seed;
3699 for (i = 0; i < tape.length; i++)
3701 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3702 int demo_repeat = tape.pos[i].delay;
3704 for (j = 0; j < demo_repeat / 16; j++)
3705 demo->data[demo->length++] = 0xf0 | demo_action;
3707 if (demo_repeat % 16)
3708 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3711 demo->data[demo->length++] = 0xff;
3713 demo->is_available = TRUE;
3716 static void setTapeInfoToDefaults();
3718 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3720 struct LevelInfo_SP *level_sp = level->native_sp_level;
3721 struct DemoInfo_SP *demo = &level_sp->demo;
3722 char *filename = level->file_info.filename;
3725 /* always start with reliable default values */
3726 setTapeInfoToDefaults();
3728 if (!demo->is_available)
3731 tape.level_nr = demo->level_nr; /* (currently not used) */
3732 tape.length = demo->length - 1; /* without "end of demo" byte */
3733 tape.random_seed = level_sp->header.DemoRandomSeed;
3735 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3737 for (i = 0; i < demo->length - 1; i++)
3739 int demo_action = demo->data[i] & 0x0f;
3740 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3742 tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
3743 tape.pos[i].delay = demo_repeat + 1;
3746 tape.length_frames = GetTapeLengthFrames();
3747 tape.length_seconds = GetTapeLengthSeconds();
3751 /* ------------------------------------------------------------------------- */
3752 /* functions for loading DC level */
3753 /* ------------------------------------------------------------------------- */
3755 #define DC_LEVEL_HEADER_SIZE 344
3757 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
3759 static int last_data_encoded;
3763 int diff_hi, diff_lo;
3764 int data_hi, data_lo;
3765 unsigned short data_decoded;
3769 last_data_encoded = 0;
3776 diff = data_encoded - last_data_encoded;
3777 diff_hi = diff & ~0xff;
3778 diff_lo = diff & 0xff;
3782 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
3783 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
3784 data_hi = data_hi & 0xff00;
3786 data_decoded = data_hi | data_lo;
3788 last_data_encoded = data_encoded;
3790 offset1 = (offset1 + 1) % 31;
3791 offset2 = offset2 & 0xff;
3793 return data_decoded;
3796 int getMappedElement_DC(int element)
3804 /* 0x0117 - 0x036e: (?) */
3807 /* 0x042d - 0x0684: (?) */
3823 element = EL_CRYSTAL;
3826 case 0x0e77: /* quicksand (boulder) */
3827 element = EL_QUICKSAND_FAST_FULL;
3830 case 0x0e99: /* slow quicksand (boulder) */
3831 element = EL_QUICKSAND_FULL;
3835 element = EL_EM_EXIT_OPEN;
3839 element = EL_EM_EXIT_CLOSED;
3843 element = EL_EM_STEEL_EXIT_OPEN;
3847 element = EL_EM_STEEL_EXIT_CLOSED;
3850 case 0x0f4f: /* dynamite (lit 1) */
3851 element = EL_EM_DYNAMITE_ACTIVE;
3854 case 0x0f57: /* dynamite (lit 2) */
3855 element = EL_EM_DYNAMITE_ACTIVE;
3858 case 0x0f5f: /* dynamite (lit 3) */
3859 element = EL_EM_DYNAMITE_ACTIVE;
3862 case 0x0f67: /* dynamite (lit 4) */
3863 element = EL_EM_DYNAMITE_ACTIVE;
3870 element = EL_AMOEBA_WET;
3874 element = EL_AMOEBA_DROP;
3878 element = EL_DC_MAGIC_WALL;
3882 element = EL_SPACESHIP_UP;
3886 element = EL_SPACESHIP_DOWN;
3890 element = EL_SPACESHIP_LEFT;
3894 element = EL_SPACESHIP_RIGHT;
3898 element = EL_BUG_UP;
3902 element = EL_BUG_DOWN;
3906 element = EL_BUG_LEFT;
3910 element = EL_BUG_RIGHT;
3914 element = EL_MOLE_UP;
3918 element = EL_MOLE_DOWN;
3922 element = EL_MOLE_LEFT;
3926 element = EL_MOLE_RIGHT;
3934 element = EL_YAMYAM;
3938 element = EL_SWITCHGATE_OPEN;
3942 element = EL_SWITCHGATE_CLOSED;
3946 element = EL_DC_SWITCHGATE_SWITCH_UP;
3950 element = EL_TIMEGATE_CLOSED;
3953 case 0x144c: /* conveyor belt switch (green) */
3954 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
3957 case 0x144f: /* conveyor belt switch (red) */
3958 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
3961 case 0x1452: /* conveyor belt switch (blue) */
3962 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
3966 element = EL_CONVEYOR_BELT_3_MIDDLE;
3970 element = EL_CONVEYOR_BELT_3_LEFT;
3974 element = EL_CONVEYOR_BELT_3_RIGHT;
3978 element = EL_CONVEYOR_BELT_1_MIDDLE;
3982 element = EL_CONVEYOR_BELT_1_LEFT;
3986 element = EL_CONVEYOR_BELT_1_RIGHT;
3990 element = EL_CONVEYOR_BELT_4_MIDDLE;
3994 element = EL_CONVEYOR_BELT_4_LEFT;
3998 element = EL_CONVEYOR_BELT_4_RIGHT;
4002 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4006 element = EL_EXPANDABLE_WALL_VERTICAL;
4010 element = EL_EXPANDABLE_WALL_ANY;
4013 case 0x14ce: /* growing steel wall (left/right) */
4014 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4017 case 0x14df: /* growing steel wall (up/down) */
4018 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4021 case 0x14e8: /* growing steel wall (up/down/left/right) */
4022 element = EL_EXPANDABLE_STEELWALL_ANY;
4026 element = EL_SHIELD_DEADLY;
4030 element = EL_EXTRA_TIME;
4038 element = EL_EMPTY_SPACE;
4041 case 0x1578: /* quicksand (empty) */
4042 element = EL_QUICKSAND_FAST_EMPTY;
4045 case 0x1579: /* slow quicksand (empty) */
4046 element = EL_QUICKSAND_EMPTY;
4049 /* 0x157c - 0x158b: */
4052 /* 0x1590 - 0x159f: */
4053 /* EL_DC_LANDMINE */
4056 element = EL_EM_DYNAMITE;
4059 case 0x15a1: /* key (red) */
4060 element = EL_EM_KEY_1;
4063 case 0x15a2: /* key (yellow) */
4064 element = EL_EM_KEY_2;
4067 case 0x15a3: /* key (blue) */
4068 element = EL_EM_KEY_4;
4071 case 0x15a4: /* key (green) */
4072 element = EL_EM_KEY_3;
4075 case 0x15a5: /* key (white) */
4076 element = EL_DC_KEY_WHITE;
4080 element = EL_WALL_SLIPPERY;
4087 case 0x15a8: /* wall (not round) */
4091 case 0x15a9: /* (blue) */
4092 element = EL_CHAR_A;
4095 case 0x15aa: /* (blue) */
4096 element = EL_CHAR_B;
4099 case 0x15ab: /* (blue) */
4100 element = EL_CHAR_C;
4103 case 0x15ac: /* (blue) */
4104 element = EL_CHAR_D;
4107 case 0x15ad: /* (blue) */
4108 element = EL_CHAR_E;
4111 case 0x15ae: /* (blue) */
4112 element = EL_CHAR_F;
4115 case 0x15af: /* (blue) */
4116 element = EL_CHAR_G;
4119 case 0x15b0: /* (blue) */
4120 element = EL_CHAR_H;
4123 case 0x15b1: /* (blue) */
4124 element = EL_CHAR_I;
4127 case 0x15b2: /* (blue) */
4128 element = EL_CHAR_J;
4131 case 0x15b3: /* (blue) */
4132 element = EL_CHAR_K;
4135 case 0x15b4: /* (blue) */
4136 element = EL_CHAR_L;
4139 case 0x15b5: /* (blue) */
4140 element = EL_CHAR_M;
4143 case 0x15b6: /* (blue) */
4144 element = EL_CHAR_N;
4147 case 0x15b7: /* (blue) */
4148 element = EL_CHAR_O;
4151 case 0x15b8: /* (blue) */
4152 element = EL_CHAR_P;
4155 case 0x15b9: /* (blue) */
4156 element = EL_CHAR_Q;
4159 case 0x15ba: /* (blue) */
4160 element = EL_CHAR_R;
4163 case 0x15bb: /* (blue) */
4164 element = EL_CHAR_S;
4167 case 0x15bc: /* (blue) */
4168 element = EL_CHAR_T;
4171 case 0x15bd: /* (blue) */
4172 element = EL_CHAR_U;
4175 case 0x15be: /* (blue) */
4176 element = EL_CHAR_V;
4179 case 0x15bf: /* (blue) */
4180 element = EL_CHAR_W;
4183 case 0x15c0: /* (blue) */
4184 element = EL_CHAR_X;
4187 case 0x15c1: /* (blue) */
4188 element = EL_CHAR_Y;
4191 case 0x15c2: /* (blue) */
4192 element = EL_CHAR_Z;
4195 case 0x15c3: /* (blue) */
4196 element = EL_CHAR_AUMLAUT;
4199 case 0x15c4: /* (blue) */
4200 element = EL_CHAR_OUMLAUT;
4203 case 0x15c5: /* (blue) */
4204 element = EL_CHAR_UUMLAUT;
4207 case 0x15c6: /* (blue) */
4208 element = EL_CHAR_0;
4211 case 0x15c7: /* (blue) */
4212 element = EL_CHAR_1;
4215 case 0x15c8: /* (blue) */
4216 element = EL_CHAR_2;
4219 case 0x15c9: /* (blue) */
4220 element = EL_CHAR_3;
4223 case 0x15ca: /* (blue) */
4224 element = EL_CHAR_4;
4227 case 0x15cb: /* (blue) */
4228 element = EL_CHAR_5;
4231 case 0x15cc: /* (blue) */
4232 element = EL_CHAR_6;
4235 case 0x15cd: /* (blue) */
4236 element = EL_CHAR_7;
4239 case 0x15ce: /* (blue) */
4240 element = EL_CHAR_8;
4243 case 0x15cf: /* (blue) */
4244 element = EL_CHAR_9;
4247 case 0x15d0: /* (blue) */
4248 element = EL_CHAR_PERIOD;
4251 case 0x15d1: /* (blue) */
4252 element = EL_CHAR_EXCLAM;
4255 case 0x15d2: /* (blue) */
4256 element = EL_CHAR_COLON;
4259 case 0x15d3: /* (blue) */
4260 element = EL_CHAR_LESS;
4263 case 0x15d4: /* (blue) */
4264 element = EL_CHAR_GREATER;
4267 case 0x15d5: /* (blue) */
4268 element = EL_CHAR_QUESTION;
4271 case 0x15d6: /* (blue) */
4272 element = EL_CHAR_COPYRIGHT;
4275 case 0x15d7: /* (blue) */
4276 element = EL_CHAR_UP;
4279 case 0x15d8: /* (blue) */
4280 element = EL_CHAR_DOWN;
4283 case 0x15d9: /* (blue) */
4284 element = EL_CHAR_BUTTON;
4287 case 0x15da: /* (blue) */
4288 element = EL_CHAR_PLUS;
4291 case 0x15db: /* (blue) */
4292 element = EL_CHAR_MINUS;
4295 case 0x15dc: /* (blue) */
4296 element = EL_CHAR_APOSTROPHE;
4299 case 0x15dd: /* (blue) */
4300 element = EL_CHAR_PARENLEFT;
4303 case 0x15de: /* (blue) */
4304 element = EL_CHAR_PARENRIGHT;
4307 case 0x15df: /* (green) */
4308 element = EL_CHAR_A;
4311 case 0x15e0: /* (green) */
4312 element = EL_CHAR_B;
4315 case 0x15e1: /* (green) */
4316 element = EL_CHAR_C;
4319 case 0x15e2: /* (green) */
4320 element = EL_CHAR_D;
4323 case 0x15e3: /* (green) */
4324 element = EL_CHAR_E;
4327 case 0x15e4: /* (green) */
4328 element = EL_CHAR_F;
4331 case 0x15e5: /* (green) */
4332 element = EL_CHAR_G;
4335 case 0x15e6: /* (green) */
4336 element = EL_CHAR_H;
4339 case 0x15e7: /* (green) */
4340 element = EL_CHAR_I;
4343 case 0x15e8: /* (green) */
4344 element = EL_CHAR_J;
4347 case 0x15e9: /* (green) */
4348 element = EL_CHAR_K;
4351 case 0x15ea: /* (green) */
4352 element = EL_CHAR_L;
4355 case 0x15eb: /* (green) */
4356 element = EL_CHAR_M;
4359 case 0x15ec: /* (green) */
4360 element = EL_CHAR_N;
4363 case 0x15ed: /* (green) */
4364 element = EL_CHAR_O;
4367 case 0x15ee: /* (green) */
4368 element = EL_CHAR_P;
4371 case 0x15ef: /* (green) */
4372 element = EL_CHAR_Q;
4375 case 0x15f0: /* (green) */
4376 element = EL_CHAR_R;
4379 case 0x15f1: /* (green) */
4380 element = EL_CHAR_S;
4383 case 0x15f2: /* (green) */
4384 element = EL_CHAR_T;
4387 case 0x15f3: /* (green) */
4388 element = EL_CHAR_U;
4391 case 0x15f4: /* (green) */
4392 element = EL_CHAR_V;
4395 case 0x15f5: /* (green) */
4396 element = EL_CHAR_W;
4399 case 0x15f6: /* (green) */
4400 element = EL_CHAR_X;
4403 case 0x15f7: /* (green) */
4404 element = EL_CHAR_Y;
4407 case 0x15f8: /* (green) */
4408 element = EL_CHAR_Z;
4411 case 0x15f9: /* (green) */
4412 element = EL_CHAR_AUMLAUT;
4415 case 0x15fa: /* (green) */
4416 element = EL_CHAR_OUMLAUT;
4419 case 0x15fb: /* (green) */
4420 element = EL_CHAR_UUMLAUT;
4423 case 0x15fc: /* (green) */
4424 element = EL_CHAR_0;
4427 case 0x15fd: /* (green) */
4428 element = EL_CHAR_1;
4431 case 0x15fe: /* (green) */
4432 element = EL_CHAR_2;
4435 case 0x15ff: /* (green) */
4436 element = EL_CHAR_3;
4439 case 0x1600: /* (green) */
4440 element = EL_CHAR_4;
4443 case 0x1601: /* (green) */
4444 element = EL_CHAR_5;
4447 case 0x1602: /* (green) */
4448 element = EL_CHAR_6;
4451 case 0x1603: /* (green) */
4452 element = EL_CHAR_7;
4455 case 0x1604: /* (green) */
4456 element = EL_CHAR_8;
4459 case 0x1605: /* (green) */
4460 element = EL_CHAR_9;
4463 case 0x1606: /* (green) */
4464 element = EL_CHAR_PERIOD;
4467 case 0x1607: /* (green) */
4468 element = EL_CHAR_EXCLAM;
4471 case 0x1608: /* (green) */
4472 element = EL_CHAR_COLON;
4475 case 0x1609: /* (green) */
4476 element = EL_CHAR_LESS;
4479 case 0x160a: /* (green) */
4480 element = EL_CHAR_GREATER;
4483 case 0x160b: /* (green) */
4484 element = EL_CHAR_QUESTION;
4487 case 0x160c: /* (green) */
4488 element = EL_CHAR_COPYRIGHT;
4491 case 0x160d: /* (green) */
4492 element = EL_CHAR_UP;
4495 case 0x160e: /* (green) */
4496 element = EL_CHAR_DOWN;
4499 case 0x160f: /* (green) */
4500 element = EL_CHAR_BUTTON;
4503 case 0x1610: /* (green) */
4504 element = EL_CHAR_PLUS;
4507 case 0x1611: /* (green) */
4508 element = EL_CHAR_MINUS;
4511 case 0x1612: /* (green) */
4512 element = EL_CHAR_APOSTROPHE;
4515 case 0x1613: /* (green) */
4516 element = EL_CHAR_PARENLEFT;
4519 case 0x1614: /* (green) */
4520 element = EL_CHAR_PARENRIGHT;
4523 case 0x1615: /* (blue steel) */
4524 element = EL_STEEL_CHAR_A;
4527 case 0x1616: /* (blue steel) */
4528 element = EL_STEEL_CHAR_B;
4531 case 0x1617: /* (blue steel) */
4532 element = EL_STEEL_CHAR_C;
4535 case 0x1618: /* (blue steel) */
4536 element = EL_STEEL_CHAR_D;
4539 case 0x1619: /* (blue steel) */
4540 element = EL_STEEL_CHAR_E;
4543 case 0x161a: /* (blue steel) */
4544 element = EL_STEEL_CHAR_F;
4547 case 0x161b: /* (blue steel) */
4548 element = EL_STEEL_CHAR_G;
4551 case 0x161c: /* (blue steel) */
4552 element = EL_STEEL_CHAR_H;
4555 case 0x161d: /* (blue steel) */
4556 element = EL_STEEL_CHAR_I;
4559 case 0x161e: /* (blue steel) */
4560 element = EL_STEEL_CHAR_J;
4563 case 0x161f: /* (blue steel) */
4564 element = EL_STEEL_CHAR_K;
4567 case 0x1620: /* (blue steel) */
4568 element = EL_STEEL_CHAR_L;
4571 case 0x1621: /* (blue steel) */
4572 element = EL_STEEL_CHAR_M;
4575 case 0x1622: /* (blue steel) */
4576 element = EL_STEEL_CHAR_N;
4579 case 0x1623: /* (blue steel) */
4580 element = EL_STEEL_CHAR_O;
4583 case 0x1624: /* (blue steel) */
4584 element = EL_STEEL_CHAR_P;
4587 case 0x1625: /* (blue steel) */
4588 element = EL_STEEL_CHAR_Q;
4591 case 0x1626: /* (blue steel) */
4592 element = EL_STEEL_CHAR_R;
4595 case 0x1627: /* (blue steel) */
4596 element = EL_STEEL_CHAR_S;
4599 case 0x1628: /* (blue steel) */
4600 element = EL_STEEL_CHAR_T;
4603 case 0x1629: /* (blue steel) */
4604 element = EL_STEEL_CHAR_U;
4607 case 0x162a: /* (blue steel) */
4608 element = EL_STEEL_CHAR_V;
4611 case 0x162b: /* (blue steel) */
4612 element = EL_STEEL_CHAR_W;
4615 case 0x162c: /* (blue steel) */
4616 element = EL_STEEL_CHAR_X;
4619 case 0x162d: /* (blue steel) */
4620 element = EL_STEEL_CHAR_Y;
4623 case 0x162e: /* (blue steel) */
4624 element = EL_STEEL_CHAR_Z;
4627 case 0x162f: /* (blue steel) */
4628 element = EL_STEEL_CHAR_AUMLAUT;
4631 case 0x1630: /* (blue steel) */
4632 element = EL_STEEL_CHAR_OUMLAUT;
4635 case 0x1631: /* (blue steel) */
4636 element = EL_STEEL_CHAR_UUMLAUT;
4639 case 0x1632: /* (blue steel) */
4640 element = EL_STEEL_CHAR_0;
4643 case 0x1633: /* (blue steel) */
4644 element = EL_STEEL_CHAR_1;
4647 case 0x1634: /* (blue steel) */
4648 element = EL_STEEL_CHAR_2;
4651 case 0x1635: /* (blue steel) */
4652 element = EL_STEEL_CHAR_3;
4655 case 0x1636: /* (blue steel) */
4656 element = EL_STEEL_CHAR_4;
4659 case 0x1637: /* (blue steel) */
4660 element = EL_STEEL_CHAR_5;
4663 case 0x1638: /* (blue steel) */
4664 element = EL_STEEL_CHAR_6;
4667 case 0x1639: /* (blue steel) */
4668 element = EL_STEEL_CHAR_7;
4671 case 0x163a: /* (blue steel) */
4672 element = EL_STEEL_CHAR_8;
4675 case 0x163b: /* (blue steel) */
4676 element = EL_STEEL_CHAR_9;
4679 case 0x163c: /* (blue steel) */
4680 element = EL_STEEL_CHAR_PERIOD;
4683 case 0x163d: /* (blue steel) */
4684 element = EL_STEEL_CHAR_EXCLAM;
4687 case 0x163e: /* (blue steel) */
4688 element = EL_STEEL_CHAR_COLON;
4691 case 0x163f: /* (blue steel) */
4692 element = EL_STEEL_CHAR_LESS;
4695 case 0x1640: /* (blue steel) */
4696 element = EL_STEEL_CHAR_GREATER;
4699 case 0x1641: /* (blue steel) */
4700 element = EL_STEEL_CHAR_QUESTION;
4703 case 0x1642: /* (blue steel) */
4704 element = EL_STEEL_CHAR_COPYRIGHT;
4707 case 0x1643: /* (blue steel) */
4708 element = EL_STEEL_CHAR_UP;
4711 case 0x1644: /* (blue steel) */
4712 element = EL_STEEL_CHAR_DOWN;
4715 case 0x1645: /* (blue steel) */
4716 element = EL_STEEL_CHAR_BUTTON;
4719 case 0x1646: /* (blue steel) */
4720 element = EL_STEEL_CHAR_PLUS;
4723 case 0x1647: /* (blue steel) */
4724 element = EL_STEEL_CHAR_MINUS;
4727 case 0x1648: /* (blue steel) */
4728 element = EL_STEEL_CHAR_APOSTROPHE;
4731 case 0x1649: /* (blue steel) */
4732 element = EL_STEEL_CHAR_PARENLEFT;
4735 case 0x164a: /* (blue steel) */
4736 element = EL_STEEL_CHAR_PARENRIGHT;
4739 case 0x164b: /* (green steel) */
4740 element = EL_STEEL_CHAR_A;
4743 case 0x164c: /* (green steel) */
4744 element = EL_STEEL_CHAR_B;
4747 case 0x164d: /* (green steel) */
4748 element = EL_STEEL_CHAR_C;
4751 case 0x164e: /* (green steel) */
4752 element = EL_STEEL_CHAR_D;
4755 case 0x164f: /* (green steel) */
4756 element = EL_STEEL_CHAR_E;
4759 case 0x1650: /* (green steel) */
4760 element = EL_STEEL_CHAR_F;
4763 case 0x1651: /* (green steel) */
4764 element = EL_STEEL_CHAR_G;
4767 case 0x1652: /* (green steel) */
4768 element = EL_STEEL_CHAR_H;
4771 case 0x1653: /* (green steel) */
4772 element = EL_STEEL_CHAR_I;
4775 case 0x1654: /* (green steel) */
4776 element = EL_STEEL_CHAR_J;
4779 case 0x1655: /* (green steel) */
4780 element = EL_STEEL_CHAR_K;
4783 case 0x1656: /* (green steel) */
4784 element = EL_STEEL_CHAR_L;
4787 case 0x1657: /* (green steel) */
4788 element = EL_STEEL_CHAR_M;
4791 case 0x1658: /* (green steel) */
4792 element = EL_STEEL_CHAR_N;
4795 case 0x1659: /* (green steel) */
4796 element = EL_STEEL_CHAR_O;
4799 case 0x165a: /* (green steel) */
4800 element = EL_STEEL_CHAR_P;
4803 case 0x165b: /* (green steel) */
4804 element = EL_STEEL_CHAR_Q;
4807 case 0x165c: /* (green steel) */
4808 element = EL_STEEL_CHAR_R;
4811 case 0x165d: /* (green steel) */
4812 element = EL_STEEL_CHAR_S;
4815 case 0x165e: /* (green steel) */
4816 element = EL_STEEL_CHAR_T;
4819 case 0x165f: /* (green steel) */
4820 element = EL_STEEL_CHAR_U;
4823 case 0x1660: /* (green steel) */
4824 element = EL_STEEL_CHAR_V;
4827 case 0x1661: /* (green steel) */
4828 element = EL_STEEL_CHAR_W;
4831 case 0x1662: /* (green steel) */
4832 element = EL_STEEL_CHAR_X;
4835 case 0x1663: /* (green steel) */
4836 element = EL_STEEL_CHAR_Y;
4839 case 0x1664: /* (green steel) */
4840 element = EL_STEEL_CHAR_Z;
4843 case 0x1665: /* (green steel) */
4844 element = EL_STEEL_CHAR_AUMLAUT;
4847 case 0x1666: /* (green steel) */
4848 element = EL_STEEL_CHAR_OUMLAUT;
4851 case 0x1667: /* (green steel) */
4852 element = EL_STEEL_CHAR_UUMLAUT;
4855 case 0x1668: /* (green steel) */
4856 element = EL_STEEL_CHAR_0;
4859 case 0x1669: /* (green steel) */
4860 element = EL_STEEL_CHAR_1;
4863 case 0x166a: /* (green steel) */
4864 element = EL_STEEL_CHAR_2;
4867 case 0x166b: /* (green steel) */
4868 element = EL_STEEL_CHAR_3;
4871 case 0x166c: /* (green steel) */
4872 element = EL_STEEL_CHAR_4;
4875 case 0x166d: /* (green steel) */
4876 element = EL_STEEL_CHAR_5;
4879 case 0x166e: /* (green steel) */
4880 element = EL_STEEL_CHAR_6;
4883 case 0x166f: /* (green steel) */
4884 element = EL_STEEL_CHAR_7;
4887 case 0x1670: /* (green steel) */
4888 element = EL_STEEL_CHAR_8;
4891 case 0x1671: /* (green steel) */
4892 element = EL_STEEL_CHAR_9;
4895 case 0x1672: /* (green steel) */
4896 element = EL_STEEL_CHAR_PERIOD;
4899 case 0x1673: /* (green steel) */
4900 element = EL_STEEL_CHAR_EXCLAM;
4903 case 0x1674: /* (green steel) */
4904 element = EL_STEEL_CHAR_COLON;
4907 case 0x1675: /* (green steel) */
4908 element = EL_STEEL_CHAR_LESS;
4911 case 0x1676: /* (green steel) */
4912 element = EL_STEEL_CHAR_GREATER;
4915 case 0x1677: /* (green steel) */
4916 element = EL_STEEL_CHAR_QUESTION;
4919 case 0x1678: /* (green steel) */
4920 element = EL_STEEL_CHAR_COPYRIGHT;
4923 case 0x1679: /* (green steel) */
4924 element = EL_STEEL_CHAR_UP;
4927 case 0x167a: /* (green steel) */
4928 element = EL_STEEL_CHAR_DOWN;
4931 case 0x167b: /* (green steel) */
4932 element = EL_STEEL_CHAR_BUTTON;
4935 case 0x167c: /* (green steel) */
4936 element = EL_STEEL_CHAR_PLUS;
4939 case 0x167d: /* (green steel) */
4940 element = EL_STEEL_CHAR_MINUS;
4943 case 0x167e: /* (green steel) */
4944 element = EL_STEEL_CHAR_APOSTROPHE;
4947 case 0x167f: /* (green steel) */
4948 element = EL_STEEL_CHAR_PARENLEFT;
4951 case 0x1680: /* (green steel) */
4952 element = EL_STEEL_CHAR_PARENRIGHT;
4955 case 0x1681: /* gate (red) */
4956 element = EL_EM_GATE_1;
4959 case 0x1682: /* secret gate (red) */
4960 element = EL_GATE_1_GRAY;
4963 case 0x1683: /* gate (yellow) */
4964 element = EL_EM_GATE_2;
4967 case 0x1684: /* secret gate (yellow) */
4968 element = EL_GATE_2_GRAY;
4971 case 0x1685: /* gate (blue) */
4972 element = EL_EM_GATE_4;
4975 case 0x1686: /* secret gate (blue) */
4976 element = EL_GATE_4_GRAY;
4979 case 0x1687: /* gate (green) */
4980 element = EL_EM_GATE_3;
4983 case 0x1688: /* secret gate (green) */
4984 element = EL_GATE_3_GRAY;
4987 case 0x1689: /* gate (white) */
4988 element = EL_DC_GATE_WHITE;
4991 case 0x168a: /* secret gate (white) */
4992 element = EL_DC_GATE_WHITE_GRAY;
4995 case 0x168b: /* secret gate (no key) */
4996 element = EL_DC_GATE_FAKE_GRAY;
5000 element = EL_ROBOT_WHEEL;
5004 element = EL_DC_TIMEGATE_SWITCH;
5008 element = EL_ACID_POOL_BOTTOM;
5012 element = EL_ACID_POOL_TOPLEFT;
5016 element = EL_ACID_POOL_TOPRIGHT;
5020 element = EL_ACID_POOL_BOTTOMLEFT;
5024 element = EL_ACID_POOL_BOTTOMRIGHT;
5028 element = EL_STEELWALL;
5032 element = EL_STEELWALL_SLIPPERY;
5035 case 0x1695: /* steel wall (not round) */
5036 element = EL_STEELWALL;
5039 case 0x1696: /* steel wall (left) */
5040 element = EL_DC_STEELWALL_1_LEFT;
5043 case 0x1697: /* steel wall (bottom) */
5044 element = EL_DC_STEELWALL_1_BOTTOM;
5047 case 0x1698: /* steel wall (right) */
5048 element = EL_DC_STEELWALL_1_RIGHT;
5051 case 0x1699: /* steel wall (top) */
5052 element = EL_DC_STEELWALL_1_TOP;
5055 case 0x169a: /* steel wall (left/bottom) */
5056 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5059 case 0x169b: /* steel wall (right/bottom) */
5060 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5063 case 0x169c: /* steel wall (right/top) */
5064 element = EL_DC_STEELWALL_1_TOPRIGHT;
5067 case 0x169d: /* steel wall (left/top) */
5068 element = EL_DC_STEELWALL_1_TOPLEFT;
5071 case 0x169e: /* steel wall (right/bottom small) */
5072 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5075 case 0x169f: /* steel wall (left/bottom small) */
5076 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5079 case 0x16a0: /* steel wall (right/top small) */
5080 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5083 case 0x16a1: /* steel wall (left/top small) */
5084 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5087 case 0x16a2: /* steel wall (left/right) */
5088 element = EL_DC_STEELWALL_1_VERTICAL;
5091 case 0x16a3: /* steel wall (top/bottom) */
5092 element = EL_DC_STEELWALL_1_HORIZONTAL;
5095 case 0x16a4: /* steel wall 2 (left end) */
5096 element = EL_DC_STEELWALL_2_LEFT;
5099 case 0x16a5: /* steel wall 2 (right end) */
5100 element = EL_DC_STEELWALL_2_RIGHT;
5103 case 0x16a6: /* steel wall 2 (top end) */
5104 element = EL_DC_STEELWALL_2_TOP;
5107 case 0x16a7: /* steel wall 2 (bottom end) */
5108 element = EL_DC_STEELWALL_2_BOTTOM;
5111 case 0x16a8: /* steel wall 2 (left/right) */
5112 element = EL_DC_STEELWALL_2_HORIZONTAL;
5115 case 0x16a9: /* steel wall 2 (up/down) */
5116 element = EL_DC_STEELWALL_2_VERTICAL;
5119 case 0x16aa: /* steel wall 2 (mid) */
5120 element = EL_DC_STEELWALL_2_MIDDLE;
5124 element = EL_SIGN_EXCLAMATION;
5128 element = EL_SIGN_RADIOACTIVITY;
5132 element = EL_SIGN_STOP;
5136 element = EL_SIGN_WHEELCHAIR;
5140 element = EL_SIGN_PARKING;
5144 element = EL_SIGN_NO_ENTRY;
5148 element = EL_SIGN_HEART;
5152 element = EL_SIGN_GIVE_WAY;
5156 element = EL_SIGN_ENTRY_FORBIDDEN;
5160 element = EL_SIGN_EMERGENCY_EXIT;
5164 element = EL_SIGN_YIN_YANG;
5168 element = EL_WALL_EMERALD;
5172 element = EL_WALL_DIAMOND;
5176 element = EL_WALL_PEARL;
5180 element = EL_WALL_CRYSTAL;
5184 element = EL_INVISIBLE_WALL;
5188 element = EL_INVISIBLE_STEELWALL;
5191 /* 0x16bc - 0x16cb: */
5192 /* EL_INVISIBLE_SAND */
5195 element = EL_LIGHT_SWITCH;
5199 element = EL_ENVELOPE_1;
5203 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5204 element = EL_DIAMOND;
5205 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5206 element = EL_EMERALD;
5207 else if (element >= 0x157c && element <= 0x158b)
5209 else if (element >= 0x1590 && element <= 0x159f)
5210 element = EL_DC_LANDMINE;
5211 else if (element >= 0x16bc && element <= 0x16cb)
5212 element = EL_INVISIBLE_SAND;
5215 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5216 element = EL_UNKNOWN;
5221 return getMappedElement(element);
5224 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5227 byte header[DC_LEVEL_HEADER_SIZE];
5229 int envelope_header_pos = 62;
5230 int envelope_content_pos = 94;
5231 int level_name_pos = 251;
5232 int level_author_pos = 292;
5233 int envelope_header_len;
5234 int envelope_content_len;
5236 int level_author_len;
5238 int num_yamyam_contents;
5241 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5243 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5245 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5247 header[i * 2 + 0] = header_word >> 8;
5248 header[i * 2 + 1] = header_word & 0xff;
5251 /* read some values from level header to check level decoding integrity */
5252 fieldx = header[6] | (header[7] << 8);
5253 fieldy = header[8] | (header[9] << 8);
5254 num_yamyam_contents = header[60] | (header[61] << 8);
5256 /* do some simple sanity checks to ensure that level was correctly decoded */
5257 if (fieldx < 1 || fieldx > 256 ||
5258 fieldy < 1 || fieldy > 256 ||
5259 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5261 level->no_valid_file = TRUE;
5263 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5268 /* maximum envelope header size is 31 bytes */
5269 envelope_header_len = header[envelope_header_pos];
5270 /* maximum envelope content size is 110 (156?) bytes */
5271 envelope_content_len = header[envelope_content_pos];
5273 /* maximum level title size is 40 bytes */
5274 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5275 /* maximum level author size is 30 (51?) bytes */
5276 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5280 for (i = 0; i < envelope_header_len; i++)
5281 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5282 level->envelope[0].text[envelope_size++] =
5283 header[envelope_header_pos + 1 + i];
5285 if (envelope_header_len > 0 && envelope_content_len > 0)
5287 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5288 level->envelope[0].text[envelope_size++] = '\n';
5289 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5290 level->envelope[0].text[envelope_size++] = '\n';
5293 for (i = 0; i < envelope_content_len; i++)
5294 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5295 level->envelope[0].text[envelope_size++] =
5296 header[envelope_content_pos + 1 + i];
5298 level->envelope[0].text[envelope_size] = '\0';
5300 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5301 level->envelope[0].ysize = 10;
5302 level->envelope[0].autowrap = TRUE;
5303 level->envelope[0].centered = TRUE;
5305 for (i = 0; i < level_name_len; i++)
5306 level->name[i] = header[level_name_pos + 1 + i];
5307 level->name[level_name_len] = '\0';
5309 for (i = 0; i < level_author_len; i++)
5310 level->author[i] = header[level_author_pos + 1 + i];
5311 level->author[level_author_len] = '\0';
5313 num_yamyam_contents = header[60] | (header[61] << 8);
5314 level->num_yamyam_contents =
5315 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5317 for (i = 0; i < num_yamyam_contents; i++)
5319 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5321 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5322 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5324 if (i < MAX_ELEMENT_CONTENTS)
5325 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5329 fieldx = header[6] | (header[7] << 8);
5330 fieldy = header[8] | (header[9] << 8);
5331 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5332 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5334 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5336 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5337 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5339 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5340 level->field[x][y] = getMappedElement_DC(element_dc);
5343 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5344 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5345 level->field[x][y] = EL_PLAYER_1;
5347 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5348 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5349 level->field[x][y] = EL_PLAYER_2;
5351 level->gems_needed = header[18] | (header[19] << 8);
5353 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5354 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5355 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5356 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5357 level->score[SC_NUT] = header[28] | (header[29] << 8);
5358 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5359 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5360 level->score[SC_BUG] = header[34] | (header[35] << 8);
5361 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5362 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5363 level->score[SC_KEY] = header[40] | (header[41] << 8);
5364 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5366 level->time = header[44] | (header[45] << 8);
5368 level->amoeba_speed = header[46] | (header[47] << 8);
5369 level->time_light = header[48] | (header[49] << 8);
5370 level->time_timegate = header[50] | (header[51] << 8);
5371 level->time_wheel = header[52] | (header[53] << 8);
5372 level->time_magic_wall = header[54] | (header[55] << 8);
5373 level->extra_time = header[56] | (header[57] << 8);
5374 level->shield_normal_time = header[58] | (header[59] << 8);
5376 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5377 can slip down from flat walls, like normal walls and steel walls */
5378 level->em_slippery_gems = TRUE;
5381 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5382 struct LevelFileInfo *level_file_info,
5383 boolean level_info_only)
5385 char *filename = level_file_info->filename;
5387 int num_magic_bytes = 8;
5388 char magic_bytes[num_magic_bytes + 1];
5389 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5391 if (!(file = openFile(filename, MODE_READ)))
5393 level->no_valid_file = TRUE;
5395 if (!level_info_only)
5396 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5401 // fseek(file, 0x0000, SEEK_SET);
5403 if (level_file_info->packed)
5405 /* read "magic bytes" from start of file */
5406 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5407 magic_bytes[0] = '\0';
5409 /* check "magic bytes" for correct file format */
5410 if (!strPrefix(magic_bytes, "DC2"))
5412 level->no_valid_file = TRUE;
5414 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5420 if (strPrefix(magic_bytes, "DC2Win95") ||
5421 strPrefix(magic_bytes, "DC2Win98"))
5423 int position_first_level = 0x00fa;
5424 int extra_bytes = 4;
5427 /* advance file stream to first level inside the level package */
5428 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5430 /* each block of level data is followed by block of non-level data */
5431 num_levels_to_skip *= 2;
5433 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5434 while (num_levels_to_skip >= 0)
5436 /* advance file stream to next level inside the level package */
5437 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5439 level->no_valid_file = TRUE;
5441 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5447 /* skip apparently unused extra bytes following each level */
5448 ReadUnusedBytesFromFile(file, extra_bytes);
5450 /* read size of next level in level package */
5451 skip_bytes = getFile32BitLE(file);
5453 num_levels_to_skip--;
5458 level->no_valid_file = TRUE;
5460 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5467 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5473 /* ------------------------------------------------------------------------- */
5474 /* functions for loading SB level */
5475 /* ------------------------------------------------------------------------- */
5477 int getMappedElement_SB(int element_ascii, boolean use_ces)
5485 sb_element_mapping[] =
5487 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5488 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5489 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5490 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5491 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5492 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5493 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5494 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5501 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5502 if (element_ascii == sb_element_mapping[i].ascii)
5503 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5505 return EL_UNDEFINED;
5508 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5509 struct LevelFileInfo *level_file_info,
5510 boolean level_info_only)
5512 char *filename = level_file_info->filename;
5513 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5514 char last_comment[MAX_LINE_LEN];
5515 char level_name[MAX_LINE_LEN];
5518 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5519 boolean read_continued_line = FALSE;
5520 boolean reading_playfield = FALSE;
5521 boolean got_valid_playfield_line = FALSE;
5522 boolean invalid_playfield_char = FALSE;
5523 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5524 int file_level_nr = 0;
5526 int x = 0, y = 0; /* initialized to make compilers happy */
5528 last_comment[0] = '\0';
5529 level_name[0] = '\0';
5531 if (!(file = openFile(filename, MODE_READ)))
5533 level->no_valid_file = TRUE;
5535 if (!level_info_only)
5536 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5541 while (!checkEndOfFile(file))
5543 /* level successfully read, but next level may follow here */
5544 if (!got_valid_playfield_line && reading_playfield)
5546 /* read playfield from single level file -- skip remaining file */
5547 if (!level_file_info->packed)
5550 if (file_level_nr >= num_levels_to_skip)
5555 last_comment[0] = '\0';
5556 level_name[0] = '\0';
5558 reading_playfield = FALSE;
5561 got_valid_playfield_line = FALSE;
5563 /* read next line of input file */
5564 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5567 /* check if line was completely read and is terminated by line break */
5568 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5571 /* cut trailing line break (this can be newline and/or carriage return) */
5572 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5573 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5576 /* copy raw input line for later use (mainly debugging output) */
5577 strcpy(line_raw, line);
5579 if (read_continued_line)
5581 /* append new line to existing line, if there is enough space */
5582 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5583 strcat(previous_line, line_ptr);
5585 strcpy(line, previous_line); /* copy storage buffer to line */
5587 read_continued_line = FALSE;
5590 /* if the last character is '\', continue at next line */
5591 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5593 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5594 strcpy(previous_line, line); /* copy line to storage buffer */
5596 read_continued_line = TRUE;
5601 /* skip empty lines */
5602 if (line[0] == '\0')
5605 /* extract comment text from comment line */
5608 for (line_ptr = line; *line_ptr; line_ptr++)
5609 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5612 strcpy(last_comment, line_ptr);
5617 /* extract level title text from line containing level title */
5618 if (line[0] == '\'')
5620 strcpy(level_name, &line[1]);
5622 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5623 level_name[strlen(level_name) - 1] = '\0';
5628 /* skip lines containing only spaces (or empty lines) */
5629 for (line_ptr = line; *line_ptr; line_ptr++)
5630 if (*line_ptr != ' ')
5632 if (*line_ptr == '\0')
5635 /* at this point, we have found a line containing part of a playfield */
5637 got_valid_playfield_line = TRUE;
5639 if (!reading_playfield)
5641 reading_playfield = TRUE;
5642 invalid_playfield_char = FALSE;
5644 for (x = 0; x < MAX_LEV_FIELDX; x++)
5645 for (y = 0; y < MAX_LEV_FIELDY; y++)
5646 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5651 /* start with topmost tile row */
5655 /* skip playfield line if larger row than allowed */
5656 if (y >= MAX_LEV_FIELDY)
5659 /* start with leftmost tile column */
5662 /* read playfield elements from line */
5663 for (line_ptr = line; *line_ptr; line_ptr++)
5665 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5667 /* stop parsing playfield line if larger column than allowed */
5668 if (x >= MAX_LEV_FIELDX)
5671 if (mapped_sb_element == EL_UNDEFINED)
5673 invalid_playfield_char = TRUE;
5678 level->field[x][y] = mapped_sb_element;
5680 /* continue with next tile column */
5683 level->fieldx = MAX(x, level->fieldx);
5686 if (invalid_playfield_char)
5688 /* if first playfield line, treat invalid lines as comment lines */
5690 reading_playfield = FALSE;
5695 /* continue with next tile row */
5703 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
5704 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
5706 if (!reading_playfield)
5708 level->no_valid_file = TRUE;
5710 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5715 if (*level_name != '\0')
5717 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
5718 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5720 else if (*last_comment != '\0')
5722 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
5723 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5727 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
5730 /* set all empty fields beyond the border walls to invisible steel wall */
5731 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5733 if ((x == 0 || x == level->fieldx - 1 ||
5734 y == 0 || y == level->fieldy - 1) &&
5735 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
5736 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
5737 level->field, level->fieldx, level->fieldy);
5740 /* set special level settings for Sokoban levels */
5743 level->use_step_counter = TRUE;
5745 if (load_xsb_to_ces)
5747 /* special global settings can now be set in level template */
5749 /* fill smaller playfields with padding "beyond border wall" elements */
5750 if (level->fieldx < SCR_FIELDX ||
5751 level->fieldy < SCR_FIELDY)
5753 short field[level->fieldx][level->fieldy];
5754 int new_fieldx = MAX(level->fieldx, SCR_FIELDX);
5755 int new_fieldy = MAX(level->fieldy, SCR_FIELDY);
5756 int pos_fieldx = (new_fieldx - level->fieldx) / 2;
5757 int pos_fieldy = (new_fieldy - level->fieldy) / 2;
5759 /* copy old playfield (which is smaller than the visible area) */
5760 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5761 field[x][y] = level->field[x][y];
5763 /* fill new, larger playfield with "beyond border wall" elements */
5764 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
5765 level->field[x][y] = getMappedElement_SB('_', load_xsb_to_ces);
5767 /* copy the old playfield to the middle of the new playfield */
5768 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5769 level->field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
5771 level->fieldx = new_fieldx;
5772 level->fieldy = new_fieldy;
5775 level->use_custom_template = TRUE;
5780 /* ------------------------------------------------------------------------- */
5781 /* functions for handling native levels */
5782 /* ------------------------------------------------------------------------- */
5784 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
5785 struct LevelFileInfo *level_file_info,
5786 boolean level_info_only)
5788 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
5789 level->no_valid_file = TRUE;
5792 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
5793 struct LevelFileInfo *level_file_info,
5794 boolean level_info_only)
5798 /* determine position of requested level inside level package */
5799 if (level_file_info->packed)
5800 pos = level_file_info->nr - leveldir_current->first_level;
5802 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
5803 level->no_valid_file = TRUE;
5806 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
5808 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5809 CopyNativeLevel_RND_to_EM(level);
5810 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5811 CopyNativeLevel_RND_to_SP(level);
5814 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
5816 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5817 CopyNativeLevel_EM_to_RND(level);
5818 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5819 CopyNativeLevel_SP_to_RND(level);
5822 void SaveNativeLevel(struct LevelInfo *level)
5824 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5826 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
5827 char *filename = getLevelFilenameFromBasename(basename);
5829 CopyNativeLevel_RND_to_SP(level);
5830 CopyNativeTape_RND_to_SP(level);
5832 SaveNativeLevel_SP(filename);
5837 /* ------------------------------------------------------------------------- */
5838 /* functions for loading generic level */
5839 /* ------------------------------------------------------------------------- */
5841 static void LoadLevelFromFileInfo(struct LevelInfo *level,
5842 struct LevelFileInfo *level_file_info,
5843 boolean level_info_only)
5845 /* always start with reliable default values */
5846 setLevelInfoToDefaults(level, level_info_only);
5848 switch (level_file_info->type)
5850 case LEVEL_FILE_TYPE_RND:
5851 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5854 case LEVEL_FILE_TYPE_EM:
5855 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
5856 level->game_engine_type = GAME_ENGINE_TYPE_EM;
5859 case LEVEL_FILE_TYPE_SP:
5860 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
5861 level->game_engine_type = GAME_ENGINE_TYPE_SP;
5864 case LEVEL_FILE_TYPE_DC:
5865 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
5868 case LEVEL_FILE_TYPE_SB:
5869 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
5873 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5877 /* if level file is invalid, restore level structure to default values */
5878 if (level->no_valid_file)
5880 setLevelInfoToDefaults(level, level_info_only);
5882 level->no_valid_file = TRUE; /* but keep "no valid file" flag */
5885 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
5886 level->game_engine_type = GAME_ENGINE_TYPE_RND;
5888 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
5889 CopyNativeLevel_Native_to_RND(level);
5892 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
5894 static struct LevelFileInfo level_file_info;
5896 /* always start with reliable default values */
5897 setFileInfoToDefaults(&level_file_info);
5899 level_file_info.nr = 0; /* unknown level number */
5900 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
5901 level_file_info.filename = filename;
5903 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
5906 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
5910 if (leveldir_current == NULL) /* only when dumping level */
5913 /* all engine modifications also valid for levels which use latest engine */
5914 if (level->game_version < VERSION_IDENT(3,2,0,5))
5916 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
5917 level->score[SC_TIME_BONUS] /= 10;
5920 if (leveldir_current->latest_engine)
5922 /* ---------- use latest game engine ----------------------------------- */
5924 /* For all levels which are forced to use the latest game engine version
5925 (normally all but user contributed, private and undefined levels), set
5926 the game engine version to the actual version; this allows for actual
5927 corrections in the game engine to take effect for existing, converted
5928 levels (from "classic" or other existing games) to make the emulation
5929 of the corresponding game more accurate, while (hopefully) not breaking
5930 existing levels created from other players. */
5932 level->game_version = GAME_VERSION_ACTUAL;
5934 /* Set special EM style gems behaviour: EM style gems slip down from
5935 normal, steel and growing wall. As this is a more fundamental change,
5936 it seems better to set the default behaviour to "off" (as it is more
5937 natural) and make it configurable in the level editor (as a property
5938 of gem style elements). Already existing converted levels (neither
5939 private nor contributed levels) are changed to the new behaviour. */
5941 if (level->file_version < FILE_VERSION_2_0)
5942 level->em_slippery_gems = TRUE;
5947 /* ---------- use game engine the level was created with ----------------- */
5949 /* For all levels which are not forced to use the latest game engine
5950 version (normally user contributed, private and undefined levels),
5951 use the version of the game engine the levels were created for.
5953 Since 2.0.1, the game engine version is now directly stored
5954 in the level file (chunk "VERS"), so there is no need anymore
5955 to set the game version from the file version (except for old,
5956 pre-2.0 levels, where the game version is still taken from the
5957 file format version used to store the level -- see above). */
5959 /* player was faster than enemies in 1.0.0 and before */
5960 if (level->file_version == FILE_VERSION_1_0)
5961 for (i = 0; i < MAX_PLAYERS; i++)
5962 level->initial_player_stepsize[i] = STEPSIZE_FAST;
5964 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
5965 if (level->game_version == VERSION_IDENT(2,0,1,0))
5966 level->em_slippery_gems = TRUE;
5968 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
5969 if (level->game_version < VERSION_IDENT(2,2,0,0))
5970 level->use_spring_bug = TRUE;
5972 if (level->game_version < VERSION_IDENT(3,2,0,5))
5974 /* time orb caused limited time in endless time levels before 3.2.0-5 */
5975 level->use_time_orb_bug = TRUE;
5977 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
5978 level->block_snap_field = FALSE;
5980 /* extra time score was same value as time left score before 3.2.0-5 */
5981 level->extra_time_score = level->score[SC_TIME_BONUS];
5984 if (level->game_version < VERSION_IDENT(3,2,0,7))
5986 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
5987 level->continuous_snapping = FALSE;
5990 /* only few elements were able to actively move into acid before 3.1.0 */
5991 /* trigger settings did not exist before 3.1.0; set to default "any" */
5992 if (level->game_version < VERSION_IDENT(3,1,0,0))
5994 /* correct "can move into acid" settings (all zero in old levels) */
5996 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
5997 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
5999 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6000 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6001 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6002 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6004 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6005 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6007 /* correct trigger settings (stored as zero == "none" in old levels) */
6009 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6011 int element = EL_CUSTOM_START + i;
6012 struct ElementInfo *ei = &element_info[element];
6014 for (j = 0; j < ei->num_change_pages; j++)
6016 struct ElementChangeInfo *change = &ei->change_page[j];
6018 change->trigger_player = CH_PLAYER_ANY;
6019 change->trigger_page = CH_PAGE_ANY;
6024 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6026 int element = EL_CUSTOM_256;
6027 struct ElementInfo *ei = &element_info[element];
6028 struct ElementChangeInfo *change = &ei->change_page[0];
6030 /* This is needed to fix a problem that was caused by a bugfix in function
6031 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6032 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6033 not replace walkable elements, but instead just placed the player on it,
6034 without placing the Sokoban field under the player). Unfortunately, this
6035 breaks "Snake Bite" style levels when the snake is halfway through a door
6036 that just closes (the snake head is still alive and can be moved in this
6037 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6038 player (without Sokoban element) which then gets killed as designed). */
6040 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6041 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6042 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6043 change->target_element = EL_PLAYER_1;
6046 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6047 if (level->game_version < VERSION_IDENT(3,2,5,0))
6049 /* This is needed to fix a problem that was caused by a bugfix in function
6050 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6051 corrects the behaviour when a custom element changes to another custom
6052 element with a higher element number that has change actions defined.
6053 Normally, only one change per frame is allowed for custom elements.
6054 Therefore, it is checked if a custom element already changed in the
6055 current frame; if it did, subsequent changes are suppressed.
6056 Unfortunately, this is only checked for element changes, but not for
6057 change actions, which are still executed. As the function above loops
6058 through all custom elements from lower to higher, an element change
6059 resulting in a lower CE number won't be checked again, while a target
6060 element with a higher number will also be checked, and potential change
6061 actions will get executed for this CE, too (which is wrong), while
6062 further changes are ignored (which is correct). As this bugfix breaks
6063 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6064 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6065 behaviour for existing levels and tapes that make use of this bug */
6067 level->use_action_after_change_bug = TRUE;
6070 /* not centering level after relocating player was default only in 3.2.3 */
6071 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6072 level->shifted_relocation = TRUE;
6074 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6075 if (level->game_version < VERSION_IDENT(3,2,6,0))
6076 level->em_explodes_by_fire = TRUE;
6079 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6083 /* map custom element change events that have changed in newer versions
6084 (these following values were accidentally changed in version 3.0.1)
6085 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6086 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6088 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6090 int element = EL_CUSTOM_START + i;
6092 /* order of checking and copying events to be mapped is important */
6093 /* (do not change the start and end value -- they are constant) */
6094 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6096 if (HAS_CHANGE_EVENT(element, j - 2))
6098 SET_CHANGE_EVENT(element, j - 2, FALSE);
6099 SET_CHANGE_EVENT(element, j, TRUE);
6103 /* order of checking and copying events to be mapped is important */
6104 /* (do not change the start and end value -- they are constant) */
6105 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6107 if (HAS_CHANGE_EVENT(element, j - 1))
6109 SET_CHANGE_EVENT(element, j - 1, FALSE);
6110 SET_CHANGE_EVENT(element, j, TRUE);
6116 /* initialize "can_change" field for old levels with only one change page */
6117 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6119 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6121 int element = EL_CUSTOM_START + i;
6123 if (CAN_CHANGE(element))
6124 element_info[element].change->can_change = TRUE;
6128 /* correct custom element values (for old levels without these options) */
6129 if (level->game_version < VERSION_IDENT(3,1,1,0))
6131 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6133 int element = EL_CUSTOM_START + i;
6134 struct ElementInfo *ei = &element_info[element];
6136 if (ei->access_direction == MV_NO_DIRECTION)
6137 ei->access_direction = MV_ALL_DIRECTIONS;
6141 /* correct custom element values (fix invalid values for all versions) */
6144 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6146 int element = EL_CUSTOM_START + i;
6147 struct ElementInfo *ei = &element_info[element];
6149 for (j = 0; j < ei->num_change_pages; j++)
6151 struct ElementChangeInfo *change = &ei->change_page[j];
6153 if (change->trigger_player == CH_PLAYER_NONE)
6154 change->trigger_player = CH_PLAYER_ANY;
6156 if (change->trigger_side == CH_SIDE_NONE)
6157 change->trigger_side = CH_SIDE_ANY;
6162 /* initialize "can_explode" field for old levels which did not store this */
6163 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6164 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6166 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6168 int element = EL_CUSTOM_START + i;
6170 if (EXPLODES_1X1_OLD(element))
6171 element_info[element].explosion_type = EXPLODES_1X1;
6173 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6174 EXPLODES_SMASHED(element) ||
6175 EXPLODES_IMPACT(element)));
6179 /* correct previously hard-coded move delay values for maze runner style */
6180 if (level->game_version < VERSION_IDENT(3,1,1,0))
6182 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6184 int element = EL_CUSTOM_START + i;
6186 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6188 /* previously hard-coded and therefore ignored */
6189 element_info[element].move_delay_fixed = 9;
6190 element_info[element].move_delay_random = 0;
6195 /* map elements that have changed in newer versions */
6196 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6197 level->game_version);
6198 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6199 for (x = 0; x < 3; x++)
6200 for (y = 0; y < 3; y++)
6201 level->yamyam_content[i].e[x][y] =
6202 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6203 level->game_version);
6205 /* initialize element properties for level editor etc. */
6206 InitElementPropertiesEngine(level->game_version);
6207 InitElementPropertiesAfterLoading(level->game_version);
6208 InitElementPropertiesGfxElement();
6211 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6215 /* map elements that have changed in newer versions */
6216 for (y = 0; y < level->fieldy; y++)
6217 for (x = 0; x < level->fieldx; x++)
6218 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6219 level->game_version);
6221 /* copy elements to runtime playfield array */
6222 for (x = 0; x < MAX_LEV_FIELDX; x++)
6223 for (y = 0; y < MAX_LEV_FIELDY; y++)
6224 Feld[x][y] = level->field[x][y];
6226 /* initialize level size variables for faster access */
6227 lev_fieldx = level->fieldx;
6228 lev_fieldy = level->fieldy;
6230 /* determine border element for this level */
6231 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6232 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6237 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6239 struct LevelFileInfo *level_file_info = &level->file_info;
6241 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6242 CopyNativeLevel_RND_to_Native(level);
6245 void LoadLevelTemplate(int nr)
6249 setLevelFileInfo(&level_template.file_info, nr);
6250 filename = level_template.file_info.filename;
6252 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6254 LoadLevel_InitVersion(&level_template, filename);
6255 LoadLevel_InitElements(&level_template, filename);
6257 ActivateLevelTemplate();
6260 void LoadLevel(int nr)
6264 setLevelFileInfo(&level.file_info, nr);
6265 filename = level.file_info.filename;
6267 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6269 if (level.use_custom_template)
6270 LoadLevelTemplate(-1);
6272 LoadLevel_InitVersion(&level, filename);
6273 LoadLevel_InitElements(&level, filename);
6274 LoadLevel_InitPlayfield(&level, filename);
6276 LoadLevel_InitNativeEngines(&level, filename);
6279 void LoadLevelInfoOnly(int nr)
6281 setLevelFileInfo(&level.file_info, nr);
6283 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6286 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6290 chunk_size += putFileVersion(file, level->file_version);
6291 chunk_size += putFileVersion(file, level->game_version);
6296 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6300 chunk_size += putFile16BitBE(file, level->creation_date.year);
6301 chunk_size += putFile8Bit(file, level->creation_date.month);
6302 chunk_size += putFile8Bit(file, level->creation_date.day);
6307 #if ENABLE_HISTORIC_CHUNKS
6308 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6312 putFile8Bit(file, level->fieldx);
6313 putFile8Bit(file, level->fieldy);
6315 putFile16BitBE(file, level->time);
6316 putFile16BitBE(file, level->gems_needed);
6318 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6319 putFile8Bit(file, level->name[i]);
6321 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6322 putFile8Bit(file, level->score[i]);
6324 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6325 for (y = 0; y < 3; y++)
6326 for (x = 0; x < 3; x++)
6327 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6328 level->yamyam_content[i].e[x][y]));
6329 putFile8Bit(file, level->amoeba_speed);
6330 putFile8Bit(file, level->time_magic_wall);
6331 putFile8Bit(file, level->time_wheel);
6332 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6333 level->amoeba_content));
6334 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6335 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6336 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6337 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6339 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6341 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6342 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6343 putFile32BitBE(file, level->can_move_into_acid_bits);
6344 putFile8Bit(file, level->dont_collide_with_bits);
6346 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6347 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6349 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6350 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6351 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6353 putFile8Bit(file, level->game_engine_type);
6355 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6359 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6364 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6365 chunk_size += putFile8Bit(file, level->name[i]);
6370 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6375 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6376 chunk_size += putFile8Bit(file, level->author[i]);
6381 #if ENABLE_HISTORIC_CHUNKS
6382 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6387 for (y = 0; y < level->fieldy; y++)
6388 for (x = 0; x < level->fieldx; x++)
6389 if (level->encoding_16bit_field)
6390 chunk_size += putFile16BitBE(file, level->field[x][y]);
6392 chunk_size += putFile8Bit(file, level->field[x][y]);
6398 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6403 for (y = 0; y < level->fieldy; y++)
6404 for (x = 0; x < level->fieldx; x++)
6405 chunk_size += putFile16BitBE(file, level->field[x][y]);
6410 #if ENABLE_HISTORIC_CHUNKS
6411 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6415 putFile8Bit(file, EL_YAMYAM);
6416 putFile8Bit(file, level->num_yamyam_contents);
6417 putFile8Bit(file, 0);
6418 putFile8Bit(file, 0);
6420 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6421 for (y = 0; y < 3; y++)
6422 for (x = 0; x < 3; x++)
6423 if (level->encoding_16bit_field)
6424 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6426 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6430 #if ENABLE_HISTORIC_CHUNKS
6431 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6434 int num_contents, content_xsize, content_ysize;
6435 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6437 if (element == EL_YAMYAM)
6439 num_contents = level->num_yamyam_contents;
6443 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6444 for (y = 0; y < 3; y++)
6445 for (x = 0; x < 3; x++)
6446 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6448 else if (element == EL_BD_AMOEBA)
6454 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6455 for (y = 0; y < 3; y++)
6456 for (x = 0; x < 3; x++)
6457 content_array[i][x][y] = EL_EMPTY;
6458 content_array[0][0][0] = level->amoeba_content;
6462 /* chunk header already written -- write empty chunk data */
6463 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6465 Error(ERR_WARN, "cannot save content for element '%d'", element);
6469 putFile16BitBE(file, element);
6470 putFile8Bit(file, num_contents);
6471 putFile8Bit(file, content_xsize);
6472 putFile8Bit(file, content_ysize);
6474 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6476 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6477 for (y = 0; y < 3; y++)
6478 for (x = 0; x < 3; x++)
6479 putFile16BitBE(file, content_array[i][x][y]);
6483 #if ENABLE_HISTORIC_CHUNKS
6484 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6486 int envelope_nr = element - EL_ENVELOPE_1;
6487 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6491 chunk_size += putFile16BitBE(file, element);
6492 chunk_size += putFile16BitBE(file, envelope_len);
6493 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6494 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6496 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6497 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6499 for (i = 0; i < envelope_len; i++)
6500 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6506 #if ENABLE_HISTORIC_CHUNKS
6507 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6508 int num_changed_custom_elements)
6512 putFile16BitBE(file, num_changed_custom_elements);
6514 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6516 int element = EL_CUSTOM_START + i;
6518 struct ElementInfo *ei = &element_info[element];
6520 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6522 if (check < num_changed_custom_elements)
6524 putFile16BitBE(file, element);
6525 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6532 if (check != num_changed_custom_elements) /* should not happen */
6533 Error(ERR_WARN, "inconsistent number of custom element properties");
6537 #if ENABLE_HISTORIC_CHUNKS
6538 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6539 int num_changed_custom_elements)
6543 putFile16BitBE(file, num_changed_custom_elements);
6545 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6547 int element = EL_CUSTOM_START + i;
6549 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6551 if (check < num_changed_custom_elements)
6553 putFile16BitBE(file, element);
6554 putFile16BitBE(file, element_info[element].change->target_element);
6561 if (check != num_changed_custom_elements) /* should not happen */
6562 Error(ERR_WARN, "inconsistent number of custom target elements");
6566 #if ENABLE_HISTORIC_CHUNKS
6567 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6568 int num_changed_custom_elements)
6570 int i, j, x, y, check = 0;
6572 putFile16BitBE(file, num_changed_custom_elements);
6574 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6576 int element = EL_CUSTOM_START + i;
6577 struct ElementInfo *ei = &element_info[element];
6579 if (ei->modified_settings)
6581 if (check < num_changed_custom_elements)
6583 putFile16BitBE(file, element);
6585 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6586 putFile8Bit(file, ei->description[j]);
6588 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6590 /* some free bytes for future properties and padding */
6591 WriteUnusedBytesToFile(file, 7);
6593 putFile8Bit(file, ei->use_gfx_element);
6594 putFile16BitBE(file, ei->gfx_element_initial);
6596 putFile8Bit(file, ei->collect_score_initial);
6597 putFile8Bit(file, ei->collect_count_initial);
6599 putFile16BitBE(file, ei->push_delay_fixed);
6600 putFile16BitBE(file, ei->push_delay_random);
6601 putFile16BitBE(file, ei->move_delay_fixed);
6602 putFile16BitBE(file, ei->move_delay_random);
6604 putFile16BitBE(file, ei->move_pattern);
6605 putFile8Bit(file, ei->move_direction_initial);
6606 putFile8Bit(file, ei->move_stepsize);
6608 for (y = 0; y < 3; y++)
6609 for (x = 0; x < 3; x++)
6610 putFile16BitBE(file, ei->content.e[x][y]);
6612 putFile32BitBE(file, ei->change->events);
6614 putFile16BitBE(file, ei->change->target_element);
6616 putFile16BitBE(file, ei->change->delay_fixed);
6617 putFile16BitBE(file, ei->change->delay_random);
6618 putFile16BitBE(file, ei->change->delay_frames);
6620 putFile16BitBE(file, ei->change->initial_trigger_element);
6622 putFile8Bit(file, ei->change->explode);
6623 putFile8Bit(file, ei->change->use_target_content);
6624 putFile8Bit(file, ei->change->only_if_complete);
6625 putFile8Bit(file, ei->change->use_random_replace);
6627 putFile8Bit(file, ei->change->random_percentage);
6628 putFile8Bit(file, ei->change->replace_when);
6630 for (y = 0; y < 3; y++)
6631 for (x = 0; x < 3; x++)
6632 putFile16BitBE(file, ei->change->content.e[x][y]);
6634 putFile8Bit(file, ei->slippery_type);
6636 /* some free bytes for future properties and padding */
6637 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6644 if (check != num_changed_custom_elements) /* should not happen */
6645 Error(ERR_WARN, "inconsistent number of custom element properties");
6649 #if ENABLE_HISTORIC_CHUNKS
6650 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6652 struct ElementInfo *ei = &element_info[element];
6655 /* ---------- custom element base property values (96 bytes) ------------- */
6657 putFile16BitBE(file, element);
6659 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6660 putFile8Bit(file, ei->description[i]);
6662 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6664 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
6666 putFile8Bit(file, ei->num_change_pages);
6668 putFile16BitBE(file, ei->ce_value_fixed_initial);
6669 putFile16BitBE(file, ei->ce_value_random_initial);
6670 putFile8Bit(file, ei->use_last_ce_value);
6672 putFile8Bit(file, ei->use_gfx_element);
6673 putFile16BitBE(file, ei->gfx_element_initial);
6675 putFile8Bit(file, ei->collect_score_initial);
6676 putFile8Bit(file, ei->collect_count_initial);
6678 putFile8Bit(file, ei->drop_delay_fixed);
6679 putFile8Bit(file, ei->push_delay_fixed);
6680 putFile8Bit(file, ei->drop_delay_random);
6681 putFile8Bit(file, ei->push_delay_random);
6682 putFile16BitBE(file, ei->move_delay_fixed);
6683 putFile16BitBE(file, ei->move_delay_random);
6685 /* bits 0 - 15 of "move_pattern" ... */
6686 putFile16BitBE(file, ei->move_pattern & 0xffff);
6687 putFile8Bit(file, ei->move_direction_initial);
6688 putFile8Bit(file, ei->move_stepsize);
6690 putFile8Bit(file, ei->slippery_type);
6692 for (y = 0; y < 3; y++)
6693 for (x = 0; x < 3; x++)
6694 putFile16BitBE(file, ei->content.e[x][y]);
6696 putFile16BitBE(file, ei->move_enter_element);
6697 putFile16BitBE(file, ei->move_leave_element);
6698 putFile8Bit(file, ei->move_leave_type);
6700 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
6701 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
6703 putFile8Bit(file, ei->access_direction);
6705 putFile8Bit(file, ei->explosion_delay);
6706 putFile8Bit(file, ei->ignition_delay);
6707 putFile8Bit(file, ei->explosion_type);
6709 /* some free bytes for future custom property values and padding */
6710 WriteUnusedBytesToFile(file, 1);
6712 /* ---------- change page property values (48 bytes) --------------------- */
6714 for (i = 0; i < ei->num_change_pages; i++)
6716 struct ElementChangeInfo *change = &ei->change_page[i];
6717 unsigned int event_bits;
6719 /* bits 0 - 31 of "has_event[]" ... */
6721 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
6722 if (change->has_event[j])
6723 event_bits |= (1 << j);
6724 putFile32BitBE(file, event_bits);
6726 putFile16BitBE(file, change->target_element);
6728 putFile16BitBE(file, change->delay_fixed);
6729 putFile16BitBE(file, change->delay_random);
6730 putFile16BitBE(file, change->delay_frames);
6732 putFile16BitBE(file, change->initial_trigger_element);
6734 putFile8Bit(file, change->explode);
6735 putFile8Bit(file, change->use_target_content);
6736 putFile8Bit(file, change->only_if_complete);
6737 putFile8Bit(file, change->use_random_replace);
6739 putFile8Bit(file, change->random_percentage);
6740 putFile8Bit(file, change->replace_when);
6742 for (y = 0; y < 3; y++)
6743 for (x = 0; x < 3; x++)
6744 putFile16BitBE(file, change->target_content.e[x][y]);
6746 putFile8Bit(file, change->can_change);
6748 putFile8Bit(file, change->trigger_side);
6750 putFile8Bit(file, change->trigger_player);
6751 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
6752 log_2(change->trigger_page)));
6754 putFile8Bit(file, change->has_action);
6755 putFile8Bit(file, change->action_type);
6756 putFile8Bit(file, change->action_mode);
6757 putFile16BitBE(file, change->action_arg);
6759 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
6761 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
6762 if (change->has_event[j])
6763 event_bits |= (1 << (j - 32));
6764 putFile8Bit(file, event_bits);
6769 #if ENABLE_HISTORIC_CHUNKS
6770 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
6772 struct ElementInfo *ei = &element_info[element];
6773 struct ElementGroupInfo *group = ei->group;
6776 putFile16BitBE(file, element);
6778 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6779 putFile8Bit(file, ei->description[i]);
6781 putFile8Bit(file, group->num_elements);
6783 putFile8Bit(file, ei->use_gfx_element);
6784 putFile16BitBE(file, ei->gfx_element_initial);
6786 putFile8Bit(file, group->choice_mode);
6788 /* some free bytes for future values and padding */
6789 WriteUnusedBytesToFile(file, 3);
6791 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
6792 putFile16BitBE(file, group->element[i]);
6796 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
6797 boolean write_element)
6799 int save_type = entry->save_type;
6800 int data_type = entry->data_type;
6801 int conf_type = entry->conf_type;
6802 int byte_mask = conf_type & CONF_MASK_BYTES;
6803 int element = entry->element;
6804 int default_value = entry->default_value;
6806 boolean modified = FALSE;
6808 if (byte_mask != CONF_MASK_MULTI_BYTES)
6810 void *value_ptr = entry->value;
6811 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
6814 /* check if any settings have been modified before saving them */
6815 if (value != default_value)
6818 /* do not save if explicitly told or if unmodified default settings */
6819 if ((save_type == SAVE_CONF_NEVER) ||
6820 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6824 num_bytes += putFile16BitBE(file, element);
6826 num_bytes += putFile8Bit(file, conf_type);
6827 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
6828 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
6829 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
6832 else if (data_type == TYPE_STRING)
6834 char *default_string = entry->default_string;
6835 char *string = (char *)(entry->value);
6836 int string_length = strlen(string);
6839 /* check if any settings have been modified before saving them */
6840 if (!strEqual(string, default_string))
6843 /* do not save if explicitly told or if unmodified default settings */
6844 if ((save_type == SAVE_CONF_NEVER) ||
6845 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6849 num_bytes += putFile16BitBE(file, element);
6851 num_bytes += putFile8Bit(file, conf_type);
6852 num_bytes += putFile16BitBE(file, string_length);
6854 for (i = 0; i < string_length; i++)
6855 num_bytes += putFile8Bit(file, string[i]);
6857 else if (data_type == TYPE_ELEMENT_LIST)
6859 int *element_array = (int *)(entry->value);
6860 int num_elements = *(int *)(entry->num_entities);
6863 /* check if any settings have been modified before saving them */
6864 for (i = 0; i < num_elements; i++)
6865 if (element_array[i] != default_value)
6868 /* do not save if explicitly told or if unmodified default settings */
6869 if ((save_type == SAVE_CONF_NEVER) ||
6870 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6874 num_bytes += putFile16BitBE(file, element);
6876 num_bytes += putFile8Bit(file, conf_type);
6877 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
6879 for (i = 0; i < num_elements; i++)
6880 num_bytes += putFile16BitBE(file, element_array[i]);
6882 else if (data_type == TYPE_CONTENT_LIST)
6884 struct Content *content = (struct Content *)(entry->value);
6885 int num_contents = *(int *)(entry->num_entities);
6888 /* check if any settings have been modified before saving them */
6889 for (i = 0; i < num_contents; i++)
6890 for (y = 0; y < 3; y++)
6891 for (x = 0; x < 3; x++)
6892 if (content[i].e[x][y] != default_value)
6895 /* do not save if explicitly told or if unmodified default settings */
6896 if ((save_type == SAVE_CONF_NEVER) ||
6897 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6901 num_bytes += putFile16BitBE(file, element);
6903 num_bytes += putFile8Bit(file, conf_type);
6904 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
6906 for (i = 0; i < num_contents; i++)
6907 for (y = 0; y < 3; y++)
6908 for (x = 0; x < 3; x++)
6909 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
6915 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
6920 li = *level; /* copy level data into temporary buffer */
6922 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
6923 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
6928 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
6933 li = *level; /* copy level data into temporary buffer */
6935 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
6936 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
6941 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
6943 int envelope_nr = element - EL_ENVELOPE_1;
6947 chunk_size += putFile16BitBE(file, element);
6949 /* copy envelope data into temporary buffer */
6950 xx_envelope = level->envelope[envelope_nr];
6952 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
6953 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
6958 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
6960 struct ElementInfo *ei = &element_info[element];
6964 chunk_size += putFile16BitBE(file, element);
6966 xx_ei = *ei; /* copy element data into temporary buffer */
6968 /* set default description string for this specific element */
6969 strcpy(xx_default_description, getDefaultElementDescription(ei));
6971 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
6972 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
6974 for (i = 0; i < ei->num_change_pages; i++)
6976 struct ElementChangeInfo *change = &ei->change_page[i];
6978 xx_current_change_page = i;
6980 xx_change = *change; /* copy change data into temporary buffer */
6983 setEventBitsFromEventFlags(change);
6985 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
6986 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
6993 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
6995 struct ElementInfo *ei = &element_info[element];
6996 struct ElementGroupInfo *group = ei->group;
7000 chunk_size += putFile16BitBE(file, element);
7002 xx_ei = *ei; /* copy element data into temporary buffer */
7003 xx_group = *group; /* copy group data into temporary buffer */
7005 /* set default description string for this specific element */
7006 strcpy(xx_default_description, getDefaultElementDescription(ei));
7008 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7009 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7014 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
7020 if (!(file = fopen(filename, MODE_WRITE)))
7022 Error(ERR_WARN, "cannot save level file '%s'", filename);
7026 level->file_version = FILE_VERSION_ACTUAL;
7027 level->game_version = GAME_VERSION_ACTUAL;
7029 level->creation_date = getCurrentDate();
7031 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7032 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7034 chunk_size = SaveLevel_VERS(NULL, level);
7035 putFileChunkBE(file, "VERS", chunk_size);
7036 SaveLevel_VERS(file, level);
7038 chunk_size = SaveLevel_DATE(NULL, level);
7039 putFileChunkBE(file, "DATE", chunk_size);
7040 SaveLevel_DATE(file, level);
7042 chunk_size = SaveLevel_NAME(NULL, level);
7043 putFileChunkBE(file, "NAME", chunk_size);
7044 SaveLevel_NAME(file, level);
7046 chunk_size = SaveLevel_AUTH(NULL, level);
7047 putFileChunkBE(file, "AUTH", chunk_size);
7048 SaveLevel_AUTH(file, level);
7050 chunk_size = SaveLevel_INFO(NULL, level);
7051 putFileChunkBE(file, "INFO", chunk_size);
7052 SaveLevel_INFO(file, level);
7054 chunk_size = SaveLevel_BODY(NULL, level);
7055 putFileChunkBE(file, "BODY", chunk_size);
7056 SaveLevel_BODY(file, level);
7058 chunk_size = SaveLevel_ELEM(NULL, level);
7059 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7061 putFileChunkBE(file, "ELEM", chunk_size);
7062 SaveLevel_ELEM(file, level);
7065 for (i = 0; i < NUM_ENVELOPES; i++)
7067 int element = EL_ENVELOPE_1 + i;
7069 chunk_size = SaveLevel_NOTE(NULL, level, element);
7070 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7072 putFileChunkBE(file, "NOTE", chunk_size);
7073 SaveLevel_NOTE(file, level, element);
7077 /* if not using template level, check for non-default custom/group elements */
7078 if (!level->use_custom_template)
7080 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7082 int element = EL_CUSTOM_START + i;
7084 chunk_size = SaveLevel_CUSX(NULL, level, element);
7085 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7087 putFileChunkBE(file, "CUSX", chunk_size);
7088 SaveLevel_CUSX(file, level, element);
7092 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7094 int element = EL_GROUP_START + i;
7096 chunk_size = SaveLevel_GRPX(NULL, level, element);
7097 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7099 putFileChunkBE(file, "GRPX", chunk_size);
7100 SaveLevel_GRPX(file, level, element);
7107 SetFilePermissions(filename, PERMS_PRIVATE);
7110 void SaveLevel(int nr)
7112 char *filename = getDefaultLevelFilename(nr);
7114 SaveLevelFromFilename(&level, filename);
7117 void SaveLevelTemplate()
7119 char *filename = getDefaultLevelFilename(-1);
7121 SaveLevelFromFilename(&level, filename);
7124 boolean SaveLevelChecked(int nr)
7126 char *filename = getDefaultLevelFilename(nr);
7127 boolean new_level = !fileExists(filename);
7128 boolean level_saved = FALSE;
7130 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7135 Request("Level saved!", REQ_CONFIRM);
7143 void DumpLevel(struct LevelInfo *level)
7145 if (level->no_valid_file)
7147 Error(ERR_WARN, "cannot dump -- no valid level file found");
7152 printf_line("-", 79);
7153 printf("Level xxx (file version %08d, game version %08d)\n",
7154 level->file_version, level->game_version);
7155 printf_line("-", 79);
7157 printf("Level author: '%s'\n", level->author);
7158 printf("Level title: '%s'\n", level->name);
7160 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7162 printf("Level time: %d seconds\n", level->time);
7163 printf("Gems needed: %d\n", level->gems_needed);
7165 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
7166 printf("Time for wheel: %d seconds\n", level->time_wheel);
7167 printf("Time for light: %d seconds\n", level->time_light);
7168 printf("Time for timegate: %d seconds\n", level->time_timegate);
7170 printf("Amoeba speed: %d\n", level->amoeba_speed);
7173 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7174 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7175 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7176 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7177 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7179 printf_line("-", 79);
7183 /* ========================================================================= */
7184 /* tape file functions */
7185 /* ========================================================================= */
7187 static void setTapeInfoToDefaults()
7191 /* always start with reliable default values (empty tape) */
7194 /* default values (also for pre-1.2 tapes) with only the first player */
7195 tape.player_participates[0] = TRUE;
7196 for (i = 1; i < MAX_PLAYERS; i++)
7197 tape.player_participates[i] = FALSE;
7199 /* at least one (default: the first) player participates in every tape */
7200 tape.num_participating_players = 1;
7202 tape.level_nr = level_nr;
7204 tape.changed = FALSE;
7206 tape.recording = FALSE;
7207 tape.playing = FALSE;
7208 tape.pausing = FALSE;
7210 tape.no_valid_file = FALSE;
7213 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7215 tape->file_version = getFileVersion(file);
7216 tape->game_version = getFileVersion(file);
7221 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7225 tape->random_seed = getFile32BitBE(file);
7226 tape->date = getFile32BitBE(file);
7227 tape->length = getFile32BitBE(file);
7229 /* read header fields that are new since version 1.2 */
7230 if (tape->file_version >= FILE_VERSION_1_2)
7232 byte store_participating_players = getFile8Bit(file);
7235 /* since version 1.2, tapes store which players participate in the tape */
7236 tape->num_participating_players = 0;
7237 for (i = 0; i < MAX_PLAYERS; i++)
7239 tape->player_participates[i] = FALSE;
7241 if (store_participating_players & (1 << i))
7243 tape->player_participates[i] = TRUE;
7244 tape->num_participating_players++;
7248 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7250 engine_version = getFileVersion(file);
7251 if (engine_version > 0)
7252 tape->engine_version = engine_version;
7254 tape->engine_version = tape->game_version;
7260 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7262 int level_identifier_size;
7265 level_identifier_size = getFile16BitBE(file);
7267 tape->level_identifier =
7268 checked_realloc(tape->level_identifier, level_identifier_size);
7270 for (i = 0; i < level_identifier_size; i++)
7271 tape->level_identifier[i] = getFile8Bit(file);
7273 tape->level_nr = getFile16BitBE(file);
7275 chunk_size = 2 + level_identifier_size + 2;
7280 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7283 int chunk_size_expected =
7284 (tape->num_participating_players + 1) * tape->length;
7286 if (chunk_size_expected != chunk_size)
7288 ReadUnusedBytesFromFile(file, chunk_size);
7289 return chunk_size_expected;
7292 for (i = 0; i < tape->length; i++)
7294 if (i >= MAX_TAPE_LEN)
7297 for (j = 0; j < MAX_PLAYERS; j++)
7299 tape->pos[i].action[j] = MV_NONE;
7301 if (tape->player_participates[j])
7302 tape->pos[i].action[j] = getFile8Bit(file);
7305 tape->pos[i].delay = getFile8Bit(file);
7307 if (tape->file_version == FILE_VERSION_1_0)
7309 /* eliminate possible diagonal moves in old tapes */
7310 /* this is only for backward compatibility */
7312 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7313 byte action = tape->pos[i].action[0];
7314 int k, num_moves = 0;
7316 for (k = 0; k<4; k++)
7318 if (action & joy_dir[k])
7320 tape->pos[i + num_moves].action[0] = joy_dir[k];
7322 tape->pos[i + num_moves].delay = 0;
7331 tape->length += num_moves;
7334 else if (tape->file_version < FILE_VERSION_2_0)
7336 /* convert pre-2.0 tapes to new tape format */
7338 if (tape->pos[i].delay > 1)
7341 tape->pos[i + 1] = tape->pos[i];
7342 tape->pos[i + 1].delay = 1;
7345 for (j = 0; j < MAX_PLAYERS; j++)
7346 tape->pos[i].action[j] = MV_NONE;
7347 tape->pos[i].delay--;
7354 if (checkEndOfFile(file))
7358 if (i != tape->length)
7359 chunk_size = (tape->num_participating_players + 1) * i;
7364 void LoadTape_SokobanSolution(char *filename)
7367 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7369 if (!(file = openFile(filename, MODE_READ)))
7371 tape.no_valid_file = TRUE;
7376 while (!checkEndOfFile(file))
7378 unsigned char c = getByteFromFile(file);
7380 if (checkEndOfFile(file))
7387 tape.pos[tape.length].action[0] = MV_UP;
7388 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7394 tape.pos[tape.length].action[0] = MV_DOWN;
7395 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7401 tape.pos[tape.length].action[0] = MV_LEFT;
7402 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7408 tape.pos[tape.length].action[0] = MV_RIGHT;
7409 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7417 /* ignore white-space characters */
7421 tape.no_valid_file = TRUE;
7423 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7431 if (tape.no_valid_file)
7434 tape.length_frames = GetTapeLengthFrames();
7435 tape.length_seconds = GetTapeLengthSeconds();
7438 void LoadTapeFromFilename(char *filename)
7440 char cookie[MAX_LINE_LEN];
7441 char chunk_name[CHUNK_ID_LEN + 1];
7445 /* always start with reliable default values */
7446 setTapeInfoToDefaults();
7448 if (strSuffix(filename, ".sln"))
7450 LoadTape_SokobanSolution(filename);
7455 if (!(file = openFile(filename, MODE_READ)))
7457 tape.no_valid_file = TRUE;
7462 getFileChunkBE(file, chunk_name, NULL);
7463 if (strEqual(chunk_name, "RND1"))
7465 getFile32BitBE(file); /* not used */
7467 getFileChunkBE(file, chunk_name, NULL);
7468 if (!strEqual(chunk_name, "TAPE"))
7470 tape.no_valid_file = TRUE;
7472 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7479 else /* check for pre-2.0 file format with cookie string */
7481 strcpy(cookie, chunk_name);
7482 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7484 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7485 cookie[strlen(cookie) - 1] = '\0';
7487 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7489 tape.no_valid_file = TRUE;
7491 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7498 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7500 tape.no_valid_file = TRUE;
7502 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7509 /* pre-2.0 tape files have no game version, so use file version here */
7510 tape.game_version = tape.file_version;
7513 if (tape.file_version < FILE_VERSION_1_2)
7515 /* tape files from versions before 1.2.0 without chunk structure */
7516 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7517 LoadTape_BODY(file, 2 * tape.length, &tape);
7525 int (*loader)(File *, int, struct TapeInfo *);
7529 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7530 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7531 { "INFO", -1, LoadTape_INFO },
7532 { "BODY", -1, LoadTape_BODY },
7536 while (getFileChunkBE(file, chunk_name, &chunk_size))
7540 while (chunk_info[i].name != NULL &&
7541 !strEqual(chunk_name, chunk_info[i].name))
7544 if (chunk_info[i].name == NULL)
7546 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7547 chunk_name, filename);
7548 ReadUnusedBytesFromFile(file, chunk_size);
7550 else if (chunk_info[i].size != -1 &&
7551 chunk_info[i].size != chunk_size)
7553 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7554 chunk_size, chunk_name, filename);
7555 ReadUnusedBytesFromFile(file, chunk_size);
7559 /* call function to load this tape chunk */
7560 int chunk_size_expected =
7561 (chunk_info[i].loader)(file, chunk_size, &tape);
7563 /* the size of some chunks cannot be checked before reading other
7564 chunks first (like "HEAD" and "BODY") that contain some header
7565 information, so check them here */
7566 if (chunk_size_expected != chunk_size)
7568 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7569 chunk_size, chunk_name, filename);
7577 tape.length_frames = GetTapeLengthFrames();
7578 tape.length_seconds = GetTapeLengthSeconds();
7581 printf("::: tape file version: %d\n", tape.file_version);
7582 printf("::: tape game version: %d\n", tape.game_version);
7583 printf("::: tape engine version: %d\n", tape.engine_version);
7587 void LoadTape(int nr)
7589 char *filename = getTapeFilename(nr);
7591 LoadTapeFromFilename(filename);
7594 void LoadSolutionTape(int nr)
7596 char *filename = getSolutionTapeFilename(nr);
7598 LoadTapeFromFilename(filename);
7600 if (TAPE_IS_EMPTY(tape) &&
7601 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7602 level.native_sp_level->demo.is_available)
7603 CopyNativeTape_SP_to_RND(&level);
7606 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7608 putFileVersion(file, tape->file_version);
7609 putFileVersion(file, tape->game_version);
7612 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7615 byte store_participating_players = 0;
7617 /* set bits for participating players for compact storage */
7618 for (i = 0; i < MAX_PLAYERS; i++)
7619 if (tape->player_participates[i])
7620 store_participating_players |= (1 << i);
7622 putFile32BitBE(file, tape->random_seed);
7623 putFile32BitBE(file, tape->date);
7624 putFile32BitBE(file, tape->length);
7626 putFile8Bit(file, store_participating_players);
7628 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7629 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7631 putFileVersion(file, tape->engine_version);
7634 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7636 int level_identifier_size = strlen(tape->level_identifier) + 1;
7639 putFile16BitBE(file, level_identifier_size);
7641 for (i = 0; i < level_identifier_size; i++)
7642 putFile8Bit(file, tape->level_identifier[i]);
7644 putFile16BitBE(file, tape->level_nr);
7647 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7651 for (i = 0; i < tape->length; i++)
7653 for (j = 0; j < MAX_PLAYERS; j++)
7654 if (tape->player_participates[j])
7655 putFile8Bit(file, tape->pos[i].action[j]);
7657 putFile8Bit(file, tape->pos[i].delay);
7661 void SaveTape(int nr)
7663 char *filename = getTapeFilename(nr);
7665 int num_participating_players = 0;
7666 int info_chunk_size;
7667 int body_chunk_size;
7670 InitTapeDirectory(leveldir_current->subdir);
7672 if (!(file = fopen(filename, MODE_WRITE)))
7674 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7678 tape.file_version = FILE_VERSION_ACTUAL;
7679 tape.game_version = GAME_VERSION_ACTUAL;
7681 /* count number of participating players */
7682 for (i = 0; i < MAX_PLAYERS; i++)
7683 if (tape.player_participates[i])
7684 num_participating_players++;
7686 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7687 body_chunk_size = (num_participating_players + 1) * tape.length;
7689 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7690 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7692 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7693 SaveTape_VERS(file, &tape);
7695 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7696 SaveTape_HEAD(file, &tape);
7698 putFileChunkBE(file, "INFO", info_chunk_size);
7699 SaveTape_INFO(file, &tape);
7701 putFileChunkBE(file, "BODY", body_chunk_size);
7702 SaveTape_BODY(file, &tape);
7706 SetFilePermissions(filename, PERMS_PRIVATE);
7708 tape.changed = FALSE;
7711 boolean SaveTapeChecked(int nr)
7713 char *filename = getTapeFilename(nr);
7714 boolean new_tape = !fileExists(filename);
7715 boolean tape_saved = FALSE;
7717 if (new_tape || Request("Replace old tape?", REQ_ASK))
7722 Request("Tape saved!", REQ_CONFIRM);
7730 void DumpTape(struct TapeInfo *tape)
7732 int tape_frame_counter;
7735 if (tape->no_valid_file)
7737 Error(ERR_WARN, "cannot dump -- no valid tape file found");
7742 printf_line("-", 79);
7743 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
7744 tape->level_nr, tape->file_version, tape->game_version);
7745 printf(" (effective engine version %08d)\n",
7746 tape->engine_version);
7747 printf("Level series identifier: '%s'\n", tape->level_identifier);
7748 printf_line("-", 79);
7750 tape_frame_counter = 0;
7752 for (i = 0; i < tape->length; i++)
7754 if (i >= MAX_TAPE_LEN)
7757 printf("%04d: ", i);
7759 for (j = 0; j < MAX_PLAYERS; j++)
7761 if (tape->player_participates[j])
7763 int action = tape->pos[i].action[j];
7765 printf("%d:%02x ", j, action);
7766 printf("[%c%c%c%c|%c%c] - ",
7767 (action & JOY_LEFT ? '<' : ' '),
7768 (action & JOY_RIGHT ? '>' : ' '),
7769 (action & JOY_UP ? '^' : ' '),
7770 (action & JOY_DOWN ? 'v' : ' '),
7771 (action & JOY_BUTTON_1 ? '1' : ' '),
7772 (action & JOY_BUTTON_2 ? '2' : ' '));
7776 printf("(%03d) ", tape->pos[i].delay);
7777 printf("[%05d]\n", tape_frame_counter);
7779 tape_frame_counter += tape->pos[i].delay;
7782 printf_line("-", 79);
7786 /* ========================================================================= */
7787 /* score file functions */
7788 /* ========================================================================= */
7790 void LoadScore(int nr)
7793 char *filename = getScoreFilename(nr);
7794 char cookie[MAX_LINE_LEN];
7795 char line[MAX_LINE_LEN];
7799 /* always start with reliable default values */
7800 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7802 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
7803 highscore[i].Score = 0;
7806 if (!(file = fopen(filename, MODE_READ)))
7809 /* check file identifier */
7810 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
7812 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7813 cookie[strlen(cookie) - 1] = '\0';
7815 if (!checkCookieString(cookie, SCORE_COOKIE))
7817 Error(ERR_WARN, "unknown format of score file '%s'", filename);
7822 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7824 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
7825 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
7826 if (fgets(line, MAX_LINE_LEN, file) == NULL)
7829 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
7830 line[strlen(line) - 1] = '\0';
7832 for (line_ptr = line; *line_ptr; line_ptr++)
7834 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
7836 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
7837 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
7846 void SaveScore(int nr)
7849 char *filename = getScoreFilename(nr);
7852 InitScoreDirectory(leveldir_current->subdir);
7854 if (!(file = fopen(filename, MODE_WRITE)))
7856 Error(ERR_WARN, "cannot save score for level %d", nr);
7860 fprintf(file, "%s\n\n", SCORE_COOKIE);
7862 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7863 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
7867 SetFilePermissions(filename, PERMS_PUBLIC);
7871 /* ========================================================================= */
7872 /* setup file functions */
7873 /* ========================================================================= */
7875 #define TOKEN_STR_PLAYER_PREFIX "player_"
7878 #define SETUP_TOKEN_PLAYER_NAME 0
7879 #define SETUP_TOKEN_SOUND 1
7880 #define SETUP_TOKEN_SOUND_LOOPS 2
7881 #define SETUP_TOKEN_SOUND_MUSIC 3
7882 #define SETUP_TOKEN_SOUND_SIMPLE 4
7883 #define SETUP_TOKEN_TOONS 5
7884 #define SETUP_TOKEN_SCROLL_DELAY 6
7885 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
7886 #define SETUP_TOKEN_SOFT_SCROLLING 8
7887 #define SETUP_TOKEN_FADE_SCREENS 9
7888 #define SETUP_TOKEN_AUTORECORD 10
7889 #define SETUP_TOKEN_SHOW_TITLESCREEN 11
7890 #define SETUP_TOKEN_QUICK_DOORS 12
7891 #define SETUP_TOKEN_TEAM_MODE 13
7892 #define SETUP_TOKEN_HANDICAP 14
7893 #define SETUP_TOKEN_SKIP_LEVELS 15
7894 #define SETUP_TOKEN_TIME_LIMIT 16
7895 #define SETUP_TOKEN_FULLSCREEN 17
7896 #define SETUP_TOKEN_FULLSCREEN_MODE 18
7897 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT 19
7898 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY 20
7899 #define SETUP_TOKEN_ASK_ON_ESCAPE 21
7900 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 22
7901 #define SETUP_TOKEN_QUICK_SWITCH 23
7902 #define SETUP_TOKEN_INPUT_ON_FOCUS 24
7903 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 25
7904 #define SETUP_TOKEN_GAME_FRAME_DELAY 26
7905 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 27
7906 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 28
7907 #define SETUP_TOKEN_GRAPHICS_SET 29
7908 #define SETUP_TOKEN_SOUNDS_SET 30
7909 #define SETUP_TOKEN_MUSIC_SET 31
7910 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 32
7911 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 33
7912 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 34
7913 #define SETUP_TOKEN_VOLUME_SIMPLE 35
7914 #define SETUP_TOKEN_VOLUME_LOOPS 36
7915 #define SETUP_TOKEN_VOLUME_MUSIC 37
7916 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE 38
7917 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 39
7918 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE 40
7920 #define NUM_GLOBAL_SETUP_TOKENS 41
7923 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
7924 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
7925 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
7926 #define SETUP_TOKEN_EDITOR_EL_MORE 3
7927 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 4
7928 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 5
7929 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 6
7930 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 7
7931 #define SETUP_TOKEN_EDITOR_EL_CHARS 8
7932 #define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS 9
7933 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 10
7934 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 11
7935 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 12
7936 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 13
7937 #define SETUP_TOKEN_EDITOR_EL_BY_GAME 14
7938 #define SETUP_TOKEN_EDITOR_EL_BY_TYPE 15
7939 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 16
7941 #define NUM_EDITOR_SETUP_TOKENS 17
7943 /* editor cascade setup */
7944 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
7945 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
7946 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
7947 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
7948 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
7949 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
7950 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
7951 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
7952 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
7953 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
7954 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
7955 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
7956 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
7957 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
7958 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
7960 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
7962 /* shortcut setup */
7963 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
7964 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
7965 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
7966 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
7967 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
7968 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
7969 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
7970 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
7971 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
7972 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
7973 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
7974 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
7975 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
7976 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
7977 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
7978 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
7979 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
7980 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
7981 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
7982 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
7983 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
7985 #define NUM_SHORTCUT_SETUP_TOKENS 21
7988 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
7989 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
7990 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
7991 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
7992 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
7993 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
7994 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
7995 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
7996 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
7997 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
7998 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
7999 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
8000 #define SETUP_TOKEN_PLAYER_KEY_UP 12
8001 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
8002 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8003 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8005 #define NUM_PLAYER_SETUP_TOKENS 16
8008 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8009 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8010 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8012 #define NUM_SYSTEM_SETUP_TOKENS 3
8015 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8017 #define NUM_OPTIONS_SETUP_TOKENS 1
8020 static struct SetupInfo si;
8021 static struct SetupEditorInfo sei;
8022 static struct SetupEditorCascadeInfo seci;
8023 static struct SetupShortcutInfo ssi;
8024 static struct SetupInputInfo sii;
8025 static struct SetupSystemInfo syi;
8026 static struct OptionInfo soi;
8028 static struct TokenInfo global_setup_tokens[] =
8030 { TYPE_STRING, &si.player_name, "player_name" },
8031 { TYPE_SWITCH, &si.sound, "sound" },
8032 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8033 { TYPE_SWITCH, &si.sound_music, "background_music" },
8034 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8035 { TYPE_SWITCH, &si.toons, "toons" },
8036 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8037 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8038 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
8039 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8040 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8041 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8042 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8043 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8044 { TYPE_SWITCH, &si.handicap, "handicap" },
8045 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8046 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8047 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8048 { TYPE_STRING, &si.fullscreen_mode, "fullscreen_mode" },
8049 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8050 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8051 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8052 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8053 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8054 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8055 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8056 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8057 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8058 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8059 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8060 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8061 { TYPE_STRING, &si.music_set, "music_set" },
8062 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8063 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8064 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8065 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8066 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8067 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8068 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8069 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8070 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8073 static struct TokenInfo editor_setup_tokens[] =
8075 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
8076 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
8077 { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
8078 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
8079 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
8080 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
8081 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
8082 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
8083 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
8084 { TYPE_SWITCH, &sei.el_steel_chars, "editor.el_steel_chars" },
8085 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8086 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8087 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8088 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8089 { TYPE_SWITCH, &sei.el_by_game, "editor.el_by_game" },
8090 { TYPE_SWITCH, &sei.el_by_type, "editor.el_by_type" },
8091 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8094 static struct TokenInfo editor_cascade_setup_tokens[] =
8096 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8097 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8098 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8099 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8100 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8101 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8102 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8103 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8104 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8105 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8106 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8107 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8108 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8109 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8110 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8113 static struct TokenInfo shortcut_setup_tokens[] =
8115 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8116 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8117 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8118 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8119 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8120 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8121 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8122 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8123 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8124 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8125 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8126 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8127 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8128 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8129 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8130 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8131 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8132 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8133 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8134 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8135 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8138 static struct TokenInfo player_setup_tokens[] =
8140 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8141 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8142 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8143 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8144 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8145 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8146 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8147 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8148 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8149 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8150 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8151 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8152 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8153 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8154 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8155 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8158 static struct TokenInfo system_setup_tokens[] =
8160 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8161 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8162 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8165 static struct TokenInfo options_setup_tokens[] =
8167 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8170 static char *get_corrected_login_name(char *login_name)
8172 /* needed because player name must be a fixed length string */
8173 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8175 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8176 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8178 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8179 if (strchr(login_name_new, ' '))
8180 *strchr(login_name_new, ' ') = '\0';
8182 return login_name_new;
8185 static void setSetupInfoToDefaults(struct SetupInfo *si)
8189 si->player_name = get_corrected_login_name(getLoginName());
8192 si->sound_loops = TRUE;
8193 si->sound_music = TRUE;
8194 si->sound_simple = TRUE;
8196 si->scroll_delay = TRUE;
8197 si->scroll_delay_value = STD_SCROLL_DELAY;
8198 si->soft_scrolling = TRUE;
8199 si->fade_screens = TRUE;
8200 si->autorecord = TRUE;
8201 si->show_titlescreen = TRUE;
8202 si->quick_doors = FALSE;
8203 si->team_mode = FALSE;
8204 si->handicap = TRUE;
8205 si->skip_levels = TRUE;
8206 si->time_limit = TRUE;
8207 si->fullscreen = FALSE;
8208 si->fullscreen_mode = getStringCopy(DEFAULT_FULLSCREEN_MODE);
8209 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8210 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8211 si->ask_on_escape = TRUE;
8212 si->ask_on_escape_editor = TRUE;
8213 si->quick_switch = FALSE;
8214 si->input_on_focus = FALSE;
8215 si->prefer_aga_graphics = TRUE;
8216 si->game_frame_delay = GAME_FRAME_DELAY;
8217 si->sp_show_border_elements = FALSE;
8218 si->small_game_graphics = FALSE;
8220 si->graphics_set = getStringCopy(GFX_DEFAULT_SUBDIR);
8221 si->sounds_set = getStringCopy(SND_DEFAULT_SUBDIR);
8222 si->music_set = getStringCopy(MUS_DEFAULT_SUBDIR);
8223 si->override_level_graphics = FALSE;
8224 si->override_level_sounds = FALSE;
8225 si->override_level_music = FALSE;
8227 si->volume_simple = 100; /* percent */
8228 si->volume_loops = 100; /* percent */
8229 si->volume_music = 100; /* percent */
8231 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8232 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8233 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8235 si->editor.el_boulderdash = TRUE;
8236 si->editor.el_emerald_mine = TRUE;
8237 si->editor.el_emerald_mine_club = TRUE;
8238 si->editor.el_more = TRUE;
8239 si->editor.el_sokoban = TRUE;
8240 si->editor.el_supaplex = TRUE;
8241 si->editor.el_diamond_caves = TRUE;
8242 si->editor.el_dx_boulderdash = TRUE;
8243 si->editor.el_chars = TRUE;
8244 si->editor.el_steel_chars = TRUE;
8245 si->editor.el_custom = TRUE;
8247 si->editor.el_headlines = TRUE;
8248 si->editor.el_user_defined = FALSE;
8249 si->editor.el_dynamic = TRUE;
8251 si->editor.show_element_token = FALSE;
8253 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8254 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8255 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8257 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8258 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8259 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8260 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8261 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8263 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8264 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8265 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8266 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8267 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8268 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8270 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8271 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8272 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8274 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8275 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8276 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8277 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8279 for (i = 0; i < MAX_PLAYERS; i++)
8281 si->input[i].use_joystick = FALSE;
8282 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8283 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8284 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8285 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8286 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8287 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8288 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8289 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8290 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8291 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8292 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8293 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8294 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8295 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8296 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8299 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8300 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8301 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8303 si->options.verbose = FALSE;
8305 #if defined(CREATE_SPECIAL_EDITION_RND_JUE)
8307 si->handicap = FALSE;
8308 si->fullscreen = TRUE;
8309 si->override_level_graphics = AUTO;
8310 si->override_level_sounds = AUTO;
8311 si->override_level_music = AUTO;
8314 #if defined(PLATFORM_ANDROID)
8315 si->fullscreen = TRUE;
8319 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8321 si->editor_cascade.el_bd = TRUE;
8322 si->editor_cascade.el_em = TRUE;
8323 si->editor_cascade.el_emc = TRUE;
8324 si->editor_cascade.el_rnd = TRUE;
8325 si->editor_cascade.el_sb = TRUE;
8326 si->editor_cascade.el_sp = TRUE;
8327 si->editor_cascade.el_dc = TRUE;
8328 si->editor_cascade.el_dx = TRUE;
8330 si->editor_cascade.el_chars = FALSE;
8331 si->editor_cascade.el_steel_chars = FALSE;
8332 si->editor_cascade.el_ce = FALSE;
8333 si->editor_cascade.el_ge = FALSE;
8334 si->editor_cascade.el_ref = FALSE;
8335 si->editor_cascade.el_user = FALSE;
8336 si->editor_cascade.el_dynamic = FALSE;
8339 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8343 if (!setup_file_hash)
8348 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8349 setSetupInfo(global_setup_tokens, i,
8350 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
8355 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8356 setSetupInfo(editor_setup_tokens, i,
8357 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
8360 /* shortcut setup */
8361 ssi = setup.shortcut;
8362 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8363 setSetupInfo(shortcut_setup_tokens, i,
8364 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
8365 setup.shortcut = ssi;
8368 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8372 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8374 sii = setup.input[pnr];
8375 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8377 char full_token[100];
8379 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8380 setSetupInfo(player_setup_tokens, i,
8381 getHashEntry(setup_file_hash, full_token));
8383 setup.input[pnr] = sii;
8388 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8389 setSetupInfo(system_setup_tokens, i,
8390 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
8394 soi = setup.options;
8395 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8396 setSetupInfo(options_setup_tokens, i,
8397 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
8398 setup.options = soi;
8401 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8405 if (!setup_file_hash)
8408 /* editor cascade setup */
8409 seci = setup.editor_cascade;
8410 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8411 setSetupInfo(editor_cascade_setup_tokens, i,
8412 getHashEntry(setup_file_hash,
8413 editor_cascade_setup_tokens[i].text));
8414 setup.editor_cascade = seci;
8419 char *filename = getSetupFilename();
8420 SetupFileHash *setup_file_hash = NULL;
8422 /* always start with reliable default values */
8423 setSetupInfoToDefaults(&setup);
8425 setup_file_hash = loadSetupFileHash(filename);
8427 if (setup_file_hash)
8429 char *player_name_new;
8431 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
8432 decodeSetupFileHash(setup_file_hash);
8434 freeSetupFileHash(setup_file_hash);
8436 /* needed to work around problems with fixed length strings */
8437 player_name_new = get_corrected_login_name(setup.player_name);
8438 free(setup.player_name);
8439 setup.player_name = player_name_new;
8441 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
8442 if (setup.scroll_delay == FALSE)
8444 setup.scroll_delay_value = MIN_SCROLL_DELAY;
8445 setup.scroll_delay = TRUE; /* now always "on" */
8448 /* make sure that scroll delay value stays inside valid range */
8449 setup.scroll_delay_value =
8450 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
8453 Error(ERR_WARN, "using default setup values");
8456 void LoadSetup_EditorCascade()
8458 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8459 SetupFileHash *setup_file_hash = NULL;
8461 /* always start with reliable default values */
8462 setSetupInfoToDefaults_EditorCascade(&setup);
8464 setup_file_hash = loadSetupFileHash(filename);
8466 if (setup_file_hash)
8468 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
8469 decodeSetupFileHash_EditorCascade(setup_file_hash);
8471 freeSetupFileHash(setup_file_hash);
8479 char *filename = getSetupFilename();
8483 InitUserDataDirectory();
8485 if (!(file = fopen(filename, MODE_WRITE)))
8487 Error(ERR_WARN, "cannot write setup file '%s'", filename);
8491 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
8492 getCookie("SETUP")));
8493 fprintf(file, "\n");
8497 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8499 /* just to make things nicer :) */
8500 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
8501 i == SETUP_TOKEN_GRAPHICS_SET ||
8502 i == SETUP_TOKEN_VOLUME_SIMPLE ||
8503 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
8504 fprintf(file, "\n");
8506 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
8511 fprintf(file, "\n");
8512 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8513 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
8515 /* shortcut setup */
8516 ssi = setup.shortcut;
8517 fprintf(file, "\n");
8518 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8519 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
8522 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8526 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8527 fprintf(file, "\n");
8529 sii = setup.input[pnr];
8530 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8531 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
8536 fprintf(file, "\n");
8537 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8538 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
8541 soi = setup.options;
8542 fprintf(file, "\n");
8543 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8544 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
8548 SetFilePermissions(filename, PERMS_PRIVATE);
8551 void SaveSetup_EditorCascade()
8553 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8557 InitUserDataDirectory();
8559 if (!(file = fopen(filename, MODE_WRITE)))
8561 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
8566 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
8567 getCookie("SETUP")));
8568 fprintf(file, "\n");
8570 seci = setup.editor_cascade;
8571 fprintf(file, "\n");
8572 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8573 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
8577 SetFilePermissions(filename, PERMS_PRIVATE);
8582 void LoadCustomElementDescriptions()
8584 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8585 SetupFileHash *setup_file_hash;
8588 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8590 if (element_info[i].custom_description != NULL)
8592 free(element_info[i].custom_description);
8593 element_info[i].custom_description = NULL;
8597 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8600 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8602 char *token = getStringCat2(element_info[i].token_name, ".name");
8603 char *value = getHashEntry(setup_file_hash, token);
8606 element_info[i].custom_description = getStringCopy(value);
8611 freeSetupFileHash(setup_file_hash);
8614 static int getElementFromToken(char *token)
8616 char *value = getHashEntry(element_token_hash, token);
8621 Error(ERR_WARN, "unknown element token '%s'", token);
8623 return EL_UNDEFINED;
8626 static int get_token_parameter_value(char *token, char *value_raw)
8630 if (token == NULL || value_raw == NULL)
8631 return ARG_UNDEFINED_VALUE;
8633 suffix = strrchr(token, '.');
8637 if (strEqual(suffix, ".element"))
8638 return getElementFromToken(value_raw);
8640 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
8641 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
8644 void InitMenuDesignSettings_Static()
8648 /* always start with reliable default values from static default config */
8649 for (i = 0; image_config_vars[i].token != NULL; i++)
8651 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
8654 *image_config_vars[i].value =
8655 get_token_parameter_value(image_config_vars[i].token, value);
8659 static void InitMenuDesignSettings_SpecialPreProcessing()
8663 /* the following initializes hierarchical values from static configuration */
8665 /* special case: initialize "ARG_DEFAULT" values in static default config */
8666 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
8667 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
8668 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
8669 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
8670 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
8671 titlemessage_default.fade_mode = title_default.fade_mode;
8672 titlemessage_default.fade_delay = title_default.fade_delay;
8673 titlemessage_default.post_delay = title_default.post_delay;
8674 titlemessage_default.auto_delay = title_default.auto_delay;
8676 /* special case: initialize "ARG_DEFAULT" values in static default config */
8677 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
8678 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
8680 titlemessage_initial[i] = titlemessage_initial_default;
8681 titlemessage[i] = titlemessage_default;
8684 /* special case: initialize "ARG_DEFAULT" values in static default config */
8685 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
8686 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8688 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
8689 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
8692 /* special case: initialize "ARG_DEFAULT" values in static default config */
8693 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
8694 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8696 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
8697 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
8698 if (i != GFX_SPECIAL_ARG_EDITOR) /* editor value already initialized */
8699 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
8703 static void InitMenuDesignSettings_SpecialPostProcessing()
8705 /* special case: initialize later added SETUP list size from LEVELS value */
8706 if (menu.list_size[GAME_MODE_SETUP] == -1)
8707 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
8710 static void LoadMenuDesignSettingsFromFilename(char *filename)
8712 static struct TitleMessageInfo tmi;
8713 static struct TokenInfo titlemessage_tokens[] =
8715 { TYPE_INTEGER, &tmi.x, ".x" },
8716 { TYPE_INTEGER, &tmi.y, ".y" },
8717 { TYPE_INTEGER, &tmi.width, ".width" },
8718 { TYPE_INTEGER, &tmi.height, ".height" },
8719 { TYPE_INTEGER, &tmi.chars, ".chars" },
8720 { TYPE_INTEGER, &tmi.lines, ".lines" },
8721 { TYPE_INTEGER, &tmi.align, ".align" },
8722 { TYPE_INTEGER, &tmi.valign, ".valign" },
8723 { TYPE_INTEGER, &tmi.font, ".font" },
8724 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
8725 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
8726 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
8727 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
8728 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
8729 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
8730 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
8731 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
8737 struct TitleMessageInfo *array;
8740 titlemessage_arrays[] =
8742 { titlemessage_initial, "[titlemessage_initial]" },
8743 { titlemessage, "[titlemessage]" },
8747 SetupFileHash *setup_file_hash;
8750 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8753 /* the following initializes hierarchical values from dynamic configuration */
8755 /* special case: initialize with default values that may be overwritten */
8756 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
8757 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8759 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
8760 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
8761 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
8763 if (value_1 != NULL)
8764 menu.draw_xoffset[i] = get_integer_from_string(value_1);
8765 if (value_2 != NULL)
8766 menu.draw_yoffset[i] = get_integer_from_string(value_2);
8767 if (value_3 != NULL)
8768 menu.list_size[i] = get_integer_from_string(value_3);
8771 /* special case: initialize with default values that may be overwritten */
8772 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
8773 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
8775 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
8776 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
8778 if (value_1 != NULL)
8779 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
8780 if (value_2 != NULL)
8781 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
8784 /* special case: initialize with default values that may be overwritten */
8785 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
8786 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
8788 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
8789 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
8791 if (value_1 != NULL)
8792 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
8793 if (value_2 != NULL)
8794 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
8797 /* special case: initialize with default values that may be overwritten */
8798 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
8799 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8801 char *token_1 = "menu.enter_screen.fade_mode";
8802 char *token_2 = "menu.enter_screen.fade_delay";
8803 char *token_3 = "menu.enter_screen.post_delay";
8804 char *token_4 = "menu.leave_screen.fade_mode";
8805 char *token_5 = "menu.leave_screen.fade_delay";
8806 char *token_6 = "menu.leave_screen.post_delay";
8807 char *value_1 = getHashEntry(setup_file_hash, token_1);
8808 char *value_2 = getHashEntry(setup_file_hash, token_2);
8809 char *value_3 = getHashEntry(setup_file_hash, token_3);
8810 char *value_4 = getHashEntry(setup_file_hash, token_4);
8811 char *value_5 = getHashEntry(setup_file_hash, token_5);
8812 char *value_6 = getHashEntry(setup_file_hash, token_6);
8814 if (value_1 != NULL)
8815 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
8817 if (value_2 != NULL)
8818 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
8820 if (value_3 != NULL)
8821 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
8823 if (value_4 != NULL)
8824 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
8826 if (value_5 != NULL)
8827 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
8829 if (value_6 != NULL)
8830 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
8834 /* special case: initialize with default values that may be overwritten */
8835 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
8836 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8838 char *token_01 = "viewport.playfield.x";
8839 char *token_02 = "viewport.playfield.y";
8840 char *token_03 = "viewport.playfield.width";
8841 char *token_04 = "viewport.playfield.height";
8842 char *token_05 = "viewport.playfield.border_size";
8843 char *token_06 = "viewport.door_1.x";
8844 char *token_07 = "viewport.door_1.y";
8845 char *token_08 = "viewport.door_1.width";
8846 char *token_09 = "viewport.door_1.height";
8847 char *token_10 = "viewport.door_1.border_size";
8848 char *token_11 = "viewport.door_2.x";
8849 char *token_12 = "viewport.door_2.y";
8850 char *token_13 = "viewport.door_2.width";
8851 char *token_14 = "viewport.door_2.height";
8852 char *token_15 = "viewport.door_2.border_size";
8853 char *value_01 = getHashEntry(setup_file_hash, token_01);
8854 char *value_02 = getHashEntry(setup_file_hash, token_02);
8855 char *value_03 = getHashEntry(setup_file_hash, token_03);
8856 char *value_04 = getHashEntry(setup_file_hash, token_04);
8857 char *value_05 = getHashEntry(setup_file_hash, token_05);
8858 char *value_06 = getHashEntry(setup_file_hash, token_06);
8859 char *value_07 = getHashEntry(setup_file_hash, token_07);
8860 char *value_08 = getHashEntry(setup_file_hash, token_08);
8861 char *value_09 = getHashEntry(setup_file_hash, token_09);
8862 char *value_10 = getHashEntry(setup_file_hash, token_10);
8863 char *value_11 = getHashEntry(setup_file_hash, token_11);
8864 char *value_12 = getHashEntry(setup_file_hash, token_12);
8865 char *value_13 = getHashEntry(setup_file_hash, token_13);
8866 char *value_14 = getHashEntry(setup_file_hash, token_14);
8867 char *value_15 = getHashEntry(setup_file_hash, token_15);
8869 if (value_01 != NULL)
8870 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
8871 if (value_02 != NULL)
8872 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
8873 if (value_03 != NULL)
8874 viewport.playfield[i].width = get_token_parameter_value(token_03,
8876 if (value_04 != NULL)
8877 viewport.playfield[i].height = get_token_parameter_value(token_04,
8879 if (value_05 != NULL)
8880 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
8882 if (value_06 != NULL)
8883 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
8884 if (value_07 != NULL)
8885 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
8886 if (value_08 != NULL)
8887 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
8888 if (value_09 != NULL)
8889 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
8890 if (value_10 != NULL)
8891 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
8893 if (value_11 != NULL)
8894 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
8895 if (value_12 != NULL)
8896 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
8897 if (value_13 != NULL)
8898 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
8899 if (value_14 != NULL)
8900 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
8901 if (value_15 != NULL)
8902 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
8906 /* special case: initialize with default values that may be overwritten */
8907 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
8908 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
8910 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
8911 char *base_token = titlemessage_arrays[i].text;
8913 for (j = 0; titlemessage_tokens[j].type != -1; j++)
8915 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
8916 char *value = getHashEntry(setup_file_hash, token);
8920 int parameter_value = get_token_parameter_value(token, value);
8922 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
8926 if (titlemessage_tokens[j].type == TYPE_INTEGER)
8927 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
8929 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
8939 /* read (and overwrite with) values that may be specified in config file */
8940 for (i = 0; image_config_vars[i].token != NULL; i++)
8942 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
8944 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
8945 if (value != NULL && !strEqual(value, ARG_DEFAULT))
8946 *image_config_vars[i].value =
8947 get_token_parameter_value(image_config_vars[i].token, value);
8950 freeSetupFileHash(setup_file_hash);
8953 void LoadMenuDesignSettings()
8955 char *filename_base = UNDEFINED_FILENAME, *filename_local;
8957 InitMenuDesignSettings_Static();
8958 InitMenuDesignSettings_SpecialPreProcessing();
8960 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
8962 /* first look for special settings configured in level series config */
8963 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
8965 if (fileExists(filename_base))
8966 LoadMenuDesignSettingsFromFilename(filename_base);
8969 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8971 if (filename_local != NULL && !strEqual(filename_base, filename_local))
8972 LoadMenuDesignSettingsFromFilename(filename_local);
8974 InitMenuDesignSettings_SpecialPostProcessing();
8977 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
8979 char *filename = getEditorSetupFilename();
8980 SetupFileList *setup_file_list, *list;
8981 SetupFileHash *element_hash;
8982 int num_unknown_tokens = 0;
8985 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
8988 element_hash = newSetupFileHash();
8990 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8991 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
8993 /* determined size may be larger than needed (due to unknown elements) */
8995 for (list = setup_file_list; list != NULL; list = list->next)
8998 /* add space for up to 3 more elements for padding that may be needed */
9001 /* free memory for old list of elements, if needed */
9002 checked_free(*elements);
9004 /* allocate memory for new list of elements */
9005 *elements = checked_malloc(*num_elements * sizeof(int));
9008 for (list = setup_file_list; list != NULL; list = list->next)
9010 char *value = getHashEntry(element_hash, list->token);
9012 if (value == NULL) /* try to find obsolete token mapping */
9014 char *mapped_token = get_mapped_token(list->token);
9016 if (mapped_token != NULL)
9018 value = getHashEntry(element_hash, mapped_token);
9026 (*elements)[(*num_elements)++] = atoi(value);
9030 if (num_unknown_tokens == 0)
9032 Error(ERR_INFO_LINE, "-");
9033 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9034 Error(ERR_INFO, "- config file: '%s'", filename);
9036 num_unknown_tokens++;
9039 Error(ERR_INFO, "- token: '%s'", list->token);
9043 if (num_unknown_tokens > 0)
9044 Error(ERR_INFO_LINE, "-");
9046 while (*num_elements % 4) /* pad with empty elements, if needed */
9047 (*elements)[(*num_elements)++] = EL_EMPTY;
9049 freeSetupFileList(setup_file_list);
9050 freeSetupFileHash(element_hash);
9053 for (i = 0; i < *num_elements; i++)
9054 printf("editor: element '%s' [%d]\n",
9055 element_info[(*elements)[i]].token_name, (*elements)[i]);
9059 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9062 SetupFileHash *setup_file_hash = NULL;
9063 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9064 char *filename_music, *filename_prefix, *filename_info;
9070 token_to_value_ptr[] =
9072 { "title_header", &tmp_music_file_info.title_header },
9073 { "artist_header", &tmp_music_file_info.artist_header },
9074 { "album_header", &tmp_music_file_info.album_header },
9075 { "year_header", &tmp_music_file_info.year_header },
9077 { "title", &tmp_music_file_info.title },
9078 { "artist", &tmp_music_file_info.artist },
9079 { "album", &tmp_music_file_info.album },
9080 { "year", &tmp_music_file_info.year },
9086 filename_music = (is_sound ? getCustomSoundFilename(basename) :
9087 getCustomMusicFilename(basename));
9089 if (filename_music == NULL)
9092 /* ---------- try to replace file extension ---------- */
9094 filename_prefix = getStringCopy(filename_music);
9095 if (strrchr(filename_prefix, '.') != NULL)
9096 *strrchr(filename_prefix, '.') = '\0';
9097 filename_info = getStringCat2(filename_prefix, ".txt");
9099 if (fileExists(filename_info))
9100 setup_file_hash = loadSetupFileHash(filename_info);
9102 free(filename_prefix);
9103 free(filename_info);
9105 if (setup_file_hash == NULL)
9107 /* ---------- try to add file extension ---------- */
9109 filename_prefix = getStringCopy(filename_music);
9110 filename_info = getStringCat2(filename_prefix, ".txt");
9112 if (fileExists(filename_info))
9113 setup_file_hash = loadSetupFileHash(filename_info);
9115 free(filename_prefix);
9116 free(filename_info);
9119 if (setup_file_hash == NULL)
9122 /* ---------- music file info found ---------- */
9124 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
9126 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
9128 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
9130 *token_to_value_ptr[i].value_ptr =
9131 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
9134 tmp_music_file_info.basename = getStringCopy(basename);
9135 tmp_music_file_info.music = music;
9136 tmp_music_file_info.is_sound = is_sound;
9138 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
9139 *new_music_file_info = tmp_music_file_info;
9141 return new_music_file_info;
9144 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
9146 return get_music_file_info_ext(basename, music, FALSE);
9149 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
9151 return get_music_file_info_ext(basename, sound, TRUE);
9154 static boolean music_info_listed_ext(struct MusicFileInfo *list,
9155 char *basename, boolean is_sound)
9157 for (; list != NULL; list = list->next)
9158 if (list->is_sound == is_sound && strEqual(list->basename, basename))
9164 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
9166 return music_info_listed_ext(list, basename, FALSE);
9169 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
9171 return music_info_listed_ext(list, basename, TRUE);
9174 void LoadMusicInfo()
9176 char *music_directory = getCustomMusicDirectory();
9177 int num_music = getMusicListSize();
9178 int num_music_noconf = 0;
9179 int num_sounds = getSoundListSize();
9181 DirectoryEntry *dir_entry;
9182 struct FileInfo *music, *sound;
9183 struct MusicFileInfo *next, **new;
9186 while (music_file_info != NULL)
9188 next = music_file_info->next;
9190 checked_free(music_file_info->basename);
9192 checked_free(music_file_info->title_header);
9193 checked_free(music_file_info->artist_header);
9194 checked_free(music_file_info->album_header);
9195 checked_free(music_file_info->year_header);
9197 checked_free(music_file_info->title);
9198 checked_free(music_file_info->artist);
9199 checked_free(music_file_info->album);
9200 checked_free(music_file_info->year);
9202 free(music_file_info);
9204 music_file_info = next;
9207 new = &music_file_info;
9209 for (i = 0; i < num_music; i++)
9211 music = getMusicListEntry(i);
9213 if (music->filename == NULL)
9216 if (strEqual(music->filename, UNDEFINED_FILENAME))
9219 /* a configured file may be not recognized as music */
9220 if (!FileIsMusic(music->filename))
9223 if (!music_info_listed(music_file_info, music->filename))
9225 *new = get_music_file_info(music->filename, i);
9228 new = &(*new)->next;
9232 if ((dir = openDirectory(music_directory)) == NULL)
9234 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
9238 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
9240 char *basename = dir_entry->basename;
9241 boolean music_already_used = FALSE;
9244 /* skip all music files that are configured in music config file */
9245 for (i = 0; i < num_music; i++)
9247 music = getMusicListEntry(i);
9249 if (music->filename == NULL)
9252 if (strEqual(basename, music->filename))
9254 music_already_used = TRUE;
9259 if (music_already_used)
9262 if (!FileIsMusic(dir_entry->filename))
9265 if (!music_info_listed(music_file_info, basename))
9267 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
9270 new = &(*new)->next;
9276 closeDirectory(dir);
9278 for (i = 0; i < num_sounds; i++)
9280 sound = getSoundListEntry(i);
9282 if (sound->filename == NULL)
9285 if (strEqual(sound->filename, UNDEFINED_FILENAME))
9288 /* a configured file may be not recognized as sound */
9289 if (!FileIsSound(sound->filename))
9292 if (!sound_info_listed(music_file_info, sound->filename))
9294 *new = get_sound_file_info(sound->filename, i);
9296 new = &(*new)->next;
9301 void add_helpanim_entry(int element, int action, int direction, int delay,
9302 int *num_list_entries)
9304 struct HelpAnimInfo *new_list_entry;
9305 (*num_list_entries)++;
9308 checked_realloc(helpanim_info,
9309 *num_list_entries * sizeof(struct HelpAnimInfo));
9310 new_list_entry = &helpanim_info[*num_list_entries - 1];
9312 new_list_entry->element = element;
9313 new_list_entry->action = action;
9314 new_list_entry->direction = direction;
9315 new_list_entry->delay = delay;
9318 void print_unknown_token(char *filename, char *token, int token_nr)
9322 Error(ERR_INFO_LINE, "-");
9323 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9324 Error(ERR_INFO, "- config file: '%s'", filename);
9327 Error(ERR_INFO, "- token: '%s'", token);
9330 void print_unknown_token_end(int token_nr)
9333 Error(ERR_INFO_LINE, "-");
9336 void LoadHelpAnimInfo()
9338 char *filename = getHelpAnimFilename();
9339 SetupFileList *setup_file_list = NULL, *list;
9340 SetupFileHash *element_hash, *action_hash, *direction_hash;
9341 int num_list_entries = 0;
9342 int num_unknown_tokens = 0;
9345 if (fileExists(filename))
9346 setup_file_list = loadSetupFileList(filename);
9348 if (setup_file_list == NULL)
9350 /* use reliable default values from static configuration */
9351 SetupFileList *insert_ptr;
9353 insert_ptr = setup_file_list =
9354 newSetupFileList(helpanim_config[0].token,
9355 helpanim_config[0].value);
9357 for (i = 1; helpanim_config[i].token; i++)
9358 insert_ptr = addListEntry(insert_ptr,
9359 helpanim_config[i].token,
9360 helpanim_config[i].value);
9363 element_hash = newSetupFileHash();
9364 action_hash = newSetupFileHash();
9365 direction_hash = newSetupFileHash();
9367 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
9368 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9370 for (i = 0; i < NUM_ACTIONS; i++)
9371 setHashEntry(action_hash, element_action_info[i].suffix,
9372 i_to_a(element_action_info[i].value));
9374 /* do not store direction index (bit) here, but direction value! */
9375 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
9376 setHashEntry(direction_hash, element_direction_info[i].suffix,
9377 i_to_a(1 << element_direction_info[i].value));
9379 for (list = setup_file_list; list != NULL; list = list->next)
9381 char *element_token, *action_token, *direction_token;
9382 char *element_value, *action_value, *direction_value;
9383 int delay = atoi(list->value);
9385 if (strEqual(list->token, "end"))
9387 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
9392 /* first try to break element into element/action/direction parts;
9393 if this does not work, also accept combined "element[.act][.dir]"
9394 elements (like "dynamite.active"), which are unique elements */
9396 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
9398 element_value = getHashEntry(element_hash, list->token);
9399 if (element_value != NULL) /* element found */
9400 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9404 /* no further suffixes found -- this is not an element */
9405 print_unknown_token(filename, list->token, num_unknown_tokens++);
9411 /* token has format "<prefix>.<something>" */
9413 action_token = strchr(list->token, '.'); /* suffix may be action ... */
9414 direction_token = action_token; /* ... or direction */
9416 element_token = getStringCopy(list->token);
9417 *strchr(element_token, '.') = '\0';
9419 element_value = getHashEntry(element_hash, element_token);
9421 if (element_value == NULL) /* this is no element */
9423 element_value = getHashEntry(element_hash, list->token);
9424 if (element_value != NULL) /* combined element found */
9425 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9428 print_unknown_token(filename, list->token, num_unknown_tokens++);
9430 free(element_token);
9435 action_value = getHashEntry(action_hash, action_token);
9437 if (action_value != NULL) /* action found */
9439 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
9442 free(element_token);
9447 direction_value = getHashEntry(direction_hash, direction_token);
9449 if (direction_value != NULL) /* direction found */
9451 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
9454 free(element_token);
9459 if (strchr(action_token + 1, '.') == NULL)
9461 /* no further suffixes found -- this is not an action nor direction */
9463 element_value = getHashEntry(element_hash, list->token);
9464 if (element_value != NULL) /* combined element found */
9465 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9468 print_unknown_token(filename, list->token, num_unknown_tokens++);
9470 free(element_token);
9475 /* token has format "<prefix>.<suffix>.<something>" */
9477 direction_token = strchr(action_token + 1, '.');
9479 action_token = getStringCopy(action_token);
9480 *strchr(action_token + 1, '.') = '\0';
9482 action_value = getHashEntry(action_hash, action_token);
9484 if (action_value == NULL) /* this is no action */
9486 element_value = getHashEntry(element_hash, list->token);
9487 if (element_value != NULL) /* combined element found */
9488 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9491 print_unknown_token(filename, list->token, num_unknown_tokens++);
9493 free(element_token);
9499 direction_value = getHashEntry(direction_hash, direction_token);
9501 if (direction_value != NULL) /* direction found */
9503 add_helpanim_entry(atoi(element_value), atoi(action_value),
9504 atoi(direction_value), delay, &num_list_entries);
9506 free(element_token);
9512 /* this is no direction */
9514 element_value = getHashEntry(element_hash, list->token);
9515 if (element_value != NULL) /* combined element found */
9516 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9519 print_unknown_token(filename, list->token, num_unknown_tokens++);
9521 free(element_token);
9525 print_unknown_token_end(num_unknown_tokens);
9527 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
9528 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
9530 freeSetupFileList(setup_file_list);
9531 freeSetupFileHash(element_hash);
9532 freeSetupFileHash(action_hash);
9533 freeSetupFileHash(direction_hash);
9536 for (i = 0; i < num_list_entries; i++)
9537 printf("::: '%s': %d, %d, %d => %d\n",
9538 EL_NAME(helpanim_info[i].element),
9539 helpanim_info[i].element,
9540 helpanim_info[i].action,
9541 helpanim_info[i].direction,
9542 helpanim_info[i].delay);
9546 void LoadHelpTextInfo()
9548 char *filename = getHelpTextFilename();
9551 if (helptext_info != NULL)
9553 freeSetupFileHash(helptext_info);
9554 helptext_info = NULL;
9557 if (fileExists(filename))
9558 helptext_info = loadSetupFileHash(filename);
9560 if (helptext_info == NULL)
9562 /* use reliable default values from static configuration */
9563 helptext_info = newSetupFileHash();
9565 for (i = 0; helptext_config[i].token; i++)
9566 setHashEntry(helptext_info,
9567 helptext_config[i].token,
9568 helptext_config[i].value);
9572 BEGIN_HASH_ITERATION(helptext_info, itr)
9574 printf("::: '%s' => '%s'\n",
9575 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
9577 END_HASH_ITERATION(hash, itr)
9582 /* ------------------------------------------------------------------------- */
9583 /* convert levels */
9584 /* ------------------------------------------------------------------------- */
9586 #define MAX_NUM_CONVERT_LEVELS 1000
9588 void ConvertLevels()
9590 static LevelDirTree *convert_leveldir = NULL;
9591 static int convert_level_nr = -1;
9592 static int num_levels_handled = 0;
9593 static int num_levels_converted = 0;
9594 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
9597 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9598 global.convert_leveldir);
9600 if (convert_leveldir == NULL)
9601 Error(ERR_EXIT, "no such level identifier: '%s'",
9602 global.convert_leveldir);
9604 leveldir_current = convert_leveldir;
9606 if (global.convert_level_nr != -1)
9608 convert_leveldir->first_level = global.convert_level_nr;
9609 convert_leveldir->last_level = global.convert_level_nr;
9612 convert_level_nr = convert_leveldir->first_level;
9614 printf_line("=", 79);
9615 printf("Converting levels\n");
9616 printf_line("-", 79);
9617 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
9618 printf("Level series name: '%s'\n", convert_leveldir->name);
9619 printf("Level series author: '%s'\n", convert_leveldir->author);
9620 printf("Number of levels: %d\n", convert_leveldir->levels);
9621 printf_line("=", 79);
9624 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
9625 levels_failed[i] = FALSE;
9627 while (convert_level_nr <= convert_leveldir->last_level)
9629 char *level_filename;
9632 level_nr = convert_level_nr++;
9634 printf("Level %03d: ", level_nr);
9636 LoadLevel(level_nr);
9637 if (level.no_valid_file)
9639 printf("(no level)\n");
9643 printf("converting level ... ");
9645 level_filename = getDefaultLevelFilename(level_nr);
9646 new_level = !fileExists(level_filename);
9650 SaveLevel(level_nr);
9652 num_levels_converted++;
9654 printf("converted.\n");
9658 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
9659 levels_failed[level_nr] = TRUE;
9661 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
9664 num_levels_handled++;
9668 printf_line("=", 79);
9669 printf("Number of levels handled: %d\n", num_levels_handled);
9670 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
9671 (num_levels_handled ?
9672 num_levels_converted * 100 / num_levels_handled : 0));
9673 printf_line("-", 79);
9674 printf("Summary (for automatic parsing by scripts):\n");
9675 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
9676 convert_leveldir->identifier, num_levels_converted,
9678 (num_levels_handled ?
9679 num_levels_converted * 100 / num_levels_handled : 0));
9681 if (num_levels_handled != num_levels_converted)
9683 printf(", FAILED:");
9684 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
9685 if (levels_failed[i])
9690 printf_line("=", 79);
9696 /* ------------------------------------------------------------------------- */
9697 /* create and save images for use in level sketches (raw BMP format) */
9698 /* ------------------------------------------------------------------------- */
9700 void CreateLevelSketchImages()
9702 #if defined(TARGET_SDL)
9707 InitElementPropertiesGfxElement();
9709 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
9710 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
9712 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9716 int element = getMappedElement(i);
9717 int graphic = el2edimg(element);
9723 sprintf(basename1, "%03d.bmp", i);
9724 sprintf(basename2, "%03ds.bmp", i);
9726 filename1 = getPath2(global.create_images_dir, basename1);
9727 filename2 = getPath2(global.create_images_dir, basename2);
9729 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
9730 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
9733 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
9734 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
9736 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
9737 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
9739 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
9740 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
9746 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
9749 FreeBitmap(bitmap1);
9750 FreeBitmap(bitmap2);
9755 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
9762 /* ------------------------------------------------------------------------- */
9763 /* create and save images for custom and group elements (raw BMP format) */
9764 /* ------------------------------------------------------------------------- */
9766 void CreateCustomElementImages()
9768 #if defined(TARGET_SDL)
9769 char *filename = "graphics.classic/RocksCE.bmp";
9772 int dummy_graphic = IMG_CUSTOM_99;
9774 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
9778 bitmap = CreateBitmap(TILEX * 16 * 2,
9779 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
9782 getFixedGraphicSource(dummy_graphic, 0, &src_bitmap, &src_x, &src_y);
9784 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9791 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
9792 TILEX * x, TILEY * y + yoffset_ce);
9794 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
9796 TILEX * x + TILEX * 16,
9797 TILEY * y + yoffset_ce);
9799 for (j = 2; j >= 0; j--)
9803 BlitBitmap(src_bitmap, bitmap,
9804 TILEX + c * 7, 0, 6, 10,
9805 TILEX * x + 6 + j * 7,
9806 TILEY * y + 11 + yoffset_ce);
9808 BlitBitmap(src_bitmap, bitmap,
9809 TILEX + c * 8, TILEY, 6, 10,
9810 TILEX * 16 + TILEX * x + 6 + j * 8,
9811 TILEY * y + 10 + yoffset_ce);
9817 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
9824 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
9825 TILEX * x, TILEY * y + yoffset_ge);
9827 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
9829 TILEX * x + TILEX * 16,
9830 TILEY * y + yoffset_ge);
9832 for (j = 1; j >= 0; j--)
9836 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
9837 TILEX * x + 6 + j * 10,
9838 TILEY * y + 11 + yoffset_ge);
9840 BlitBitmap(src_bitmap, bitmap,
9841 TILEX + c * 8, TILEY + 12, 6, 10,
9842 TILEX * 16 + TILEX * x + 10 + j * 8,
9843 TILEY * y + 10 + yoffset_ge);
9849 if (SDL_SaveBMP(bitmap->surface, filename) != 0)
9850 Error(ERR_EXIT, "cannot save CE graphics file '%s'", filename);