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_seconds = GetTapeLength();
3750 /* ------------------------------------------------------------------------- */
3751 /* functions for loading DC level */
3752 /* ------------------------------------------------------------------------- */
3754 #define DC_LEVEL_HEADER_SIZE 344
3756 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
3758 static int last_data_encoded;
3762 int diff_hi, diff_lo;
3763 int data_hi, data_lo;
3764 unsigned short data_decoded;
3768 last_data_encoded = 0;
3775 diff = data_encoded - last_data_encoded;
3776 diff_hi = diff & ~0xff;
3777 diff_lo = diff & 0xff;
3781 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
3782 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
3783 data_hi = data_hi & 0xff00;
3785 data_decoded = data_hi | data_lo;
3787 last_data_encoded = data_encoded;
3789 offset1 = (offset1 + 1) % 31;
3790 offset2 = offset2 & 0xff;
3792 return data_decoded;
3795 int getMappedElement_DC(int element)
3803 /* 0x0117 - 0x036e: (?) */
3806 /* 0x042d - 0x0684: (?) */
3822 element = EL_CRYSTAL;
3825 case 0x0e77: /* quicksand (boulder) */
3826 element = EL_QUICKSAND_FAST_FULL;
3829 case 0x0e99: /* slow quicksand (boulder) */
3830 element = EL_QUICKSAND_FULL;
3834 element = EL_EM_EXIT_OPEN;
3838 element = EL_EM_EXIT_CLOSED;
3842 element = EL_EM_STEEL_EXIT_OPEN;
3846 element = EL_EM_STEEL_EXIT_CLOSED;
3849 case 0x0f4f: /* dynamite (lit 1) */
3850 element = EL_EM_DYNAMITE_ACTIVE;
3853 case 0x0f57: /* dynamite (lit 2) */
3854 element = EL_EM_DYNAMITE_ACTIVE;
3857 case 0x0f5f: /* dynamite (lit 3) */
3858 element = EL_EM_DYNAMITE_ACTIVE;
3861 case 0x0f67: /* dynamite (lit 4) */
3862 element = EL_EM_DYNAMITE_ACTIVE;
3869 element = EL_AMOEBA_WET;
3873 element = EL_AMOEBA_DROP;
3877 element = EL_DC_MAGIC_WALL;
3881 element = EL_SPACESHIP_UP;
3885 element = EL_SPACESHIP_DOWN;
3889 element = EL_SPACESHIP_LEFT;
3893 element = EL_SPACESHIP_RIGHT;
3897 element = EL_BUG_UP;
3901 element = EL_BUG_DOWN;
3905 element = EL_BUG_LEFT;
3909 element = EL_BUG_RIGHT;
3913 element = EL_MOLE_UP;
3917 element = EL_MOLE_DOWN;
3921 element = EL_MOLE_LEFT;
3925 element = EL_MOLE_RIGHT;
3933 element = EL_YAMYAM;
3937 element = EL_SWITCHGATE_OPEN;
3941 element = EL_SWITCHGATE_CLOSED;
3945 element = EL_DC_SWITCHGATE_SWITCH_UP;
3949 element = EL_TIMEGATE_CLOSED;
3952 case 0x144c: /* conveyor belt switch (green) */
3953 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
3956 case 0x144f: /* conveyor belt switch (red) */
3957 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
3960 case 0x1452: /* conveyor belt switch (blue) */
3961 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
3965 element = EL_CONVEYOR_BELT_3_MIDDLE;
3969 element = EL_CONVEYOR_BELT_3_LEFT;
3973 element = EL_CONVEYOR_BELT_3_RIGHT;
3977 element = EL_CONVEYOR_BELT_1_MIDDLE;
3981 element = EL_CONVEYOR_BELT_1_LEFT;
3985 element = EL_CONVEYOR_BELT_1_RIGHT;
3989 element = EL_CONVEYOR_BELT_4_MIDDLE;
3993 element = EL_CONVEYOR_BELT_4_LEFT;
3997 element = EL_CONVEYOR_BELT_4_RIGHT;
4001 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4005 element = EL_EXPANDABLE_WALL_VERTICAL;
4009 element = EL_EXPANDABLE_WALL_ANY;
4012 case 0x14ce: /* growing steel wall (left/right) */
4013 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4016 case 0x14df: /* growing steel wall (up/down) */
4017 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4020 case 0x14e8: /* growing steel wall (up/down/left/right) */
4021 element = EL_EXPANDABLE_STEELWALL_ANY;
4025 element = EL_SHIELD_DEADLY;
4029 element = EL_EXTRA_TIME;
4037 element = EL_EMPTY_SPACE;
4040 case 0x1578: /* quicksand (empty) */
4041 element = EL_QUICKSAND_FAST_EMPTY;
4044 case 0x1579: /* slow quicksand (empty) */
4045 element = EL_QUICKSAND_EMPTY;
4048 /* 0x157c - 0x158b: */
4051 /* 0x1590 - 0x159f: */
4052 /* EL_DC_LANDMINE */
4055 element = EL_EM_DYNAMITE;
4058 case 0x15a1: /* key (red) */
4059 element = EL_EM_KEY_1;
4062 case 0x15a2: /* key (yellow) */
4063 element = EL_EM_KEY_2;
4066 case 0x15a3: /* key (blue) */
4067 element = EL_EM_KEY_4;
4070 case 0x15a4: /* key (green) */
4071 element = EL_EM_KEY_3;
4074 case 0x15a5: /* key (white) */
4075 element = EL_DC_KEY_WHITE;
4079 element = EL_WALL_SLIPPERY;
4086 case 0x15a8: /* wall (not round) */
4090 case 0x15a9: /* (blue) */
4091 element = EL_CHAR_A;
4094 case 0x15aa: /* (blue) */
4095 element = EL_CHAR_B;
4098 case 0x15ab: /* (blue) */
4099 element = EL_CHAR_C;
4102 case 0x15ac: /* (blue) */
4103 element = EL_CHAR_D;
4106 case 0x15ad: /* (blue) */
4107 element = EL_CHAR_E;
4110 case 0x15ae: /* (blue) */
4111 element = EL_CHAR_F;
4114 case 0x15af: /* (blue) */
4115 element = EL_CHAR_G;
4118 case 0x15b0: /* (blue) */
4119 element = EL_CHAR_H;
4122 case 0x15b1: /* (blue) */
4123 element = EL_CHAR_I;
4126 case 0x15b2: /* (blue) */
4127 element = EL_CHAR_J;
4130 case 0x15b3: /* (blue) */
4131 element = EL_CHAR_K;
4134 case 0x15b4: /* (blue) */
4135 element = EL_CHAR_L;
4138 case 0x15b5: /* (blue) */
4139 element = EL_CHAR_M;
4142 case 0x15b6: /* (blue) */
4143 element = EL_CHAR_N;
4146 case 0x15b7: /* (blue) */
4147 element = EL_CHAR_O;
4150 case 0x15b8: /* (blue) */
4151 element = EL_CHAR_P;
4154 case 0x15b9: /* (blue) */
4155 element = EL_CHAR_Q;
4158 case 0x15ba: /* (blue) */
4159 element = EL_CHAR_R;
4162 case 0x15bb: /* (blue) */
4163 element = EL_CHAR_S;
4166 case 0x15bc: /* (blue) */
4167 element = EL_CHAR_T;
4170 case 0x15bd: /* (blue) */
4171 element = EL_CHAR_U;
4174 case 0x15be: /* (blue) */
4175 element = EL_CHAR_V;
4178 case 0x15bf: /* (blue) */
4179 element = EL_CHAR_W;
4182 case 0x15c0: /* (blue) */
4183 element = EL_CHAR_X;
4186 case 0x15c1: /* (blue) */
4187 element = EL_CHAR_Y;
4190 case 0x15c2: /* (blue) */
4191 element = EL_CHAR_Z;
4194 case 0x15c3: /* (blue) */
4195 element = EL_CHAR_AUMLAUT;
4198 case 0x15c4: /* (blue) */
4199 element = EL_CHAR_OUMLAUT;
4202 case 0x15c5: /* (blue) */
4203 element = EL_CHAR_UUMLAUT;
4206 case 0x15c6: /* (blue) */
4207 element = EL_CHAR_0;
4210 case 0x15c7: /* (blue) */
4211 element = EL_CHAR_1;
4214 case 0x15c8: /* (blue) */
4215 element = EL_CHAR_2;
4218 case 0x15c9: /* (blue) */
4219 element = EL_CHAR_3;
4222 case 0x15ca: /* (blue) */
4223 element = EL_CHAR_4;
4226 case 0x15cb: /* (blue) */
4227 element = EL_CHAR_5;
4230 case 0x15cc: /* (blue) */
4231 element = EL_CHAR_6;
4234 case 0x15cd: /* (blue) */
4235 element = EL_CHAR_7;
4238 case 0x15ce: /* (blue) */
4239 element = EL_CHAR_8;
4242 case 0x15cf: /* (blue) */
4243 element = EL_CHAR_9;
4246 case 0x15d0: /* (blue) */
4247 element = EL_CHAR_PERIOD;
4250 case 0x15d1: /* (blue) */
4251 element = EL_CHAR_EXCLAM;
4254 case 0x15d2: /* (blue) */
4255 element = EL_CHAR_COLON;
4258 case 0x15d3: /* (blue) */
4259 element = EL_CHAR_LESS;
4262 case 0x15d4: /* (blue) */
4263 element = EL_CHAR_GREATER;
4266 case 0x15d5: /* (blue) */
4267 element = EL_CHAR_QUESTION;
4270 case 0x15d6: /* (blue) */
4271 element = EL_CHAR_COPYRIGHT;
4274 case 0x15d7: /* (blue) */
4275 element = EL_CHAR_UP;
4278 case 0x15d8: /* (blue) */
4279 element = EL_CHAR_DOWN;
4282 case 0x15d9: /* (blue) */
4283 element = EL_CHAR_BUTTON;
4286 case 0x15da: /* (blue) */
4287 element = EL_CHAR_PLUS;
4290 case 0x15db: /* (blue) */
4291 element = EL_CHAR_MINUS;
4294 case 0x15dc: /* (blue) */
4295 element = EL_CHAR_APOSTROPHE;
4298 case 0x15dd: /* (blue) */
4299 element = EL_CHAR_PARENLEFT;
4302 case 0x15de: /* (blue) */
4303 element = EL_CHAR_PARENRIGHT;
4306 case 0x15df: /* (green) */
4307 element = EL_CHAR_A;
4310 case 0x15e0: /* (green) */
4311 element = EL_CHAR_B;
4314 case 0x15e1: /* (green) */
4315 element = EL_CHAR_C;
4318 case 0x15e2: /* (green) */
4319 element = EL_CHAR_D;
4322 case 0x15e3: /* (green) */
4323 element = EL_CHAR_E;
4326 case 0x15e4: /* (green) */
4327 element = EL_CHAR_F;
4330 case 0x15e5: /* (green) */
4331 element = EL_CHAR_G;
4334 case 0x15e6: /* (green) */
4335 element = EL_CHAR_H;
4338 case 0x15e7: /* (green) */
4339 element = EL_CHAR_I;
4342 case 0x15e8: /* (green) */
4343 element = EL_CHAR_J;
4346 case 0x15e9: /* (green) */
4347 element = EL_CHAR_K;
4350 case 0x15ea: /* (green) */
4351 element = EL_CHAR_L;
4354 case 0x15eb: /* (green) */
4355 element = EL_CHAR_M;
4358 case 0x15ec: /* (green) */
4359 element = EL_CHAR_N;
4362 case 0x15ed: /* (green) */
4363 element = EL_CHAR_O;
4366 case 0x15ee: /* (green) */
4367 element = EL_CHAR_P;
4370 case 0x15ef: /* (green) */
4371 element = EL_CHAR_Q;
4374 case 0x15f0: /* (green) */
4375 element = EL_CHAR_R;
4378 case 0x15f1: /* (green) */
4379 element = EL_CHAR_S;
4382 case 0x15f2: /* (green) */
4383 element = EL_CHAR_T;
4386 case 0x15f3: /* (green) */
4387 element = EL_CHAR_U;
4390 case 0x15f4: /* (green) */
4391 element = EL_CHAR_V;
4394 case 0x15f5: /* (green) */
4395 element = EL_CHAR_W;
4398 case 0x15f6: /* (green) */
4399 element = EL_CHAR_X;
4402 case 0x15f7: /* (green) */
4403 element = EL_CHAR_Y;
4406 case 0x15f8: /* (green) */
4407 element = EL_CHAR_Z;
4410 case 0x15f9: /* (green) */
4411 element = EL_CHAR_AUMLAUT;
4414 case 0x15fa: /* (green) */
4415 element = EL_CHAR_OUMLAUT;
4418 case 0x15fb: /* (green) */
4419 element = EL_CHAR_UUMLAUT;
4422 case 0x15fc: /* (green) */
4423 element = EL_CHAR_0;
4426 case 0x15fd: /* (green) */
4427 element = EL_CHAR_1;
4430 case 0x15fe: /* (green) */
4431 element = EL_CHAR_2;
4434 case 0x15ff: /* (green) */
4435 element = EL_CHAR_3;
4438 case 0x1600: /* (green) */
4439 element = EL_CHAR_4;
4442 case 0x1601: /* (green) */
4443 element = EL_CHAR_5;
4446 case 0x1602: /* (green) */
4447 element = EL_CHAR_6;
4450 case 0x1603: /* (green) */
4451 element = EL_CHAR_7;
4454 case 0x1604: /* (green) */
4455 element = EL_CHAR_8;
4458 case 0x1605: /* (green) */
4459 element = EL_CHAR_9;
4462 case 0x1606: /* (green) */
4463 element = EL_CHAR_PERIOD;
4466 case 0x1607: /* (green) */
4467 element = EL_CHAR_EXCLAM;
4470 case 0x1608: /* (green) */
4471 element = EL_CHAR_COLON;
4474 case 0x1609: /* (green) */
4475 element = EL_CHAR_LESS;
4478 case 0x160a: /* (green) */
4479 element = EL_CHAR_GREATER;
4482 case 0x160b: /* (green) */
4483 element = EL_CHAR_QUESTION;
4486 case 0x160c: /* (green) */
4487 element = EL_CHAR_COPYRIGHT;
4490 case 0x160d: /* (green) */
4491 element = EL_CHAR_UP;
4494 case 0x160e: /* (green) */
4495 element = EL_CHAR_DOWN;
4498 case 0x160f: /* (green) */
4499 element = EL_CHAR_BUTTON;
4502 case 0x1610: /* (green) */
4503 element = EL_CHAR_PLUS;
4506 case 0x1611: /* (green) */
4507 element = EL_CHAR_MINUS;
4510 case 0x1612: /* (green) */
4511 element = EL_CHAR_APOSTROPHE;
4514 case 0x1613: /* (green) */
4515 element = EL_CHAR_PARENLEFT;
4518 case 0x1614: /* (green) */
4519 element = EL_CHAR_PARENRIGHT;
4522 case 0x1615: /* (blue steel) */
4523 element = EL_STEEL_CHAR_A;
4526 case 0x1616: /* (blue steel) */
4527 element = EL_STEEL_CHAR_B;
4530 case 0x1617: /* (blue steel) */
4531 element = EL_STEEL_CHAR_C;
4534 case 0x1618: /* (blue steel) */
4535 element = EL_STEEL_CHAR_D;
4538 case 0x1619: /* (blue steel) */
4539 element = EL_STEEL_CHAR_E;
4542 case 0x161a: /* (blue steel) */
4543 element = EL_STEEL_CHAR_F;
4546 case 0x161b: /* (blue steel) */
4547 element = EL_STEEL_CHAR_G;
4550 case 0x161c: /* (blue steel) */
4551 element = EL_STEEL_CHAR_H;
4554 case 0x161d: /* (blue steel) */
4555 element = EL_STEEL_CHAR_I;
4558 case 0x161e: /* (blue steel) */
4559 element = EL_STEEL_CHAR_J;
4562 case 0x161f: /* (blue steel) */
4563 element = EL_STEEL_CHAR_K;
4566 case 0x1620: /* (blue steel) */
4567 element = EL_STEEL_CHAR_L;
4570 case 0x1621: /* (blue steel) */
4571 element = EL_STEEL_CHAR_M;
4574 case 0x1622: /* (blue steel) */
4575 element = EL_STEEL_CHAR_N;
4578 case 0x1623: /* (blue steel) */
4579 element = EL_STEEL_CHAR_O;
4582 case 0x1624: /* (blue steel) */
4583 element = EL_STEEL_CHAR_P;
4586 case 0x1625: /* (blue steel) */
4587 element = EL_STEEL_CHAR_Q;
4590 case 0x1626: /* (blue steel) */
4591 element = EL_STEEL_CHAR_R;
4594 case 0x1627: /* (blue steel) */
4595 element = EL_STEEL_CHAR_S;
4598 case 0x1628: /* (blue steel) */
4599 element = EL_STEEL_CHAR_T;
4602 case 0x1629: /* (blue steel) */
4603 element = EL_STEEL_CHAR_U;
4606 case 0x162a: /* (blue steel) */
4607 element = EL_STEEL_CHAR_V;
4610 case 0x162b: /* (blue steel) */
4611 element = EL_STEEL_CHAR_W;
4614 case 0x162c: /* (blue steel) */
4615 element = EL_STEEL_CHAR_X;
4618 case 0x162d: /* (blue steel) */
4619 element = EL_STEEL_CHAR_Y;
4622 case 0x162e: /* (blue steel) */
4623 element = EL_STEEL_CHAR_Z;
4626 case 0x162f: /* (blue steel) */
4627 element = EL_STEEL_CHAR_AUMLAUT;
4630 case 0x1630: /* (blue steel) */
4631 element = EL_STEEL_CHAR_OUMLAUT;
4634 case 0x1631: /* (blue steel) */
4635 element = EL_STEEL_CHAR_UUMLAUT;
4638 case 0x1632: /* (blue steel) */
4639 element = EL_STEEL_CHAR_0;
4642 case 0x1633: /* (blue steel) */
4643 element = EL_STEEL_CHAR_1;
4646 case 0x1634: /* (blue steel) */
4647 element = EL_STEEL_CHAR_2;
4650 case 0x1635: /* (blue steel) */
4651 element = EL_STEEL_CHAR_3;
4654 case 0x1636: /* (blue steel) */
4655 element = EL_STEEL_CHAR_4;
4658 case 0x1637: /* (blue steel) */
4659 element = EL_STEEL_CHAR_5;
4662 case 0x1638: /* (blue steel) */
4663 element = EL_STEEL_CHAR_6;
4666 case 0x1639: /* (blue steel) */
4667 element = EL_STEEL_CHAR_7;
4670 case 0x163a: /* (blue steel) */
4671 element = EL_STEEL_CHAR_8;
4674 case 0x163b: /* (blue steel) */
4675 element = EL_STEEL_CHAR_9;
4678 case 0x163c: /* (blue steel) */
4679 element = EL_STEEL_CHAR_PERIOD;
4682 case 0x163d: /* (blue steel) */
4683 element = EL_STEEL_CHAR_EXCLAM;
4686 case 0x163e: /* (blue steel) */
4687 element = EL_STEEL_CHAR_COLON;
4690 case 0x163f: /* (blue steel) */
4691 element = EL_STEEL_CHAR_LESS;
4694 case 0x1640: /* (blue steel) */
4695 element = EL_STEEL_CHAR_GREATER;
4698 case 0x1641: /* (blue steel) */
4699 element = EL_STEEL_CHAR_QUESTION;
4702 case 0x1642: /* (blue steel) */
4703 element = EL_STEEL_CHAR_COPYRIGHT;
4706 case 0x1643: /* (blue steel) */
4707 element = EL_STEEL_CHAR_UP;
4710 case 0x1644: /* (blue steel) */
4711 element = EL_STEEL_CHAR_DOWN;
4714 case 0x1645: /* (blue steel) */
4715 element = EL_STEEL_CHAR_BUTTON;
4718 case 0x1646: /* (blue steel) */
4719 element = EL_STEEL_CHAR_PLUS;
4722 case 0x1647: /* (blue steel) */
4723 element = EL_STEEL_CHAR_MINUS;
4726 case 0x1648: /* (blue steel) */
4727 element = EL_STEEL_CHAR_APOSTROPHE;
4730 case 0x1649: /* (blue steel) */
4731 element = EL_STEEL_CHAR_PARENLEFT;
4734 case 0x164a: /* (blue steel) */
4735 element = EL_STEEL_CHAR_PARENRIGHT;
4738 case 0x164b: /* (green steel) */
4739 element = EL_STEEL_CHAR_A;
4742 case 0x164c: /* (green steel) */
4743 element = EL_STEEL_CHAR_B;
4746 case 0x164d: /* (green steel) */
4747 element = EL_STEEL_CHAR_C;
4750 case 0x164e: /* (green steel) */
4751 element = EL_STEEL_CHAR_D;
4754 case 0x164f: /* (green steel) */
4755 element = EL_STEEL_CHAR_E;
4758 case 0x1650: /* (green steel) */
4759 element = EL_STEEL_CHAR_F;
4762 case 0x1651: /* (green steel) */
4763 element = EL_STEEL_CHAR_G;
4766 case 0x1652: /* (green steel) */
4767 element = EL_STEEL_CHAR_H;
4770 case 0x1653: /* (green steel) */
4771 element = EL_STEEL_CHAR_I;
4774 case 0x1654: /* (green steel) */
4775 element = EL_STEEL_CHAR_J;
4778 case 0x1655: /* (green steel) */
4779 element = EL_STEEL_CHAR_K;
4782 case 0x1656: /* (green steel) */
4783 element = EL_STEEL_CHAR_L;
4786 case 0x1657: /* (green steel) */
4787 element = EL_STEEL_CHAR_M;
4790 case 0x1658: /* (green steel) */
4791 element = EL_STEEL_CHAR_N;
4794 case 0x1659: /* (green steel) */
4795 element = EL_STEEL_CHAR_O;
4798 case 0x165a: /* (green steel) */
4799 element = EL_STEEL_CHAR_P;
4802 case 0x165b: /* (green steel) */
4803 element = EL_STEEL_CHAR_Q;
4806 case 0x165c: /* (green steel) */
4807 element = EL_STEEL_CHAR_R;
4810 case 0x165d: /* (green steel) */
4811 element = EL_STEEL_CHAR_S;
4814 case 0x165e: /* (green steel) */
4815 element = EL_STEEL_CHAR_T;
4818 case 0x165f: /* (green steel) */
4819 element = EL_STEEL_CHAR_U;
4822 case 0x1660: /* (green steel) */
4823 element = EL_STEEL_CHAR_V;
4826 case 0x1661: /* (green steel) */
4827 element = EL_STEEL_CHAR_W;
4830 case 0x1662: /* (green steel) */
4831 element = EL_STEEL_CHAR_X;
4834 case 0x1663: /* (green steel) */
4835 element = EL_STEEL_CHAR_Y;
4838 case 0x1664: /* (green steel) */
4839 element = EL_STEEL_CHAR_Z;
4842 case 0x1665: /* (green steel) */
4843 element = EL_STEEL_CHAR_AUMLAUT;
4846 case 0x1666: /* (green steel) */
4847 element = EL_STEEL_CHAR_OUMLAUT;
4850 case 0x1667: /* (green steel) */
4851 element = EL_STEEL_CHAR_UUMLAUT;
4854 case 0x1668: /* (green steel) */
4855 element = EL_STEEL_CHAR_0;
4858 case 0x1669: /* (green steel) */
4859 element = EL_STEEL_CHAR_1;
4862 case 0x166a: /* (green steel) */
4863 element = EL_STEEL_CHAR_2;
4866 case 0x166b: /* (green steel) */
4867 element = EL_STEEL_CHAR_3;
4870 case 0x166c: /* (green steel) */
4871 element = EL_STEEL_CHAR_4;
4874 case 0x166d: /* (green steel) */
4875 element = EL_STEEL_CHAR_5;
4878 case 0x166e: /* (green steel) */
4879 element = EL_STEEL_CHAR_6;
4882 case 0x166f: /* (green steel) */
4883 element = EL_STEEL_CHAR_7;
4886 case 0x1670: /* (green steel) */
4887 element = EL_STEEL_CHAR_8;
4890 case 0x1671: /* (green steel) */
4891 element = EL_STEEL_CHAR_9;
4894 case 0x1672: /* (green steel) */
4895 element = EL_STEEL_CHAR_PERIOD;
4898 case 0x1673: /* (green steel) */
4899 element = EL_STEEL_CHAR_EXCLAM;
4902 case 0x1674: /* (green steel) */
4903 element = EL_STEEL_CHAR_COLON;
4906 case 0x1675: /* (green steel) */
4907 element = EL_STEEL_CHAR_LESS;
4910 case 0x1676: /* (green steel) */
4911 element = EL_STEEL_CHAR_GREATER;
4914 case 0x1677: /* (green steel) */
4915 element = EL_STEEL_CHAR_QUESTION;
4918 case 0x1678: /* (green steel) */
4919 element = EL_STEEL_CHAR_COPYRIGHT;
4922 case 0x1679: /* (green steel) */
4923 element = EL_STEEL_CHAR_UP;
4926 case 0x167a: /* (green steel) */
4927 element = EL_STEEL_CHAR_DOWN;
4930 case 0x167b: /* (green steel) */
4931 element = EL_STEEL_CHAR_BUTTON;
4934 case 0x167c: /* (green steel) */
4935 element = EL_STEEL_CHAR_PLUS;
4938 case 0x167d: /* (green steel) */
4939 element = EL_STEEL_CHAR_MINUS;
4942 case 0x167e: /* (green steel) */
4943 element = EL_STEEL_CHAR_APOSTROPHE;
4946 case 0x167f: /* (green steel) */
4947 element = EL_STEEL_CHAR_PARENLEFT;
4950 case 0x1680: /* (green steel) */
4951 element = EL_STEEL_CHAR_PARENRIGHT;
4954 case 0x1681: /* gate (red) */
4955 element = EL_EM_GATE_1;
4958 case 0x1682: /* secret gate (red) */
4959 element = EL_GATE_1_GRAY;
4962 case 0x1683: /* gate (yellow) */
4963 element = EL_EM_GATE_2;
4966 case 0x1684: /* secret gate (yellow) */
4967 element = EL_GATE_2_GRAY;
4970 case 0x1685: /* gate (blue) */
4971 element = EL_EM_GATE_4;
4974 case 0x1686: /* secret gate (blue) */
4975 element = EL_GATE_4_GRAY;
4978 case 0x1687: /* gate (green) */
4979 element = EL_EM_GATE_3;
4982 case 0x1688: /* secret gate (green) */
4983 element = EL_GATE_3_GRAY;
4986 case 0x1689: /* gate (white) */
4987 element = EL_DC_GATE_WHITE;
4990 case 0x168a: /* secret gate (white) */
4991 element = EL_DC_GATE_WHITE_GRAY;
4994 case 0x168b: /* secret gate (no key) */
4995 element = EL_DC_GATE_FAKE_GRAY;
4999 element = EL_ROBOT_WHEEL;
5003 element = EL_DC_TIMEGATE_SWITCH;
5007 element = EL_ACID_POOL_BOTTOM;
5011 element = EL_ACID_POOL_TOPLEFT;
5015 element = EL_ACID_POOL_TOPRIGHT;
5019 element = EL_ACID_POOL_BOTTOMLEFT;
5023 element = EL_ACID_POOL_BOTTOMRIGHT;
5027 element = EL_STEELWALL;
5031 element = EL_STEELWALL_SLIPPERY;
5034 case 0x1695: /* steel wall (not round) */
5035 element = EL_STEELWALL;
5038 case 0x1696: /* steel wall (left) */
5039 element = EL_DC_STEELWALL_1_LEFT;
5042 case 0x1697: /* steel wall (bottom) */
5043 element = EL_DC_STEELWALL_1_BOTTOM;
5046 case 0x1698: /* steel wall (right) */
5047 element = EL_DC_STEELWALL_1_RIGHT;
5050 case 0x1699: /* steel wall (top) */
5051 element = EL_DC_STEELWALL_1_TOP;
5054 case 0x169a: /* steel wall (left/bottom) */
5055 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5058 case 0x169b: /* steel wall (right/bottom) */
5059 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5062 case 0x169c: /* steel wall (right/top) */
5063 element = EL_DC_STEELWALL_1_TOPRIGHT;
5066 case 0x169d: /* steel wall (left/top) */
5067 element = EL_DC_STEELWALL_1_TOPLEFT;
5070 case 0x169e: /* steel wall (right/bottom small) */
5071 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5074 case 0x169f: /* steel wall (left/bottom small) */
5075 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5078 case 0x16a0: /* steel wall (right/top small) */
5079 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5082 case 0x16a1: /* steel wall (left/top small) */
5083 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5086 case 0x16a2: /* steel wall (left/right) */
5087 element = EL_DC_STEELWALL_1_VERTICAL;
5090 case 0x16a3: /* steel wall (top/bottom) */
5091 element = EL_DC_STEELWALL_1_HORIZONTAL;
5094 case 0x16a4: /* steel wall 2 (left end) */
5095 element = EL_DC_STEELWALL_2_LEFT;
5098 case 0x16a5: /* steel wall 2 (right end) */
5099 element = EL_DC_STEELWALL_2_RIGHT;
5102 case 0x16a6: /* steel wall 2 (top end) */
5103 element = EL_DC_STEELWALL_2_TOP;
5106 case 0x16a7: /* steel wall 2 (bottom end) */
5107 element = EL_DC_STEELWALL_2_BOTTOM;
5110 case 0x16a8: /* steel wall 2 (left/right) */
5111 element = EL_DC_STEELWALL_2_HORIZONTAL;
5114 case 0x16a9: /* steel wall 2 (up/down) */
5115 element = EL_DC_STEELWALL_2_VERTICAL;
5118 case 0x16aa: /* steel wall 2 (mid) */
5119 element = EL_DC_STEELWALL_2_MIDDLE;
5123 element = EL_SIGN_EXCLAMATION;
5127 element = EL_SIGN_RADIOACTIVITY;
5131 element = EL_SIGN_STOP;
5135 element = EL_SIGN_WHEELCHAIR;
5139 element = EL_SIGN_PARKING;
5143 element = EL_SIGN_NO_ENTRY;
5147 element = EL_SIGN_HEART;
5151 element = EL_SIGN_GIVE_WAY;
5155 element = EL_SIGN_ENTRY_FORBIDDEN;
5159 element = EL_SIGN_EMERGENCY_EXIT;
5163 element = EL_SIGN_YIN_YANG;
5167 element = EL_WALL_EMERALD;
5171 element = EL_WALL_DIAMOND;
5175 element = EL_WALL_PEARL;
5179 element = EL_WALL_CRYSTAL;
5183 element = EL_INVISIBLE_WALL;
5187 element = EL_INVISIBLE_STEELWALL;
5190 /* 0x16bc - 0x16cb: */
5191 /* EL_INVISIBLE_SAND */
5194 element = EL_LIGHT_SWITCH;
5198 element = EL_ENVELOPE_1;
5202 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5203 element = EL_DIAMOND;
5204 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5205 element = EL_EMERALD;
5206 else if (element >= 0x157c && element <= 0x158b)
5208 else if (element >= 0x1590 && element <= 0x159f)
5209 element = EL_DC_LANDMINE;
5210 else if (element >= 0x16bc && element <= 0x16cb)
5211 element = EL_INVISIBLE_SAND;
5214 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5215 element = EL_UNKNOWN;
5220 return getMappedElement(element);
5223 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5226 byte header[DC_LEVEL_HEADER_SIZE];
5228 int envelope_header_pos = 62;
5229 int envelope_content_pos = 94;
5230 int level_name_pos = 251;
5231 int level_author_pos = 292;
5232 int envelope_header_len;
5233 int envelope_content_len;
5235 int level_author_len;
5237 int num_yamyam_contents;
5240 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5242 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5244 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5246 header[i * 2 + 0] = header_word >> 8;
5247 header[i * 2 + 1] = header_word & 0xff;
5250 /* read some values from level header to check level decoding integrity */
5251 fieldx = header[6] | (header[7] << 8);
5252 fieldy = header[8] | (header[9] << 8);
5253 num_yamyam_contents = header[60] | (header[61] << 8);
5255 /* do some simple sanity checks to ensure that level was correctly decoded */
5256 if (fieldx < 1 || fieldx > 256 ||
5257 fieldy < 1 || fieldy > 256 ||
5258 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5260 level->no_valid_file = TRUE;
5262 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5267 /* maximum envelope header size is 31 bytes */
5268 envelope_header_len = header[envelope_header_pos];
5269 /* maximum envelope content size is 110 (156?) bytes */
5270 envelope_content_len = header[envelope_content_pos];
5272 /* maximum level title size is 40 bytes */
5273 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5274 /* maximum level author size is 30 (51?) bytes */
5275 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5279 for (i = 0; i < envelope_header_len; i++)
5280 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5281 level->envelope[0].text[envelope_size++] =
5282 header[envelope_header_pos + 1 + i];
5284 if (envelope_header_len > 0 && envelope_content_len > 0)
5286 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5287 level->envelope[0].text[envelope_size++] = '\n';
5288 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5289 level->envelope[0].text[envelope_size++] = '\n';
5292 for (i = 0; i < envelope_content_len; i++)
5293 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5294 level->envelope[0].text[envelope_size++] =
5295 header[envelope_content_pos + 1 + i];
5297 level->envelope[0].text[envelope_size] = '\0';
5299 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5300 level->envelope[0].ysize = 10;
5301 level->envelope[0].autowrap = TRUE;
5302 level->envelope[0].centered = TRUE;
5304 for (i = 0; i < level_name_len; i++)
5305 level->name[i] = header[level_name_pos + 1 + i];
5306 level->name[level_name_len] = '\0';
5308 for (i = 0; i < level_author_len; i++)
5309 level->author[i] = header[level_author_pos + 1 + i];
5310 level->author[level_author_len] = '\0';
5312 num_yamyam_contents = header[60] | (header[61] << 8);
5313 level->num_yamyam_contents =
5314 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5316 for (i = 0; i < num_yamyam_contents; i++)
5318 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5320 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5321 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5323 if (i < MAX_ELEMENT_CONTENTS)
5324 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5328 fieldx = header[6] | (header[7] << 8);
5329 fieldy = header[8] | (header[9] << 8);
5330 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5331 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5333 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5335 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5336 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5338 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5339 level->field[x][y] = getMappedElement_DC(element_dc);
5342 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5343 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5344 level->field[x][y] = EL_PLAYER_1;
5346 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5347 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5348 level->field[x][y] = EL_PLAYER_2;
5350 level->gems_needed = header[18] | (header[19] << 8);
5352 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5353 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5354 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5355 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5356 level->score[SC_NUT] = header[28] | (header[29] << 8);
5357 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5358 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5359 level->score[SC_BUG] = header[34] | (header[35] << 8);
5360 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5361 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5362 level->score[SC_KEY] = header[40] | (header[41] << 8);
5363 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5365 level->time = header[44] | (header[45] << 8);
5367 level->amoeba_speed = header[46] | (header[47] << 8);
5368 level->time_light = header[48] | (header[49] << 8);
5369 level->time_timegate = header[50] | (header[51] << 8);
5370 level->time_wheel = header[52] | (header[53] << 8);
5371 level->time_magic_wall = header[54] | (header[55] << 8);
5372 level->extra_time = header[56] | (header[57] << 8);
5373 level->shield_normal_time = header[58] | (header[59] << 8);
5375 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5376 can slip down from flat walls, like normal walls and steel walls */
5377 level->em_slippery_gems = TRUE;
5380 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5381 struct LevelFileInfo *level_file_info,
5382 boolean level_info_only)
5384 char *filename = level_file_info->filename;
5386 int num_magic_bytes = 8;
5387 char magic_bytes[num_magic_bytes + 1];
5388 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5390 if (!(file = openFile(filename, MODE_READ)))
5392 level->no_valid_file = TRUE;
5394 if (!level_info_only)
5395 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5400 // fseek(file, 0x0000, SEEK_SET);
5402 if (level_file_info->packed)
5404 /* read "magic bytes" from start of file */
5405 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5406 magic_bytes[0] = '\0';
5408 /* check "magic bytes" for correct file format */
5409 if (!strPrefix(magic_bytes, "DC2"))
5411 level->no_valid_file = TRUE;
5413 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5419 if (strPrefix(magic_bytes, "DC2Win95") ||
5420 strPrefix(magic_bytes, "DC2Win98"))
5422 int position_first_level = 0x00fa;
5423 int extra_bytes = 4;
5426 /* advance file stream to first level inside the level package */
5427 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5429 /* each block of level data is followed by block of non-level data */
5430 num_levels_to_skip *= 2;
5432 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5433 while (num_levels_to_skip >= 0)
5435 /* advance file stream to next level inside the level package */
5436 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5438 level->no_valid_file = TRUE;
5440 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5446 /* skip apparently unused extra bytes following each level */
5447 ReadUnusedBytesFromFile(file, extra_bytes);
5449 /* read size of next level in level package */
5450 skip_bytes = getFile32BitLE(file);
5452 num_levels_to_skip--;
5457 level->no_valid_file = TRUE;
5459 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5466 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5472 /* ------------------------------------------------------------------------- */
5473 /* functions for loading SB level */
5474 /* ------------------------------------------------------------------------- */
5476 int getMappedElement_SB(int element_ascii, boolean use_ces)
5484 sb_element_mapping[] =
5486 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5487 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5488 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5489 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5490 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5491 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5492 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5493 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5500 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5501 if (element_ascii == sb_element_mapping[i].ascii)
5502 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5504 return EL_UNDEFINED;
5507 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5508 struct LevelFileInfo *level_file_info,
5509 boolean level_info_only)
5511 char *filename = level_file_info->filename;
5512 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5513 char last_comment[MAX_LINE_LEN];
5514 char level_name[MAX_LINE_LEN];
5517 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5518 boolean read_continued_line = FALSE;
5519 boolean reading_playfield = FALSE;
5520 boolean got_valid_playfield_line = FALSE;
5521 boolean invalid_playfield_char = FALSE;
5522 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5523 int file_level_nr = 0;
5525 int x = 0, y = 0; /* initialized to make compilers happy */
5527 last_comment[0] = '\0';
5528 level_name[0] = '\0';
5530 if (!(file = openFile(filename, MODE_READ)))
5532 level->no_valid_file = TRUE;
5534 if (!level_info_only)
5535 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5540 while (!checkEndOfFile(file))
5542 /* level successfully read, but next level may follow here */
5543 if (!got_valid_playfield_line && reading_playfield)
5545 /* read playfield from single level file -- skip remaining file */
5546 if (!level_file_info->packed)
5549 if (file_level_nr >= num_levels_to_skip)
5554 last_comment[0] = '\0';
5555 level_name[0] = '\0';
5557 reading_playfield = FALSE;
5560 got_valid_playfield_line = FALSE;
5562 /* read next line of input file */
5563 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5566 /* check if line was completely read and is terminated by line break */
5567 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5570 /* cut trailing line break (this can be newline and/or carriage return) */
5571 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5572 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5575 /* copy raw input line for later use (mainly debugging output) */
5576 strcpy(line_raw, line);
5578 if (read_continued_line)
5580 /* append new line to existing line, if there is enough space */
5581 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5582 strcat(previous_line, line_ptr);
5584 strcpy(line, previous_line); /* copy storage buffer to line */
5586 read_continued_line = FALSE;
5589 /* if the last character is '\', continue at next line */
5590 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5592 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5593 strcpy(previous_line, line); /* copy line to storage buffer */
5595 read_continued_line = TRUE;
5600 /* skip empty lines */
5601 if (line[0] == '\0')
5604 /* extract comment text from comment line */
5607 for (line_ptr = line; *line_ptr; line_ptr++)
5608 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5611 strcpy(last_comment, line_ptr);
5616 /* extract level title text from line containing level title */
5617 if (line[0] == '\'')
5619 strcpy(level_name, &line[1]);
5621 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5622 level_name[strlen(level_name) - 1] = '\0';
5627 /* skip lines containing only spaces (or empty lines) */
5628 for (line_ptr = line; *line_ptr; line_ptr++)
5629 if (*line_ptr != ' ')
5631 if (*line_ptr == '\0')
5634 /* at this point, we have found a line containing part of a playfield */
5636 got_valid_playfield_line = TRUE;
5638 if (!reading_playfield)
5640 reading_playfield = TRUE;
5641 invalid_playfield_char = FALSE;
5643 for (x = 0; x < MAX_LEV_FIELDX; x++)
5644 for (y = 0; y < MAX_LEV_FIELDY; y++)
5645 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5650 /* start with topmost tile row */
5654 /* skip playfield line if larger row than allowed */
5655 if (y >= MAX_LEV_FIELDY)
5658 /* start with leftmost tile column */
5661 /* read playfield elements from line */
5662 for (line_ptr = line; *line_ptr; line_ptr++)
5664 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5666 /* stop parsing playfield line if larger column than allowed */
5667 if (x >= MAX_LEV_FIELDX)
5670 if (mapped_sb_element == EL_UNDEFINED)
5672 invalid_playfield_char = TRUE;
5677 level->field[x][y] = mapped_sb_element;
5679 /* continue with next tile column */
5682 level->fieldx = MAX(x, level->fieldx);
5685 if (invalid_playfield_char)
5687 /* if first playfield line, treat invalid lines as comment lines */
5689 reading_playfield = FALSE;
5694 /* continue with next tile row */
5702 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
5703 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
5705 if (!reading_playfield)
5707 level->no_valid_file = TRUE;
5709 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5714 if (*level_name != '\0')
5716 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
5717 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5719 else if (*last_comment != '\0')
5721 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
5722 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5726 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
5729 /* set all empty fields beyond the border walls to invisible steel wall */
5730 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5732 if ((x == 0 || x == level->fieldx - 1 ||
5733 y == 0 || y == level->fieldy - 1) &&
5734 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
5735 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
5736 level->field, level->fieldx, level->fieldy);
5739 /* set special level settings for Sokoban levels */
5742 level->use_step_counter = TRUE;
5744 if (load_xsb_to_ces)
5746 /* special global settings can now be set in level template */
5748 /* fill smaller playfields with padding "beyond border wall" elements */
5749 if (level->fieldx < SCR_FIELDX ||
5750 level->fieldy < SCR_FIELDY)
5752 short field[level->fieldx][level->fieldy];
5753 int new_fieldx = MAX(level->fieldx, SCR_FIELDX);
5754 int new_fieldy = MAX(level->fieldy, SCR_FIELDY);
5755 int pos_fieldx = (new_fieldx - level->fieldx) / 2;
5756 int pos_fieldy = (new_fieldy - level->fieldy) / 2;
5758 /* copy old playfield (which is smaller than the visible area) */
5759 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5760 field[x][y] = level->field[x][y];
5762 /* fill new, larger playfield with "beyond border wall" elements */
5763 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
5764 level->field[x][y] = getMappedElement_SB('_', load_xsb_to_ces);
5766 /* copy the old playfield to the middle of the new playfield */
5767 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5768 level->field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
5770 level->fieldx = new_fieldx;
5771 level->fieldy = new_fieldy;
5774 level->use_custom_template = TRUE;
5779 /* ------------------------------------------------------------------------- */
5780 /* functions for handling native levels */
5781 /* ------------------------------------------------------------------------- */
5783 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
5784 struct LevelFileInfo *level_file_info,
5785 boolean level_info_only)
5787 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
5788 level->no_valid_file = TRUE;
5791 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
5792 struct LevelFileInfo *level_file_info,
5793 boolean level_info_only)
5797 /* determine position of requested level inside level package */
5798 if (level_file_info->packed)
5799 pos = level_file_info->nr - leveldir_current->first_level;
5801 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
5802 level->no_valid_file = TRUE;
5805 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
5807 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5808 CopyNativeLevel_RND_to_EM(level);
5809 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5810 CopyNativeLevel_RND_to_SP(level);
5813 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
5815 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5816 CopyNativeLevel_EM_to_RND(level);
5817 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5818 CopyNativeLevel_SP_to_RND(level);
5821 void SaveNativeLevel(struct LevelInfo *level)
5823 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5825 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
5826 char *filename = getLevelFilenameFromBasename(basename);
5828 CopyNativeLevel_RND_to_SP(level);
5829 CopyNativeTape_RND_to_SP(level);
5831 SaveNativeLevel_SP(filename);
5836 /* ------------------------------------------------------------------------- */
5837 /* functions for loading generic level */
5838 /* ------------------------------------------------------------------------- */
5840 static void LoadLevelFromFileInfo(struct LevelInfo *level,
5841 struct LevelFileInfo *level_file_info,
5842 boolean level_info_only)
5844 /* always start with reliable default values */
5845 setLevelInfoToDefaults(level, level_info_only);
5847 switch (level_file_info->type)
5849 case LEVEL_FILE_TYPE_RND:
5850 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5853 case LEVEL_FILE_TYPE_EM:
5854 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
5855 level->game_engine_type = GAME_ENGINE_TYPE_EM;
5858 case LEVEL_FILE_TYPE_SP:
5859 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
5860 level->game_engine_type = GAME_ENGINE_TYPE_SP;
5863 case LEVEL_FILE_TYPE_DC:
5864 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
5867 case LEVEL_FILE_TYPE_SB:
5868 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
5872 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5876 /* if level file is invalid, restore level structure to default values */
5877 if (level->no_valid_file)
5879 setLevelInfoToDefaults(level, level_info_only);
5881 level->no_valid_file = TRUE; /* but keep "no valid file" flag */
5884 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
5885 level->game_engine_type = GAME_ENGINE_TYPE_RND;
5887 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
5888 CopyNativeLevel_Native_to_RND(level);
5891 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
5893 static struct LevelFileInfo level_file_info;
5895 /* always start with reliable default values */
5896 setFileInfoToDefaults(&level_file_info);
5898 level_file_info.nr = 0; /* unknown level number */
5899 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
5900 level_file_info.filename = filename;
5902 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
5905 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
5909 if (leveldir_current == NULL) /* only when dumping level */
5912 /* all engine modifications also valid for levels which use latest engine */
5913 if (level->game_version < VERSION_IDENT(3,2,0,5))
5915 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
5916 level->score[SC_TIME_BONUS] /= 10;
5919 if (leveldir_current->latest_engine)
5921 /* ---------- use latest game engine ----------------------------------- */
5923 /* For all levels which are forced to use the latest game engine version
5924 (normally all but user contributed, private and undefined levels), set
5925 the game engine version to the actual version; this allows for actual
5926 corrections in the game engine to take effect for existing, converted
5927 levels (from "classic" or other existing games) to make the emulation
5928 of the corresponding game more accurate, while (hopefully) not breaking
5929 existing levels created from other players. */
5931 level->game_version = GAME_VERSION_ACTUAL;
5933 /* Set special EM style gems behaviour: EM style gems slip down from
5934 normal, steel and growing wall. As this is a more fundamental change,
5935 it seems better to set the default behaviour to "off" (as it is more
5936 natural) and make it configurable in the level editor (as a property
5937 of gem style elements). Already existing converted levels (neither
5938 private nor contributed levels) are changed to the new behaviour. */
5940 if (level->file_version < FILE_VERSION_2_0)
5941 level->em_slippery_gems = TRUE;
5946 /* ---------- use game engine the level was created with ----------------- */
5948 /* For all levels which are not forced to use the latest game engine
5949 version (normally user contributed, private and undefined levels),
5950 use the version of the game engine the levels were created for.
5952 Since 2.0.1, the game engine version is now directly stored
5953 in the level file (chunk "VERS"), so there is no need anymore
5954 to set the game version from the file version (except for old,
5955 pre-2.0 levels, where the game version is still taken from the
5956 file format version used to store the level -- see above). */
5958 /* player was faster than enemies in 1.0.0 and before */
5959 if (level->file_version == FILE_VERSION_1_0)
5960 for (i = 0; i < MAX_PLAYERS; i++)
5961 level->initial_player_stepsize[i] = STEPSIZE_FAST;
5963 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
5964 if (level->game_version == VERSION_IDENT(2,0,1,0))
5965 level->em_slippery_gems = TRUE;
5967 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
5968 if (level->game_version < VERSION_IDENT(2,2,0,0))
5969 level->use_spring_bug = TRUE;
5971 if (level->game_version < VERSION_IDENT(3,2,0,5))
5973 /* time orb caused limited time in endless time levels before 3.2.0-5 */
5974 level->use_time_orb_bug = TRUE;
5976 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
5977 level->block_snap_field = FALSE;
5979 /* extra time score was same value as time left score before 3.2.0-5 */
5980 level->extra_time_score = level->score[SC_TIME_BONUS];
5983 if (level->game_version < VERSION_IDENT(3,2,0,7))
5985 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
5986 level->continuous_snapping = FALSE;
5989 /* only few elements were able to actively move into acid before 3.1.0 */
5990 /* trigger settings did not exist before 3.1.0; set to default "any" */
5991 if (level->game_version < VERSION_IDENT(3,1,0,0))
5993 /* correct "can move into acid" settings (all zero in old levels) */
5995 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
5996 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
5998 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
5999 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6000 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6001 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6003 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6004 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6006 /* correct trigger settings (stored as zero == "none" in old levels) */
6008 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6010 int element = EL_CUSTOM_START + i;
6011 struct ElementInfo *ei = &element_info[element];
6013 for (j = 0; j < ei->num_change_pages; j++)
6015 struct ElementChangeInfo *change = &ei->change_page[j];
6017 change->trigger_player = CH_PLAYER_ANY;
6018 change->trigger_page = CH_PAGE_ANY;
6023 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6025 int element = EL_CUSTOM_256;
6026 struct ElementInfo *ei = &element_info[element];
6027 struct ElementChangeInfo *change = &ei->change_page[0];
6029 /* This is needed to fix a problem that was caused by a bugfix in function
6030 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6031 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6032 not replace walkable elements, but instead just placed the player on it,
6033 without placing the Sokoban field under the player). Unfortunately, this
6034 breaks "Snake Bite" style levels when the snake is halfway through a door
6035 that just closes (the snake head is still alive and can be moved in this
6036 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6037 player (without Sokoban element) which then gets killed as designed). */
6039 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6040 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6041 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6042 change->target_element = EL_PLAYER_1;
6045 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6046 if (level->game_version < VERSION_IDENT(3,2,5,0))
6048 /* This is needed to fix a problem that was caused by a bugfix in function
6049 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6050 corrects the behaviour when a custom element changes to another custom
6051 element with a higher element number that has change actions defined.
6052 Normally, only one change per frame is allowed for custom elements.
6053 Therefore, it is checked if a custom element already changed in the
6054 current frame; if it did, subsequent changes are suppressed.
6055 Unfortunately, this is only checked for element changes, but not for
6056 change actions, which are still executed. As the function above loops
6057 through all custom elements from lower to higher, an element change
6058 resulting in a lower CE number won't be checked again, while a target
6059 element with a higher number will also be checked, and potential change
6060 actions will get executed for this CE, too (which is wrong), while
6061 further changes are ignored (which is correct). As this bugfix breaks
6062 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6063 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6064 behaviour for existing levels and tapes that make use of this bug */
6066 level->use_action_after_change_bug = TRUE;
6069 /* not centering level after relocating player was default only in 3.2.3 */
6070 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6071 level->shifted_relocation = TRUE;
6073 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6074 if (level->game_version < VERSION_IDENT(3,2,6,0))
6075 level->em_explodes_by_fire = TRUE;
6078 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6082 /* map custom element change events that have changed in newer versions
6083 (these following values were accidentally changed in version 3.0.1)
6084 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6085 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6087 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6089 int element = EL_CUSTOM_START + i;
6091 /* order of checking and copying events to be mapped is important */
6092 /* (do not change the start and end value -- they are constant) */
6093 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6095 if (HAS_CHANGE_EVENT(element, j - 2))
6097 SET_CHANGE_EVENT(element, j - 2, FALSE);
6098 SET_CHANGE_EVENT(element, j, TRUE);
6102 /* order of checking and copying events to be mapped is important */
6103 /* (do not change the start and end value -- they are constant) */
6104 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6106 if (HAS_CHANGE_EVENT(element, j - 1))
6108 SET_CHANGE_EVENT(element, j - 1, FALSE);
6109 SET_CHANGE_EVENT(element, j, TRUE);
6115 /* initialize "can_change" field for old levels with only one change page */
6116 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6118 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6120 int element = EL_CUSTOM_START + i;
6122 if (CAN_CHANGE(element))
6123 element_info[element].change->can_change = TRUE;
6127 /* correct custom element values (for old levels without these options) */
6128 if (level->game_version < VERSION_IDENT(3,1,1,0))
6130 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6132 int element = EL_CUSTOM_START + i;
6133 struct ElementInfo *ei = &element_info[element];
6135 if (ei->access_direction == MV_NO_DIRECTION)
6136 ei->access_direction = MV_ALL_DIRECTIONS;
6140 /* correct custom element values (fix invalid values for all versions) */
6143 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6145 int element = EL_CUSTOM_START + i;
6146 struct ElementInfo *ei = &element_info[element];
6148 for (j = 0; j < ei->num_change_pages; j++)
6150 struct ElementChangeInfo *change = &ei->change_page[j];
6152 if (change->trigger_player == CH_PLAYER_NONE)
6153 change->trigger_player = CH_PLAYER_ANY;
6155 if (change->trigger_side == CH_SIDE_NONE)
6156 change->trigger_side = CH_SIDE_ANY;
6161 /* initialize "can_explode" field for old levels which did not store this */
6162 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6163 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6165 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6167 int element = EL_CUSTOM_START + i;
6169 if (EXPLODES_1X1_OLD(element))
6170 element_info[element].explosion_type = EXPLODES_1X1;
6172 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6173 EXPLODES_SMASHED(element) ||
6174 EXPLODES_IMPACT(element)));
6178 /* correct previously hard-coded move delay values for maze runner style */
6179 if (level->game_version < VERSION_IDENT(3,1,1,0))
6181 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6183 int element = EL_CUSTOM_START + i;
6185 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6187 /* previously hard-coded and therefore ignored */
6188 element_info[element].move_delay_fixed = 9;
6189 element_info[element].move_delay_random = 0;
6194 /* map elements that have changed in newer versions */
6195 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6196 level->game_version);
6197 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6198 for (x = 0; x < 3; x++)
6199 for (y = 0; y < 3; y++)
6200 level->yamyam_content[i].e[x][y] =
6201 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6202 level->game_version);
6204 /* initialize element properties for level editor etc. */
6205 InitElementPropertiesEngine(level->game_version);
6206 InitElementPropertiesAfterLoading(level->game_version);
6207 InitElementPropertiesGfxElement();
6210 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6214 /* map elements that have changed in newer versions */
6215 for (y = 0; y < level->fieldy; y++)
6216 for (x = 0; x < level->fieldx; x++)
6217 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6218 level->game_version);
6220 /* copy elements to runtime playfield array */
6221 for (x = 0; x < MAX_LEV_FIELDX; x++)
6222 for (y = 0; y < MAX_LEV_FIELDY; y++)
6223 Feld[x][y] = level->field[x][y];
6225 /* initialize level size variables for faster access */
6226 lev_fieldx = level->fieldx;
6227 lev_fieldy = level->fieldy;
6229 /* determine border element for this level */
6230 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6231 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6236 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6238 struct LevelFileInfo *level_file_info = &level->file_info;
6240 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6241 CopyNativeLevel_RND_to_Native(level);
6244 void LoadLevelTemplate(int nr)
6248 setLevelFileInfo(&level_template.file_info, nr);
6249 filename = level_template.file_info.filename;
6251 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6253 LoadLevel_InitVersion(&level_template, filename);
6254 LoadLevel_InitElements(&level_template, filename);
6256 ActivateLevelTemplate();
6259 void LoadLevel(int nr)
6263 setLevelFileInfo(&level.file_info, nr);
6264 filename = level.file_info.filename;
6266 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6268 if (level.use_custom_template)
6269 LoadLevelTemplate(-1);
6271 LoadLevel_InitVersion(&level, filename);
6272 LoadLevel_InitElements(&level, filename);
6273 LoadLevel_InitPlayfield(&level, filename);
6275 LoadLevel_InitNativeEngines(&level, filename);
6278 void LoadLevelInfoOnly(int nr)
6280 setLevelFileInfo(&level.file_info, nr);
6282 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6285 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6289 chunk_size += putFileVersion(file, level->file_version);
6290 chunk_size += putFileVersion(file, level->game_version);
6295 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6299 chunk_size += putFile16BitBE(file, level->creation_date.year);
6300 chunk_size += putFile8Bit(file, level->creation_date.month);
6301 chunk_size += putFile8Bit(file, level->creation_date.day);
6306 #if ENABLE_HISTORIC_CHUNKS
6307 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6311 putFile8Bit(file, level->fieldx);
6312 putFile8Bit(file, level->fieldy);
6314 putFile16BitBE(file, level->time);
6315 putFile16BitBE(file, level->gems_needed);
6317 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6318 putFile8Bit(file, level->name[i]);
6320 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6321 putFile8Bit(file, level->score[i]);
6323 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6324 for (y = 0; y < 3; y++)
6325 for (x = 0; x < 3; x++)
6326 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6327 level->yamyam_content[i].e[x][y]));
6328 putFile8Bit(file, level->amoeba_speed);
6329 putFile8Bit(file, level->time_magic_wall);
6330 putFile8Bit(file, level->time_wheel);
6331 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6332 level->amoeba_content));
6333 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6334 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6335 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6336 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6338 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6340 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6341 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6342 putFile32BitBE(file, level->can_move_into_acid_bits);
6343 putFile8Bit(file, level->dont_collide_with_bits);
6345 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6346 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6348 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6349 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6350 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6352 putFile8Bit(file, level->game_engine_type);
6354 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6358 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6363 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6364 chunk_size += putFile8Bit(file, level->name[i]);
6369 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6374 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6375 chunk_size += putFile8Bit(file, level->author[i]);
6380 #if ENABLE_HISTORIC_CHUNKS
6381 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6386 for (y = 0; y < level->fieldy; y++)
6387 for (x = 0; x < level->fieldx; x++)
6388 if (level->encoding_16bit_field)
6389 chunk_size += putFile16BitBE(file, level->field[x][y]);
6391 chunk_size += putFile8Bit(file, level->field[x][y]);
6397 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6402 for (y = 0; y < level->fieldy; y++)
6403 for (x = 0; x < level->fieldx; x++)
6404 chunk_size += putFile16BitBE(file, level->field[x][y]);
6409 #if ENABLE_HISTORIC_CHUNKS
6410 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6414 putFile8Bit(file, EL_YAMYAM);
6415 putFile8Bit(file, level->num_yamyam_contents);
6416 putFile8Bit(file, 0);
6417 putFile8Bit(file, 0);
6419 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6420 for (y = 0; y < 3; y++)
6421 for (x = 0; x < 3; x++)
6422 if (level->encoding_16bit_field)
6423 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6425 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6429 #if ENABLE_HISTORIC_CHUNKS
6430 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6433 int num_contents, content_xsize, content_ysize;
6434 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6436 if (element == EL_YAMYAM)
6438 num_contents = level->num_yamyam_contents;
6442 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6443 for (y = 0; y < 3; y++)
6444 for (x = 0; x < 3; x++)
6445 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6447 else if (element == EL_BD_AMOEBA)
6453 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6454 for (y = 0; y < 3; y++)
6455 for (x = 0; x < 3; x++)
6456 content_array[i][x][y] = EL_EMPTY;
6457 content_array[0][0][0] = level->amoeba_content;
6461 /* chunk header already written -- write empty chunk data */
6462 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6464 Error(ERR_WARN, "cannot save content for element '%d'", element);
6468 putFile16BitBE(file, element);
6469 putFile8Bit(file, num_contents);
6470 putFile8Bit(file, content_xsize);
6471 putFile8Bit(file, content_ysize);
6473 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6475 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6476 for (y = 0; y < 3; y++)
6477 for (x = 0; x < 3; x++)
6478 putFile16BitBE(file, content_array[i][x][y]);
6482 #if ENABLE_HISTORIC_CHUNKS
6483 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6485 int envelope_nr = element - EL_ENVELOPE_1;
6486 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6490 chunk_size += putFile16BitBE(file, element);
6491 chunk_size += putFile16BitBE(file, envelope_len);
6492 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6493 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6495 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6496 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6498 for (i = 0; i < envelope_len; i++)
6499 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6505 #if ENABLE_HISTORIC_CHUNKS
6506 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6507 int num_changed_custom_elements)
6511 putFile16BitBE(file, num_changed_custom_elements);
6513 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6515 int element = EL_CUSTOM_START + i;
6517 struct ElementInfo *ei = &element_info[element];
6519 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6521 if (check < num_changed_custom_elements)
6523 putFile16BitBE(file, element);
6524 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6531 if (check != num_changed_custom_elements) /* should not happen */
6532 Error(ERR_WARN, "inconsistent number of custom element properties");
6536 #if ENABLE_HISTORIC_CHUNKS
6537 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6538 int num_changed_custom_elements)
6542 putFile16BitBE(file, num_changed_custom_elements);
6544 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6546 int element = EL_CUSTOM_START + i;
6548 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6550 if (check < num_changed_custom_elements)
6552 putFile16BitBE(file, element);
6553 putFile16BitBE(file, element_info[element].change->target_element);
6560 if (check != num_changed_custom_elements) /* should not happen */
6561 Error(ERR_WARN, "inconsistent number of custom target elements");
6565 #if ENABLE_HISTORIC_CHUNKS
6566 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6567 int num_changed_custom_elements)
6569 int i, j, x, y, check = 0;
6571 putFile16BitBE(file, num_changed_custom_elements);
6573 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6575 int element = EL_CUSTOM_START + i;
6576 struct ElementInfo *ei = &element_info[element];
6578 if (ei->modified_settings)
6580 if (check < num_changed_custom_elements)
6582 putFile16BitBE(file, element);
6584 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6585 putFile8Bit(file, ei->description[j]);
6587 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6589 /* some free bytes for future properties and padding */
6590 WriteUnusedBytesToFile(file, 7);
6592 putFile8Bit(file, ei->use_gfx_element);
6593 putFile16BitBE(file, ei->gfx_element_initial);
6595 putFile8Bit(file, ei->collect_score_initial);
6596 putFile8Bit(file, ei->collect_count_initial);
6598 putFile16BitBE(file, ei->push_delay_fixed);
6599 putFile16BitBE(file, ei->push_delay_random);
6600 putFile16BitBE(file, ei->move_delay_fixed);
6601 putFile16BitBE(file, ei->move_delay_random);
6603 putFile16BitBE(file, ei->move_pattern);
6604 putFile8Bit(file, ei->move_direction_initial);
6605 putFile8Bit(file, ei->move_stepsize);
6607 for (y = 0; y < 3; y++)
6608 for (x = 0; x < 3; x++)
6609 putFile16BitBE(file, ei->content.e[x][y]);
6611 putFile32BitBE(file, ei->change->events);
6613 putFile16BitBE(file, ei->change->target_element);
6615 putFile16BitBE(file, ei->change->delay_fixed);
6616 putFile16BitBE(file, ei->change->delay_random);
6617 putFile16BitBE(file, ei->change->delay_frames);
6619 putFile16BitBE(file, ei->change->initial_trigger_element);
6621 putFile8Bit(file, ei->change->explode);
6622 putFile8Bit(file, ei->change->use_target_content);
6623 putFile8Bit(file, ei->change->only_if_complete);
6624 putFile8Bit(file, ei->change->use_random_replace);
6626 putFile8Bit(file, ei->change->random_percentage);
6627 putFile8Bit(file, ei->change->replace_when);
6629 for (y = 0; y < 3; y++)
6630 for (x = 0; x < 3; x++)
6631 putFile16BitBE(file, ei->change->content.e[x][y]);
6633 putFile8Bit(file, ei->slippery_type);
6635 /* some free bytes for future properties and padding */
6636 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6643 if (check != num_changed_custom_elements) /* should not happen */
6644 Error(ERR_WARN, "inconsistent number of custom element properties");
6648 #if ENABLE_HISTORIC_CHUNKS
6649 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6651 struct ElementInfo *ei = &element_info[element];
6654 /* ---------- custom element base property values (96 bytes) ------------- */
6656 putFile16BitBE(file, element);
6658 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6659 putFile8Bit(file, ei->description[i]);
6661 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6663 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
6665 putFile8Bit(file, ei->num_change_pages);
6667 putFile16BitBE(file, ei->ce_value_fixed_initial);
6668 putFile16BitBE(file, ei->ce_value_random_initial);
6669 putFile8Bit(file, ei->use_last_ce_value);
6671 putFile8Bit(file, ei->use_gfx_element);
6672 putFile16BitBE(file, ei->gfx_element_initial);
6674 putFile8Bit(file, ei->collect_score_initial);
6675 putFile8Bit(file, ei->collect_count_initial);
6677 putFile8Bit(file, ei->drop_delay_fixed);
6678 putFile8Bit(file, ei->push_delay_fixed);
6679 putFile8Bit(file, ei->drop_delay_random);
6680 putFile8Bit(file, ei->push_delay_random);
6681 putFile16BitBE(file, ei->move_delay_fixed);
6682 putFile16BitBE(file, ei->move_delay_random);
6684 /* bits 0 - 15 of "move_pattern" ... */
6685 putFile16BitBE(file, ei->move_pattern & 0xffff);
6686 putFile8Bit(file, ei->move_direction_initial);
6687 putFile8Bit(file, ei->move_stepsize);
6689 putFile8Bit(file, ei->slippery_type);
6691 for (y = 0; y < 3; y++)
6692 for (x = 0; x < 3; x++)
6693 putFile16BitBE(file, ei->content.e[x][y]);
6695 putFile16BitBE(file, ei->move_enter_element);
6696 putFile16BitBE(file, ei->move_leave_element);
6697 putFile8Bit(file, ei->move_leave_type);
6699 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
6700 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
6702 putFile8Bit(file, ei->access_direction);
6704 putFile8Bit(file, ei->explosion_delay);
6705 putFile8Bit(file, ei->ignition_delay);
6706 putFile8Bit(file, ei->explosion_type);
6708 /* some free bytes for future custom property values and padding */
6709 WriteUnusedBytesToFile(file, 1);
6711 /* ---------- change page property values (48 bytes) --------------------- */
6713 for (i = 0; i < ei->num_change_pages; i++)
6715 struct ElementChangeInfo *change = &ei->change_page[i];
6716 unsigned int event_bits;
6718 /* bits 0 - 31 of "has_event[]" ... */
6720 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
6721 if (change->has_event[j])
6722 event_bits |= (1 << j);
6723 putFile32BitBE(file, event_bits);
6725 putFile16BitBE(file, change->target_element);
6727 putFile16BitBE(file, change->delay_fixed);
6728 putFile16BitBE(file, change->delay_random);
6729 putFile16BitBE(file, change->delay_frames);
6731 putFile16BitBE(file, change->initial_trigger_element);
6733 putFile8Bit(file, change->explode);
6734 putFile8Bit(file, change->use_target_content);
6735 putFile8Bit(file, change->only_if_complete);
6736 putFile8Bit(file, change->use_random_replace);
6738 putFile8Bit(file, change->random_percentage);
6739 putFile8Bit(file, change->replace_when);
6741 for (y = 0; y < 3; y++)
6742 for (x = 0; x < 3; x++)
6743 putFile16BitBE(file, change->target_content.e[x][y]);
6745 putFile8Bit(file, change->can_change);
6747 putFile8Bit(file, change->trigger_side);
6749 putFile8Bit(file, change->trigger_player);
6750 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
6751 log_2(change->trigger_page)));
6753 putFile8Bit(file, change->has_action);
6754 putFile8Bit(file, change->action_type);
6755 putFile8Bit(file, change->action_mode);
6756 putFile16BitBE(file, change->action_arg);
6758 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
6760 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
6761 if (change->has_event[j])
6762 event_bits |= (1 << (j - 32));
6763 putFile8Bit(file, event_bits);
6768 #if ENABLE_HISTORIC_CHUNKS
6769 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
6771 struct ElementInfo *ei = &element_info[element];
6772 struct ElementGroupInfo *group = ei->group;
6775 putFile16BitBE(file, element);
6777 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6778 putFile8Bit(file, ei->description[i]);
6780 putFile8Bit(file, group->num_elements);
6782 putFile8Bit(file, ei->use_gfx_element);
6783 putFile16BitBE(file, ei->gfx_element_initial);
6785 putFile8Bit(file, group->choice_mode);
6787 /* some free bytes for future values and padding */
6788 WriteUnusedBytesToFile(file, 3);
6790 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
6791 putFile16BitBE(file, group->element[i]);
6795 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
6796 boolean write_element)
6798 int save_type = entry->save_type;
6799 int data_type = entry->data_type;
6800 int conf_type = entry->conf_type;
6801 int byte_mask = conf_type & CONF_MASK_BYTES;
6802 int element = entry->element;
6803 int default_value = entry->default_value;
6805 boolean modified = FALSE;
6807 if (byte_mask != CONF_MASK_MULTI_BYTES)
6809 void *value_ptr = entry->value;
6810 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
6813 /* check if any settings have been modified before saving them */
6814 if (value != default_value)
6817 /* do not save if explicitly told or if unmodified default settings */
6818 if ((save_type == SAVE_CONF_NEVER) ||
6819 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6823 num_bytes += putFile16BitBE(file, element);
6825 num_bytes += putFile8Bit(file, conf_type);
6826 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
6827 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
6828 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
6831 else if (data_type == TYPE_STRING)
6833 char *default_string = entry->default_string;
6834 char *string = (char *)(entry->value);
6835 int string_length = strlen(string);
6838 /* check if any settings have been modified before saving them */
6839 if (!strEqual(string, default_string))
6842 /* do not save if explicitly told or if unmodified default settings */
6843 if ((save_type == SAVE_CONF_NEVER) ||
6844 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6848 num_bytes += putFile16BitBE(file, element);
6850 num_bytes += putFile8Bit(file, conf_type);
6851 num_bytes += putFile16BitBE(file, string_length);
6853 for (i = 0; i < string_length; i++)
6854 num_bytes += putFile8Bit(file, string[i]);
6856 else if (data_type == TYPE_ELEMENT_LIST)
6858 int *element_array = (int *)(entry->value);
6859 int num_elements = *(int *)(entry->num_entities);
6862 /* check if any settings have been modified before saving them */
6863 for (i = 0; i < num_elements; i++)
6864 if (element_array[i] != default_value)
6867 /* do not save if explicitly told or if unmodified default settings */
6868 if ((save_type == SAVE_CONF_NEVER) ||
6869 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6873 num_bytes += putFile16BitBE(file, element);
6875 num_bytes += putFile8Bit(file, conf_type);
6876 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
6878 for (i = 0; i < num_elements; i++)
6879 num_bytes += putFile16BitBE(file, element_array[i]);
6881 else if (data_type == TYPE_CONTENT_LIST)
6883 struct Content *content = (struct Content *)(entry->value);
6884 int num_contents = *(int *)(entry->num_entities);
6887 /* check if any settings have been modified before saving them */
6888 for (i = 0; i < num_contents; i++)
6889 for (y = 0; y < 3; y++)
6890 for (x = 0; x < 3; x++)
6891 if (content[i].e[x][y] != default_value)
6894 /* do not save if explicitly told or if unmodified default settings */
6895 if ((save_type == SAVE_CONF_NEVER) ||
6896 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6900 num_bytes += putFile16BitBE(file, element);
6902 num_bytes += putFile8Bit(file, conf_type);
6903 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
6905 for (i = 0; i < num_contents; i++)
6906 for (y = 0; y < 3; y++)
6907 for (x = 0; x < 3; x++)
6908 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
6914 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
6919 li = *level; /* copy level data into temporary buffer */
6921 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
6922 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
6927 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
6932 li = *level; /* copy level data into temporary buffer */
6934 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
6935 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
6940 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
6942 int envelope_nr = element - EL_ENVELOPE_1;
6946 chunk_size += putFile16BitBE(file, element);
6948 /* copy envelope data into temporary buffer */
6949 xx_envelope = level->envelope[envelope_nr];
6951 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
6952 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
6957 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
6959 struct ElementInfo *ei = &element_info[element];
6963 chunk_size += putFile16BitBE(file, element);
6965 xx_ei = *ei; /* copy element data into temporary buffer */
6967 /* set default description string for this specific element */
6968 strcpy(xx_default_description, getDefaultElementDescription(ei));
6970 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
6971 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
6973 for (i = 0; i < ei->num_change_pages; i++)
6975 struct ElementChangeInfo *change = &ei->change_page[i];
6977 xx_current_change_page = i;
6979 xx_change = *change; /* copy change data into temporary buffer */
6982 setEventBitsFromEventFlags(change);
6984 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
6985 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
6992 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
6994 struct ElementInfo *ei = &element_info[element];
6995 struct ElementGroupInfo *group = ei->group;
6999 chunk_size += putFile16BitBE(file, element);
7001 xx_ei = *ei; /* copy element data into temporary buffer */
7002 xx_group = *group; /* copy group data into temporary buffer */
7004 /* set default description string for this specific element */
7005 strcpy(xx_default_description, getDefaultElementDescription(ei));
7007 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7008 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7013 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
7019 if (!(file = fopen(filename, MODE_WRITE)))
7021 Error(ERR_WARN, "cannot save level file '%s'", filename);
7025 level->file_version = FILE_VERSION_ACTUAL;
7026 level->game_version = GAME_VERSION_ACTUAL;
7028 level->creation_date = getCurrentDate();
7030 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7031 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7033 chunk_size = SaveLevel_VERS(NULL, level);
7034 putFileChunkBE(file, "VERS", chunk_size);
7035 SaveLevel_VERS(file, level);
7037 chunk_size = SaveLevel_DATE(NULL, level);
7038 putFileChunkBE(file, "DATE", chunk_size);
7039 SaveLevel_DATE(file, level);
7041 chunk_size = SaveLevel_NAME(NULL, level);
7042 putFileChunkBE(file, "NAME", chunk_size);
7043 SaveLevel_NAME(file, level);
7045 chunk_size = SaveLevel_AUTH(NULL, level);
7046 putFileChunkBE(file, "AUTH", chunk_size);
7047 SaveLevel_AUTH(file, level);
7049 chunk_size = SaveLevel_INFO(NULL, level);
7050 putFileChunkBE(file, "INFO", chunk_size);
7051 SaveLevel_INFO(file, level);
7053 chunk_size = SaveLevel_BODY(NULL, level);
7054 putFileChunkBE(file, "BODY", chunk_size);
7055 SaveLevel_BODY(file, level);
7057 chunk_size = SaveLevel_ELEM(NULL, level);
7058 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7060 putFileChunkBE(file, "ELEM", chunk_size);
7061 SaveLevel_ELEM(file, level);
7064 for (i = 0; i < NUM_ENVELOPES; i++)
7066 int element = EL_ENVELOPE_1 + i;
7068 chunk_size = SaveLevel_NOTE(NULL, level, element);
7069 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7071 putFileChunkBE(file, "NOTE", chunk_size);
7072 SaveLevel_NOTE(file, level, element);
7076 /* if not using template level, check for non-default custom/group elements */
7077 if (!level->use_custom_template)
7079 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7081 int element = EL_CUSTOM_START + i;
7083 chunk_size = SaveLevel_CUSX(NULL, level, element);
7084 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7086 putFileChunkBE(file, "CUSX", chunk_size);
7087 SaveLevel_CUSX(file, level, element);
7091 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7093 int element = EL_GROUP_START + i;
7095 chunk_size = SaveLevel_GRPX(NULL, level, element);
7096 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7098 putFileChunkBE(file, "GRPX", chunk_size);
7099 SaveLevel_GRPX(file, level, element);
7106 SetFilePermissions(filename, PERMS_PRIVATE);
7109 void SaveLevel(int nr)
7111 char *filename = getDefaultLevelFilename(nr);
7113 SaveLevelFromFilename(&level, filename);
7116 void SaveLevelTemplate()
7118 char *filename = getDefaultLevelFilename(-1);
7120 SaveLevelFromFilename(&level, filename);
7123 boolean SaveLevelChecked(int nr)
7125 char *filename = getDefaultLevelFilename(nr);
7126 boolean new_level = !fileExists(filename);
7127 boolean level_saved = FALSE;
7129 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7134 Request("Level saved!", REQ_CONFIRM);
7142 void DumpLevel(struct LevelInfo *level)
7144 if (level->no_valid_file)
7146 Error(ERR_WARN, "cannot dump -- no valid level file found");
7151 printf_line("-", 79);
7152 printf("Level xxx (file version %08d, game version %08d)\n",
7153 level->file_version, level->game_version);
7154 printf_line("-", 79);
7156 printf("Level author: '%s'\n", level->author);
7157 printf("Level title: '%s'\n", level->name);
7159 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7161 printf("Level time: %d seconds\n", level->time);
7162 printf("Gems needed: %d\n", level->gems_needed);
7164 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
7165 printf("Time for wheel: %d seconds\n", level->time_wheel);
7166 printf("Time for light: %d seconds\n", level->time_light);
7167 printf("Time for timegate: %d seconds\n", level->time_timegate);
7169 printf("Amoeba speed: %d\n", level->amoeba_speed);
7172 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7173 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7174 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7175 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7176 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7178 printf_line("-", 79);
7182 /* ========================================================================= */
7183 /* tape file functions */
7184 /* ========================================================================= */
7186 static void setTapeInfoToDefaults()
7190 /* always start with reliable default values (empty tape) */
7193 /* default values (also for pre-1.2 tapes) with only the first player */
7194 tape.player_participates[0] = TRUE;
7195 for (i = 1; i < MAX_PLAYERS; i++)
7196 tape.player_participates[i] = FALSE;
7198 /* at least one (default: the first) player participates in every tape */
7199 tape.num_participating_players = 1;
7201 tape.level_nr = level_nr;
7203 tape.changed = FALSE;
7205 tape.recording = FALSE;
7206 tape.playing = FALSE;
7207 tape.pausing = FALSE;
7209 tape.no_valid_file = FALSE;
7212 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7214 tape->file_version = getFileVersion(file);
7215 tape->game_version = getFileVersion(file);
7220 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7224 tape->random_seed = getFile32BitBE(file);
7225 tape->date = getFile32BitBE(file);
7226 tape->length = getFile32BitBE(file);
7228 /* read header fields that are new since version 1.2 */
7229 if (tape->file_version >= FILE_VERSION_1_2)
7231 byte store_participating_players = getFile8Bit(file);
7234 /* since version 1.2, tapes store which players participate in the tape */
7235 tape->num_participating_players = 0;
7236 for (i = 0; i < MAX_PLAYERS; i++)
7238 tape->player_participates[i] = FALSE;
7240 if (store_participating_players & (1 << i))
7242 tape->player_participates[i] = TRUE;
7243 tape->num_participating_players++;
7247 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7249 engine_version = getFileVersion(file);
7250 if (engine_version > 0)
7251 tape->engine_version = engine_version;
7253 tape->engine_version = tape->game_version;
7259 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7261 int level_identifier_size;
7264 level_identifier_size = getFile16BitBE(file);
7266 tape->level_identifier =
7267 checked_realloc(tape->level_identifier, level_identifier_size);
7269 for (i = 0; i < level_identifier_size; i++)
7270 tape->level_identifier[i] = getFile8Bit(file);
7272 tape->level_nr = getFile16BitBE(file);
7274 chunk_size = 2 + level_identifier_size + 2;
7279 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7282 int chunk_size_expected =
7283 (tape->num_participating_players + 1) * tape->length;
7285 if (chunk_size_expected != chunk_size)
7287 ReadUnusedBytesFromFile(file, chunk_size);
7288 return chunk_size_expected;
7291 for (i = 0; i < tape->length; i++)
7293 if (i >= MAX_TAPE_LEN)
7296 for (j = 0; j < MAX_PLAYERS; j++)
7298 tape->pos[i].action[j] = MV_NONE;
7300 if (tape->player_participates[j])
7301 tape->pos[i].action[j] = getFile8Bit(file);
7304 tape->pos[i].delay = getFile8Bit(file);
7306 if (tape->file_version == FILE_VERSION_1_0)
7308 /* eliminate possible diagonal moves in old tapes */
7309 /* this is only for backward compatibility */
7311 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7312 byte action = tape->pos[i].action[0];
7313 int k, num_moves = 0;
7315 for (k = 0; k<4; k++)
7317 if (action & joy_dir[k])
7319 tape->pos[i + num_moves].action[0] = joy_dir[k];
7321 tape->pos[i + num_moves].delay = 0;
7330 tape->length += num_moves;
7333 else if (tape->file_version < FILE_VERSION_2_0)
7335 /* convert pre-2.0 tapes to new tape format */
7337 if (tape->pos[i].delay > 1)
7340 tape->pos[i + 1] = tape->pos[i];
7341 tape->pos[i + 1].delay = 1;
7344 for (j = 0; j < MAX_PLAYERS; j++)
7345 tape->pos[i].action[j] = MV_NONE;
7346 tape->pos[i].delay--;
7353 if (checkEndOfFile(file))
7357 if (i != tape->length)
7358 chunk_size = (tape->num_participating_players + 1) * i;
7363 void LoadTape_SokobanSolution(char *filename)
7366 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7368 if (!(file = openFile(filename, MODE_READ)))
7370 tape.no_valid_file = TRUE;
7375 while (!checkEndOfFile(file))
7377 unsigned char c = getByteFromFile(file);
7379 if (checkEndOfFile(file))
7386 tape.pos[tape.length].action[0] = MV_UP;
7387 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7393 tape.pos[tape.length].action[0] = MV_DOWN;
7394 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7400 tape.pos[tape.length].action[0] = MV_LEFT;
7401 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7407 tape.pos[tape.length].action[0] = MV_RIGHT;
7408 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7416 /* ignore white-space characters */
7420 tape.no_valid_file = TRUE;
7422 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7430 if (tape.no_valid_file)
7433 tape.length_seconds = GetTapeLength();
7436 void LoadTapeFromFilename(char *filename)
7438 char cookie[MAX_LINE_LEN];
7439 char chunk_name[CHUNK_ID_LEN + 1];
7443 /* always start with reliable default values */
7444 setTapeInfoToDefaults();
7446 if (strSuffix(filename, ".sln"))
7448 LoadTape_SokobanSolution(filename);
7453 if (!(file = openFile(filename, MODE_READ)))
7455 tape.no_valid_file = TRUE;
7460 getFileChunkBE(file, chunk_name, NULL);
7461 if (strEqual(chunk_name, "RND1"))
7463 getFile32BitBE(file); /* not used */
7465 getFileChunkBE(file, chunk_name, NULL);
7466 if (!strEqual(chunk_name, "TAPE"))
7468 tape.no_valid_file = TRUE;
7470 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7477 else /* check for pre-2.0 file format with cookie string */
7479 strcpy(cookie, chunk_name);
7480 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7482 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7483 cookie[strlen(cookie) - 1] = '\0';
7485 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7487 tape.no_valid_file = TRUE;
7489 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7496 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7498 tape.no_valid_file = TRUE;
7500 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7507 /* pre-2.0 tape files have no game version, so use file version here */
7508 tape.game_version = tape.file_version;
7511 if (tape.file_version < FILE_VERSION_1_2)
7513 /* tape files from versions before 1.2.0 without chunk structure */
7514 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7515 LoadTape_BODY(file, 2 * tape.length, &tape);
7523 int (*loader)(File *, int, struct TapeInfo *);
7527 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7528 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7529 { "INFO", -1, LoadTape_INFO },
7530 { "BODY", -1, LoadTape_BODY },
7534 while (getFileChunkBE(file, chunk_name, &chunk_size))
7538 while (chunk_info[i].name != NULL &&
7539 !strEqual(chunk_name, chunk_info[i].name))
7542 if (chunk_info[i].name == NULL)
7544 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7545 chunk_name, filename);
7546 ReadUnusedBytesFromFile(file, chunk_size);
7548 else if (chunk_info[i].size != -1 &&
7549 chunk_info[i].size != chunk_size)
7551 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7552 chunk_size, chunk_name, filename);
7553 ReadUnusedBytesFromFile(file, chunk_size);
7557 /* call function to load this tape chunk */
7558 int chunk_size_expected =
7559 (chunk_info[i].loader)(file, chunk_size, &tape);
7561 /* the size of some chunks cannot be checked before reading other
7562 chunks first (like "HEAD" and "BODY") that contain some header
7563 information, so check them here */
7564 if (chunk_size_expected != chunk_size)
7566 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7567 chunk_size, chunk_name, filename);
7575 tape.length_seconds = GetTapeLength();
7578 printf("::: tape file version: %d\n", tape.file_version);
7579 printf("::: tape game version: %d\n", tape.game_version);
7580 printf("::: tape engine version: %d\n", tape.engine_version);
7584 void LoadTape(int nr)
7586 char *filename = getTapeFilename(nr);
7588 LoadTapeFromFilename(filename);
7591 void LoadSolutionTape(int nr)
7593 char *filename = getSolutionTapeFilename(nr);
7595 LoadTapeFromFilename(filename);
7597 if (TAPE_IS_EMPTY(tape) &&
7598 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7599 level.native_sp_level->demo.is_available)
7600 CopyNativeTape_SP_to_RND(&level);
7603 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7605 putFileVersion(file, tape->file_version);
7606 putFileVersion(file, tape->game_version);
7609 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7612 byte store_participating_players = 0;
7614 /* set bits for participating players for compact storage */
7615 for (i = 0; i < MAX_PLAYERS; i++)
7616 if (tape->player_participates[i])
7617 store_participating_players |= (1 << i);
7619 putFile32BitBE(file, tape->random_seed);
7620 putFile32BitBE(file, tape->date);
7621 putFile32BitBE(file, tape->length);
7623 putFile8Bit(file, store_participating_players);
7625 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7626 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7628 putFileVersion(file, tape->engine_version);
7631 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7633 int level_identifier_size = strlen(tape->level_identifier) + 1;
7636 putFile16BitBE(file, level_identifier_size);
7638 for (i = 0; i < level_identifier_size; i++)
7639 putFile8Bit(file, tape->level_identifier[i]);
7641 putFile16BitBE(file, tape->level_nr);
7644 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7648 for (i = 0; i < tape->length; i++)
7650 for (j = 0; j < MAX_PLAYERS; j++)
7651 if (tape->player_participates[j])
7652 putFile8Bit(file, tape->pos[i].action[j]);
7654 putFile8Bit(file, tape->pos[i].delay);
7658 void SaveTape(int nr)
7660 char *filename = getTapeFilename(nr);
7662 int num_participating_players = 0;
7663 int info_chunk_size;
7664 int body_chunk_size;
7667 InitTapeDirectory(leveldir_current->subdir);
7669 if (!(file = fopen(filename, MODE_WRITE)))
7671 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7675 tape.file_version = FILE_VERSION_ACTUAL;
7676 tape.game_version = GAME_VERSION_ACTUAL;
7678 /* count number of participating players */
7679 for (i = 0; i < MAX_PLAYERS; i++)
7680 if (tape.player_participates[i])
7681 num_participating_players++;
7683 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7684 body_chunk_size = (num_participating_players + 1) * tape.length;
7686 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7687 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7689 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7690 SaveTape_VERS(file, &tape);
7692 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7693 SaveTape_HEAD(file, &tape);
7695 putFileChunkBE(file, "INFO", info_chunk_size);
7696 SaveTape_INFO(file, &tape);
7698 putFileChunkBE(file, "BODY", body_chunk_size);
7699 SaveTape_BODY(file, &tape);
7703 SetFilePermissions(filename, PERMS_PRIVATE);
7705 tape.changed = FALSE;
7708 boolean SaveTapeChecked(int nr)
7710 char *filename = getTapeFilename(nr);
7711 boolean new_tape = !fileExists(filename);
7712 boolean tape_saved = FALSE;
7714 if (new_tape || Request("Replace old tape?", REQ_ASK))
7719 Request("Tape saved!", REQ_CONFIRM);
7727 void DumpTape(struct TapeInfo *tape)
7729 int tape_frame_counter;
7732 if (tape->no_valid_file)
7734 Error(ERR_WARN, "cannot dump -- no valid tape file found");
7739 printf_line("-", 79);
7740 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
7741 tape->level_nr, tape->file_version, tape->game_version);
7742 printf(" (effective engine version %08d)\n",
7743 tape->engine_version);
7744 printf("Level series identifier: '%s'\n", tape->level_identifier);
7745 printf_line("-", 79);
7747 tape_frame_counter = 0;
7749 for (i = 0; i < tape->length; i++)
7751 if (i >= MAX_TAPE_LEN)
7754 printf("%04d: ", i);
7756 for (j = 0; j < MAX_PLAYERS; j++)
7758 if (tape->player_participates[j])
7760 int action = tape->pos[i].action[j];
7762 printf("%d:%02x ", j, action);
7763 printf("[%c%c%c%c|%c%c] - ",
7764 (action & JOY_LEFT ? '<' : ' '),
7765 (action & JOY_RIGHT ? '>' : ' '),
7766 (action & JOY_UP ? '^' : ' '),
7767 (action & JOY_DOWN ? 'v' : ' '),
7768 (action & JOY_BUTTON_1 ? '1' : ' '),
7769 (action & JOY_BUTTON_2 ? '2' : ' '));
7773 printf("(%03d) ", tape->pos[i].delay);
7774 printf("[%05d]\n", tape_frame_counter);
7776 tape_frame_counter += tape->pos[i].delay;
7779 printf_line("-", 79);
7783 /* ========================================================================= */
7784 /* score file functions */
7785 /* ========================================================================= */
7787 void LoadScore(int nr)
7790 char *filename = getScoreFilename(nr);
7791 char cookie[MAX_LINE_LEN];
7792 char line[MAX_LINE_LEN];
7796 /* always start with reliable default values */
7797 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7799 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
7800 highscore[i].Score = 0;
7803 if (!(file = fopen(filename, MODE_READ)))
7806 /* check file identifier */
7807 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
7809 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7810 cookie[strlen(cookie) - 1] = '\0';
7812 if (!checkCookieString(cookie, SCORE_COOKIE))
7814 Error(ERR_WARN, "unknown format of score file '%s'", filename);
7819 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7821 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
7822 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
7823 if (fgets(line, MAX_LINE_LEN, file) == NULL)
7826 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
7827 line[strlen(line) - 1] = '\0';
7829 for (line_ptr = line; *line_ptr; line_ptr++)
7831 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
7833 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
7834 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
7843 void SaveScore(int nr)
7846 char *filename = getScoreFilename(nr);
7849 InitScoreDirectory(leveldir_current->subdir);
7851 if (!(file = fopen(filename, MODE_WRITE)))
7853 Error(ERR_WARN, "cannot save score for level %d", nr);
7857 fprintf(file, "%s\n\n", SCORE_COOKIE);
7859 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7860 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
7864 SetFilePermissions(filename, PERMS_PUBLIC);
7868 /* ========================================================================= */
7869 /* setup file functions */
7870 /* ========================================================================= */
7872 #define TOKEN_STR_PLAYER_PREFIX "player_"
7875 #define SETUP_TOKEN_PLAYER_NAME 0
7876 #define SETUP_TOKEN_SOUND 1
7877 #define SETUP_TOKEN_SOUND_LOOPS 2
7878 #define SETUP_TOKEN_SOUND_MUSIC 3
7879 #define SETUP_TOKEN_SOUND_SIMPLE 4
7880 #define SETUP_TOKEN_TOONS 5
7881 #define SETUP_TOKEN_SCROLL_DELAY 6
7882 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
7883 #define SETUP_TOKEN_SOFT_SCROLLING 8
7884 #define SETUP_TOKEN_FADE_SCREENS 9
7885 #define SETUP_TOKEN_AUTORECORD 10
7886 #define SETUP_TOKEN_SHOW_TITLESCREEN 11
7887 #define SETUP_TOKEN_QUICK_DOORS 12
7888 #define SETUP_TOKEN_TEAM_MODE 13
7889 #define SETUP_TOKEN_HANDICAP 14
7890 #define SETUP_TOKEN_SKIP_LEVELS 15
7891 #define SETUP_TOKEN_TIME_LIMIT 16
7892 #define SETUP_TOKEN_FULLSCREEN 17
7893 #define SETUP_TOKEN_FULLSCREEN_MODE 18
7894 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT 19
7895 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY 20
7896 #define SETUP_TOKEN_ASK_ON_ESCAPE 21
7897 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 22
7898 #define SETUP_TOKEN_QUICK_SWITCH 23
7899 #define SETUP_TOKEN_INPUT_ON_FOCUS 24
7900 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 25
7901 #define SETUP_TOKEN_GAME_FRAME_DELAY 26
7902 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 27
7903 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 28
7904 #define SETUP_TOKEN_GRAPHICS_SET 29
7905 #define SETUP_TOKEN_SOUNDS_SET 30
7906 #define SETUP_TOKEN_MUSIC_SET 31
7907 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 32
7908 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 33
7909 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 34
7910 #define SETUP_TOKEN_VOLUME_SIMPLE 35
7911 #define SETUP_TOKEN_VOLUME_LOOPS 36
7912 #define SETUP_TOKEN_VOLUME_MUSIC 37
7913 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE 38
7914 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 39
7915 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE 40
7917 #define NUM_GLOBAL_SETUP_TOKENS 41
7920 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
7921 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
7922 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
7923 #define SETUP_TOKEN_EDITOR_EL_MORE 3
7924 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 4
7925 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 5
7926 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 6
7927 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 7
7928 #define SETUP_TOKEN_EDITOR_EL_CHARS 8
7929 #define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS 9
7930 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 10
7931 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 11
7932 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 12
7933 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 13
7934 #define SETUP_TOKEN_EDITOR_EL_BY_GAME 14
7935 #define SETUP_TOKEN_EDITOR_EL_BY_TYPE 15
7936 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 16
7938 #define NUM_EDITOR_SETUP_TOKENS 17
7940 /* editor cascade setup */
7941 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
7942 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
7943 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
7944 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
7945 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
7946 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
7947 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
7948 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
7949 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
7950 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
7951 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
7952 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
7953 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
7954 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
7955 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
7957 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
7959 /* shortcut setup */
7960 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
7961 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
7962 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
7963 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
7964 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
7965 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
7966 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
7967 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
7968 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
7969 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
7970 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
7971 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
7972 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
7973 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
7974 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
7975 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
7976 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
7977 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
7978 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
7979 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
7980 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
7982 #define NUM_SHORTCUT_SETUP_TOKENS 21
7985 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
7986 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
7987 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
7988 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
7989 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
7990 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
7991 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
7992 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
7993 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
7994 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
7995 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
7996 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
7997 #define SETUP_TOKEN_PLAYER_KEY_UP 12
7998 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
7999 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8000 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8002 #define NUM_PLAYER_SETUP_TOKENS 16
8005 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8006 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8007 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8009 #define NUM_SYSTEM_SETUP_TOKENS 3
8012 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8014 #define NUM_OPTIONS_SETUP_TOKENS 1
8017 static struct SetupInfo si;
8018 static struct SetupEditorInfo sei;
8019 static struct SetupEditorCascadeInfo seci;
8020 static struct SetupShortcutInfo ssi;
8021 static struct SetupInputInfo sii;
8022 static struct SetupSystemInfo syi;
8023 static struct OptionInfo soi;
8025 static struct TokenInfo global_setup_tokens[] =
8027 { TYPE_STRING, &si.player_name, "player_name" },
8028 { TYPE_SWITCH, &si.sound, "sound" },
8029 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8030 { TYPE_SWITCH, &si.sound_music, "background_music" },
8031 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8032 { TYPE_SWITCH, &si.toons, "toons" },
8033 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8034 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8035 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
8036 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8037 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8038 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8039 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8040 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8041 { TYPE_SWITCH, &si.handicap, "handicap" },
8042 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8043 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8044 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8045 { TYPE_STRING, &si.fullscreen_mode, "fullscreen_mode" },
8046 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8047 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8048 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8049 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8050 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8051 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8052 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8053 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8054 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8055 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8056 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8057 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8058 { TYPE_STRING, &si.music_set, "music_set" },
8059 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8060 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8061 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8062 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8063 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8064 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8065 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8066 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8067 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8070 static boolean not_used = FALSE;
8071 static struct TokenInfo editor_setup_tokens[] =
8073 { TYPE_SWITCH, ¬_used, "editor.el_boulderdash" },
8074 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine" },
8075 { TYPE_SWITCH, ¬_used, "editor.el_emerald_mine_club" },
8076 { TYPE_SWITCH, ¬_used, "editor.el_more" },
8077 { TYPE_SWITCH, ¬_used, "editor.el_sokoban" },
8078 { TYPE_SWITCH, ¬_used, "editor.el_supaplex" },
8079 { TYPE_SWITCH, ¬_used, "editor.el_diamond_caves" },
8080 { TYPE_SWITCH, ¬_used, "editor.el_dx_boulderdash" },
8081 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
8082 { TYPE_SWITCH, &sei.el_steel_chars, "editor.el_steel_chars" },
8083 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8084 { TYPE_SWITCH, ¬_used, "editor.el_headlines" },
8085 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8086 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8087 { TYPE_SWITCH, &sei.el_by_game, "editor.el_by_game" },
8088 { TYPE_SWITCH, &sei.el_by_type, "editor.el_by_type" },
8089 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8092 static struct TokenInfo editor_cascade_setup_tokens[] =
8094 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8095 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8096 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8097 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8098 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8099 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8100 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8101 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8102 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8103 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8104 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8105 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8106 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8107 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8108 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8111 static struct TokenInfo shortcut_setup_tokens[] =
8113 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8114 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8115 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8116 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8117 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8118 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8119 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8120 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8121 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8122 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8123 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8124 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8125 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8126 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8127 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8128 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8129 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8130 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8131 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8132 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8133 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8136 static struct TokenInfo player_setup_tokens[] =
8138 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8139 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8140 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8141 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8142 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8143 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8144 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8145 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8146 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8147 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8148 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8149 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8150 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8151 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8152 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8153 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8156 static struct TokenInfo system_setup_tokens[] =
8158 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8159 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8160 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8163 static struct TokenInfo options_setup_tokens[] =
8165 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8168 static char *get_corrected_login_name(char *login_name)
8170 /* needed because player name must be a fixed length string */
8171 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8173 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8174 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8176 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8177 if (strchr(login_name_new, ' '))
8178 *strchr(login_name_new, ' ') = '\0';
8180 return login_name_new;
8183 static void setSetupInfoToDefaults(struct SetupInfo *si)
8187 si->player_name = get_corrected_login_name(getLoginName());
8190 si->sound_loops = TRUE;
8191 si->sound_music = TRUE;
8192 si->sound_simple = TRUE;
8194 si->scroll_delay = TRUE;
8195 si->scroll_delay_value = STD_SCROLL_DELAY;
8196 si->soft_scrolling = TRUE;
8197 si->fade_screens = TRUE;
8198 si->autorecord = TRUE;
8199 si->show_titlescreen = TRUE;
8200 si->quick_doors = FALSE;
8201 si->team_mode = FALSE;
8202 si->handicap = TRUE;
8203 si->skip_levels = TRUE;
8204 si->time_limit = TRUE;
8205 si->fullscreen = FALSE;
8206 si->fullscreen_mode = getStringCopy(DEFAULT_FULLSCREEN_MODE);
8207 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8208 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8209 si->ask_on_escape = TRUE;
8210 si->ask_on_escape_editor = TRUE;
8211 si->quick_switch = FALSE;
8212 si->input_on_focus = FALSE;
8213 si->prefer_aga_graphics = TRUE;
8214 si->game_frame_delay = GAME_FRAME_DELAY;
8215 si->sp_show_border_elements = FALSE;
8216 si->small_game_graphics = FALSE;
8218 si->graphics_set = getStringCopy(GFX_DEFAULT_SUBDIR);
8219 si->sounds_set = getStringCopy(SND_DEFAULT_SUBDIR);
8220 si->music_set = getStringCopy(MUS_DEFAULT_SUBDIR);
8221 si->override_level_graphics = FALSE;
8222 si->override_level_sounds = FALSE;
8223 si->override_level_music = FALSE;
8225 si->volume_simple = 100; /* percent */
8226 si->volume_loops = 100; /* percent */
8227 si->volume_music = 100; /* percent */
8229 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8230 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8231 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8233 si->editor.el_boulderdash = TRUE;
8234 si->editor.el_emerald_mine = TRUE;
8235 si->editor.el_emerald_mine_club = TRUE;
8236 si->editor.el_more = TRUE;
8237 si->editor.el_sokoban = TRUE;
8238 si->editor.el_supaplex = TRUE;
8239 si->editor.el_diamond_caves = TRUE;
8240 si->editor.el_dx_boulderdash = TRUE;
8241 si->editor.el_chars = TRUE;
8242 si->editor.el_steel_chars = TRUE;
8243 si->editor.el_custom = TRUE;
8245 si->editor.el_headlines = TRUE;
8246 si->editor.el_user_defined = FALSE;
8247 si->editor.el_dynamic = TRUE;
8249 si->editor.show_element_token = FALSE;
8251 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8252 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8253 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8255 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8256 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8257 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8258 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8259 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8261 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8262 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8263 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8264 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8265 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8266 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8268 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8269 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8270 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8272 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8273 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8274 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8275 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8277 for (i = 0; i < MAX_PLAYERS; i++)
8279 si->input[i].use_joystick = FALSE;
8280 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8281 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8282 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8283 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8284 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8285 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8286 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8287 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8288 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8289 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8290 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8291 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8292 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8293 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8294 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8297 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8298 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8299 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8301 si->options.verbose = FALSE;
8303 #if defined(CREATE_SPECIAL_EDITION_RND_JUE)
8305 si->handicap = FALSE;
8306 si->fullscreen = TRUE;
8307 si->override_level_graphics = AUTO;
8308 si->override_level_sounds = AUTO;
8309 si->override_level_music = AUTO;
8312 #if defined(PLATFORM_ANDROID)
8313 si->fullscreen = TRUE;
8317 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8319 si->editor_cascade.el_bd = TRUE;
8320 si->editor_cascade.el_em = TRUE;
8321 si->editor_cascade.el_emc = TRUE;
8322 si->editor_cascade.el_rnd = TRUE;
8323 si->editor_cascade.el_sb = TRUE;
8324 si->editor_cascade.el_sp = TRUE;
8325 si->editor_cascade.el_dc = TRUE;
8326 si->editor_cascade.el_dx = TRUE;
8328 si->editor_cascade.el_chars = FALSE;
8329 si->editor_cascade.el_steel_chars = FALSE;
8330 si->editor_cascade.el_ce = FALSE;
8331 si->editor_cascade.el_ge = FALSE;
8332 si->editor_cascade.el_ref = FALSE;
8333 si->editor_cascade.el_user = FALSE;
8334 si->editor_cascade.el_dynamic = FALSE;
8337 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8341 if (!setup_file_hash)
8346 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8347 setSetupInfo(global_setup_tokens, i,
8348 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
8353 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8354 setSetupInfo(editor_setup_tokens, i,
8355 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
8358 /* shortcut setup */
8359 ssi = setup.shortcut;
8360 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8361 setSetupInfo(shortcut_setup_tokens, i,
8362 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
8363 setup.shortcut = ssi;
8366 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8370 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8372 sii = setup.input[pnr];
8373 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8375 char full_token[100];
8377 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8378 setSetupInfo(player_setup_tokens, i,
8379 getHashEntry(setup_file_hash, full_token));
8381 setup.input[pnr] = sii;
8386 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8387 setSetupInfo(system_setup_tokens, i,
8388 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
8392 soi = setup.options;
8393 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8394 setSetupInfo(options_setup_tokens, i,
8395 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
8396 setup.options = soi;
8399 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8403 if (!setup_file_hash)
8406 /* editor cascade setup */
8407 seci = setup.editor_cascade;
8408 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8409 setSetupInfo(editor_cascade_setup_tokens, i,
8410 getHashEntry(setup_file_hash,
8411 editor_cascade_setup_tokens[i].text));
8412 setup.editor_cascade = seci;
8417 char *filename = getSetupFilename();
8418 SetupFileHash *setup_file_hash = NULL;
8420 /* always start with reliable default values */
8421 setSetupInfoToDefaults(&setup);
8423 setup_file_hash = loadSetupFileHash(filename);
8425 if (setup_file_hash)
8427 char *player_name_new;
8429 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
8430 decodeSetupFileHash(setup_file_hash);
8432 freeSetupFileHash(setup_file_hash);
8434 /* needed to work around problems with fixed length strings */
8435 player_name_new = get_corrected_login_name(setup.player_name);
8436 free(setup.player_name);
8437 setup.player_name = player_name_new;
8439 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
8440 if (setup.scroll_delay == FALSE)
8442 setup.scroll_delay_value = MIN_SCROLL_DELAY;
8443 setup.scroll_delay = TRUE; /* now always "on" */
8446 /* make sure that scroll delay value stays inside valid range */
8447 setup.scroll_delay_value =
8448 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
8451 Error(ERR_WARN, "using default setup values");
8454 void LoadSetup_EditorCascade()
8456 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8457 SetupFileHash *setup_file_hash = NULL;
8459 /* always start with reliable default values */
8460 setSetupInfoToDefaults_EditorCascade(&setup);
8462 setup_file_hash = loadSetupFileHash(filename);
8464 if (setup_file_hash)
8466 checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
8467 decodeSetupFileHash_EditorCascade(setup_file_hash);
8469 freeSetupFileHash(setup_file_hash);
8477 char *filename = getSetupFilename();
8481 InitUserDataDirectory();
8483 if (!(file = fopen(filename, MODE_WRITE)))
8485 Error(ERR_WARN, "cannot write setup file '%s'", filename);
8489 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
8490 getCookie("SETUP")));
8491 fprintf(file, "\n");
8495 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8497 /* just to make things nicer :) */
8498 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
8499 i == SETUP_TOKEN_GRAPHICS_SET ||
8500 i == SETUP_TOKEN_VOLUME_SIMPLE ||
8501 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
8502 fprintf(file, "\n");
8504 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
8509 fprintf(file, "\n");
8510 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8511 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
8513 /* shortcut setup */
8514 ssi = setup.shortcut;
8515 fprintf(file, "\n");
8516 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8517 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
8520 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8524 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8525 fprintf(file, "\n");
8527 sii = setup.input[pnr];
8528 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8529 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
8534 fprintf(file, "\n");
8535 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8536 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
8539 soi = setup.options;
8540 fprintf(file, "\n");
8541 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8542 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
8546 SetFilePermissions(filename, PERMS_PRIVATE);
8549 void SaveSetup_EditorCascade()
8551 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8555 InitUserDataDirectory();
8557 if (!(file = fopen(filename, MODE_WRITE)))
8559 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
8564 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
8565 getCookie("SETUP")));
8566 fprintf(file, "\n");
8568 seci = setup.editor_cascade;
8569 fprintf(file, "\n");
8570 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8571 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
8575 SetFilePermissions(filename, PERMS_PRIVATE);
8580 void LoadCustomElementDescriptions()
8582 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8583 SetupFileHash *setup_file_hash;
8586 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8588 if (element_info[i].custom_description != NULL)
8590 free(element_info[i].custom_description);
8591 element_info[i].custom_description = NULL;
8595 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8598 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8600 char *token = getStringCat2(element_info[i].token_name, ".name");
8601 char *value = getHashEntry(setup_file_hash, token);
8604 element_info[i].custom_description = getStringCopy(value);
8609 freeSetupFileHash(setup_file_hash);
8612 static int getElementFromToken(char *token)
8614 char *value = getHashEntry(element_token_hash, token);
8619 Error(ERR_WARN, "unknown element token '%s'", token);
8621 return EL_UNDEFINED;
8624 static int get_token_parameter_value(char *token, char *value_raw)
8628 if (token == NULL || value_raw == NULL)
8629 return ARG_UNDEFINED_VALUE;
8631 suffix = strrchr(token, '.');
8635 if (strEqual(suffix, ".element"))
8636 return getElementFromToken(value_raw);
8638 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
8639 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
8642 void InitMenuDesignSettings_Static()
8646 /* always start with reliable default values from static default config */
8647 for (i = 0; image_config_vars[i].token != NULL; i++)
8649 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
8652 *image_config_vars[i].value =
8653 get_token_parameter_value(image_config_vars[i].token, value);
8657 static void InitMenuDesignSettings_SpecialPreProcessing()
8661 /* the following initializes hierarchical values from static configuration */
8663 /* special case: initialize "ARG_DEFAULT" values in static default config */
8664 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
8665 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
8666 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
8667 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
8668 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
8669 titlemessage_default.fade_mode = title_default.fade_mode;
8670 titlemessage_default.fade_delay = title_default.fade_delay;
8671 titlemessage_default.post_delay = title_default.post_delay;
8672 titlemessage_default.auto_delay = title_default.auto_delay;
8674 /* special case: initialize "ARG_DEFAULT" values in static default config */
8675 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
8676 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
8678 titlemessage_initial[i] = titlemessage_initial_default;
8679 titlemessage[i] = titlemessage_default;
8682 /* special case: initialize "ARG_DEFAULT" values in static default config */
8683 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
8684 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8686 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
8687 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
8690 /* special case: initialize "ARG_DEFAULT" values in static default config */
8691 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
8692 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8694 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
8695 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
8696 if (i != GFX_SPECIAL_ARG_EDITOR) /* editor value already initialized */
8697 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
8701 static void InitMenuDesignSettings_SpecialPostProcessing()
8703 /* special case: initialize later added SETUP list size from LEVELS value */
8704 if (menu.list_size[GAME_MODE_SETUP] == -1)
8705 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
8708 static void LoadMenuDesignSettingsFromFilename(char *filename)
8710 static struct TitleMessageInfo tmi;
8711 static struct TokenInfo titlemessage_tokens[] =
8713 { TYPE_INTEGER, &tmi.x, ".x" },
8714 { TYPE_INTEGER, &tmi.y, ".y" },
8715 { TYPE_INTEGER, &tmi.width, ".width" },
8716 { TYPE_INTEGER, &tmi.height, ".height" },
8717 { TYPE_INTEGER, &tmi.chars, ".chars" },
8718 { TYPE_INTEGER, &tmi.lines, ".lines" },
8719 { TYPE_INTEGER, &tmi.align, ".align" },
8720 { TYPE_INTEGER, &tmi.valign, ".valign" },
8721 { TYPE_INTEGER, &tmi.font, ".font" },
8722 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
8723 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
8724 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
8725 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
8726 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
8727 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
8728 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
8729 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
8735 struct TitleMessageInfo *array;
8738 titlemessage_arrays[] =
8740 { titlemessage_initial, "[titlemessage_initial]" },
8741 { titlemessage, "[titlemessage]" },
8745 SetupFileHash *setup_file_hash;
8748 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8751 /* the following initializes hierarchical values from dynamic configuration */
8753 /* special case: initialize with default values that may be overwritten */
8754 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
8755 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8757 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
8758 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
8759 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
8761 if (value_1 != NULL)
8762 menu.draw_xoffset[i] = get_integer_from_string(value_1);
8763 if (value_2 != NULL)
8764 menu.draw_yoffset[i] = get_integer_from_string(value_2);
8765 if (value_3 != NULL)
8766 menu.list_size[i] = get_integer_from_string(value_3);
8769 /* special case: initialize with default values that may be overwritten */
8770 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
8771 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
8773 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
8774 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
8776 if (value_1 != NULL)
8777 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
8778 if (value_2 != NULL)
8779 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
8782 /* special case: initialize with default values that may be overwritten */
8783 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
8784 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
8786 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
8787 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
8789 if (value_1 != NULL)
8790 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
8791 if (value_2 != NULL)
8792 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
8795 /* special case: initialize with default values that may be overwritten */
8796 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
8797 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8799 char *token_1 = "menu.enter_screen.fade_mode";
8800 char *token_2 = "menu.enter_screen.fade_delay";
8801 char *token_3 = "menu.enter_screen.post_delay";
8802 char *token_4 = "menu.leave_screen.fade_mode";
8803 char *token_5 = "menu.leave_screen.fade_delay";
8804 char *token_6 = "menu.leave_screen.post_delay";
8805 char *value_1 = getHashEntry(setup_file_hash, token_1);
8806 char *value_2 = getHashEntry(setup_file_hash, token_2);
8807 char *value_3 = getHashEntry(setup_file_hash, token_3);
8808 char *value_4 = getHashEntry(setup_file_hash, token_4);
8809 char *value_5 = getHashEntry(setup_file_hash, token_5);
8810 char *value_6 = getHashEntry(setup_file_hash, token_6);
8812 if (value_1 != NULL)
8813 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
8815 if (value_2 != NULL)
8816 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
8818 if (value_3 != NULL)
8819 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
8821 if (value_4 != NULL)
8822 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
8824 if (value_5 != NULL)
8825 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
8827 if (value_6 != NULL)
8828 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
8832 /* special case: initialize with default values that may be overwritten */
8833 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
8834 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8836 char *token_01 = "viewport.playfield.x";
8837 char *token_02 = "viewport.playfield.y";
8838 char *token_03 = "viewport.playfield.width";
8839 char *token_04 = "viewport.playfield.height";
8840 char *token_05 = "viewport.playfield.border_size";
8841 char *token_06 = "viewport.door_1.x";
8842 char *token_07 = "viewport.door_1.y";
8843 char *token_08 = "viewport.door_1.width";
8844 char *token_09 = "viewport.door_1.height";
8845 char *token_10 = "viewport.door_1.border_size";
8846 char *token_11 = "viewport.door_2.x";
8847 char *token_12 = "viewport.door_2.y";
8848 char *token_13 = "viewport.door_2.width";
8849 char *token_14 = "viewport.door_2.height";
8850 char *token_15 = "viewport.door_2.border_size";
8851 char *value_01 = getHashEntry(setup_file_hash, token_01);
8852 char *value_02 = getHashEntry(setup_file_hash, token_02);
8853 char *value_03 = getHashEntry(setup_file_hash, token_03);
8854 char *value_04 = getHashEntry(setup_file_hash, token_04);
8855 char *value_05 = getHashEntry(setup_file_hash, token_05);
8856 char *value_06 = getHashEntry(setup_file_hash, token_06);
8857 char *value_07 = getHashEntry(setup_file_hash, token_07);
8858 char *value_08 = getHashEntry(setup_file_hash, token_08);
8859 char *value_09 = getHashEntry(setup_file_hash, token_09);
8860 char *value_10 = getHashEntry(setup_file_hash, token_10);
8861 char *value_11 = getHashEntry(setup_file_hash, token_11);
8862 char *value_12 = getHashEntry(setup_file_hash, token_12);
8863 char *value_13 = getHashEntry(setup_file_hash, token_13);
8864 char *value_14 = getHashEntry(setup_file_hash, token_14);
8865 char *value_15 = getHashEntry(setup_file_hash, token_15);
8867 if (value_01 != NULL)
8868 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
8869 if (value_02 != NULL)
8870 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
8871 if (value_03 != NULL)
8872 viewport.playfield[i].width = get_token_parameter_value(token_03,
8874 if (value_04 != NULL)
8875 viewport.playfield[i].height = get_token_parameter_value(token_04,
8877 if (value_05 != NULL)
8878 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
8880 if (value_06 != NULL)
8881 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
8882 if (value_07 != NULL)
8883 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
8884 if (value_08 != NULL)
8885 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
8886 if (value_09 != NULL)
8887 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
8888 if (value_10 != NULL)
8889 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
8891 if (value_11 != NULL)
8892 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
8893 if (value_12 != NULL)
8894 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
8895 if (value_13 != NULL)
8896 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
8897 if (value_14 != NULL)
8898 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
8899 if (value_15 != NULL)
8900 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
8904 /* special case: initialize with default values that may be overwritten */
8905 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
8906 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
8908 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
8909 char *base_token = titlemessage_arrays[i].text;
8911 for (j = 0; titlemessage_tokens[j].type != -1; j++)
8913 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
8914 char *value = getHashEntry(setup_file_hash, token);
8918 int parameter_value = get_token_parameter_value(token, value);
8920 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
8924 if (titlemessage_tokens[j].type == TYPE_INTEGER)
8925 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
8927 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
8937 /* read (and overwrite with) values that may be specified in config file */
8938 for (i = 0; image_config_vars[i].token != NULL; i++)
8940 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
8942 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
8943 if (value != NULL && !strEqual(value, ARG_DEFAULT))
8944 *image_config_vars[i].value =
8945 get_token_parameter_value(image_config_vars[i].token, value);
8948 freeSetupFileHash(setup_file_hash);
8951 void LoadMenuDesignSettings()
8953 char *filename_base = UNDEFINED_FILENAME, *filename_local;
8955 InitMenuDesignSettings_Static();
8956 InitMenuDesignSettings_SpecialPreProcessing();
8958 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
8960 /* first look for special settings configured in level series config */
8961 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
8963 if (fileExists(filename_base))
8964 LoadMenuDesignSettingsFromFilename(filename_base);
8967 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8969 if (filename_local != NULL && !strEqual(filename_base, filename_local))
8970 LoadMenuDesignSettingsFromFilename(filename_local);
8972 InitMenuDesignSettings_SpecialPostProcessing();
8975 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
8977 char *filename = getEditorSetupFilename();
8978 SetupFileList *setup_file_list, *list;
8979 SetupFileHash *element_hash;
8980 int num_unknown_tokens = 0;
8983 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
8986 element_hash = newSetupFileHash();
8988 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8989 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
8991 /* determined size may be larger than needed (due to unknown elements) */
8993 for (list = setup_file_list; list != NULL; list = list->next)
8996 /* add space for up to 3 more elements for padding that may be needed */
8999 /* free memory for old list of elements, if needed */
9000 checked_free(*elements);
9002 /* allocate memory for new list of elements */
9003 *elements = checked_malloc(*num_elements * sizeof(int));
9006 for (list = setup_file_list; list != NULL; list = list->next)
9008 char *value = getHashEntry(element_hash, list->token);
9010 if (value == NULL) /* try to find obsolete token mapping */
9012 char *mapped_token = get_mapped_token(list->token);
9014 if (mapped_token != NULL)
9016 value = getHashEntry(element_hash, mapped_token);
9024 (*elements)[(*num_elements)++] = atoi(value);
9028 if (num_unknown_tokens == 0)
9030 Error(ERR_INFO_LINE, "-");
9031 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9032 Error(ERR_INFO, "- config file: '%s'", filename);
9034 num_unknown_tokens++;
9037 Error(ERR_INFO, "- token: '%s'", list->token);
9041 if (num_unknown_tokens > 0)
9042 Error(ERR_INFO_LINE, "-");
9044 while (*num_elements % 4) /* pad with empty elements, if needed */
9045 (*elements)[(*num_elements)++] = EL_EMPTY;
9047 freeSetupFileList(setup_file_list);
9048 freeSetupFileHash(element_hash);
9051 for (i = 0; i < *num_elements; i++)
9052 printf("editor: element '%s' [%d]\n",
9053 element_info[(*elements)[i]].token_name, (*elements)[i]);
9057 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9060 SetupFileHash *setup_file_hash = NULL;
9061 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9062 char *filename_music, *filename_prefix, *filename_info;
9068 token_to_value_ptr[] =
9070 { "title_header", &tmp_music_file_info.title_header },
9071 { "artist_header", &tmp_music_file_info.artist_header },
9072 { "album_header", &tmp_music_file_info.album_header },
9073 { "year_header", &tmp_music_file_info.year_header },
9075 { "title", &tmp_music_file_info.title },
9076 { "artist", &tmp_music_file_info.artist },
9077 { "album", &tmp_music_file_info.album },
9078 { "year", &tmp_music_file_info.year },
9084 filename_music = (is_sound ? getCustomSoundFilename(basename) :
9085 getCustomMusicFilename(basename));
9087 if (filename_music == NULL)
9090 /* ---------- try to replace file extension ---------- */
9092 filename_prefix = getStringCopy(filename_music);
9093 if (strrchr(filename_prefix, '.') != NULL)
9094 *strrchr(filename_prefix, '.') = '\0';
9095 filename_info = getStringCat2(filename_prefix, ".txt");
9097 if (fileExists(filename_info))
9098 setup_file_hash = loadSetupFileHash(filename_info);
9100 free(filename_prefix);
9101 free(filename_info);
9103 if (setup_file_hash == NULL)
9105 /* ---------- try to add file extension ---------- */
9107 filename_prefix = getStringCopy(filename_music);
9108 filename_info = getStringCat2(filename_prefix, ".txt");
9110 if (fileExists(filename_info))
9111 setup_file_hash = loadSetupFileHash(filename_info);
9113 free(filename_prefix);
9114 free(filename_info);
9117 if (setup_file_hash == NULL)
9120 /* ---------- music file info found ---------- */
9122 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
9124 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
9126 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
9128 *token_to_value_ptr[i].value_ptr =
9129 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
9132 tmp_music_file_info.basename = getStringCopy(basename);
9133 tmp_music_file_info.music = music;
9134 tmp_music_file_info.is_sound = is_sound;
9136 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
9137 *new_music_file_info = tmp_music_file_info;
9139 return new_music_file_info;
9142 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
9144 return get_music_file_info_ext(basename, music, FALSE);
9147 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
9149 return get_music_file_info_ext(basename, sound, TRUE);
9152 static boolean music_info_listed_ext(struct MusicFileInfo *list,
9153 char *basename, boolean is_sound)
9155 for (; list != NULL; list = list->next)
9156 if (list->is_sound == is_sound && strEqual(list->basename, basename))
9162 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
9164 return music_info_listed_ext(list, basename, FALSE);
9167 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
9169 return music_info_listed_ext(list, basename, TRUE);
9172 void LoadMusicInfo()
9174 char *music_directory = getCustomMusicDirectory();
9175 int num_music = getMusicListSize();
9176 int num_music_noconf = 0;
9177 int num_sounds = getSoundListSize();
9179 DirectoryEntry *dir_entry;
9180 struct FileInfo *music, *sound;
9181 struct MusicFileInfo *next, **new;
9184 while (music_file_info != NULL)
9186 next = music_file_info->next;
9188 checked_free(music_file_info->basename);
9190 checked_free(music_file_info->title_header);
9191 checked_free(music_file_info->artist_header);
9192 checked_free(music_file_info->album_header);
9193 checked_free(music_file_info->year_header);
9195 checked_free(music_file_info->title);
9196 checked_free(music_file_info->artist);
9197 checked_free(music_file_info->album);
9198 checked_free(music_file_info->year);
9200 free(music_file_info);
9202 music_file_info = next;
9205 new = &music_file_info;
9207 for (i = 0; i < num_music; i++)
9209 music = getMusicListEntry(i);
9211 if (music->filename == NULL)
9214 if (strEqual(music->filename, UNDEFINED_FILENAME))
9217 /* a configured file may be not recognized as music */
9218 if (!FileIsMusic(music->filename))
9221 if (!music_info_listed(music_file_info, music->filename))
9223 *new = get_music_file_info(music->filename, i);
9226 new = &(*new)->next;
9230 if ((dir = openDirectory(music_directory)) == NULL)
9232 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
9236 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
9238 char *basename = dir_entry->basename;
9239 boolean music_already_used = FALSE;
9242 /* skip all music files that are configured in music config file */
9243 for (i = 0; i < num_music; i++)
9245 music = getMusicListEntry(i);
9247 if (music->filename == NULL)
9250 if (strEqual(basename, music->filename))
9252 music_already_used = TRUE;
9257 if (music_already_used)
9260 if (!FileIsMusic(basename))
9263 if (!music_info_listed(music_file_info, basename))
9265 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
9268 new = &(*new)->next;
9274 closeDirectory(dir);
9276 for (i = 0; i < num_sounds; i++)
9278 sound = getSoundListEntry(i);
9280 if (sound->filename == NULL)
9283 if (strEqual(sound->filename, UNDEFINED_FILENAME))
9286 /* a configured file may be not recognized as sound */
9287 if (!FileIsSound(sound->filename))
9290 if (!sound_info_listed(music_file_info, sound->filename))
9292 *new = get_sound_file_info(sound->filename, i);
9294 new = &(*new)->next;
9299 void add_helpanim_entry(int element, int action, int direction, int delay,
9300 int *num_list_entries)
9302 struct HelpAnimInfo *new_list_entry;
9303 (*num_list_entries)++;
9306 checked_realloc(helpanim_info,
9307 *num_list_entries * sizeof(struct HelpAnimInfo));
9308 new_list_entry = &helpanim_info[*num_list_entries - 1];
9310 new_list_entry->element = element;
9311 new_list_entry->action = action;
9312 new_list_entry->direction = direction;
9313 new_list_entry->delay = delay;
9316 void print_unknown_token(char *filename, char *token, int token_nr)
9320 Error(ERR_INFO_LINE, "-");
9321 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9322 Error(ERR_INFO, "- config file: '%s'", filename);
9325 Error(ERR_INFO, "- token: '%s'", token);
9328 void print_unknown_token_end(int token_nr)
9331 Error(ERR_INFO_LINE, "-");
9334 void LoadHelpAnimInfo()
9336 char *filename = getHelpAnimFilename();
9337 SetupFileList *setup_file_list = NULL, *list;
9338 SetupFileHash *element_hash, *action_hash, *direction_hash;
9339 int num_list_entries = 0;
9340 int num_unknown_tokens = 0;
9343 if (fileExists(filename))
9344 setup_file_list = loadSetupFileList(filename);
9346 if (setup_file_list == NULL)
9348 /* use reliable default values from static configuration */
9349 SetupFileList *insert_ptr;
9351 insert_ptr = setup_file_list =
9352 newSetupFileList(helpanim_config[0].token,
9353 helpanim_config[0].value);
9355 for (i = 1; helpanim_config[i].token; i++)
9356 insert_ptr = addListEntry(insert_ptr,
9357 helpanim_config[i].token,
9358 helpanim_config[i].value);
9361 element_hash = newSetupFileHash();
9362 action_hash = newSetupFileHash();
9363 direction_hash = newSetupFileHash();
9365 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
9366 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9368 for (i = 0; i < NUM_ACTIONS; i++)
9369 setHashEntry(action_hash, element_action_info[i].suffix,
9370 i_to_a(element_action_info[i].value));
9372 /* do not store direction index (bit) here, but direction value! */
9373 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
9374 setHashEntry(direction_hash, element_direction_info[i].suffix,
9375 i_to_a(1 << element_direction_info[i].value));
9377 for (list = setup_file_list; list != NULL; list = list->next)
9379 char *element_token, *action_token, *direction_token;
9380 char *element_value, *action_value, *direction_value;
9381 int delay = atoi(list->value);
9383 if (strEqual(list->token, "end"))
9385 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
9390 /* first try to break element into element/action/direction parts;
9391 if this does not work, also accept combined "element[.act][.dir]"
9392 elements (like "dynamite.active"), which are unique elements */
9394 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
9396 element_value = getHashEntry(element_hash, list->token);
9397 if (element_value != NULL) /* element found */
9398 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9402 /* no further suffixes found -- this is not an element */
9403 print_unknown_token(filename, list->token, num_unknown_tokens++);
9409 /* token has format "<prefix>.<something>" */
9411 action_token = strchr(list->token, '.'); /* suffix may be action ... */
9412 direction_token = action_token; /* ... or direction */
9414 element_token = getStringCopy(list->token);
9415 *strchr(element_token, '.') = '\0';
9417 element_value = getHashEntry(element_hash, element_token);
9419 if (element_value == NULL) /* this is no element */
9421 element_value = getHashEntry(element_hash, list->token);
9422 if (element_value != NULL) /* combined element found */
9423 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9426 print_unknown_token(filename, list->token, num_unknown_tokens++);
9428 free(element_token);
9433 action_value = getHashEntry(action_hash, action_token);
9435 if (action_value != NULL) /* action found */
9437 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
9440 free(element_token);
9445 direction_value = getHashEntry(direction_hash, direction_token);
9447 if (direction_value != NULL) /* direction found */
9449 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
9452 free(element_token);
9457 if (strchr(action_token + 1, '.') == NULL)
9459 /* no further suffixes found -- this is not an action nor direction */
9461 element_value = getHashEntry(element_hash, list->token);
9462 if (element_value != NULL) /* combined element found */
9463 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9466 print_unknown_token(filename, list->token, num_unknown_tokens++);
9468 free(element_token);
9473 /* token has format "<prefix>.<suffix>.<something>" */
9475 direction_token = strchr(action_token + 1, '.');
9477 action_token = getStringCopy(action_token);
9478 *strchr(action_token + 1, '.') = '\0';
9480 action_value = getHashEntry(action_hash, action_token);
9482 if (action_value == NULL) /* this is no action */
9484 element_value = getHashEntry(element_hash, list->token);
9485 if (element_value != NULL) /* combined element found */
9486 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9489 print_unknown_token(filename, list->token, num_unknown_tokens++);
9491 free(element_token);
9497 direction_value = getHashEntry(direction_hash, direction_token);
9499 if (direction_value != NULL) /* direction found */
9501 add_helpanim_entry(atoi(element_value), atoi(action_value),
9502 atoi(direction_value), delay, &num_list_entries);
9504 free(element_token);
9510 /* this is no direction */
9512 element_value = getHashEntry(element_hash, list->token);
9513 if (element_value != NULL) /* combined element found */
9514 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9517 print_unknown_token(filename, list->token, num_unknown_tokens++);
9519 free(element_token);
9523 print_unknown_token_end(num_unknown_tokens);
9525 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
9526 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
9528 freeSetupFileList(setup_file_list);
9529 freeSetupFileHash(element_hash);
9530 freeSetupFileHash(action_hash);
9531 freeSetupFileHash(direction_hash);
9534 for (i = 0; i < num_list_entries; i++)
9535 printf("::: '%s': %d, %d, %d => %d\n",
9536 EL_NAME(helpanim_info[i].element),
9537 helpanim_info[i].element,
9538 helpanim_info[i].action,
9539 helpanim_info[i].direction,
9540 helpanim_info[i].delay);
9544 void LoadHelpTextInfo()
9546 char *filename = getHelpTextFilename();
9549 if (helptext_info != NULL)
9551 freeSetupFileHash(helptext_info);
9552 helptext_info = NULL;
9555 if (fileExists(filename))
9556 helptext_info = loadSetupFileHash(filename);
9558 if (helptext_info == NULL)
9560 /* use reliable default values from static configuration */
9561 helptext_info = newSetupFileHash();
9563 for (i = 0; helptext_config[i].token; i++)
9564 setHashEntry(helptext_info,
9565 helptext_config[i].token,
9566 helptext_config[i].value);
9570 BEGIN_HASH_ITERATION(helptext_info, itr)
9572 printf("::: '%s' => '%s'\n",
9573 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
9575 END_HASH_ITERATION(hash, itr)
9580 /* ------------------------------------------------------------------------- */
9581 /* convert levels */
9582 /* ------------------------------------------------------------------------- */
9584 #define MAX_NUM_CONVERT_LEVELS 1000
9586 void ConvertLevels()
9588 static LevelDirTree *convert_leveldir = NULL;
9589 static int convert_level_nr = -1;
9590 static int num_levels_handled = 0;
9591 static int num_levels_converted = 0;
9592 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
9595 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9596 global.convert_leveldir);
9598 if (convert_leveldir == NULL)
9599 Error(ERR_EXIT, "no such level identifier: '%s'",
9600 global.convert_leveldir);
9602 leveldir_current = convert_leveldir;
9604 if (global.convert_level_nr != -1)
9606 convert_leveldir->first_level = global.convert_level_nr;
9607 convert_leveldir->last_level = global.convert_level_nr;
9610 convert_level_nr = convert_leveldir->first_level;
9612 printf_line("=", 79);
9613 printf("Converting levels\n");
9614 printf_line("-", 79);
9615 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
9616 printf("Level series name: '%s'\n", convert_leveldir->name);
9617 printf("Level series author: '%s'\n", convert_leveldir->author);
9618 printf("Number of levels: %d\n", convert_leveldir->levels);
9619 printf_line("=", 79);
9622 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
9623 levels_failed[i] = FALSE;
9625 while (convert_level_nr <= convert_leveldir->last_level)
9627 char *level_filename;
9630 level_nr = convert_level_nr++;
9632 printf("Level %03d: ", level_nr);
9634 LoadLevel(level_nr);
9635 if (level.no_valid_file)
9637 printf("(no level)\n");
9641 printf("converting level ... ");
9643 level_filename = getDefaultLevelFilename(level_nr);
9644 new_level = !fileExists(level_filename);
9648 SaveLevel(level_nr);
9650 num_levels_converted++;
9652 printf("converted.\n");
9656 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
9657 levels_failed[level_nr] = TRUE;
9659 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
9662 num_levels_handled++;
9666 printf_line("=", 79);
9667 printf("Number of levels handled: %d\n", num_levels_handled);
9668 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
9669 (num_levels_handled ?
9670 num_levels_converted * 100 / num_levels_handled : 0));
9671 printf_line("-", 79);
9672 printf("Summary (for automatic parsing by scripts):\n");
9673 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
9674 convert_leveldir->identifier, num_levels_converted,
9676 (num_levels_handled ?
9677 num_levels_converted * 100 / num_levels_handled : 0));
9679 if (num_levels_handled != num_levels_converted)
9681 printf(", FAILED:");
9682 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
9683 if (levels_failed[i])
9688 printf_line("=", 79);
9694 /* ------------------------------------------------------------------------- */
9695 /* create and save images for use in level sketches (raw BMP format) */
9696 /* ------------------------------------------------------------------------- */
9698 void CreateLevelSketchImages()
9700 #if defined(TARGET_SDL)
9705 InitElementPropertiesGfxElement();
9707 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
9708 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
9710 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9714 int element = getMappedElement(i);
9715 int graphic = el2edimg(element);
9721 sprintf(basename1, "%03d.bmp", i);
9722 sprintf(basename2, "%03ds.bmp", i);
9724 filename1 = getPath2(global.create_images_dir, basename1);
9725 filename2 = getPath2(global.create_images_dir, basename2);
9727 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
9728 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
9731 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
9732 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
9734 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
9735 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
9737 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
9738 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
9744 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
9747 FreeBitmap(bitmap1);
9748 FreeBitmap(bitmap2);
9753 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
9760 /* ------------------------------------------------------------------------- */
9761 /* create and save images for custom and group elements (raw BMP format) */
9762 /* ------------------------------------------------------------------------- */
9764 void CreateCustomElementImages()
9766 #if defined(TARGET_SDL)
9767 char *filename = "graphics.classic/RocksCE.bmp";
9770 int dummy_graphic = IMG_CUSTOM_99;
9772 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
9776 bitmap = CreateBitmap(TILEX * 16 * 2,
9777 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
9780 getFixedGraphicSource(dummy_graphic, 0, &src_bitmap, &src_x, &src_y);
9782 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9789 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
9790 TILEX * x, TILEY * y + yoffset_ce);
9792 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
9794 TILEX * x + TILEX * 16,
9795 TILEY * y + yoffset_ce);
9797 for (j = 2; j >= 0; j--)
9801 BlitBitmap(src_bitmap, bitmap,
9802 TILEX + c * 7, 0, 6, 10,
9803 TILEX * x + 6 + j * 7,
9804 TILEY * y + 11 + yoffset_ce);
9806 BlitBitmap(src_bitmap, bitmap,
9807 TILEX + c * 8, TILEY, 6, 10,
9808 TILEX * 16 + TILEX * x + 6 + j * 8,
9809 TILEY * y + 10 + yoffset_ce);
9815 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
9822 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
9823 TILEX * x, TILEY * y + yoffset_ge);
9825 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
9827 TILEX * x + TILEX * 16,
9828 TILEY * y + yoffset_ge);
9830 for (j = 1; j >= 0; j--)
9834 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
9835 TILEX * x + 6 + j * 10,
9836 TILEY * y + 11 + yoffset_ge);
9838 BlitBitmap(src_bitmap, bitmap,
9839 TILEX + c * 8, TILEY + 12, 6, 10,
9840 TILEX * 16 + TILEX * x + 10 + j * 8,
9841 TILEY * y + 10 + yoffset_ge);
9847 if (SDL_SaveBMP(bitmap->surface, filename) != 0)
9848 Error(ERR_EXIT, "cannot save CE graphics file '%s'", filename);