1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
24 #define ENABLE_UNUSED_CODE 0 /* currently unused functions */
25 #define ENABLE_HISTORIC_CHUNKS 0 /* only for historic reference */
26 #define ENABLE_RESERVED_CODE 0 /* reserved for later use */
28 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
29 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
30 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
32 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
33 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
35 #define LEVEL_CHUNK_VERS_SIZE 8 /* size of file version chunk */
36 #define LEVEL_CHUNK_DATE_SIZE 4 /* size of file date chunk */
37 #define LEVEL_CHUNK_HEAD_SIZE 80 /* size of level file header */
38 #define LEVEL_CHUNK_HEAD_UNUSED 0 /* unused level header bytes */
39 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
40 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
41 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
42 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
43 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
44 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
45 #define LEVEL_CHUNK_GRP1_SIZE 74 /* size of level GRP1 chunk */
47 /* (element number, number of change pages, change page number) */
48 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
50 /* (element number only) */
51 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
52 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
54 /* (nothing at all if unchanged) */
55 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
57 #define TAPE_CHUNK_VERS_SIZE 8 /* size of file version chunk */
58 #define TAPE_CHUNK_HEAD_SIZE 20 /* size of tape file header */
59 #define TAPE_CHUNK_HEAD_UNUSED 3 /* unused tape header bytes */
61 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
62 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
63 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
65 /* file identifier strings */
66 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
67 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
68 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
70 /* values for deciding when (not) to save configuration data */
71 #define SAVE_CONF_NEVER 0
72 #define SAVE_CONF_ALWAYS 1
73 #define SAVE_CONF_WHEN_CHANGED -1
75 /* values for chunks using micro chunks */
76 #define CONF_MASK_1_BYTE 0x00
77 #define CONF_MASK_2_BYTE 0x40
78 #define CONF_MASK_4_BYTE 0x80
79 #define CONF_MASK_MULTI_BYTES 0xc0
81 #define CONF_MASK_BYTES 0xc0
82 #define CONF_MASK_TOKEN 0x3f
84 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
85 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
86 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
87 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
89 /* these definitions are just for convenience of use and readability */
90 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
91 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
92 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
93 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
95 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
96 (x) == CONF_MASK_2_BYTE ? 2 : \
97 (x) == CONF_MASK_4_BYTE ? 4 : 0)
99 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
100 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
101 #define CONF_ELEMENT_NUM_BYTES (2)
103 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
104 (t) == TYPE_ELEMENT_LIST ? \
105 CONF_ELEMENT_NUM_BYTES : \
106 (t) == TYPE_CONTENT || \
107 (t) == TYPE_CONTENT_LIST ? \
108 CONF_CONTENT_NUM_BYTES : 1)
110 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
111 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
112 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
114 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
116 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
117 CONF_ELEMENT_NUM_BYTES)
118 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
119 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
121 /* temporary variables used to store pointers to structure members */
122 static struct LevelInfo li;
123 static struct ElementInfo xx_ei, yy_ei;
124 static struct ElementChangeInfo xx_change;
125 static struct ElementGroupInfo xx_group;
126 static struct EnvelopeInfo xx_envelope;
127 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
128 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
129 static int xx_num_contents;
130 static int xx_current_change_page;
131 static char xx_default_string_empty[1] = "";
132 static int xx_string_length_unused;
134 struct LevelFileConfigInfo
136 int element; /* element for which data is to be stored */
137 int save_type; /* save data always, never or when changed */
138 int data_type; /* data type (used internally, not stored) */
139 int conf_type; /* micro chunk identifier (stored in file) */
142 void *value; /* variable that holds the data to be stored */
143 int default_value; /* initial default value for this variable */
146 void *value_copy; /* variable that holds the data to be copied */
147 void *num_entities; /* number of entities for multi-byte data */
148 int default_num_entities; /* default number of entities for this data */
149 int max_num_entities; /* maximal number of entities for this data */
150 char *default_string; /* optional default string for string data */
153 static struct LevelFileConfigInfo chunk_config_INFO[] =
155 /* ---------- values not related to single elements ----------------------- */
158 -1, SAVE_CONF_ALWAYS,
159 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
160 &li.game_engine_type, GAME_ENGINE_TYPE_RND
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
166 &li.fieldx, STD_LEV_FIELDX
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
171 &li.fieldy, STD_LEV_FIELDY
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
181 -1, SAVE_CONF_ALWAYS,
182 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
188 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
194 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
195 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
206 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
207 &li.em_slippery_gems, FALSE
212 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
213 &li.use_custom_template, FALSE
218 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
219 &li.can_move_into_acid_bits, ~0 /* default: everything can */
224 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
225 &li.dont_collide_with_bits, ~0 /* default: always deadly */
230 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
231 &li.em_explodes_by_fire, FALSE
236 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
237 &li.score[SC_TIME_BONUS], 1
242 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
243 &li.auto_exit_sokoban, FALSE
253 static struct LevelFileConfigInfo chunk_config_ELEM[] =
255 /* (these values are the same for each player) */
258 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
259 &li.block_last_field, FALSE /* default case for EM levels */
263 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
264 &li.sp_block_last_field, TRUE /* default case for SP levels */
268 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
269 &li.instant_relocation, FALSE
273 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
274 &li.can_pass_to_walkable, FALSE
278 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
279 &li.block_snap_field, TRUE
283 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
284 &li.continuous_snapping, TRUE
288 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
289 &li.shifted_relocation, FALSE
292 /* (these values are different for each player) */
295 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
296 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
301 &li.initial_player_gravity[0], FALSE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
306 &li.use_start_element[0], FALSE
310 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
311 &li.start_element[0], EL_PLAYER_1
315 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
316 &li.use_artwork_element[0], FALSE
320 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
321 &li.artwork_element[0], EL_PLAYER_1
325 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
326 &li.use_explosion_element[0], FALSE
330 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
331 &li.explosion_element[0], EL_PLAYER_1
335 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
336 &li.use_initial_inventory[0], FALSE
340 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
341 &li.initial_inventory_size[0], 1
345 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
346 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
347 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
352 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
353 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
357 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
358 &li.initial_player_gravity[1], FALSE
362 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
363 &li.use_start_element[1], FALSE
367 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
368 &li.start_element[1], EL_PLAYER_2
372 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
373 &li.use_artwork_element[1], FALSE
377 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
378 &li.artwork_element[1], EL_PLAYER_2
382 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
383 &li.use_explosion_element[1], FALSE
387 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
388 &li.explosion_element[1], EL_PLAYER_2
392 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
393 &li.use_initial_inventory[1], FALSE
397 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
398 &li.initial_inventory_size[1], 1
402 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
403 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
404 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
409 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
410 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
414 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
415 &li.initial_player_gravity[2], FALSE
419 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
420 &li.use_start_element[2], FALSE
424 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
425 &li.start_element[2], EL_PLAYER_3
429 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
430 &li.use_artwork_element[2], FALSE
434 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
435 &li.artwork_element[2], EL_PLAYER_3
439 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
440 &li.use_explosion_element[2], FALSE
444 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
445 &li.explosion_element[2], EL_PLAYER_3
449 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
450 &li.use_initial_inventory[2], FALSE
454 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
455 &li.initial_inventory_size[2], 1
459 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
460 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
461 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
466 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
467 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
471 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
472 &li.initial_player_gravity[3], FALSE
476 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
477 &li.use_start_element[3], FALSE
481 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
482 &li.start_element[3], EL_PLAYER_4
486 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
487 &li.use_artwork_element[3], FALSE
491 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
492 &li.artwork_element[3], EL_PLAYER_4
496 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
497 &li.use_explosion_element[3], FALSE
501 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
502 &li.explosion_element[3], EL_PLAYER_4
506 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
507 &li.use_initial_inventory[3], FALSE
511 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
512 &li.initial_inventory_size[3], 1
516 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
517 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
518 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
523 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
524 &li.score[SC_EMERALD], 10
529 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
530 &li.score[SC_DIAMOND], 10
535 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
536 &li.score[SC_BUG], 10
541 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
542 &li.score[SC_SPACESHIP], 10
547 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
548 &li.score[SC_PACMAN], 10
553 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
554 &li.score[SC_NUT], 10
559 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
560 &li.score[SC_DYNAMITE], 10
565 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
566 &li.score[SC_KEY], 10
571 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
572 &li.score[SC_PEARL], 10
577 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
578 &li.score[SC_CRYSTAL], 10
583 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
584 &li.amoeba_content, EL_DIAMOND
588 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
593 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
594 &li.grow_into_diggable, TRUE
599 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
600 &li.yamyam_content, EL_ROCK, NULL,
601 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
605 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
606 &li.score[SC_YAMYAM], 10
611 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
612 &li.score[SC_ROBOT], 10
616 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
622 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
628 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
629 &li.time_magic_wall, 10
634 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
635 &li.game_of_life[0], 2
639 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
640 &li.game_of_life[1], 3
644 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
645 &li.game_of_life[2], 3
649 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
650 &li.game_of_life[3], 3
655 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
660 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
665 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
670 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
675 EL_TIMEGATE_SWITCH, -1,
676 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
677 &li.time_timegate, 10
681 EL_LIGHT_SWITCH_ACTIVE, -1,
682 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
687 EL_SHIELD_NORMAL, -1,
688 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
689 &li.shield_normal_time, 10
692 EL_SHIELD_NORMAL, -1,
693 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
694 &li.score[SC_SHIELD], 10
698 EL_SHIELD_DEADLY, -1,
699 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
700 &li.shield_deadly_time, 10
703 EL_SHIELD_DEADLY, -1,
704 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
705 &li.score[SC_SHIELD], 10
710 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
715 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
716 &li.extra_time_score, 10
720 EL_TIME_ORB_FULL, -1,
721 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
722 &li.time_orb_time, 10
725 EL_TIME_ORB_FULL, -1,
726 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
727 &li.use_time_orb_bug, FALSE
732 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
733 &li.use_spring_bug, FALSE
738 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
739 &li.android_move_time, 10
743 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
744 &li.android_clone_time, 10
748 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
749 &li.android_clone_element[0], EL_EMPTY, NULL,
750 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
755 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
760 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
765 EL_EMC_MAGNIFIER, -1,
766 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
767 &li.magnify_score, 10
770 EL_EMC_MAGNIFIER, -1,
771 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
776 EL_EMC_MAGIC_BALL, -1,
777 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
781 EL_EMC_MAGIC_BALL, -1,
782 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
783 &li.ball_random, FALSE
786 EL_EMC_MAGIC_BALL, -1,
787 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
788 &li.ball_state_initial, FALSE
791 EL_EMC_MAGIC_BALL, -1,
792 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
793 &li.ball_content, EL_EMPTY, NULL,
794 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
797 /* ---------- unused values ----------------------------------------------- */
800 EL_UNKNOWN, SAVE_CONF_NEVER,
801 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
802 &li.score[SC_UNKNOWN_14], 10
805 EL_UNKNOWN, SAVE_CONF_NEVER,
806 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
807 &li.score[SC_UNKNOWN_15], 10
817 static struct LevelFileConfigInfo chunk_config_NOTE[] =
821 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
822 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
826 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
827 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
832 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
833 &xx_envelope.autowrap, FALSE
837 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
838 &xx_envelope.centered, FALSE
843 TYPE_STRING, CONF_VALUE_BYTES(1),
844 &xx_envelope.text, -1, NULL,
845 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
846 &xx_default_string_empty[0]
856 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
860 TYPE_STRING, CONF_VALUE_BYTES(1),
861 &xx_ei.description[0], -1,
862 &yy_ei.description[0],
863 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
864 &xx_default_description[0]
869 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
870 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
871 &yy_ei.properties[EP_BITFIELD_BASE_NR]
873 #if ENABLE_RESERVED_CODE
874 /* (reserved for later use) */
877 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
878 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
879 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
885 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
886 &xx_ei.use_gfx_element, FALSE,
887 &yy_ei.use_gfx_element
891 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
892 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
893 &yy_ei.gfx_element_initial
898 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
899 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
900 &yy_ei.access_direction
905 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
906 &xx_ei.collect_score_initial, 10,
907 &yy_ei.collect_score_initial
911 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
912 &xx_ei.collect_count_initial, 1,
913 &yy_ei.collect_count_initial
918 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
919 &xx_ei.ce_value_fixed_initial, 0,
920 &yy_ei.ce_value_fixed_initial
924 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
925 &xx_ei.ce_value_random_initial, 0,
926 &yy_ei.ce_value_random_initial
930 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
931 &xx_ei.use_last_ce_value, FALSE,
932 &yy_ei.use_last_ce_value
937 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
938 &xx_ei.push_delay_fixed, 8,
939 &yy_ei.push_delay_fixed
943 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
944 &xx_ei.push_delay_random, 8,
945 &yy_ei.push_delay_random
949 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
950 &xx_ei.drop_delay_fixed, 0,
951 &yy_ei.drop_delay_fixed
955 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
956 &xx_ei.drop_delay_random, 0,
957 &yy_ei.drop_delay_random
961 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
962 &xx_ei.move_delay_fixed, 0,
963 &yy_ei.move_delay_fixed
967 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
968 &xx_ei.move_delay_random, 0,
969 &yy_ei.move_delay_random
974 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
975 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
980 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
981 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
982 &yy_ei.move_direction_initial
986 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
987 &xx_ei.move_stepsize, TILEX / 8,
993 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
994 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
995 &yy_ei.move_enter_element
999 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1000 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1001 &yy_ei.move_leave_element
1005 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1006 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1007 &yy_ei.move_leave_type
1012 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1013 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1014 &yy_ei.slippery_type
1019 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1020 &xx_ei.explosion_type, EXPLODES_3X3,
1021 &yy_ei.explosion_type
1025 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1026 &xx_ei.explosion_delay, 16,
1027 &yy_ei.explosion_delay
1031 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1032 &xx_ei.ignition_delay, 8,
1033 &yy_ei.ignition_delay
1038 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1039 &xx_ei.content, EL_EMPTY_SPACE,
1041 &xx_num_contents, 1, 1
1044 /* ---------- "num_change_pages" must be the last entry ------------------- */
1047 -1, SAVE_CONF_ALWAYS,
1048 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1049 &xx_ei.num_change_pages, 1,
1050 &yy_ei.num_change_pages
1061 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1063 /* ---------- "current_change_page" must be the first entry --------------- */
1066 -1, SAVE_CONF_ALWAYS,
1067 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1068 &xx_current_change_page, -1
1071 /* ---------- (the remaining entries can be in any order) ----------------- */
1075 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1076 &xx_change.can_change, FALSE
1081 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1082 &xx_event_bits[0], 0
1086 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1087 &xx_event_bits[1], 0
1092 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1093 &xx_change.trigger_player, CH_PLAYER_ANY
1097 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1098 &xx_change.trigger_side, CH_SIDE_ANY
1102 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1103 &xx_change.trigger_page, CH_PAGE_ANY
1108 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1109 &xx_change.target_element, EL_EMPTY_SPACE
1114 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1115 &xx_change.delay_fixed, 0
1119 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1120 &xx_change.delay_random, 0
1124 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1125 &xx_change.delay_frames, FRAMES_PER_SECOND
1130 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1131 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1136 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1137 &xx_change.explode, FALSE
1141 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1142 &xx_change.use_target_content, FALSE
1146 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1147 &xx_change.only_if_complete, FALSE
1151 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1152 &xx_change.use_random_replace, FALSE
1156 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1157 &xx_change.random_percentage, 100
1161 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1162 &xx_change.replace_when, CP_WHEN_EMPTY
1167 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1168 &xx_change.has_action, FALSE
1172 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1173 &xx_change.action_type, CA_NO_ACTION
1177 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1178 &xx_change.action_mode, CA_MODE_UNDEFINED
1182 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1183 &xx_change.action_arg, CA_ARG_UNDEFINED
1188 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1189 &xx_change.action_element, EL_EMPTY_SPACE
1194 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1195 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1196 &xx_num_contents, 1, 1
1206 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1210 TYPE_STRING, CONF_VALUE_BYTES(1),
1211 &xx_ei.description[0], -1, NULL,
1212 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1213 &xx_default_description[0]
1218 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1219 &xx_ei.use_gfx_element, FALSE
1223 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1224 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1229 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1230 &xx_group.choice_mode, ANIM_RANDOM
1235 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1236 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1237 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1247 static struct LevelFileConfigInfo chunk_config_CONF[] = /* (OBSOLETE) */
1251 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1252 &li.block_snap_field, TRUE
1256 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1257 &li.continuous_snapping, TRUE
1261 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1262 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1266 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1267 &li.use_start_element[0], FALSE
1271 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1272 &li.start_element[0], EL_PLAYER_1
1276 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1277 &li.use_artwork_element[0], FALSE
1281 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1282 &li.artwork_element[0], EL_PLAYER_1
1286 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1287 &li.use_explosion_element[0], FALSE
1291 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1292 &li.explosion_element[0], EL_PLAYER_1
1307 filetype_id_list[] =
1309 { LEVEL_FILE_TYPE_RND, "RND" },
1310 { LEVEL_FILE_TYPE_BD, "BD" },
1311 { LEVEL_FILE_TYPE_EM, "EM" },
1312 { LEVEL_FILE_TYPE_SP, "SP" },
1313 { LEVEL_FILE_TYPE_DX, "DX" },
1314 { LEVEL_FILE_TYPE_SB, "SB" },
1315 { LEVEL_FILE_TYPE_DC, "DC" },
1320 /* ========================================================================= */
1321 /* level file functions */
1322 /* ========================================================================= */
1324 static boolean check_special_flags(char *flag)
1326 if (strEqual(options.special_flags, flag) ||
1327 strEqual(leveldir_current->special_flags, flag))
1333 static struct DateInfo getCurrentDate()
1335 time_t epoch_seconds = time(NULL);
1336 struct tm *now = localtime(&epoch_seconds);
1337 struct DateInfo date;
1339 date.year = now->tm_year + 1900;
1340 date.month = now->tm_mon + 1;
1341 date.day = now->tm_mday;
1343 date.src = DATE_SRC_CLOCK;
1348 static void resetEventFlags(struct ElementChangeInfo *change)
1352 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1353 change->has_event[i] = FALSE;
1356 static void resetEventBits()
1360 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1361 xx_event_bits[i] = 0;
1364 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1368 /* important: only change event flag if corresponding event bit is set
1369 (this is because all xx_event_bits[] values are loaded separately,
1370 and all xx_event_bits[] values are set back to zero before loading
1371 another value xx_event_bits[x] (each value representing 32 flags)) */
1373 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1374 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1375 change->has_event[i] = TRUE;
1378 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1382 /* in contrast to the above function setEventFlagsFromEventBits(), it
1383 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1384 depending on the corresponding change->has_event[i] values here, as
1385 all xx_event_bits[] values are reset in resetEventBits() before */
1387 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1388 if (change->has_event[i])
1389 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1392 static char *getDefaultElementDescription(struct ElementInfo *ei)
1394 static char description[MAX_ELEMENT_NAME_LEN + 1];
1395 char *default_description = (ei->custom_description != NULL ?
1396 ei->custom_description :
1397 ei->editor_description);
1400 /* always start with reliable default values */
1401 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1402 description[i] = '\0';
1404 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1405 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1407 return &description[0];
1410 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1412 char *default_description = getDefaultElementDescription(ei);
1415 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1416 ei->description[i] = default_description[i];
1419 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1423 for (i = 0; conf[i].data_type != -1; i++)
1425 int default_value = conf[i].default_value;
1426 int data_type = conf[i].data_type;
1427 int conf_type = conf[i].conf_type;
1428 int byte_mask = conf_type & CONF_MASK_BYTES;
1430 if (byte_mask == CONF_MASK_MULTI_BYTES)
1432 int default_num_entities = conf[i].default_num_entities;
1433 int max_num_entities = conf[i].max_num_entities;
1435 *(int *)(conf[i].num_entities) = default_num_entities;
1437 if (data_type == TYPE_STRING)
1439 char *default_string = conf[i].default_string;
1440 char *string = (char *)(conf[i].value);
1442 strncpy(string, default_string, max_num_entities);
1444 else if (data_type == TYPE_ELEMENT_LIST)
1446 int *element_array = (int *)(conf[i].value);
1449 for (j = 0; j < max_num_entities; j++)
1450 element_array[j] = default_value;
1452 else if (data_type == TYPE_CONTENT_LIST)
1454 struct Content *content = (struct Content *)(conf[i].value);
1457 for (c = 0; c < max_num_entities; c++)
1458 for (y = 0; y < 3; y++)
1459 for (x = 0; x < 3; x++)
1460 content[c].e[x][y] = default_value;
1463 else /* constant size configuration data (1, 2 or 4 bytes) */
1465 if (data_type == TYPE_BOOLEAN)
1466 *(boolean *)(conf[i].value) = default_value;
1468 *(int *) (conf[i].value) = default_value;
1473 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1477 for (i = 0; conf[i].data_type != -1; i++)
1479 int data_type = conf[i].data_type;
1480 int conf_type = conf[i].conf_type;
1481 int byte_mask = conf_type & CONF_MASK_BYTES;
1483 if (byte_mask == CONF_MASK_MULTI_BYTES)
1485 int max_num_entities = conf[i].max_num_entities;
1487 if (data_type == TYPE_STRING)
1489 char *string = (char *)(conf[i].value);
1490 char *string_copy = (char *)(conf[i].value_copy);
1492 strncpy(string_copy, string, max_num_entities);
1494 else if (data_type == TYPE_ELEMENT_LIST)
1496 int *element_array = (int *)(conf[i].value);
1497 int *element_array_copy = (int *)(conf[i].value_copy);
1500 for (j = 0; j < max_num_entities; j++)
1501 element_array_copy[j] = element_array[j];
1503 else if (data_type == TYPE_CONTENT_LIST)
1505 struct Content *content = (struct Content *)(conf[i].value);
1506 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1509 for (c = 0; c < max_num_entities; c++)
1510 for (y = 0; y < 3; y++)
1511 for (x = 0; x < 3; x++)
1512 content_copy[c].e[x][y] = content[c].e[x][y];
1515 else /* constant size configuration data (1, 2 or 4 bytes) */
1517 if (data_type == TYPE_BOOLEAN)
1518 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1520 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1525 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1529 xx_ei = *ei_from; /* copy element data into temporary buffer */
1530 yy_ei = *ei_to; /* copy element data into temporary buffer */
1532 copyConfigFromConfigList(chunk_config_CUSX_base);
1537 /* ---------- reinitialize and copy change pages ---------- */
1539 ei_to->num_change_pages = ei_from->num_change_pages;
1540 ei_to->current_change_page = ei_from->current_change_page;
1542 setElementChangePages(ei_to, ei_to->num_change_pages);
1544 for (i = 0; i < ei_to->num_change_pages; i++)
1545 ei_to->change_page[i] = ei_from->change_page[i];
1547 /* ---------- copy group element info ---------- */
1548 if (ei_from->group != NULL && ei_to->group != NULL) /* group or internal */
1549 *ei_to->group = *ei_from->group;
1551 /* mark this custom element as modified */
1552 ei_to->modified_settings = TRUE;
1555 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1557 int change_page_size = sizeof(struct ElementChangeInfo);
1559 ei->num_change_pages = MAX(1, change_pages);
1562 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1564 if (ei->current_change_page >= ei->num_change_pages)
1565 ei->current_change_page = ei->num_change_pages - 1;
1567 ei->change = &ei->change_page[ei->current_change_page];
1570 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1572 xx_change = *change; /* copy change data into temporary buffer */
1574 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1576 *change = xx_change;
1578 resetEventFlags(change);
1580 change->direct_action = 0;
1581 change->other_action = 0;
1583 change->pre_change_function = NULL;
1584 change->change_function = NULL;
1585 change->post_change_function = NULL;
1588 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1592 li = *level; /* copy level data into temporary buffer */
1593 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1594 *level = li; /* copy temporary buffer back to level data */
1596 setLevelInfoToDefaults_EM();
1597 setLevelInfoToDefaults_SP();
1599 level->native_em_level = &native_em_level;
1600 level->native_sp_level = &native_sp_level;
1602 level->file_version = FILE_VERSION_ACTUAL;
1603 level->game_version = GAME_VERSION_ACTUAL;
1605 level->creation_date = getCurrentDate();
1607 level->encoding_16bit_field = TRUE;
1608 level->encoding_16bit_yamyam = TRUE;
1609 level->encoding_16bit_amoeba = TRUE;
1611 /* clear level name and level author string buffers */
1612 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1613 level->name[i] = '\0';
1614 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1615 level->author[i] = '\0';
1617 /* set level name and level author to default values */
1618 strcpy(level->name, NAMELESS_LEVEL_NAME);
1619 strcpy(level->author, ANONYMOUS_NAME);
1621 /* set level playfield to playable default level with player and exit */
1622 for (x = 0; x < MAX_LEV_FIELDX; x++)
1623 for (y = 0; y < MAX_LEV_FIELDY; y++)
1624 level->field[x][y] = EL_SAND;
1626 level->field[0][0] = EL_PLAYER_1;
1627 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1629 BorderElement = EL_STEELWALL;
1631 /* set all bug compatibility flags to "false" => do not emulate this bug */
1632 level->use_action_after_change_bug = FALSE;
1634 if (leveldir_current)
1636 /* try to determine better author name than 'anonymous' */
1637 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1639 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1640 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1644 switch (LEVELCLASS(leveldir_current))
1646 case LEVELCLASS_TUTORIAL:
1647 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1650 case LEVELCLASS_CONTRIB:
1651 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1652 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1655 case LEVELCLASS_PRIVATE:
1656 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1657 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1661 /* keep default value */
1668 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1670 static boolean clipboard_elements_initialized = FALSE;
1673 InitElementPropertiesStatic();
1675 li = *level; /* copy level data into temporary buffer */
1676 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1677 *level = li; /* copy temporary buffer back to level data */
1679 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1682 struct ElementInfo *ei = &element_info[element];
1684 /* never initialize clipboard elements after the very first time */
1685 /* (to be able to use clipboard elements between several levels) */
1686 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1689 if (IS_ENVELOPE(element))
1691 int envelope_nr = element - EL_ENVELOPE_1;
1693 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1695 level->envelope[envelope_nr] = xx_envelope;
1698 if (IS_CUSTOM_ELEMENT(element) ||
1699 IS_GROUP_ELEMENT(element) ||
1700 IS_INTERNAL_ELEMENT(element))
1702 xx_ei = *ei; /* copy element data into temporary buffer */
1704 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1709 setElementChangePages(ei, 1);
1710 setElementChangeInfoToDefaults(ei->change);
1712 if (IS_CUSTOM_ELEMENT(element) ||
1713 IS_GROUP_ELEMENT(element) ||
1714 IS_INTERNAL_ELEMENT(element))
1716 setElementDescriptionToDefault(ei);
1718 ei->modified_settings = FALSE;
1721 if (IS_CUSTOM_ELEMENT(element) ||
1722 IS_INTERNAL_ELEMENT(element))
1724 /* internal values used in level editor */
1726 ei->access_type = 0;
1727 ei->access_layer = 0;
1728 ei->access_protected = 0;
1729 ei->walk_to_action = 0;
1730 ei->smash_targets = 0;
1733 ei->can_explode_by_fire = FALSE;
1734 ei->can_explode_smashed = FALSE;
1735 ei->can_explode_impact = FALSE;
1737 ei->current_change_page = 0;
1740 if (IS_GROUP_ELEMENT(element) ||
1741 IS_INTERNAL_ELEMENT(element))
1743 struct ElementGroupInfo *group;
1745 /* initialize memory for list of elements in group */
1746 if (ei->group == NULL)
1747 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1751 xx_group = *group; /* copy group data into temporary buffer */
1753 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1759 clipboard_elements_initialized = TRUE;
1762 static void setLevelInfoToDefaults(struct LevelInfo *level,
1763 boolean level_info_only)
1765 setLevelInfoToDefaults_Level(level);
1767 if (!level_info_only)
1768 setLevelInfoToDefaults_Elements(level);
1770 level->no_valid_file = FALSE;
1772 level->changed = FALSE;
1775 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1777 level_file_info->nr = 0;
1778 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1779 level_file_info->packed = FALSE;
1780 level_file_info->basename = NULL;
1781 level_file_info->filename = NULL;
1784 static void ActivateLevelTemplate()
1788 /* Currently there is no special action needed to activate the template
1789 data, because 'element_info' property settings overwrite the original
1790 level data, while all other variables do not change. */
1792 /* Exception: 'from_level_template' elements in the original level playfield
1793 are overwritten with the corresponding elements at the same position in
1794 playfield from the level template. */
1796 for (x = 0; x < level.fieldx; x++)
1797 for (y = 0; y < level.fieldy; y++)
1798 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1799 level.field[x][y] = level_template.field[x][y];
1801 if (check_special_flags("load_xsb_to_ces"))
1803 struct LevelInfo level_backup = level;
1805 /* overwrite all individual level settings from template level settings */
1806 level = level_template;
1808 /* restore playfield size */
1809 level.fieldx = level_backup.fieldx;
1810 level.fieldy = level_backup.fieldy;
1812 /* restore playfield content */
1813 for (x = 0; x < level.fieldx; x++)
1814 for (y = 0; y < level.fieldy; y++)
1815 level.field[x][y] = level_backup.field[x][y];
1817 /* restore name and author from individual level */
1818 strcpy(level.name, level_backup.name);
1819 strcpy(level.author, level_backup.author);
1821 /* restore flag "use_custom_template" */
1822 level.use_custom_template = level_backup.use_custom_template;
1826 static char *getLevelFilenameFromBasename(char *basename)
1828 static char *filename = NULL;
1830 checked_free(filename);
1832 filename = getPath2(getCurrentLevelDir(), basename);
1837 static int getFileTypeFromBasename(char *basename)
1839 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1841 static char *filename = NULL;
1842 struct stat file_status;
1844 /* ---------- try to determine file type from filename ---------- */
1846 /* check for typical filename of a Supaplex level package file */
1847 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1848 return LEVEL_FILE_TYPE_SP;
1850 /* check for typical filename of a Diamond Caves II level package file */
1851 if (strSuffixLower(basename, ".dc") ||
1852 strSuffixLower(basename, ".dc2"))
1853 return LEVEL_FILE_TYPE_DC;
1855 /* check for typical filename of a Sokoban level package file */
1856 if (strSuffixLower(basename, ".xsb") &&
1857 strchr(basename, '%') == NULL)
1858 return LEVEL_FILE_TYPE_SB;
1860 /* ---------- try to determine file type from filesize ---------- */
1862 checked_free(filename);
1863 filename = getPath2(getCurrentLevelDir(), basename);
1865 if (stat(filename, &file_status) == 0)
1867 /* check for typical filesize of a Supaplex level package file */
1868 if (file_status.st_size == 170496)
1869 return LEVEL_FILE_TYPE_SP;
1872 return LEVEL_FILE_TYPE_UNKNOWN;
1875 static boolean checkForPackageFromBasename(char *basename)
1877 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
1878 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
1880 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
1883 static char *getSingleLevelBasenameExt(int nr, char *extension)
1885 static char basename[MAX_FILENAME_LEN];
1888 sprintf(basename, "template.%s", extension);
1890 sprintf(basename, "%03d.%s", nr, extension);
1895 static char *getSingleLevelBasename(int nr)
1897 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
1900 static char *getPackedLevelBasename(int type)
1902 static char basename[MAX_FILENAME_LEN];
1903 char *directory = getCurrentLevelDir();
1905 DirectoryEntry *dir_entry;
1907 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
1909 if ((dir = openDirectory(directory)) == NULL)
1911 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
1916 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
1918 char *entry_basename = dir_entry->basename;
1919 int entry_type = getFileTypeFromBasename(entry_basename);
1921 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
1923 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
1926 strcpy(basename, entry_basename);
1933 closeDirectory(dir);
1938 static char *getSingleLevelFilename(int nr)
1940 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
1943 #if ENABLE_UNUSED_CODE
1944 static char *getPackedLevelFilename(int type)
1946 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
1950 char *getDefaultLevelFilename(int nr)
1952 return getSingleLevelFilename(nr);
1955 #if ENABLE_UNUSED_CODE
1956 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
1960 lfi->packed = FALSE;
1961 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
1962 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1966 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
1967 int type, char *format, ...)
1969 static char basename[MAX_FILENAME_LEN];
1972 va_start(ap, format);
1973 vsprintf(basename, format, ap);
1977 lfi->packed = FALSE;
1978 lfi->basename = basename;
1979 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1982 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
1987 lfi->basename = getPackedLevelBasename(lfi->type);
1988 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1991 static int getFiletypeFromID(char *filetype_id)
1993 char *filetype_id_lower;
1994 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
1997 if (filetype_id == NULL)
1998 return LEVEL_FILE_TYPE_UNKNOWN;
2000 filetype_id_lower = getStringToLower(filetype_id);
2002 for (i = 0; filetype_id_list[i].id != NULL; i++)
2004 char *id_lower = getStringToLower(filetype_id_list[i].id);
2006 if (strEqual(filetype_id_lower, id_lower))
2007 filetype = filetype_id_list[i].filetype;
2011 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2015 free(filetype_id_lower);
2020 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2024 /* special case: level number is negative => check for level template file */
2027 /* global variable "leveldir_current" must be modified in the loop below */
2028 LevelDirTree *leveldir_current_last = leveldir_current;
2030 /* check for template level in path from current to topmost tree node */
2032 while (leveldir_current != NULL)
2034 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2035 "template.%s", LEVELFILE_EXTENSION);
2037 if (fileExists(lfi->filename))
2040 leveldir_current = leveldir_current->node_parent;
2043 /* restore global variable "leveldir_current" modified in above loop */
2044 leveldir_current = leveldir_current_last;
2046 /* no fallback if template file not existing */
2050 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2051 if (leveldir_current->level_filename != NULL)
2053 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2055 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2056 leveldir_current->level_filename, nr);
2058 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2060 if (fileExists(lfi->filename))
2064 /* check for native Rocks'n'Diamonds level file */
2065 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2066 "%03d.%s", nr, LEVELFILE_EXTENSION);
2067 if (fileExists(lfi->filename))
2070 /* check for Emerald Mine level file (V1) */
2071 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2072 'a' + (nr / 10) % 26, '0' + nr % 10);
2073 if (fileExists(lfi->filename))
2075 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2076 'A' + (nr / 10) % 26, '0' + nr % 10);
2077 if (fileExists(lfi->filename))
2080 /* check for Emerald Mine level file (V2 to V5) */
2081 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2082 if (fileExists(lfi->filename))
2085 /* check for Emerald Mine level file (V6 / single mode) */
2086 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2087 if (fileExists(lfi->filename))
2089 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2090 if (fileExists(lfi->filename))
2093 /* check for Emerald Mine level file (V6 / teamwork mode) */
2094 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2095 if (fileExists(lfi->filename))
2097 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2098 if (fileExists(lfi->filename))
2101 /* check for various packed level file formats */
2102 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2103 if (fileExists(lfi->filename))
2106 /* no known level file found -- use default values (and fail later) */
2107 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2108 "%03d.%s", nr, LEVELFILE_EXTENSION);
2111 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2113 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2114 lfi->type = getFileTypeFromBasename(lfi->basename);
2117 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2119 /* always start with reliable default values */
2120 setFileInfoToDefaults(level_file_info);
2122 level_file_info->nr = nr; /* set requested level number */
2124 determineLevelFileInfo_Filename(level_file_info);
2125 determineLevelFileInfo_Filetype(level_file_info);
2128 /* ------------------------------------------------------------------------- */
2129 /* functions for loading R'n'D level */
2130 /* ------------------------------------------------------------------------- */
2132 int getMappedElement(int element)
2134 /* remap some (historic, now obsolete) elements */
2138 case EL_PLAYER_OBSOLETE:
2139 element = EL_PLAYER_1;
2142 case EL_KEY_OBSOLETE:
2146 case EL_EM_KEY_1_FILE_OBSOLETE:
2147 element = EL_EM_KEY_1;
2150 case EL_EM_KEY_2_FILE_OBSOLETE:
2151 element = EL_EM_KEY_2;
2154 case EL_EM_KEY_3_FILE_OBSOLETE:
2155 element = EL_EM_KEY_3;
2158 case EL_EM_KEY_4_FILE_OBSOLETE:
2159 element = EL_EM_KEY_4;
2162 case EL_ENVELOPE_OBSOLETE:
2163 element = EL_ENVELOPE_1;
2171 if (element >= NUM_FILE_ELEMENTS)
2173 Error(ERR_WARN, "invalid level element %d", element);
2175 element = EL_UNKNOWN;
2183 int getMappedElementByVersion(int element, int game_version)
2185 /* remap some elements due to certain game version */
2187 if (game_version <= VERSION_IDENT(2,2,0,0))
2189 /* map game font elements */
2190 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2191 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2192 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2193 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2196 if (game_version < VERSION_IDENT(3,0,0,0))
2198 /* map Supaplex gravity tube elements */
2199 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2200 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2201 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2202 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2209 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2211 level->file_version = getFileVersion(file);
2212 level->game_version = getFileVersion(file);
2217 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2219 level->creation_date.year = getFile16BitBE(file);
2220 level->creation_date.month = getFile8Bit(file);
2221 level->creation_date.day = getFile8Bit(file);
2223 level->creation_date.src = DATE_SRC_LEVELFILE;
2228 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2230 int initial_player_stepsize;
2231 int initial_player_gravity;
2234 level->fieldx = getFile8Bit(file);
2235 level->fieldy = getFile8Bit(file);
2237 level->time = getFile16BitBE(file);
2238 level->gems_needed = getFile16BitBE(file);
2240 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2241 level->name[i] = getFile8Bit(file);
2242 level->name[MAX_LEVEL_NAME_LEN] = 0;
2244 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2245 level->score[i] = getFile8Bit(file);
2247 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2248 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2249 for (y = 0; y < 3; y++)
2250 for (x = 0; x < 3; x++)
2251 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2253 level->amoeba_speed = getFile8Bit(file);
2254 level->time_magic_wall = getFile8Bit(file);
2255 level->time_wheel = getFile8Bit(file);
2256 level->amoeba_content = getMappedElement(getFile8Bit(file));
2258 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2261 for (i = 0; i < MAX_PLAYERS; i++)
2262 level->initial_player_stepsize[i] = initial_player_stepsize;
2264 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2266 for (i = 0; i < MAX_PLAYERS; i++)
2267 level->initial_player_gravity[i] = initial_player_gravity;
2269 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2270 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2272 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2274 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2275 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2276 level->can_move_into_acid_bits = getFile32BitBE(file);
2277 level->dont_collide_with_bits = getFile8Bit(file);
2279 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2280 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2282 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2283 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2284 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2286 level->game_engine_type = getFile8Bit(file);
2288 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2293 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2297 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2298 level->name[i] = getFile8Bit(file);
2299 level->name[MAX_LEVEL_NAME_LEN] = 0;
2304 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2308 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2309 level->author[i] = getFile8Bit(file);
2310 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2315 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2318 int chunk_size_expected = level->fieldx * level->fieldy;
2320 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2321 stored with 16-bit encoding (and should be twice as big then).
2322 Even worse, playfield data was stored 16-bit when only yamyam content
2323 contained 16-bit elements and vice versa. */
2325 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2326 chunk_size_expected *= 2;
2328 if (chunk_size_expected != chunk_size)
2330 ReadUnusedBytesFromFile(file, chunk_size);
2331 return chunk_size_expected;
2334 for (y = 0; y < level->fieldy; y++)
2335 for (x = 0; x < level->fieldx; x++)
2336 level->field[x][y] =
2337 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2342 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2345 int header_size = 4;
2346 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2347 int chunk_size_expected = header_size + content_size;
2349 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2350 stored with 16-bit encoding (and should be twice as big then).
2351 Even worse, playfield data was stored 16-bit when only yamyam content
2352 contained 16-bit elements and vice versa. */
2354 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2355 chunk_size_expected += content_size;
2357 if (chunk_size_expected != chunk_size)
2359 ReadUnusedBytesFromFile(file, chunk_size);
2360 return chunk_size_expected;
2364 level->num_yamyam_contents = getFile8Bit(file);
2368 /* correct invalid number of content fields -- should never happen */
2369 if (level->num_yamyam_contents < 1 ||
2370 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2371 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2373 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2374 for (y = 0; y < 3; y++)
2375 for (x = 0; x < 3; x++)
2376 level->yamyam_content[i].e[x][y] =
2377 getMappedElement(level->encoding_16bit_field ?
2378 getFile16BitBE(file) : getFile8Bit(file));
2382 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2387 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2389 element = getMappedElement(getFile16BitBE(file));
2390 num_contents = getFile8Bit(file);
2392 getFile8Bit(file); /* content x size (unused) */
2393 getFile8Bit(file); /* content y size (unused) */
2395 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2397 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2398 for (y = 0; y < 3; y++)
2399 for (x = 0; x < 3; x++)
2400 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2402 /* correct invalid number of content fields -- should never happen */
2403 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2404 num_contents = STD_ELEMENT_CONTENTS;
2406 if (element == EL_YAMYAM)
2408 level->num_yamyam_contents = num_contents;
2410 for (i = 0; i < num_contents; i++)
2411 for (y = 0; y < 3; y++)
2412 for (x = 0; x < 3; x++)
2413 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2415 else if (element == EL_BD_AMOEBA)
2417 level->amoeba_content = content_array[0][0][0];
2421 Error(ERR_WARN, "cannot load content for element '%d'", element);
2427 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2433 int chunk_size_expected;
2435 element = getMappedElement(getFile16BitBE(file));
2436 if (!IS_ENVELOPE(element))
2437 element = EL_ENVELOPE_1;
2439 envelope_nr = element - EL_ENVELOPE_1;
2441 envelope_len = getFile16BitBE(file);
2443 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2444 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2446 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2448 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2449 if (chunk_size_expected != chunk_size)
2451 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2452 return chunk_size_expected;
2455 for (i = 0; i < envelope_len; i++)
2456 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2461 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2463 int num_changed_custom_elements = getFile16BitBE(file);
2464 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2467 if (chunk_size_expected != chunk_size)
2469 ReadUnusedBytesFromFile(file, chunk_size - 2);
2470 return chunk_size_expected;
2473 for (i = 0; i < num_changed_custom_elements; i++)
2475 int element = getMappedElement(getFile16BitBE(file));
2476 int properties = getFile32BitBE(file);
2478 if (IS_CUSTOM_ELEMENT(element))
2479 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2481 Error(ERR_WARN, "invalid custom element number %d", element);
2483 /* older game versions that wrote level files with CUS1 chunks used
2484 different default push delay values (not yet stored in level file) */
2485 element_info[element].push_delay_fixed = 2;
2486 element_info[element].push_delay_random = 8;
2492 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2494 int num_changed_custom_elements = getFile16BitBE(file);
2495 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2498 if (chunk_size_expected != chunk_size)
2500 ReadUnusedBytesFromFile(file, chunk_size - 2);
2501 return chunk_size_expected;
2504 for (i = 0; i < num_changed_custom_elements; i++)
2506 int element = getMappedElement(getFile16BitBE(file));
2507 int custom_target_element = getMappedElement(getFile16BitBE(file));
2509 if (IS_CUSTOM_ELEMENT(element))
2510 element_info[element].change->target_element = custom_target_element;
2512 Error(ERR_WARN, "invalid custom element number %d", element);
2518 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2520 int num_changed_custom_elements = getFile16BitBE(file);
2521 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2524 if (chunk_size_expected != chunk_size)
2526 ReadUnusedBytesFromFile(file, chunk_size - 2);
2527 return chunk_size_expected;
2530 for (i = 0; i < num_changed_custom_elements; i++)
2532 int element = getMappedElement(getFile16BitBE(file));
2533 struct ElementInfo *ei = &element_info[element];
2534 unsigned int event_bits;
2536 if (!IS_CUSTOM_ELEMENT(element))
2538 Error(ERR_WARN, "invalid custom element number %d", element);
2540 element = EL_INTERNAL_DUMMY;
2543 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2544 ei->description[j] = getFile8Bit(file);
2545 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2547 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2549 /* some free bytes for future properties and padding */
2550 ReadUnusedBytesFromFile(file, 7);
2552 ei->use_gfx_element = getFile8Bit(file);
2553 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2555 ei->collect_score_initial = getFile8Bit(file);
2556 ei->collect_count_initial = getFile8Bit(file);
2558 ei->push_delay_fixed = getFile16BitBE(file);
2559 ei->push_delay_random = getFile16BitBE(file);
2560 ei->move_delay_fixed = getFile16BitBE(file);
2561 ei->move_delay_random = getFile16BitBE(file);
2563 ei->move_pattern = getFile16BitBE(file);
2564 ei->move_direction_initial = getFile8Bit(file);
2565 ei->move_stepsize = getFile8Bit(file);
2567 for (y = 0; y < 3; y++)
2568 for (x = 0; x < 3; x++)
2569 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2571 event_bits = getFile32BitBE(file);
2572 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2573 if (event_bits & (1 << j))
2574 ei->change->has_event[j] = TRUE;
2576 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2578 ei->change->delay_fixed = getFile16BitBE(file);
2579 ei->change->delay_random = getFile16BitBE(file);
2580 ei->change->delay_frames = getFile16BitBE(file);
2582 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2584 ei->change->explode = getFile8Bit(file);
2585 ei->change->use_target_content = getFile8Bit(file);
2586 ei->change->only_if_complete = getFile8Bit(file);
2587 ei->change->use_random_replace = getFile8Bit(file);
2589 ei->change->random_percentage = getFile8Bit(file);
2590 ei->change->replace_when = getFile8Bit(file);
2592 for (y = 0; y < 3; y++)
2593 for (x = 0; x < 3; x++)
2594 ei->change->target_content.e[x][y] =
2595 getMappedElement(getFile16BitBE(file));
2597 ei->slippery_type = getFile8Bit(file);
2599 /* some free bytes for future properties and padding */
2600 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2602 /* mark that this custom element has been modified */
2603 ei->modified_settings = TRUE;
2609 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2611 struct ElementInfo *ei;
2612 int chunk_size_expected;
2616 /* ---------- custom element base property values (96 bytes) ------------- */
2618 element = getMappedElement(getFile16BitBE(file));
2620 if (!IS_CUSTOM_ELEMENT(element))
2622 Error(ERR_WARN, "invalid custom element number %d", element);
2624 ReadUnusedBytesFromFile(file, chunk_size - 2);
2628 ei = &element_info[element];
2630 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2631 ei->description[i] = getFile8Bit(file);
2632 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2634 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2636 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2638 ei->num_change_pages = getFile8Bit(file);
2640 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2641 if (chunk_size_expected != chunk_size)
2643 ReadUnusedBytesFromFile(file, chunk_size - 43);
2644 return chunk_size_expected;
2647 ei->ce_value_fixed_initial = getFile16BitBE(file);
2648 ei->ce_value_random_initial = getFile16BitBE(file);
2649 ei->use_last_ce_value = getFile8Bit(file);
2651 ei->use_gfx_element = getFile8Bit(file);
2652 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2654 ei->collect_score_initial = getFile8Bit(file);
2655 ei->collect_count_initial = getFile8Bit(file);
2657 ei->drop_delay_fixed = getFile8Bit(file);
2658 ei->push_delay_fixed = getFile8Bit(file);
2659 ei->drop_delay_random = getFile8Bit(file);
2660 ei->push_delay_random = getFile8Bit(file);
2661 ei->move_delay_fixed = getFile16BitBE(file);
2662 ei->move_delay_random = getFile16BitBE(file);
2664 /* bits 0 - 15 of "move_pattern" ... */
2665 ei->move_pattern = getFile16BitBE(file);
2666 ei->move_direction_initial = getFile8Bit(file);
2667 ei->move_stepsize = getFile8Bit(file);
2669 ei->slippery_type = getFile8Bit(file);
2671 for (y = 0; y < 3; y++)
2672 for (x = 0; x < 3; x++)
2673 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2675 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2676 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2677 ei->move_leave_type = getFile8Bit(file);
2679 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2680 ei->move_pattern |= (getFile16BitBE(file) << 16);
2682 ei->access_direction = getFile8Bit(file);
2684 ei->explosion_delay = getFile8Bit(file);
2685 ei->ignition_delay = getFile8Bit(file);
2686 ei->explosion_type = getFile8Bit(file);
2688 /* some free bytes for future custom property values and padding */
2689 ReadUnusedBytesFromFile(file, 1);
2691 /* ---------- change page property values (48 bytes) --------------------- */
2693 setElementChangePages(ei, ei->num_change_pages);
2695 for (i = 0; i < ei->num_change_pages; i++)
2697 struct ElementChangeInfo *change = &ei->change_page[i];
2698 unsigned int event_bits;
2700 /* always start with reliable default values */
2701 setElementChangeInfoToDefaults(change);
2703 /* bits 0 - 31 of "has_event[]" ... */
2704 event_bits = getFile32BitBE(file);
2705 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2706 if (event_bits & (1 << j))
2707 change->has_event[j] = TRUE;
2709 change->target_element = getMappedElement(getFile16BitBE(file));
2711 change->delay_fixed = getFile16BitBE(file);
2712 change->delay_random = getFile16BitBE(file);
2713 change->delay_frames = getFile16BitBE(file);
2715 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2717 change->explode = getFile8Bit(file);
2718 change->use_target_content = getFile8Bit(file);
2719 change->only_if_complete = getFile8Bit(file);
2720 change->use_random_replace = getFile8Bit(file);
2722 change->random_percentage = getFile8Bit(file);
2723 change->replace_when = getFile8Bit(file);
2725 for (y = 0; y < 3; y++)
2726 for (x = 0; x < 3; x++)
2727 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2729 change->can_change = getFile8Bit(file);
2731 change->trigger_side = getFile8Bit(file);
2733 change->trigger_player = getFile8Bit(file);
2734 change->trigger_page = getFile8Bit(file);
2736 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2737 CH_PAGE_ANY : (1 << change->trigger_page));
2739 change->has_action = getFile8Bit(file);
2740 change->action_type = getFile8Bit(file);
2741 change->action_mode = getFile8Bit(file);
2742 change->action_arg = getFile16BitBE(file);
2744 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2745 event_bits = getFile8Bit(file);
2746 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2747 if (event_bits & (1 << (j - 32)))
2748 change->has_event[j] = TRUE;
2751 /* mark this custom element as modified */
2752 ei->modified_settings = TRUE;
2757 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2759 struct ElementInfo *ei;
2760 struct ElementGroupInfo *group;
2764 element = getMappedElement(getFile16BitBE(file));
2766 if (!IS_GROUP_ELEMENT(element))
2768 Error(ERR_WARN, "invalid group element number %d", element);
2770 ReadUnusedBytesFromFile(file, chunk_size - 2);
2774 ei = &element_info[element];
2776 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2777 ei->description[i] = getFile8Bit(file);
2778 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2780 group = element_info[element].group;
2782 group->num_elements = getFile8Bit(file);
2784 ei->use_gfx_element = getFile8Bit(file);
2785 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2787 group->choice_mode = getFile8Bit(file);
2789 /* some free bytes for future values and padding */
2790 ReadUnusedBytesFromFile(file, 3);
2792 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2793 group->element[i] = getMappedElement(getFile16BitBE(file));
2795 /* mark this group element as modified */
2796 element_info[element].modified_settings = TRUE;
2801 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2802 int element, int real_element)
2804 int micro_chunk_size = 0;
2805 int conf_type = getFile8Bit(file);
2806 int byte_mask = conf_type & CONF_MASK_BYTES;
2807 boolean element_found = FALSE;
2810 micro_chunk_size += 1;
2812 if (byte_mask == CONF_MASK_MULTI_BYTES)
2814 int num_bytes = getFile16BitBE(file);
2815 byte *buffer = checked_malloc(num_bytes);
2817 ReadBytesFromFile(file, buffer, num_bytes);
2819 for (i = 0; conf[i].data_type != -1; i++)
2821 if (conf[i].element == element &&
2822 conf[i].conf_type == conf_type)
2824 int data_type = conf[i].data_type;
2825 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2826 int max_num_entities = conf[i].max_num_entities;
2828 if (num_entities > max_num_entities)
2831 "truncating number of entities for element %d from %d to %d",
2832 element, num_entities, max_num_entities);
2834 num_entities = max_num_entities;
2837 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
2838 data_type == TYPE_CONTENT_LIST))
2840 /* for element and content lists, zero entities are not allowed */
2841 Error(ERR_WARN, "found empty list of entities for element %d",
2844 /* do not set "num_entities" here to prevent reading behind buffer */
2846 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
2850 *(int *)(conf[i].num_entities) = num_entities;
2853 element_found = TRUE;
2855 if (data_type == TYPE_STRING)
2857 char *string = (char *)(conf[i].value);
2860 for (j = 0; j < max_num_entities; j++)
2861 string[j] = (j < num_entities ? buffer[j] : '\0');
2863 else if (data_type == TYPE_ELEMENT_LIST)
2865 int *element_array = (int *)(conf[i].value);
2868 for (j = 0; j < num_entities; j++)
2870 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
2872 else if (data_type == TYPE_CONTENT_LIST)
2874 struct Content *content= (struct Content *)(conf[i].value);
2877 for (c = 0; c < num_entities; c++)
2878 for (y = 0; y < 3; y++)
2879 for (x = 0; x < 3; x++)
2880 content[c].e[x][y] =
2881 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
2884 element_found = FALSE;
2890 checked_free(buffer);
2892 micro_chunk_size += 2 + num_bytes;
2894 else /* constant size configuration data (1, 2 or 4 bytes) */
2896 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
2897 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
2898 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
2900 for (i = 0; conf[i].data_type != -1; i++)
2902 if (conf[i].element == element &&
2903 conf[i].conf_type == conf_type)
2905 int data_type = conf[i].data_type;
2907 if (data_type == TYPE_ELEMENT)
2908 value = getMappedElement(value);
2910 if (data_type == TYPE_BOOLEAN)
2911 *(boolean *)(conf[i].value) = value;
2913 *(int *) (conf[i].value) = value;
2915 element_found = TRUE;
2921 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
2926 char *error_conf_chunk_bytes =
2927 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
2928 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
2929 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
2930 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
2931 int error_element = real_element;
2933 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
2934 error_conf_chunk_bytes, error_conf_chunk_token,
2935 error_element, EL_NAME(error_element));
2938 return micro_chunk_size;
2941 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
2943 int real_chunk_size = 0;
2945 li = *level; /* copy level data into temporary buffer */
2947 while (!checkEndOfFile(file))
2949 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
2951 if (real_chunk_size >= chunk_size)
2955 *level = li; /* copy temporary buffer back to level data */
2957 return real_chunk_size;
2960 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
2962 int real_chunk_size = 0;
2964 li = *level; /* copy level data into temporary buffer */
2966 while (!checkEndOfFile(file))
2968 int element = getMappedElement(getFile16BitBE(file));
2970 real_chunk_size += 2;
2971 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
2973 if (real_chunk_size >= chunk_size)
2977 *level = li; /* copy temporary buffer back to level data */
2979 return real_chunk_size;
2982 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
2984 int real_chunk_size = 0;
2986 li = *level; /* copy level data into temporary buffer */
2988 while (!checkEndOfFile(file))
2990 int element = getMappedElement(getFile16BitBE(file));
2992 real_chunk_size += 2;
2993 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
2995 if (real_chunk_size >= chunk_size)
2999 *level = li; /* copy temporary buffer back to level data */
3001 return real_chunk_size;
3004 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3006 int element = getMappedElement(getFile16BitBE(file));
3007 int envelope_nr = element - EL_ENVELOPE_1;
3008 int real_chunk_size = 2;
3010 xx_envelope = level->envelope[envelope_nr]; /* copy into temporary buffer */
3012 while (!checkEndOfFile(file))
3014 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3017 if (real_chunk_size >= chunk_size)
3021 level->envelope[envelope_nr] = xx_envelope; /* copy from temporary buffer */
3023 return real_chunk_size;
3026 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3028 int element = getMappedElement(getFile16BitBE(file));
3029 int real_chunk_size = 2;
3030 struct ElementInfo *ei = &element_info[element];
3033 xx_ei = *ei; /* copy element data into temporary buffer */
3035 xx_ei.num_change_pages = -1;
3037 while (!checkEndOfFile(file))
3039 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3041 if (xx_ei.num_change_pages != -1)
3044 if (real_chunk_size >= chunk_size)
3050 if (ei->num_change_pages == -1)
3052 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3055 ei->num_change_pages = 1;
3057 setElementChangePages(ei, 1);
3058 setElementChangeInfoToDefaults(ei->change);
3060 return real_chunk_size;
3063 /* initialize number of change pages stored for this custom element */
3064 setElementChangePages(ei, ei->num_change_pages);
3065 for (i = 0; i < ei->num_change_pages; i++)
3066 setElementChangeInfoToDefaults(&ei->change_page[i]);
3068 /* start with reading properties for the first change page */
3069 xx_current_change_page = 0;
3071 while (!checkEndOfFile(file))
3073 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3075 xx_change = *change; /* copy change data into temporary buffer */
3077 resetEventBits(); /* reset bits; change page might have changed */
3079 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3082 *change = xx_change;
3084 setEventFlagsFromEventBits(change);
3086 if (real_chunk_size >= chunk_size)
3090 return real_chunk_size;
3093 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3095 int element = getMappedElement(getFile16BitBE(file));
3096 int real_chunk_size = 2;
3097 struct ElementInfo *ei = &element_info[element];
3098 struct ElementGroupInfo *group = ei->group;
3100 xx_ei = *ei; /* copy element data into temporary buffer */
3101 xx_group = *group; /* copy group data into temporary buffer */
3103 while (!checkEndOfFile(file))
3105 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3108 if (real_chunk_size >= chunk_size)
3115 return real_chunk_size;
3118 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3119 struct LevelFileInfo *level_file_info,
3120 boolean level_info_only)
3122 char *filename = level_file_info->filename;
3123 char cookie[MAX_LINE_LEN];
3124 char chunk_name[CHUNK_ID_LEN + 1];
3128 if (!(file = openFile(filename, MODE_READ)))
3130 level->no_valid_file = TRUE;
3132 if (!level_info_only)
3133 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3138 getFileChunkBE(file, chunk_name, NULL);
3139 if (strEqual(chunk_name, "RND1"))
3141 getFile32BitBE(file); /* not used */
3143 getFileChunkBE(file, chunk_name, NULL);
3144 if (!strEqual(chunk_name, "CAVE"))
3146 level->no_valid_file = TRUE;
3148 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3155 else /* check for pre-2.0 file format with cookie string */
3157 strcpy(cookie, chunk_name);
3158 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3160 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3161 cookie[strlen(cookie) - 1] = '\0';
3163 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3165 level->no_valid_file = TRUE;
3167 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3174 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3176 level->no_valid_file = TRUE;
3178 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3185 /* pre-2.0 level files have no game version, so use file version here */
3186 level->game_version = level->file_version;
3189 if (level->file_version < FILE_VERSION_1_2)
3191 /* level files from versions before 1.2.0 without chunk structure */
3192 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3193 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3201 int (*loader)(File *, int, struct LevelInfo *);
3205 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3206 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3207 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3208 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3209 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3210 { "INFO", -1, LoadLevel_INFO },
3211 { "BODY", -1, LoadLevel_BODY },
3212 { "CONT", -1, LoadLevel_CONT },
3213 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3214 { "CNT3", -1, LoadLevel_CNT3 },
3215 { "CUS1", -1, LoadLevel_CUS1 },
3216 { "CUS2", -1, LoadLevel_CUS2 },
3217 { "CUS3", -1, LoadLevel_CUS3 },
3218 { "CUS4", -1, LoadLevel_CUS4 },
3219 { "GRP1", -1, LoadLevel_GRP1 },
3220 { "CONF", -1, LoadLevel_CONF },
3221 { "ELEM", -1, LoadLevel_ELEM },
3222 { "NOTE", -1, LoadLevel_NOTE },
3223 { "CUSX", -1, LoadLevel_CUSX },
3224 { "GRPX", -1, LoadLevel_GRPX },
3229 while (getFileChunkBE(file, chunk_name, &chunk_size))
3233 while (chunk_info[i].name != NULL &&
3234 !strEqual(chunk_name, chunk_info[i].name))
3237 if (chunk_info[i].name == NULL)
3239 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3240 chunk_name, filename);
3241 ReadUnusedBytesFromFile(file, chunk_size);
3243 else if (chunk_info[i].size != -1 &&
3244 chunk_info[i].size != chunk_size)
3246 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3247 chunk_size, chunk_name, filename);
3248 ReadUnusedBytesFromFile(file, chunk_size);
3252 /* call function to load this level chunk */
3253 int chunk_size_expected =
3254 (chunk_info[i].loader)(file, chunk_size, level);
3256 /* the size of some chunks cannot be checked before reading other
3257 chunks first (like "HEAD" and "BODY") that contain some header
3258 information, so check them here */
3259 if (chunk_size_expected != chunk_size)
3261 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3262 chunk_size, chunk_name, filename);
3272 /* ------------------------------------------------------------------------- */
3273 /* functions for loading EM level */
3274 /* ------------------------------------------------------------------------- */
3276 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3278 static int ball_xy[8][2] =
3289 struct LevelInfo_EM *level_em = level->native_em_level;
3290 struct LEVEL *lev = level_em->lev;
3291 struct PLAYER **ply = level_em->ply;
3294 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3295 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3297 lev->time_seconds = level->time;
3298 lev->required_initial = level->gems_needed;
3300 lev->emerald_score = level->score[SC_EMERALD];
3301 lev->diamond_score = level->score[SC_DIAMOND];
3302 lev->alien_score = level->score[SC_ROBOT];
3303 lev->tank_score = level->score[SC_SPACESHIP];
3304 lev->bug_score = level->score[SC_BUG];
3305 lev->eater_score = level->score[SC_YAMYAM];
3306 lev->nut_score = level->score[SC_NUT];
3307 lev->dynamite_score = level->score[SC_DYNAMITE];
3308 lev->key_score = level->score[SC_KEY];
3309 lev->exit_score = level->score[SC_TIME_BONUS];
3311 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3312 for (y = 0; y < 3; y++)
3313 for (x = 0; x < 3; x++)
3314 lev->eater_array[i][y * 3 + x] =
3315 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3317 lev->amoeba_time = level->amoeba_speed;
3318 lev->wonderwall_time_initial = level->time_magic_wall;
3319 lev->wheel_time = level->time_wheel;
3321 lev->android_move_time = level->android_move_time;
3322 lev->android_clone_time = level->android_clone_time;
3323 lev->ball_random = level->ball_random;
3324 lev->ball_state_initial = level->ball_state_initial;
3325 lev->ball_time = level->ball_time;
3326 lev->num_ball_arrays = level->num_ball_contents;
3328 lev->lenses_score = level->lenses_score;
3329 lev->magnify_score = level->magnify_score;
3330 lev->slurp_score = level->slurp_score;
3332 lev->lenses_time = level->lenses_time;
3333 lev->magnify_time = level->magnify_time;
3335 lev->wind_direction_initial =
3336 map_direction_RND_to_EM(level->wind_direction_initial);
3337 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3338 lev->wind_time : 0);
3340 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3341 for (j = 0; j < 8; j++)
3342 lev->ball_array[i][j] =
3343 map_element_RND_to_EM(level->
3344 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3346 map_android_clone_elements_RND_to_EM(level);
3348 /* first fill the complete playfield with the default border element */
3349 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3350 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3351 level_em->cave[x][y] = ZBORDER;
3353 if (BorderElement == EL_STEELWALL)
3355 for (y = 0; y < lev->height + 2; y++)
3356 for (x = 0; x < lev->width + 2; x++)
3357 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3360 /* then copy the real level contents from level file into the playfield */
3361 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3363 int new_element = map_element_RND_to_EM(level->field[x][y]);
3364 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3365 int xx = x + 1 + offset;
3366 int yy = y + 1 + offset;
3368 if (level->field[x][y] == EL_AMOEBA_DEAD)
3369 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3371 level_em->cave[xx][yy] = new_element;
3374 for (i = 0; i < MAX_PLAYERS; i++)
3376 ply[i]->x_initial = 0;
3377 ply[i]->y_initial = 0;
3380 /* initialize player positions and delete players from the playfield */
3381 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3383 if (ELEM_IS_PLAYER(level->field[x][y]))
3385 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3386 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3387 int xx = x + 1 + offset;
3388 int yy = y + 1 + offset;
3390 ply[player_nr]->x_initial = xx;
3391 ply[player_nr]->y_initial = yy;
3393 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3397 if (BorderElement == EL_STEELWALL)
3404 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3406 static int ball_xy[8][2] =
3417 struct LevelInfo_EM *level_em = level->native_em_level;
3418 struct LEVEL *lev = level_em->lev;
3419 struct PLAYER **ply = level_em->ply;
3422 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3423 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3425 level->time = lev->time_seconds;
3426 level->gems_needed = lev->required_initial;
3428 sprintf(level->name, "Level %d", level->file_info.nr);
3430 level->score[SC_EMERALD] = lev->emerald_score;
3431 level->score[SC_DIAMOND] = lev->diamond_score;
3432 level->score[SC_ROBOT] = lev->alien_score;
3433 level->score[SC_SPACESHIP] = lev->tank_score;
3434 level->score[SC_BUG] = lev->bug_score;
3435 level->score[SC_YAMYAM] = lev->eater_score;
3436 level->score[SC_NUT] = lev->nut_score;
3437 level->score[SC_DYNAMITE] = lev->dynamite_score;
3438 level->score[SC_KEY] = lev->key_score;
3439 level->score[SC_TIME_BONUS] = lev->exit_score;
3441 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3443 for (i = 0; i < level->num_yamyam_contents; i++)
3444 for (y = 0; y < 3; y++)
3445 for (x = 0; x < 3; x++)
3446 level->yamyam_content[i].e[x][y] =
3447 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3449 level->amoeba_speed = lev->amoeba_time;
3450 level->time_magic_wall = lev->wonderwall_time_initial;
3451 level->time_wheel = lev->wheel_time;
3453 level->android_move_time = lev->android_move_time;
3454 level->android_clone_time = lev->android_clone_time;
3455 level->ball_random = lev->ball_random;
3456 level->ball_state_initial = lev->ball_state_initial;
3457 level->ball_time = lev->ball_time;
3458 level->num_ball_contents = lev->num_ball_arrays;
3460 level->lenses_score = lev->lenses_score;
3461 level->magnify_score = lev->magnify_score;
3462 level->slurp_score = lev->slurp_score;
3464 level->lenses_time = lev->lenses_time;
3465 level->magnify_time = lev->magnify_time;
3467 level->wind_direction_initial =
3468 map_direction_EM_to_RND(lev->wind_direction_initial);
3470 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3471 for (j = 0; j < 8; j++)
3472 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3473 map_element_EM_to_RND(lev->ball_array[i][j]);
3475 map_android_clone_elements_EM_to_RND(level);
3477 /* convert the playfield (some elements need special treatment) */
3478 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3480 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3482 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3483 new_element = EL_AMOEBA_DEAD;
3485 level->field[x][y] = new_element;
3488 for (i = 0; i < MAX_PLAYERS; i++)
3490 /* in case of all players set to the same field, use the first player */
3491 int nr = MAX_PLAYERS - i - 1;
3492 int jx = ply[nr]->x_initial - 1;
3493 int jy = ply[nr]->y_initial - 1;
3495 if (jx != -1 && jy != -1)
3496 level->field[jx][jy] = EL_PLAYER_1 + nr;
3501 /* ------------------------------------------------------------------------- */
3502 /* functions for loading SP level */
3503 /* ------------------------------------------------------------------------- */
3505 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3507 struct LevelInfo_SP *level_sp = level->native_sp_level;
3508 LevelInfoType *header = &level_sp->header;
3511 level_sp->width = level->fieldx;
3512 level_sp->height = level->fieldy;
3514 for (x = 0; x < level->fieldx; x++)
3515 for (y = 0; y < level->fieldy; y++)
3516 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3518 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3520 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3521 header->LevelTitle[i] = level->name[i];
3522 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3524 header->InfotronsNeeded = level->gems_needed;
3526 header->SpecialPortCount = 0;
3528 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3530 boolean gravity_port_found = FALSE;
3531 boolean gravity_port_valid = FALSE;
3532 int gravity_port_flag;
3533 int gravity_port_base_element;
3534 int element = level->field[x][y];
3536 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3537 element <= EL_SP_GRAVITY_ON_PORT_UP)
3539 gravity_port_found = TRUE;
3540 gravity_port_valid = TRUE;
3541 gravity_port_flag = 1;
3542 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3544 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3545 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3547 gravity_port_found = TRUE;
3548 gravity_port_valid = TRUE;
3549 gravity_port_flag = 0;
3550 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3552 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3553 element <= EL_SP_GRAVITY_PORT_UP)
3555 /* change R'n'D style gravity inverting special port to normal port
3556 (there are no gravity inverting ports in native Supaplex engine) */
3558 gravity_port_found = TRUE;
3559 gravity_port_valid = FALSE;
3560 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3563 if (gravity_port_found)
3565 if (gravity_port_valid &&
3566 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3568 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3570 port->PortLocation = (y * level->fieldx + x) * 2;
3571 port->Gravity = gravity_port_flag;
3573 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3575 header->SpecialPortCount++;
3579 /* change special gravity port to normal port */
3581 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3584 level_sp->playfield[x][y] = element - EL_SP_START;
3589 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3591 struct LevelInfo_SP *level_sp = level->native_sp_level;
3592 LevelInfoType *header = &level_sp->header;
3595 level->fieldx = level_sp->width;
3596 level->fieldy = level_sp->height;
3598 for (x = 0; x < level->fieldx; x++)
3600 for (y = 0; y < level->fieldy; y++)
3602 int element_old = level_sp->playfield[x][y];
3603 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3605 if (element_new == EL_UNKNOWN)
3606 Error(ERR_WARN, "invalid element %d at position %d, %d",
3609 level->field[x][y] = element_new;
3613 for (i = 0; i < MAX_PLAYERS; i++)
3614 level->initial_player_gravity[i] =
3615 (header->InitialGravity == 1 ? TRUE : FALSE);
3617 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3618 level->name[i] = header->LevelTitle[i];
3619 level->name[SP_LEVEL_NAME_LEN] = '\0';
3621 level->gems_needed = header->InfotronsNeeded;
3623 for (i = 0; i < header->SpecialPortCount; i++)
3625 SpecialPortType *port = &header->SpecialPort[i];
3626 int port_location = port->PortLocation;
3627 int gravity = port->Gravity;
3628 int port_x, port_y, port_element;
3630 port_x = (port_location / 2) % level->fieldx;
3631 port_y = (port_location / 2) / level->fieldx;
3633 if (port_x < 0 || port_x >= level->fieldx ||
3634 port_y < 0 || port_y >= level->fieldy)
3636 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3642 port_element = level->field[port_x][port_y];
3644 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3645 port_element > EL_SP_GRAVITY_PORT_UP)
3647 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3652 /* change previous (wrong) gravity inverting special port to either
3653 gravity enabling special port or gravity disabling special port */
3654 level->field[port_x][port_y] +=
3655 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3656 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3659 /* change special gravity ports without database entries to normal ports */
3660 for (x = 0; x < level->fieldx; x++)
3661 for (y = 0; y < level->fieldy; y++)
3662 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3663 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3664 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3666 level->time = 0; /* no time limit */
3667 level->amoeba_speed = 0;
3668 level->time_magic_wall = 0;
3669 level->time_wheel = 0;
3670 level->amoeba_content = EL_EMPTY;
3673 /* original Supaplex does not use score values -- use default values */
3675 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3676 level->score[i] = 0;
3679 /* there are no yamyams in supaplex levels */
3680 for (i = 0; i < level->num_yamyam_contents; i++)
3681 for (x = 0; x < 3; x++)
3682 for (y = 0; y < 3; y++)
3683 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3686 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3688 struct LevelInfo_SP *level_sp = level->native_sp_level;
3689 struct DemoInfo_SP *demo = &level_sp->demo;
3692 /* always start with reliable default values */
3693 demo->is_available = FALSE;
3696 if (TAPE_IS_EMPTY(tape))
3699 demo->level_nr = tape.level_nr; /* (currently not used) */
3701 level_sp->header.DemoRandomSeed = tape.random_seed;
3704 for (i = 0; i < tape.length; i++)
3706 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3707 int demo_repeat = tape.pos[i].delay;
3709 for (j = 0; j < demo_repeat / 16; j++)
3710 demo->data[demo->length++] = 0xf0 | demo_action;
3712 if (demo_repeat % 16)
3713 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3716 demo->data[demo->length++] = 0xff;
3718 demo->is_available = TRUE;
3721 static void setTapeInfoToDefaults();
3723 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3725 struct LevelInfo_SP *level_sp = level->native_sp_level;
3726 struct DemoInfo_SP *demo = &level_sp->demo;
3727 char *filename = level->file_info.filename;
3730 /* always start with reliable default values */
3731 setTapeInfoToDefaults();
3733 if (!demo->is_available)
3736 tape.level_nr = demo->level_nr; /* (currently not used) */
3737 tape.length = demo->length - 1; /* without "end of demo" byte */
3738 tape.random_seed = level_sp->header.DemoRandomSeed;
3740 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3742 for (i = 0; i < demo->length - 1; i++)
3744 int demo_action = demo->data[i] & 0x0f;
3745 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3747 tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
3748 tape.pos[i].delay = demo_repeat + 1;
3751 tape.length_frames = GetTapeLengthFrames();
3752 tape.length_seconds = GetTapeLengthSeconds();
3756 /* ------------------------------------------------------------------------- */
3757 /* functions for loading DC level */
3758 /* ------------------------------------------------------------------------- */
3760 #define DC_LEVEL_HEADER_SIZE 344
3762 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
3764 static int last_data_encoded;
3768 int diff_hi, diff_lo;
3769 int data_hi, data_lo;
3770 unsigned short data_decoded;
3774 last_data_encoded = 0;
3781 diff = data_encoded - last_data_encoded;
3782 diff_hi = diff & ~0xff;
3783 diff_lo = diff & 0xff;
3787 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
3788 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
3789 data_hi = data_hi & 0xff00;
3791 data_decoded = data_hi | data_lo;
3793 last_data_encoded = data_encoded;
3795 offset1 = (offset1 + 1) % 31;
3796 offset2 = offset2 & 0xff;
3798 return data_decoded;
3801 int getMappedElement_DC(int element)
3809 /* 0x0117 - 0x036e: (?) */
3812 /* 0x042d - 0x0684: (?) */
3828 element = EL_CRYSTAL;
3831 case 0x0e77: /* quicksand (boulder) */
3832 element = EL_QUICKSAND_FAST_FULL;
3835 case 0x0e99: /* slow quicksand (boulder) */
3836 element = EL_QUICKSAND_FULL;
3840 element = EL_EM_EXIT_OPEN;
3844 element = EL_EM_EXIT_CLOSED;
3848 element = EL_EM_STEEL_EXIT_OPEN;
3852 element = EL_EM_STEEL_EXIT_CLOSED;
3855 case 0x0f4f: /* dynamite (lit 1) */
3856 element = EL_EM_DYNAMITE_ACTIVE;
3859 case 0x0f57: /* dynamite (lit 2) */
3860 element = EL_EM_DYNAMITE_ACTIVE;
3863 case 0x0f5f: /* dynamite (lit 3) */
3864 element = EL_EM_DYNAMITE_ACTIVE;
3867 case 0x0f67: /* dynamite (lit 4) */
3868 element = EL_EM_DYNAMITE_ACTIVE;
3875 element = EL_AMOEBA_WET;
3879 element = EL_AMOEBA_DROP;
3883 element = EL_DC_MAGIC_WALL;
3887 element = EL_SPACESHIP_UP;
3891 element = EL_SPACESHIP_DOWN;
3895 element = EL_SPACESHIP_LEFT;
3899 element = EL_SPACESHIP_RIGHT;
3903 element = EL_BUG_UP;
3907 element = EL_BUG_DOWN;
3911 element = EL_BUG_LEFT;
3915 element = EL_BUG_RIGHT;
3919 element = EL_MOLE_UP;
3923 element = EL_MOLE_DOWN;
3927 element = EL_MOLE_LEFT;
3931 element = EL_MOLE_RIGHT;
3939 element = EL_YAMYAM;
3943 element = EL_SWITCHGATE_OPEN;
3947 element = EL_SWITCHGATE_CLOSED;
3951 element = EL_DC_SWITCHGATE_SWITCH_UP;
3955 element = EL_TIMEGATE_CLOSED;
3958 case 0x144c: /* conveyor belt switch (green) */
3959 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
3962 case 0x144f: /* conveyor belt switch (red) */
3963 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
3966 case 0x1452: /* conveyor belt switch (blue) */
3967 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
3971 element = EL_CONVEYOR_BELT_3_MIDDLE;
3975 element = EL_CONVEYOR_BELT_3_LEFT;
3979 element = EL_CONVEYOR_BELT_3_RIGHT;
3983 element = EL_CONVEYOR_BELT_1_MIDDLE;
3987 element = EL_CONVEYOR_BELT_1_LEFT;
3991 element = EL_CONVEYOR_BELT_1_RIGHT;
3995 element = EL_CONVEYOR_BELT_4_MIDDLE;
3999 element = EL_CONVEYOR_BELT_4_LEFT;
4003 element = EL_CONVEYOR_BELT_4_RIGHT;
4007 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4011 element = EL_EXPANDABLE_WALL_VERTICAL;
4015 element = EL_EXPANDABLE_WALL_ANY;
4018 case 0x14ce: /* growing steel wall (left/right) */
4019 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4022 case 0x14df: /* growing steel wall (up/down) */
4023 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4026 case 0x14e8: /* growing steel wall (up/down/left/right) */
4027 element = EL_EXPANDABLE_STEELWALL_ANY;
4031 element = EL_SHIELD_DEADLY;
4035 element = EL_EXTRA_TIME;
4043 element = EL_EMPTY_SPACE;
4046 case 0x1578: /* quicksand (empty) */
4047 element = EL_QUICKSAND_FAST_EMPTY;
4050 case 0x1579: /* slow quicksand (empty) */
4051 element = EL_QUICKSAND_EMPTY;
4054 /* 0x157c - 0x158b: */
4057 /* 0x1590 - 0x159f: */
4058 /* EL_DC_LANDMINE */
4061 element = EL_EM_DYNAMITE;
4064 case 0x15a1: /* key (red) */
4065 element = EL_EM_KEY_1;
4068 case 0x15a2: /* key (yellow) */
4069 element = EL_EM_KEY_2;
4072 case 0x15a3: /* key (blue) */
4073 element = EL_EM_KEY_4;
4076 case 0x15a4: /* key (green) */
4077 element = EL_EM_KEY_3;
4080 case 0x15a5: /* key (white) */
4081 element = EL_DC_KEY_WHITE;
4085 element = EL_WALL_SLIPPERY;
4092 case 0x15a8: /* wall (not round) */
4096 case 0x15a9: /* (blue) */
4097 element = EL_CHAR_A;
4100 case 0x15aa: /* (blue) */
4101 element = EL_CHAR_B;
4104 case 0x15ab: /* (blue) */
4105 element = EL_CHAR_C;
4108 case 0x15ac: /* (blue) */
4109 element = EL_CHAR_D;
4112 case 0x15ad: /* (blue) */
4113 element = EL_CHAR_E;
4116 case 0x15ae: /* (blue) */
4117 element = EL_CHAR_F;
4120 case 0x15af: /* (blue) */
4121 element = EL_CHAR_G;
4124 case 0x15b0: /* (blue) */
4125 element = EL_CHAR_H;
4128 case 0x15b1: /* (blue) */
4129 element = EL_CHAR_I;
4132 case 0x15b2: /* (blue) */
4133 element = EL_CHAR_J;
4136 case 0x15b3: /* (blue) */
4137 element = EL_CHAR_K;
4140 case 0x15b4: /* (blue) */
4141 element = EL_CHAR_L;
4144 case 0x15b5: /* (blue) */
4145 element = EL_CHAR_M;
4148 case 0x15b6: /* (blue) */
4149 element = EL_CHAR_N;
4152 case 0x15b7: /* (blue) */
4153 element = EL_CHAR_O;
4156 case 0x15b8: /* (blue) */
4157 element = EL_CHAR_P;
4160 case 0x15b9: /* (blue) */
4161 element = EL_CHAR_Q;
4164 case 0x15ba: /* (blue) */
4165 element = EL_CHAR_R;
4168 case 0x15bb: /* (blue) */
4169 element = EL_CHAR_S;
4172 case 0x15bc: /* (blue) */
4173 element = EL_CHAR_T;
4176 case 0x15bd: /* (blue) */
4177 element = EL_CHAR_U;
4180 case 0x15be: /* (blue) */
4181 element = EL_CHAR_V;
4184 case 0x15bf: /* (blue) */
4185 element = EL_CHAR_W;
4188 case 0x15c0: /* (blue) */
4189 element = EL_CHAR_X;
4192 case 0x15c1: /* (blue) */
4193 element = EL_CHAR_Y;
4196 case 0x15c2: /* (blue) */
4197 element = EL_CHAR_Z;
4200 case 0x15c3: /* (blue) */
4201 element = EL_CHAR_AUMLAUT;
4204 case 0x15c4: /* (blue) */
4205 element = EL_CHAR_OUMLAUT;
4208 case 0x15c5: /* (blue) */
4209 element = EL_CHAR_UUMLAUT;
4212 case 0x15c6: /* (blue) */
4213 element = EL_CHAR_0;
4216 case 0x15c7: /* (blue) */
4217 element = EL_CHAR_1;
4220 case 0x15c8: /* (blue) */
4221 element = EL_CHAR_2;
4224 case 0x15c9: /* (blue) */
4225 element = EL_CHAR_3;
4228 case 0x15ca: /* (blue) */
4229 element = EL_CHAR_4;
4232 case 0x15cb: /* (blue) */
4233 element = EL_CHAR_5;
4236 case 0x15cc: /* (blue) */
4237 element = EL_CHAR_6;
4240 case 0x15cd: /* (blue) */
4241 element = EL_CHAR_7;
4244 case 0x15ce: /* (blue) */
4245 element = EL_CHAR_8;
4248 case 0x15cf: /* (blue) */
4249 element = EL_CHAR_9;
4252 case 0x15d0: /* (blue) */
4253 element = EL_CHAR_PERIOD;
4256 case 0x15d1: /* (blue) */
4257 element = EL_CHAR_EXCLAM;
4260 case 0x15d2: /* (blue) */
4261 element = EL_CHAR_COLON;
4264 case 0x15d3: /* (blue) */
4265 element = EL_CHAR_LESS;
4268 case 0x15d4: /* (blue) */
4269 element = EL_CHAR_GREATER;
4272 case 0x15d5: /* (blue) */
4273 element = EL_CHAR_QUESTION;
4276 case 0x15d6: /* (blue) */
4277 element = EL_CHAR_COPYRIGHT;
4280 case 0x15d7: /* (blue) */
4281 element = EL_CHAR_UP;
4284 case 0x15d8: /* (blue) */
4285 element = EL_CHAR_DOWN;
4288 case 0x15d9: /* (blue) */
4289 element = EL_CHAR_BUTTON;
4292 case 0x15da: /* (blue) */
4293 element = EL_CHAR_PLUS;
4296 case 0x15db: /* (blue) */
4297 element = EL_CHAR_MINUS;
4300 case 0x15dc: /* (blue) */
4301 element = EL_CHAR_APOSTROPHE;
4304 case 0x15dd: /* (blue) */
4305 element = EL_CHAR_PARENLEFT;
4308 case 0x15de: /* (blue) */
4309 element = EL_CHAR_PARENRIGHT;
4312 case 0x15df: /* (green) */
4313 element = EL_CHAR_A;
4316 case 0x15e0: /* (green) */
4317 element = EL_CHAR_B;
4320 case 0x15e1: /* (green) */
4321 element = EL_CHAR_C;
4324 case 0x15e2: /* (green) */
4325 element = EL_CHAR_D;
4328 case 0x15e3: /* (green) */
4329 element = EL_CHAR_E;
4332 case 0x15e4: /* (green) */
4333 element = EL_CHAR_F;
4336 case 0x15e5: /* (green) */
4337 element = EL_CHAR_G;
4340 case 0x15e6: /* (green) */
4341 element = EL_CHAR_H;
4344 case 0x15e7: /* (green) */
4345 element = EL_CHAR_I;
4348 case 0x15e8: /* (green) */
4349 element = EL_CHAR_J;
4352 case 0x15e9: /* (green) */
4353 element = EL_CHAR_K;
4356 case 0x15ea: /* (green) */
4357 element = EL_CHAR_L;
4360 case 0x15eb: /* (green) */
4361 element = EL_CHAR_M;
4364 case 0x15ec: /* (green) */
4365 element = EL_CHAR_N;
4368 case 0x15ed: /* (green) */
4369 element = EL_CHAR_O;
4372 case 0x15ee: /* (green) */
4373 element = EL_CHAR_P;
4376 case 0x15ef: /* (green) */
4377 element = EL_CHAR_Q;
4380 case 0x15f0: /* (green) */
4381 element = EL_CHAR_R;
4384 case 0x15f1: /* (green) */
4385 element = EL_CHAR_S;
4388 case 0x15f2: /* (green) */
4389 element = EL_CHAR_T;
4392 case 0x15f3: /* (green) */
4393 element = EL_CHAR_U;
4396 case 0x15f4: /* (green) */
4397 element = EL_CHAR_V;
4400 case 0x15f5: /* (green) */
4401 element = EL_CHAR_W;
4404 case 0x15f6: /* (green) */
4405 element = EL_CHAR_X;
4408 case 0x15f7: /* (green) */
4409 element = EL_CHAR_Y;
4412 case 0x15f8: /* (green) */
4413 element = EL_CHAR_Z;
4416 case 0x15f9: /* (green) */
4417 element = EL_CHAR_AUMLAUT;
4420 case 0x15fa: /* (green) */
4421 element = EL_CHAR_OUMLAUT;
4424 case 0x15fb: /* (green) */
4425 element = EL_CHAR_UUMLAUT;
4428 case 0x15fc: /* (green) */
4429 element = EL_CHAR_0;
4432 case 0x15fd: /* (green) */
4433 element = EL_CHAR_1;
4436 case 0x15fe: /* (green) */
4437 element = EL_CHAR_2;
4440 case 0x15ff: /* (green) */
4441 element = EL_CHAR_3;
4444 case 0x1600: /* (green) */
4445 element = EL_CHAR_4;
4448 case 0x1601: /* (green) */
4449 element = EL_CHAR_5;
4452 case 0x1602: /* (green) */
4453 element = EL_CHAR_6;
4456 case 0x1603: /* (green) */
4457 element = EL_CHAR_7;
4460 case 0x1604: /* (green) */
4461 element = EL_CHAR_8;
4464 case 0x1605: /* (green) */
4465 element = EL_CHAR_9;
4468 case 0x1606: /* (green) */
4469 element = EL_CHAR_PERIOD;
4472 case 0x1607: /* (green) */
4473 element = EL_CHAR_EXCLAM;
4476 case 0x1608: /* (green) */
4477 element = EL_CHAR_COLON;
4480 case 0x1609: /* (green) */
4481 element = EL_CHAR_LESS;
4484 case 0x160a: /* (green) */
4485 element = EL_CHAR_GREATER;
4488 case 0x160b: /* (green) */
4489 element = EL_CHAR_QUESTION;
4492 case 0x160c: /* (green) */
4493 element = EL_CHAR_COPYRIGHT;
4496 case 0x160d: /* (green) */
4497 element = EL_CHAR_UP;
4500 case 0x160e: /* (green) */
4501 element = EL_CHAR_DOWN;
4504 case 0x160f: /* (green) */
4505 element = EL_CHAR_BUTTON;
4508 case 0x1610: /* (green) */
4509 element = EL_CHAR_PLUS;
4512 case 0x1611: /* (green) */
4513 element = EL_CHAR_MINUS;
4516 case 0x1612: /* (green) */
4517 element = EL_CHAR_APOSTROPHE;
4520 case 0x1613: /* (green) */
4521 element = EL_CHAR_PARENLEFT;
4524 case 0x1614: /* (green) */
4525 element = EL_CHAR_PARENRIGHT;
4528 case 0x1615: /* (blue steel) */
4529 element = EL_STEEL_CHAR_A;
4532 case 0x1616: /* (blue steel) */
4533 element = EL_STEEL_CHAR_B;
4536 case 0x1617: /* (blue steel) */
4537 element = EL_STEEL_CHAR_C;
4540 case 0x1618: /* (blue steel) */
4541 element = EL_STEEL_CHAR_D;
4544 case 0x1619: /* (blue steel) */
4545 element = EL_STEEL_CHAR_E;
4548 case 0x161a: /* (blue steel) */
4549 element = EL_STEEL_CHAR_F;
4552 case 0x161b: /* (blue steel) */
4553 element = EL_STEEL_CHAR_G;
4556 case 0x161c: /* (blue steel) */
4557 element = EL_STEEL_CHAR_H;
4560 case 0x161d: /* (blue steel) */
4561 element = EL_STEEL_CHAR_I;
4564 case 0x161e: /* (blue steel) */
4565 element = EL_STEEL_CHAR_J;
4568 case 0x161f: /* (blue steel) */
4569 element = EL_STEEL_CHAR_K;
4572 case 0x1620: /* (blue steel) */
4573 element = EL_STEEL_CHAR_L;
4576 case 0x1621: /* (blue steel) */
4577 element = EL_STEEL_CHAR_M;
4580 case 0x1622: /* (blue steel) */
4581 element = EL_STEEL_CHAR_N;
4584 case 0x1623: /* (blue steel) */
4585 element = EL_STEEL_CHAR_O;
4588 case 0x1624: /* (blue steel) */
4589 element = EL_STEEL_CHAR_P;
4592 case 0x1625: /* (blue steel) */
4593 element = EL_STEEL_CHAR_Q;
4596 case 0x1626: /* (blue steel) */
4597 element = EL_STEEL_CHAR_R;
4600 case 0x1627: /* (blue steel) */
4601 element = EL_STEEL_CHAR_S;
4604 case 0x1628: /* (blue steel) */
4605 element = EL_STEEL_CHAR_T;
4608 case 0x1629: /* (blue steel) */
4609 element = EL_STEEL_CHAR_U;
4612 case 0x162a: /* (blue steel) */
4613 element = EL_STEEL_CHAR_V;
4616 case 0x162b: /* (blue steel) */
4617 element = EL_STEEL_CHAR_W;
4620 case 0x162c: /* (blue steel) */
4621 element = EL_STEEL_CHAR_X;
4624 case 0x162d: /* (blue steel) */
4625 element = EL_STEEL_CHAR_Y;
4628 case 0x162e: /* (blue steel) */
4629 element = EL_STEEL_CHAR_Z;
4632 case 0x162f: /* (blue steel) */
4633 element = EL_STEEL_CHAR_AUMLAUT;
4636 case 0x1630: /* (blue steel) */
4637 element = EL_STEEL_CHAR_OUMLAUT;
4640 case 0x1631: /* (blue steel) */
4641 element = EL_STEEL_CHAR_UUMLAUT;
4644 case 0x1632: /* (blue steel) */
4645 element = EL_STEEL_CHAR_0;
4648 case 0x1633: /* (blue steel) */
4649 element = EL_STEEL_CHAR_1;
4652 case 0x1634: /* (blue steel) */
4653 element = EL_STEEL_CHAR_2;
4656 case 0x1635: /* (blue steel) */
4657 element = EL_STEEL_CHAR_3;
4660 case 0x1636: /* (blue steel) */
4661 element = EL_STEEL_CHAR_4;
4664 case 0x1637: /* (blue steel) */
4665 element = EL_STEEL_CHAR_5;
4668 case 0x1638: /* (blue steel) */
4669 element = EL_STEEL_CHAR_6;
4672 case 0x1639: /* (blue steel) */
4673 element = EL_STEEL_CHAR_7;
4676 case 0x163a: /* (blue steel) */
4677 element = EL_STEEL_CHAR_8;
4680 case 0x163b: /* (blue steel) */
4681 element = EL_STEEL_CHAR_9;
4684 case 0x163c: /* (blue steel) */
4685 element = EL_STEEL_CHAR_PERIOD;
4688 case 0x163d: /* (blue steel) */
4689 element = EL_STEEL_CHAR_EXCLAM;
4692 case 0x163e: /* (blue steel) */
4693 element = EL_STEEL_CHAR_COLON;
4696 case 0x163f: /* (blue steel) */
4697 element = EL_STEEL_CHAR_LESS;
4700 case 0x1640: /* (blue steel) */
4701 element = EL_STEEL_CHAR_GREATER;
4704 case 0x1641: /* (blue steel) */
4705 element = EL_STEEL_CHAR_QUESTION;
4708 case 0x1642: /* (blue steel) */
4709 element = EL_STEEL_CHAR_COPYRIGHT;
4712 case 0x1643: /* (blue steel) */
4713 element = EL_STEEL_CHAR_UP;
4716 case 0x1644: /* (blue steel) */
4717 element = EL_STEEL_CHAR_DOWN;
4720 case 0x1645: /* (blue steel) */
4721 element = EL_STEEL_CHAR_BUTTON;
4724 case 0x1646: /* (blue steel) */
4725 element = EL_STEEL_CHAR_PLUS;
4728 case 0x1647: /* (blue steel) */
4729 element = EL_STEEL_CHAR_MINUS;
4732 case 0x1648: /* (blue steel) */
4733 element = EL_STEEL_CHAR_APOSTROPHE;
4736 case 0x1649: /* (blue steel) */
4737 element = EL_STEEL_CHAR_PARENLEFT;
4740 case 0x164a: /* (blue steel) */
4741 element = EL_STEEL_CHAR_PARENRIGHT;
4744 case 0x164b: /* (green steel) */
4745 element = EL_STEEL_CHAR_A;
4748 case 0x164c: /* (green steel) */
4749 element = EL_STEEL_CHAR_B;
4752 case 0x164d: /* (green steel) */
4753 element = EL_STEEL_CHAR_C;
4756 case 0x164e: /* (green steel) */
4757 element = EL_STEEL_CHAR_D;
4760 case 0x164f: /* (green steel) */
4761 element = EL_STEEL_CHAR_E;
4764 case 0x1650: /* (green steel) */
4765 element = EL_STEEL_CHAR_F;
4768 case 0x1651: /* (green steel) */
4769 element = EL_STEEL_CHAR_G;
4772 case 0x1652: /* (green steel) */
4773 element = EL_STEEL_CHAR_H;
4776 case 0x1653: /* (green steel) */
4777 element = EL_STEEL_CHAR_I;
4780 case 0x1654: /* (green steel) */
4781 element = EL_STEEL_CHAR_J;
4784 case 0x1655: /* (green steel) */
4785 element = EL_STEEL_CHAR_K;
4788 case 0x1656: /* (green steel) */
4789 element = EL_STEEL_CHAR_L;
4792 case 0x1657: /* (green steel) */
4793 element = EL_STEEL_CHAR_M;
4796 case 0x1658: /* (green steel) */
4797 element = EL_STEEL_CHAR_N;
4800 case 0x1659: /* (green steel) */
4801 element = EL_STEEL_CHAR_O;
4804 case 0x165a: /* (green steel) */
4805 element = EL_STEEL_CHAR_P;
4808 case 0x165b: /* (green steel) */
4809 element = EL_STEEL_CHAR_Q;
4812 case 0x165c: /* (green steel) */
4813 element = EL_STEEL_CHAR_R;
4816 case 0x165d: /* (green steel) */
4817 element = EL_STEEL_CHAR_S;
4820 case 0x165e: /* (green steel) */
4821 element = EL_STEEL_CHAR_T;
4824 case 0x165f: /* (green steel) */
4825 element = EL_STEEL_CHAR_U;
4828 case 0x1660: /* (green steel) */
4829 element = EL_STEEL_CHAR_V;
4832 case 0x1661: /* (green steel) */
4833 element = EL_STEEL_CHAR_W;
4836 case 0x1662: /* (green steel) */
4837 element = EL_STEEL_CHAR_X;
4840 case 0x1663: /* (green steel) */
4841 element = EL_STEEL_CHAR_Y;
4844 case 0x1664: /* (green steel) */
4845 element = EL_STEEL_CHAR_Z;
4848 case 0x1665: /* (green steel) */
4849 element = EL_STEEL_CHAR_AUMLAUT;
4852 case 0x1666: /* (green steel) */
4853 element = EL_STEEL_CHAR_OUMLAUT;
4856 case 0x1667: /* (green steel) */
4857 element = EL_STEEL_CHAR_UUMLAUT;
4860 case 0x1668: /* (green steel) */
4861 element = EL_STEEL_CHAR_0;
4864 case 0x1669: /* (green steel) */
4865 element = EL_STEEL_CHAR_1;
4868 case 0x166a: /* (green steel) */
4869 element = EL_STEEL_CHAR_2;
4872 case 0x166b: /* (green steel) */
4873 element = EL_STEEL_CHAR_3;
4876 case 0x166c: /* (green steel) */
4877 element = EL_STEEL_CHAR_4;
4880 case 0x166d: /* (green steel) */
4881 element = EL_STEEL_CHAR_5;
4884 case 0x166e: /* (green steel) */
4885 element = EL_STEEL_CHAR_6;
4888 case 0x166f: /* (green steel) */
4889 element = EL_STEEL_CHAR_7;
4892 case 0x1670: /* (green steel) */
4893 element = EL_STEEL_CHAR_8;
4896 case 0x1671: /* (green steel) */
4897 element = EL_STEEL_CHAR_9;
4900 case 0x1672: /* (green steel) */
4901 element = EL_STEEL_CHAR_PERIOD;
4904 case 0x1673: /* (green steel) */
4905 element = EL_STEEL_CHAR_EXCLAM;
4908 case 0x1674: /* (green steel) */
4909 element = EL_STEEL_CHAR_COLON;
4912 case 0x1675: /* (green steel) */
4913 element = EL_STEEL_CHAR_LESS;
4916 case 0x1676: /* (green steel) */
4917 element = EL_STEEL_CHAR_GREATER;
4920 case 0x1677: /* (green steel) */
4921 element = EL_STEEL_CHAR_QUESTION;
4924 case 0x1678: /* (green steel) */
4925 element = EL_STEEL_CHAR_COPYRIGHT;
4928 case 0x1679: /* (green steel) */
4929 element = EL_STEEL_CHAR_UP;
4932 case 0x167a: /* (green steel) */
4933 element = EL_STEEL_CHAR_DOWN;
4936 case 0x167b: /* (green steel) */
4937 element = EL_STEEL_CHAR_BUTTON;
4940 case 0x167c: /* (green steel) */
4941 element = EL_STEEL_CHAR_PLUS;
4944 case 0x167d: /* (green steel) */
4945 element = EL_STEEL_CHAR_MINUS;
4948 case 0x167e: /* (green steel) */
4949 element = EL_STEEL_CHAR_APOSTROPHE;
4952 case 0x167f: /* (green steel) */
4953 element = EL_STEEL_CHAR_PARENLEFT;
4956 case 0x1680: /* (green steel) */
4957 element = EL_STEEL_CHAR_PARENRIGHT;
4960 case 0x1681: /* gate (red) */
4961 element = EL_EM_GATE_1;
4964 case 0x1682: /* secret gate (red) */
4965 element = EL_GATE_1_GRAY;
4968 case 0x1683: /* gate (yellow) */
4969 element = EL_EM_GATE_2;
4972 case 0x1684: /* secret gate (yellow) */
4973 element = EL_GATE_2_GRAY;
4976 case 0x1685: /* gate (blue) */
4977 element = EL_EM_GATE_4;
4980 case 0x1686: /* secret gate (blue) */
4981 element = EL_GATE_4_GRAY;
4984 case 0x1687: /* gate (green) */
4985 element = EL_EM_GATE_3;
4988 case 0x1688: /* secret gate (green) */
4989 element = EL_GATE_3_GRAY;
4992 case 0x1689: /* gate (white) */
4993 element = EL_DC_GATE_WHITE;
4996 case 0x168a: /* secret gate (white) */
4997 element = EL_DC_GATE_WHITE_GRAY;
5000 case 0x168b: /* secret gate (no key) */
5001 element = EL_DC_GATE_FAKE_GRAY;
5005 element = EL_ROBOT_WHEEL;
5009 element = EL_DC_TIMEGATE_SWITCH;
5013 element = EL_ACID_POOL_BOTTOM;
5017 element = EL_ACID_POOL_TOPLEFT;
5021 element = EL_ACID_POOL_TOPRIGHT;
5025 element = EL_ACID_POOL_BOTTOMLEFT;
5029 element = EL_ACID_POOL_BOTTOMRIGHT;
5033 element = EL_STEELWALL;
5037 element = EL_STEELWALL_SLIPPERY;
5040 case 0x1695: /* steel wall (not round) */
5041 element = EL_STEELWALL;
5044 case 0x1696: /* steel wall (left) */
5045 element = EL_DC_STEELWALL_1_LEFT;
5048 case 0x1697: /* steel wall (bottom) */
5049 element = EL_DC_STEELWALL_1_BOTTOM;
5052 case 0x1698: /* steel wall (right) */
5053 element = EL_DC_STEELWALL_1_RIGHT;
5056 case 0x1699: /* steel wall (top) */
5057 element = EL_DC_STEELWALL_1_TOP;
5060 case 0x169a: /* steel wall (left/bottom) */
5061 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5064 case 0x169b: /* steel wall (right/bottom) */
5065 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5068 case 0x169c: /* steel wall (right/top) */
5069 element = EL_DC_STEELWALL_1_TOPRIGHT;
5072 case 0x169d: /* steel wall (left/top) */
5073 element = EL_DC_STEELWALL_1_TOPLEFT;
5076 case 0x169e: /* steel wall (right/bottom small) */
5077 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5080 case 0x169f: /* steel wall (left/bottom small) */
5081 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5084 case 0x16a0: /* steel wall (right/top small) */
5085 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5088 case 0x16a1: /* steel wall (left/top small) */
5089 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5092 case 0x16a2: /* steel wall (left/right) */
5093 element = EL_DC_STEELWALL_1_VERTICAL;
5096 case 0x16a3: /* steel wall (top/bottom) */
5097 element = EL_DC_STEELWALL_1_HORIZONTAL;
5100 case 0x16a4: /* steel wall 2 (left end) */
5101 element = EL_DC_STEELWALL_2_LEFT;
5104 case 0x16a5: /* steel wall 2 (right end) */
5105 element = EL_DC_STEELWALL_2_RIGHT;
5108 case 0x16a6: /* steel wall 2 (top end) */
5109 element = EL_DC_STEELWALL_2_TOP;
5112 case 0x16a7: /* steel wall 2 (bottom end) */
5113 element = EL_DC_STEELWALL_2_BOTTOM;
5116 case 0x16a8: /* steel wall 2 (left/right) */
5117 element = EL_DC_STEELWALL_2_HORIZONTAL;
5120 case 0x16a9: /* steel wall 2 (up/down) */
5121 element = EL_DC_STEELWALL_2_VERTICAL;
5124 case 0x16aa: /* steel wall 2 (mid) */
5125 element = EL_DC_STEELWALL_2_MIDDLE;
5129 element = EL_SIGN_EXCLAMATION;
5133 element = EL_SIGN_RADIOACTIVITY;
5137 element = EL_SIGN_STOP;
5141 element = EL_SIGN_WHEELCHAIR;
5145 element = EL_SIGN_PARKING;
5149 element = EL_SIGN_NO_ENTRY;
5153 element = EL_SIGN_HEART;
5157 element = EL_SIGN_GIVE_WAY;
5161 element = EL_SIGN_ENTRY_FORBIDDEN;
5165 element = EL_SIGN_EMERGENCY_EXIT;
5169 element = EL_SIGN_YIN_YANG;
5173 element = EL_WALL_EMERALD;
5177 element = EL_WALL_DIAMOND;
5181 element = EL_WALL_PEARL;
5185 element = EL_WALL_CRYSTAL;
5189 element = EL_INVISIBLE_WALL;
5193 element = EL_INVISIBLE_STEELWALL;
5196 /* 0x16bc - 0x16cb: */
5197 /* EL_INVISIBLE_SAND */
5200 element = EL_LIGHT_SWITCH;
5204 element = EL_ENVELOPE_1;
5208 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5209 element = EL_DIAMOND;
5210 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5211 element = EL_EMERALD;
5212 else if (element >= 0x157c && element <= 0x158b)
5214 else if (element >= 0x1590 && element <= 0x159f)
5215 element = EL_DC_LANDMINE;
5216 else if (element >= 0x16bc && element <= 0x16cb)
5217 element = EL_INVISIBLE_SAND;
5220 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5221 element = EL_UNKNOWN;
5226 return getMappedElement(element);
5229 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5232 byte header[DC_LEVEL_HEADER_SIZE];
5234 int envelope_header_pos = 62;
5235 int envelope_content_pos = 94;
5236 int level_name_pos = 251;
5237 int level_author_pos = 292;
5238 int envelope_header_len;
5239 int envelope_content_len;
5241 int level_author_len;
5243 int num_yamyam_contents;
5246 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5248 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5250 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5252 header[i * 2 + 0] = header_word >> 8;
5253 header[i * 2 + 1] = header_word & 0xff;
5256 /* read some values from level header to check level decoding integrity */
5257 fieldx = header[6] | (header[7] << 8);
5258 fieldy = header[8] | (header[9] << 8);
5259 num_yamyam_contents = header[60] | (header[61] << 8);
5261 /* do some simple sanity checks to ensure that level was correctly decoded */
5262 if (fieldx < 1 || fieldx > 256 ||
5263 fieldy < 1 || fieldy > 256 ||
5264 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5266 level->no_valid_file = TRUE;
5268 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5273 /* maximum envelope header size is 31 bytes */
5274 envelope_header_len = header[envelope_header_pos];
5275 /* maximum envelope content size is 110 (156?) bytes */
5276 envelope_content_len = header[envelope_content_pos];
5278 /* maximum level title size is 40 bytes */
5279 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5280 /* maximum level author size is 30 (51?) bytes */
5281 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5285 for (i = 0; i < envelope_header_len; i++)
5286 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5287 level->envelope[0].text[envelope_size++] =
5288 header[envelope_header_pos + 1 + i];
5290 if (envelope_header_len > 0 && envelope_content_len > 0)
5292 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5293 level->envelope[0].text[envelope_size++] = '\n';
5294 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5295 level->envelope[0].text[envelope_size++] = '\n';
5298 for (i = 0; i < envelope_content_len; i++)
5299 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5300 level->envelope[0].text[envelope_size++] =
5301 header[envelope_content_pos + 1 + i];
5303 level->envelope[0].text[envelope_size] = '\0';
5305 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5306 level->envelope[0].ysize = 10;
5307 level->envelope[0].autowrap = TRUE;
5308 level->envelope[0].centered = TRUE;
5310 for (i = 0; i < level_name_len; i++)
5311 level->name[i] = header[level_name_pos + 1 + i];
5312 level->name[level_name_len] = '\0';
5314 for (i = 0; i < level_author_len; i++)
5315 level->author[i] = header[level_author_pos + 1 + i];
5316 level->author[level_author_len] = '\0';
5318 num_yamyam_contents = header[60] | (header[61] << 8);
5319 level->num_yamyam_contents =
5320 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5322 for (i = 0; i < num_yamyam_contents; i++)
5324 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5326 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5327 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5329 if (i < MAX_ELEMENT_CONTENTS)
5330 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5334 fieldx = header[6] | (header[7] << 8);
5335 fieldy = header[8] | (header[9] << 8);
5336 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5337 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5339 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5341 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5342 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5344 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5345 level->field[x][y] = getMappedElement_DC(element_dc);
5348 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5349 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5350 level->field[x][y] = EL_PLAYER_1;
5352 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5353 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5354 level->field[x][y] = EL_PLAYER_2;
5356 level->gems_needed = header[18] | (header[19] << 8);
5358 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5359 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5360 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5361 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5362 level->score[SC_NUT] = header[28] | (header[29] << 8);
5363 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5364 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5365 level->score[SC_BUG] = header[34] | (header[35] << 8);
5366 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5367 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5368 level->score[SC_KEY] = header[40] | (header[41] << 8);
5369 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5371 level->time = header[44] | (header[45] << 8);
5373 level->amoeba_speed = header[46] | (header[47] << 8);
5374 level->time_light = header[48] | (header[49] << 8);
5375 level->time_timegate = header[50] | (header[51] << 8);
5376 level->time_wheel = header[52] | (header[53] << 8);
5377 level->time_magic_wall = header[54] | (header[55] << 8);
5378 level->extra_time = header[56] | (header[57] << 8);
5379 level->shield_normal_time = header[58] | (header[59] << 8);
5381 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5382 can slip down from flat walls, like normal walls and steel walls */
5383 level->em_slippery_gems = TRUE;
5386 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5387 struct LevelFileInfo *level_file_info,
5388 boolean level_info_only)
5390 char *filename = level_file_info->filename;
5392 int num_magic_bytes = 8;
5393 char magic_bytes[num_magic_bytes + 1];
5394 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5396 if (!(file = openFile(filename, MODE_READ)))
5398 level->no_valid_file = TRUE;
5400 if (!level_info_only)
5401 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5406 // fseek(file, 0x0000, SEEK_SET);
5408 if (level_file_info->packed)
5410 /* read "magic bytes" from start of file */
5411 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5412 magic_bytes[0] = '\0';
5414 /* check "magic bytes" for correct file format */
5415 if (!strPrefix(magic_bytes, "DC2"))
5417 level->no_valid_file = TRUE;
5419 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5425 if (strPrefix(magic_bytes, "DC2Win95") ||
5426 strPrefix(magic_bytes, "DC2Win98"))
5428 int position_first_level = 0x00fa;
5429 int extra_bytes = 4;
5432 /* advance file stream to first level inside the level package */
5433 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5435 /* each block of level data is followed by block of non-level data */
5436 num_levels_to_skip *= 2;
5438 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5439 while (num_levels_to_skip >= 0)
5441 /* advance file stream to next level inside the level package */
5442 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5444 level->no_valid_file = TRUE;
5446 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5452 /* skip apparently unused extra bytes following each level */
5453 ReadUnusedBytesFromFile(file, extra_bytes);
5455 /* read size of next level in level package */
5456 skip_bytes = getFile32BitLE(file);
5458 num_levels_to_skip--;
5463 level->no_valid_file = TRUE;
5465 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5472 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5478 /* ------------------------------------------------------------------------- */
5479 /* functions for loading SB level */
5480 /* ------------------------------------------------------------------------- */
5482 int getMappedElement_SB(int element_ascii, boolean use_ces)
5490 sb_element_mapping[] =
5492 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5493 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5494 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5495 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5496 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5497 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5498 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5499 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5506 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5507 if (element_ascii == sb_element_mapping[i].ascii)
5508 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5510 return EL_UNDEFINED;
5513 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5514 struct LevelFileInfo *level_file_info,
5515 boolean level_info_only)
5517 char *filename = level_file_info->filename;
5518 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5519 char last_comment[MAX_LINE_LEN];
5520 char level_name[MAX_LINE_LEN];
5523 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5524 boolean read_continued_line = FALSE;
5525 boolean reading_playfield = FALSE;
5526 boolean got_valid_playfield_line = FALSE;
5527 boolean invalid_playfield_char = FALSE;
5528 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5529 int file_level_nr = 0;
5531 int x = 0, y = 0; /* initialized to make compilers happy */
5533 last_comment[0] = '\0';
5534 level_name[0] = '\0';
5536 if (!(file = openFile(filename, MODE_READ)))
5538 level->no_valid_file = TRUE;
5540 if (!level_info_only)
5541 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5546 while (!checkEndOfFile(file))
5548 /* level successfully read, but next level may follow here */
5549 if (!got_valid_playfield_line && reading_playfield)
5551 /* read playfield from single level file -- skip remaining file */
5552 if (!level_file_info->packed)
5555 if (file_level_nr >= num_levels_to_skip)
5560 last_comment[0] = '\0';
5561 level_name[0] = '\0';
5563 reading_playfield = FALSE;
5566 got_valid_playfield_line = FALSE;
5568 /* read next line of input file */
5569 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5572 /* check if line was completely read and is terminated by line break */
5573 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5576 /* cut trailing line break (this can be newline and/or carriage return) */
5577 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5578 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5581 /* copy raw input line for later use (mainly debugging output) */
5582 strcpy(line_raw, line);
5584 if (read_continued_line)
5586 /* append new line to existing line, if there is enough space */
5587 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5588 strcat(previous_line, line_ptr);
5590 strcpy(line, previous_line); /* copy storage buffer to line */
5592 read_continued_line = FALSE;
5595 /* if the last character is '\', continue at next line */
5596 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5598 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5599 strcpy(previous_line, line); /* copy line to storage buffer */
5601 read_continued_line = TRUE;
5606 /* skip empty lines */
5607 if (line[0] == '\0')
5610 /* extract comment text from comment line */
5613 for (line_ptr = line; *line_ptr; line_ptr++)
5614 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5617 strcpy(last_comment, line_ptr);
5622 /* extract level title text from line containing level title */
5623 if (line[0] == '\'')
5625 strcpy(level_name, &line[1]);
5627 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5628 level_name[strlen(level_name) - 1] = '\0';
5633 /* skip lines containing only spaces (or empty lines) */
5634 for (line_ptr = line; *line_ptr; line_ptr++)
5635 if (*line_ptr != ' ')
5637 if (*line_ptr == '\0')
5640 /* at this point, we have found a line containing part of a playfield */
5642 got_valid_playfield_line = TRUE;
5644 if (!reading_playfield)
5646 reading_playfield = TRUE;
5647 invalid_playfield_char = FALSE;
5649 for (x = 0; x < MAX_LEV_FIELDX; x++)
5650 for (y = 0; y < MAX_LEV_FIELDY; y++)
5651 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5656 /* start with topmost tile row */
5660 /* skip playfield line if larger row than allowed */
5661 if (y >= MAX_LEV_FIELDY)
5664 /* start with leftmost tile column */
5667 /* read playfield elements from line */
5668 for (line_ptr = line; *line_ptr; line_ptr++)
5670 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5672 /* stop parsing playfield line if larger column than allowed */
5673 if (x >= MAX_LEV_FIELDX)
5676 if (mapped_sb_element == EL_UNDEFINED)
5678 invalid_playfield_char = TRUE;
5683 level->field[x][y] = mapped_sb_element;
5685 /* continue with next tile column */
5688 level->fieldx = MAX(x, level->fieldx);
5691 if (invalid_playfield_char)
5693 /* if first playfield line, treat invalid lines as comment lines */
5695 reading_playfield = FALSE;
5700 /* continue with next tile row */
5708 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
5709 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
5711 if (!reading_playfield)
5713 level->no_valid_file = TRUE;
5715 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5720 if (*level_name != '\0')
5722 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
5723 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5725 else if (*last_comment != '\0')
5727 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
5728 level->name[MAX_LEVEL_NAME_LEN] = '\0';
5732 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
5735 /* set all empty fields beyond the border walls to invisible steel wall */
5736 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5738 if ((x == 0 || x == level->fieldx - 1 ||
5739 y == 0 || y == level->fieldy - 1) &&
5740 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
5741 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
5742 level->field, level->fieldx, level->fieldy);
5745 /* set special level settings for Sokoban levels */
5748 level->use_step_counter = TRUE;
5750 if (load_xsb_to_ces)
5752 /* special global settings can now be set in level template */
5754 /* fill smaller playfields with padding "beyond border wall" elements */
5755 if (level->fieldx < SCR_FIELDX ||
5756 level->fieldy < SCR_FIELDY)
5758 short field[level->fieldx][level->fieldy];
5759 int new_fieldx = MAX(level->fieldx, SCR_FIELDX);
5760 int new_fieldy = MAX(level->fieldy, SCR_FIELDY);
5761 int pos_fieldx = (new_fieldx - level->fieldx) / 2;
5762 int pos_fieldy = (new_fieldy - level->fieldy) / 2;
5764 /* copy old playfield (which is smaller than the visible area) */
5765 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5766 field[x][y] = level->field[x][y];
5768 /* fill new, larger playfield with "beyond border wall" elements */
5769 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
5770 level->field[x][y] = getMappedElement_SB('_', load_xsb_to_ces);
5772 /* copy the old playfield to the middle of the new playfield */
5773 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5774 level->field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
5776 level->fieldx = new_fieldx;
5777 level->fieldy = new_fieldy;
5780 level->use_custom_template = TRUE;
5785 /* ------------------------------------------------------------------------- */
5786 /* functions for handling native levels */
5787 /* ------------------------------------------------------------------------- */
5789 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
5790 struct LevelFileInfo *level_file_info,
5791 boolean level_info_only)
5793 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
5794 level->no_valid_file = TRUE;
5797 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
5798 struct LevelFileInfo *level_file_info,
5799 boolean level_info_only)
5803 /* determine position of requested level inside level package */
5804 if (level_file_info->packed)
5805 pos = level_file_info->nr - leveldir_current->first_level;
5807 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
5808 level->no_valid_file = TRUE;
5811 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
5813 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5814 CopyNativeLevel_RND_to_EM(level);
5815 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5816 CopyNativeLevel_RND_to_SP(level);
5819 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
5821 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5822 CopyNativeLevel_EM_to_RND(level);
5823 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5824 CopyNativeLevel_SP_to_RND(level);
5827 void SaveNativeLevel(struct LevelInfo *level)
5829 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5831 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
5832 char *filename = getLevelFilenameFromBasename(basename);
5834 CopyNativeLevel_RND_to_SP(level);
5835 CopyNativeTape_RND_to_SP(level);
5837 SaveNativeLevel_SP(filename);
5842 /* ------------------------------------------------------------------------- */
5843 /* functions for loading generic level */
5844 /* ------------------------------------------------------------------------- */
5846 static void LoadLevelFromFileInfo(struct LevelInfo *level,
5847 struct LevelFileInfo *level_file_info,
5848 boolean level_info_only)
5850 /* always start with reliable default values */
5851 setLevelInfoToDefaults(level, level_info_only);
5853 switch (level_file_info->type)
5855 case LEVEL_FILE_TYPE_RND:
5856 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5859 case LEVEL_FILE_TYPE_EM:
5860 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
5861 level->game_engine_type = GAME_ENGINE_TYPE_EM;
5864 case LEVEL_FILE_TYPE_SP:
5865 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
5866 level->game_engine_type = GAME_ENGINE_TYPE_SP;
5869 case LEVEL_FILE_TYPE_DC:
5870 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
5873 case LEVEL_FILE_TYPE_SB:
5874 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
5878 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5882 /* if level file is invalid, restore level structure to default values */
5883 if (level->no_valid_file)
5885 setLevelInfoToDefaults(level, level_info_only);
5887 level->no_valid_file = TRUE; /* but keep "no valid file" flag */
5890 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
5891 level->game_engine_type = GAME_ENGINE_TYPE_RND;
5893 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
5894 CopyNativeLevel_Native_to_RND(level);
5897 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
5899 static struct LevelFileInfo level_file_info;
5901 /* always start with reliable default values */
5902 setFileInfoToDefaults(&level_file_info);
5904 level_file_info.nr = 0; /* unknown level number */
5905 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
5906 level_file_info.filename = filename;
5908 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
5911 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
5915 if (leveldir_current == NULL) /* only when dumping level */
5918 /* all engine modifications also valid for levels which use latest engine */
5919 if (level->game_version < VERSION_IDENT(3,2,0,5))
5921 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
5922 level->score[SC_TIME_BONUS] /= 10;
5925 if (leveldir_current->latest_engine)
5927 /* ---------- use latest game engine ----------------------------------- */
5929 /* For all levels which are forced to use the latest game engine version
5930 (normally all but user contributed, private and undefined levels), set
5931 the game engine version to the actual version; this allows for actual
5932 corrections in the game engine to take effect for existing, converted
5933 levels (from "classic" or other existing games) to make the emulation
5934 of the corresponding game more accurate, while (hopefully) not breaking
5935 existing levels created from other players. */
5937 level->game_version = GAME_VERSION_ACTUAL;
5939 /* Set special EM style gems behaviour: EM style gems slip down from
5940 normal, steel and growing wall. As this is a more fundamental change,
5941 it seems better to set the default behaviour to "off" (as it is more
5942 natural) and make it configurable in the level editor (as a property
5943 of gem style elements). Already existing converted levels (neither
5944 private nor contributed levels) are changed to the new behaviour. */
5946 if (level->file_version < FILE_VERSION_2_0)
5947 level->em_slippery_gems = TRUE;
5952 /* ---------- use game engine the level was created with ----------------- */
5954 /* For all levels which are not forced to use the latest game engine
5955 version (normally user contributed, private and undefined levels),
5956 use the version of the game engine the levels were created for.
5958 Since 2.0.1, the game engine version is now directly stored
5959 in the level file (chunk "VERS"), so there is no need anymore
5960 to set the game version from the file version (except for old,
5961 pre-2.0 levels, where the game version is still taken from the
5962 file format version used to store the level -- see above). */
5964 /* player was faster than enemies in 1.0.0 and before */
5965 if (level->file_version == FILE_VERSION_1_0)
5966 for (i = 0; i < MAX_PLAYERS; i++)
5967 level->initial_player_stepsize[i] = STEPSIZE_FAST;
5969 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
5970 if (level->game_version == VERSION_IDENT(2,0,1,0))
5971 level->em_slippery_gems = TRUE;
5973 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
5974 if (level->game_version < VERSION_IDENT(2,2,0,0))
5975 level->use_spring_bug = TRUE;
5977 if (level->game_version < VERSION_IDENT(3,2,0,5))
5979 /* time orb caused limited time in endless time levels before 3.2.0-5 */
5980 level->use_time_orb_bug = TRUE;
5982 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
5983 level->block_snap_field = FALSE;
5985 /* extra time score was same value as time left score before 3.2.0-5 */
5986 level->extra_time_score = level->score[SC_TIME_BONUS];
5989 if (level->game_version < VERSION_IDENT(3,2,0,7))
5991 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
5992 level->continuous_snapping = FALSE;
5995 /* only few elements were able to actively move into acid before 3.1.0 */
5996 /* trigger settings did not exist before 3.1.0; set to default "any" */
5997 if (level->game_version < VERSION_IDENT(3,1,0,0))
5999 /* correct "can move into acid" settings (all zero in old levels) */
6001 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6002 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6004 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6005 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6006 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6007 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6009 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6010 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6012 /* correct trigger settings (stored as zero == "none" in old levels) */
6014 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6016 int element = EL_CUSTOM_START + i;
6017 struct ElementInfo *ei = &element_info[element];
6019 for (j = 0; j < ei->num_change_pages; j++)
6021 struct ElementChangeInfo *change = &ei->change_page[j];
6023 change->trigger_player = CH_PLAYER_ANY;
6024 change->trigger_page = CH_PAGE_ANY;
6029 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6031 int element = EL_CUSTOM_256;
6032 struct ElementInfo *ei = &element_info[element];
6033 struct ElementChangeInfo *change = &ei->change_page[0];
6035 /* This is needed to fix a problem that was caused by a bugfix in function
6036 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6037 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6038 not replace walkable elements, but instead just placed the player on it,
6039 without placing the Sokoban field under the player). Unfortunately, this
6040 breaks "Snake Bite" style levels when the snake is halfway through a door
6041 that just closes (the snake head is still alive and can be moved in this
6042 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6043 player (without Sokoban element) which then gets killed as designed). */
6045 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6046 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6047 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6048 change->target_element = EL_PLAYER_1;
6051 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6052 if (level->game_version < VERSION_IDENT(3,2,5,0))
6054 /* This is needed to fix a problem that was caused by a bugfix in function
6055 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6056 corrects the behaviour when a custom element changes to another custom
6057 element with a higher element number that has change actions defined.
6058 Normally, only one change per frame is allowed for custom elements.
6059 Therefore, it is checked if a custom element already changed in the
6060 current frame; if it did, subsequent changes are suppressed.
6061 Unfortunately, this is only checked for element changes, but not for
6062 change actions, which are still executed. As the function above loops
6063 through all custom elements from lower to higher, an element change
6064 resulting in a lower CE number won't be checked again, while a target
6065 element with a higher number will also be checked, and potential change
6066 actions will get executed for this CE, too (which is wrong), while
6067 further changes are ignored (which is correct). As this bugfix breaks
6068 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6069 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6070 behaviour for existing levels and tapes that make use of this bug */
6072 level->use_action_after_change_bug = TRUE;
6075 /* not centering level after relocating player was default only in 3.2.3 */
6076 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6077 level->shifted_relocation = TRUE;
6079 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6080 if (level->game_version < VERSION_IDENT(3,2,6,0))
6081 level->em_explodes_by_fire = TRUE;
6084 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6088 /* map custom element change events that have changed in newer versions
6089 (these following values were accidentally changed in version 3.0.1)
6090 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6091 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6093 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6095 int element = EL_CUSTOM_START + i;
6097 /* order of checking and copying events to be mapped is important */
6098 /* (do not change the start and end value -- they are constant) */
6099 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6101 if (HAS_CHANGE_EVENT(element, j - 2))
6103 SET_CHANGE_EVENT(element, j - 2, FALSE);
6104 SET_CHANGE_EVENT(element, j, TRUE);
6108 /* order of checking and copying events to be mapped is important */
6109 /* (do not change the start and end value -- they are constant) */
6110 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6112 if (HAS_CHANGE_EVENT(element, j - 1))
6114 SET_CHANGE_EVENT(element, j - 1, FALSE);
6115 SET_CHANGE_EVENT(element, j, TRUE);
6121 /* initialize "can_change" field for old levels with only one change page */
6122 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6124 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6126 int element = EL_CUSTOM_START + i;
6128 if (CAN_CHANGE(element))
6129 element_info[element].change->can_change = TRUE;
6133 /* correct custom element values (for old levels without these options) */
6134 if (level->game_version < VERSION_IDENT(3,1,1,0))
6136 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6138 int element = EL_CUSTOM_START + i;
6139 struct ElementInfo *ei = &element_info[element];
6141 if (ei->access_direction == MV_NO_DIRECTION)
6142 ei->access_direction = MV_ALL_DIRECTIONS;
6146 /* correct custom element values (fix invalid values for all versions) */
6149 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6151 int element = EL_CUSTOM_START + i;
6152 struct ElementInfo *ei = &element_info[element];
6154 for (j = 0; j < ei->num_change_pages; j++)
6156 struct ElementChangeInfo *change = &ei->change_page[j];
6158 if (change->trigger_player == CH_PLAYER_NONE)
6159 change->trigger_player = CH_PLAYER_ANY;
6161 if (change->trigger_side == CH_SIDE_NONE)
6162 change->trigger_side = CH_SIDE_ANY;
6167 /* initialize "can_explode" field for old levels which did not store this */
6168 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6169 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6171 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6173 int element = EL_CUSTOM_START + i;
6175 if (EXPLODES_1X1_OLD(element))
6176 element_info[element].explosion_type = EXPLODES_1X1;
6178 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6179 EXPLODES_SMASHED(element) ||
6180 EXPLODES_IMPACT(element)));
6184 /* correct previously hard-coded move delay values for maze runner style */
6185 if (level->game_version < VERSION_IDENT(3,1,1,0))
6187 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6189 int element = EL_CUSTOM_START + i;
6191 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6193 /* previously hard-coded and therefore ignored */
6194 element_info[element].move_delay_fixed = 9;
6195 element_info[element].move_delay_random = 0;
6200 /* map elements that have changed in newer versions */
6201 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6202 level->game_version);
6203 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6204 for (x = 0; x < 3; x++)
6205 for (y = 0; y < 3; y++)
6206 level->yamyam_content[i].e[x][y] =
6207 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6208 level->game_version);
6210 /* initialize element properties for level editor etc. */
6211 InitElementPropertiesEngine(level->game_version);
6212 InitElementPropertiesAfterLoading(level->game_version);
6213 InitElementPropertiesGfxElement();
6216 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6220 /* map elements that have changed in newer versions */
6221 for (y = 0; y < level->fieldy; y++)
6222 for (x = 0; x < level->fieldx; x++)
6223 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6224 level->game_version);
6226 /* clear unused playfield data (nicer if level gets resized in editor) */
6227 for (x = 0; x < MAX_LEV_FIELDX; x++)
6228 for (y = 0; y < MAX_LEV_FIELDY; y++)
6229 if (x >= level->fieldx || y >= level->fieldy)
6230 level->field[x][y] = EL_EMPTY;
6232 /* copy elements to runtime playfield array */
6233 for (x = 0; x < MAX_LEV_FIELDX; x++)
6234 for (y = 0; y < MAX_LEV_FIELDY; y++)
6235 Feld[x][y] = level->field[x][y];
6237 /* initialize level size variables for faster access */
6238 lev_fieldx = level->fieldx;
6239 lev_fieldy = level->fieldy;
6241 /* determine border element for this level */
6242 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6243 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6248 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6250 struct LevelFileInfo *level_file_info = &level->file_info;
6252 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6253 CopyNativeLevel_RND_to_Native(level);
6256 void LoadLevelTemplate(int nr)
6260 setLevelFileInfo(&level_template.file_info, nr);
6261 filename = level_template.file_info.filename;
6263 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6265 LoadLevel_InitVersion(&level_template, filename);
6266 LoadLevel_InitElements(&level_template, filename);
6268 ActivateLevelTemplate();
6271 void LoadLevel(int nr)
6275 setLevelFileInfo(&level.file_info, nr);
6276 filename = level.file_info.filename;
6278 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6280 if (level.use_custom_template)
6281 LoadLevelTemplate(-1);
6283 LoadLevel_InitVersion(&level, filename);
6284 LoadLevel_InitElements(&level, filename);
6285 LoadLevel_InitPlayfield(&level, filename);
6287 LoadLevel_InitNativeEngines(&level, filename);
6290 void LoadLevelInfoOnly(int nr)
6292 setLevelFileInfo(&level.file_info, nr);
6294 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6297 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6301 chunk_size += putFileVersion(file, level->file_version);
6302 chunk_size += putFileVersion(file, level->game_version);
6307 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6311 chunk_size += putFile16BitBE(file, level->creation_date.year);
6312 chunk_size += putFile8Bit(file, level->creation_date.month);
6313 chunk_size += putFile8Bit(file, level->creation_date.day);
6318 #if ENABLE_HISTORIC_CHUNKS
6319 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6323 putFile8Bit(file, level->fieldx);
6324 putFile8Bit(file, level->fieldy);
6326 putFile16BitBE(file, level->time);
6327 putFile16BitBE(file, level->gems_needed);
6329 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6330 putFile8Bit(file, level->name[i]);
6332 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6333 putFile8Bit(file, level->score[i]);
6335 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6336 for (y = 0; y < 3; y++)
6337 for (x = 0; x < 3; x++)
6338 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6339 level->yamyam_content[i].e[x][y]));
6340 putFile8Bit(file, level->amoeba_speed);
6341 putFile8Bit(file, level->time_magic_wall);
6342 putFile8Bit(file, level->time_wheel);
6343 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6344 level->amoeba_content));
6345 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6346 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6347 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6348 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6350 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6352 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6353 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6354 putFile32BitBE(file, level->can_move_into_acid_bits);
6355 putFile8Bit(file, level->dont_collide_with_bits);
6357 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6358 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6360 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6361 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6362 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6364 putFile8Bit(file, level->game_engine_type);
6366 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6370 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6375 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6376 chunk_size += putFile8Bit(file, level->name[i]);
6381 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6386 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6387 chunk_size += putFile8Bit(file, level->author[i]);
6392 #if ENABLE_HISTORIC_CHUNKS
6393 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6398 for (y = 0; y < level->fieldy; y++)
6399 for (x = 0; x < level->fieldx; x++)
6400 if (level->encoding_16bit_field)
6401 chunk_size += putFile16BitBE(file, level->field[x][y]);
6403 chunk_size += putFile8Bit(file, level->field[x][y]);
6409 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6414 for (y = 0; y < level->fieldy; y++)
6415 for (x = 0; x < level->fieldx; x++)
6416 chunk_size += putFile16BitBE(file, level->field[x][y]);
6421 #if ENABLE_HISTORIC_CHUNKS
6422 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6426 putFile8Bit(file, EL_YAMYAM);
6427 putFile8Bit(file, level->num_yamyam_contents);
6428 putFile8Bit(file, 0);
6429 putFile8Bit(file, 0);
6431 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6432 for (y = 0; y < 3; y++)
6433 for (x = 0; x < 3; x++)
6434 if (level->encoding_16bit_field)
6435 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6437 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6441 #if ENABLE_HISTORIC_CHUNKS
6442 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6445 int num_contents, content_xsize, content_ysize;
6446 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6448 if (element == EL_YAMYAM)
6450 num_contents = level->num_yamyam_contents;
6454 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6455 for (y = 0; y < 3; y++)
6456 for (x = 0; x < 3; x++)
6457 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6459 else if (element == EL_BD_AMOEBA)
6465 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6466 for (y = 0; y < 3; y++)
6467 for (x = 0; x < 3; x++)
6468 content_array[i][x][y] = EL_EMPTY;
6469 content_array[0][0][0] = level->amoeba_content;
6473 /* chunk header already written -- write empty chunk data */
6474 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6476 Error(ERR_WARN, "cannot save content for element '%d'", element);
6480 putFile16BitBE(file, element);
6481 putFile8Bit(file, num_contents);
6482 putFile8Bit(file, content_xsize);
6483 putFile8Bit(file, content_ysize);
6485 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6487 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6488 for (y = 0; y < 3; y++)
6489 for (x = 0; x < 3; x++)
6490 putFile16BitBE(file, content_array[i][x][y]);
6494 #if ENABLE_HISTORIC_CHUNKS
6495 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6497 int envelope_nr = element - EL_ENVELOPE_1;
6498 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6502 chunk_size += putFile16BitBE(file, element);
6503 chunk_size += putFile16BitBE(file, envelope_len);
6504 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6505 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6507 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6508 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6510 for (i = 0; i < envelope_len; i++)
6511 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6517 #if ENABLE_HISTORIC_CHUNKS
6518 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6519 int num_changed_custom_elements)
6523 putFile16BitBE(file, num_changed_custom_elements);
6525 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6527 int element = EL_CUSTOM_START + i;
6529 struct ElementInfo *ei = &element_info[element];
6531 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6533 if (check < num_changed_custom_elements)
6535 putFile16BitBE(file, element);
6536 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6543 if (check != num_changed_custom_elements) /* should not happen */
6544 Error(ERR_WARN, "inconsistent number of custom element properties");
6548 #if ENABLE_HISTORIC_CHUNKS
6549 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6550 int num_changed_custom_elements)
6554 putFile16BitBE(file, num_changed_custom_elements);
6556 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6558 int element = EL_CUSTOM_START + i;
6560 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6562 if (check < num_changed_custom_elements)
6564 putFile16BitBE(file, element);
6565 putFile16BitBE(file, element_info[element].change->target_element);
6572 if (check != num_changed_custom_elements) /* should not happen */
6573 Error(ERR_WARN, "inconsistent number of custom target elements");
6577 #if ENABLE_HISTORIC_CHUNKS
6578 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6579 int num_changed_custom_elements)
6581 int i, j, x, y, check = 0;
6583 putFile16BitBE(file, num_changed_custom_elements);
6585 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6587 int element = EL_CUSTOM_START + i;
6588 struct ElementInfo *ei = &element_info[element];
6590 if (ei->modified_settings)
6592 if (check < num_changed_custom_elements)
6594 putFile16BitBE(file, element);
6596 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6597 putFile8Bit(file, ei->description[j]);
6599 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6601 /* some free bytes for future properties and padding */
6602 WriteUnusedBytesToFile(file, 7);
6604 putFile8Bit(file, ei->use_gfx_element);
6605 putFile16BitBE(file, ei->gfx_element_initial);
6607 putFile8Bit(file, ei->collect_score_initial);
6608 putFile8Bit(file, ei->collect_count_initial);
6610 putFile16BitBE(file, ei->push_delay_fixed);
6611 putFile16BitBE(file, ei->push_delay_random);
6612 putFile16BitBE(file, ei->move_delay_fixed);
6613 putFile16BitBE(file, ei->move_delay_random);
6615 putFile16BitBE(file, ei->move_pattern);
6616 putFile8Bit(file, ei->move_direction_initial);
6617 putFile8Bit(file, ei->move_stepsize);
6619 for (y = 0; y < 3; y++)
6620 for (x = 0; x < 3; x++)
6621 putFile16BitBE(file, ei->content.e[x][y]);
6623 putFile32BitBE(file, ei->change->events);
6625 putFile16BitBE(file, ei->change->target_element);
6627 putFile16BitBE(file, ei->change->delay_fixed);
6628 putFile16BitBE(file, ei->change->delay_random);
6629 putFile16BitBE(file, ei->change->delay_frames);
6631 putFile16BitBE(file, ei->change->initial_trigger_element);
6633 putFile8Bit(file, ei->change->explode);
6634 putFile8Bit(file, ei->change->use_target_content);
6635 putFile8Bit(file, ei->change->only_if_complete);
6636 putFile8Bit(file, ei->change->use_random_replace);
6638 putFile8Bit(file, ei->change->random_percentage);
6639 putFile8Bit(file, ei->change->replace_when);
6641 for (y = 0; y < 3; y++)
6642 for (x = 0; x < 3; x++)
6643 putFile16BitBE(file, ei->change->content.e[x][y]);
6645 putFile8Bit(file, ei->slippery_type);
6647 /* some free bytes for future properties and padding */
6648 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6655 if (check != num_changed_custom_elements) /* should not happen */
6656 Error(ERR_WARN, "inconsistent number of custom element properties");
6660 #if ENABLE_HISTORIC_CHUNKS
6661 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6663 struct ElementInfo *ei = &element_info[element];
6666 /* ---------- custom element base property values (96 bytes) ------------- */
6668 putFile16BitBE(file, element);
6670 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6671 putFile8Bit(file, ei->description[i]);
6673 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6675 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
6677 putFile8Bit(file, ei->num_change_pages);
6679 putFile16BitBE(file, ei->ce_value_fixed_initial);
6680 putFile16BitBE(file, ei->ce_value_random_initial);
6681 putFile8Bit(file, ei->use_last_ce_value);
6683 putFile8Bit(file, ei->use_gfx_element);
6684 putFile16BitBE(file, ei->gfx_element_initial);
6686 putFile8Bit(file, ei->collect_score_initial);
6687 putFile8Bit(file, ei->collect_count_initial);
6689 putFile8Bit(file, ei->drop_delay_fixed);
6690 putFile8Bit(file, ei->push_delay_fixed);
6691 putFile8Bit(file, ei->drop_delay_random);
6692 putFile8Bit(file, ei->push_delay_random);
6693 putFile16BitBE(file, ei->move_delay_fixed);
6694 putFile16BitBE(file, ei->move_delay_random);
6696 /* bits 0 - 15 of "move_pattern" ... */
6697 putFile16BitBE(file, ei->move_pattern & 0xffff);
6698 putFile8Bit(file, ei->move_direction_initial);
6699 putFile8Bit(file, ei->move_stepsize);
6701 putFile8Bit(file, ei->slippery_type);
6703 for (y = 0; y < 3; y++)
6704 for (x = 0; x < 3; x++)
6705 putFile16BitBE(file, ei->content.e[x][y]);
6707 putFile16BitBE(file, ei->move_enter_element);
6708 putFile16BitBE(file, ei->move_leave_element);
6709 putFile8Bit(file, ei->move_leave_type);
6711 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
6712 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
6714 putFile8Bit(file, ei->access_direction);
6716 putFile8Bit(file, ei->explosion_delay);
6717 putFile8Bit(file, ei->ignition_delay);
6718 putFile8Bit(file, ei->explosion_type);
6720 /* some free bytes for future custom property values and padding */
6721 WriteUnusedBytesToFile(file, 1);
6723 /* ---------- change page property values (48 bytes) --------------------- */
6725 for (i = 0; i < ei->num_change_pages; i++)
6727 struct ElementChangeInfo *change = &ei->change_page[i];
6728 unsigned int event_bits;
6730 /* bits 0 - 31 of "has_event[]" ... */
6732 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
6733 if (change->has_event[j])
6734 event_bits |= (1 << j);
6735 putFile32BitBE(file, event_bits);
6737 putFile16BitBE(file, change->target_element);
6739 putFile16BitBE(file, change->delay_fixed);
6740 putFile16BitBE(file, change->delay_random);
6741 putFile16BitBE(file, change->delay_frames);
6743 putFile16BitBE(file, change->initial_trigger_element);
6745 putFile8Bit(file, change->explode);
6746 putFile8Bit(file, change->use_target_content);
6747 putFile8Bit(file, change->only_if_complete);
6748 putFile8Bit(file, change->use_random_replace);
6750 putFile8Bit(file, change->random_percentage);
6751 putFile8Bit(file, change->replace_when);
6753 for (y = 0; y < 3; y++)
6754 for (x = 0; x < 3; x++)
6755 putFile16BitBE(file, change->target_content.e[x][y]);
6757 putFile8Bit(file, change->can_change);
6759 putFile8Bit(file, change->trigger_side);
6761 putFile8Bit(file, change->trigger_player);
6762 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
6763 log_2(change->trigger_page)));
6765 putFile8Bit(file, change->has_action);
6766 putFile8Bit(file, change->action_type);
6767 putFile8Bit(file, change->action_mode);
6768 putFile16BitBE(file, change->action_arg);
6770 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
6772 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
6773 if (change->has_event[j])
6774 event_bits |= (1 << (j - 32));
6775 putFile8Bit(file, event_bits);
6780 #if ENABLE_HISTORIC_CHUNKS
6781 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
6783 struct ElementInfo *ei = &element_info[element];
6784 struct ElementGroupInfo *group = ei->group;
6787 putFile16BitBE(file, element);
6789 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6790 putFile8Bit(file, ei->description[i]);
6792 putFile8Bit(file, group->num_elements);
6794 putFile8Bit(file, ei->use_gfx_element);
6795 putFile16BitBE(file, ei->gfx_element_initial);
6797 putFile8Bit(file, group->choice_mode);
6799 /* some free bytes for future values and padding */
6800 WriteUnusedBytesToFile(file, 3);
6802 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
6803 putFile16BitBE(file, group->element[i]);
6807 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
6808 boolean write_element)
6810 int save_type = entry->save_type;
6811 int data_type = entry->data_type;
6812 int conf_type = entry->conf_type;
6813 int byte_mask = conf_type & CONF_MASK_BYTES;
6814 int element = entry->element;
6815 int default_value = entry->default_value;
6817 boolean modified = FALSE;
6819 if (byte_mask != CONF_MASK_MULTI_BYTES)
6821 void *value_ptr = entry->value;
6822 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
6825 /* check if any settings have been modified before saving them */
6826 if (value != default_value)
6829 /* do not save if explicitly told or if unmodified default settings */
6830 if ((save_type == SAVE_CONF_NEVER) ||
6831 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6835 num_bytes += putFile16BitBE(file, element);
6837 num_bytes += putFile8Bit(file, conf_type);
6838 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
6839 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
6840 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
6843 else if (data_type == TYPE_STRING)
6845 char *default_string = entry->default_string;
6846 char *string = (char *)(entry->value);
6847 int string_length = strlen(string);
6850 /* check if any settings have been modified before saving them */
6851 if (!strEqual(string, default_string))
6854 /* do not save if explicitly told or if unmodified default settings */
6855 if ((save_type == SAVE_CONF_NEVER) ||
6856 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6860 num_bytes += putFile16BitBE(file, element);
6862 num_bytes += putFile8Bit(file, conf_type);
6863 num_bytes += putFile16BitBE(file, string_length);
6865 for (i = 0; i < string_length; i++)
6866 num_bytes += putFile8Bit(file, string[i]);
6868 else if (data_type == TYPE_ELEMENT_LIST)
6870 int *element_array = (int *)(entry->value);
6871 int num_elements = *(int *)(entry->num_entities);
6874 /* check if any settings have been modified before saving them */
6875 for (i = 0; i < num_elements; i++)
6876 if (element_array[i] != default_value)
6879 /* do not save if explicitly told or if unmodified default settings */
6880 if ((save_type == SAVE_CONF_NEVER) ||
6881 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6885 num_bytes += putFile16BitBE(file, element);
6887 num_bytes += putFile8Bit(file, conf_type);
6888 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
6890 for (i = 0; i < num_elements; i++)
6891 num_bytes += putFile16BitBE(file, element_array[i]);
6893 else if (data_type == TYPE_CONTENT_LIST)
6895 struct Content *content = (struct Content *)(entry->value);
6896 int num_contents = *(int *)(entry->num_entities);
6899 /* check if any settings have been modified before saving them */
6900 for (i = 0; i < num_contents; i++)
6901 for (y = 0; y < 3; y++)
6902 for (x = 0; x < 3; x++)
6903 if (content[i].e[x][y] != default_value)
6906 /* do not save if explicitly told or if unmodified default settings */
6907 if ((save_type == SAVE_CONF_NEVER) ||
6908 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6912 num_bytes += putFile16BitBE(file, element);
6914 num_bytes += putFile8Bit(file, conf_type);
6915 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
6917 for (i = 0; i < num_contents; i++)
6918 for (y = 0; y < 3; y++)
6919 for (x = 0; x < 3; x++)
6920 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
6926 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
6931 li = *level; /* copy level data into temporary buffer */
6933 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
6934 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
6939 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
6944 li = *level; /* copy level data into temporary buffer */
6946 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
6947 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
6952 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
6954 int envelope_nr = element - EL_ENVELOPE_1;
6958 chunk_size += putFile16BitBE(file, element);
6960 /* copy envelope data into temporary buffer */
6961 xx_envelope = level->envelope[envelope_nr];
6963 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
6964 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
6969 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
6971 struct ElementInfo *ei = &element_info[element];
6975 chunk_size += putFile16BitBE(file, element);
6977 xx_ei = *ei; /* copy element data into temporary buffer */
6979 /* set default description string for this specific element */
6980 strcpy(xx_default_description, getDefaultElementDescription(ei));
6982 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
6983 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
6985 for (i = 0; i < ei->num_change_pages; i++)
6987 struct ElementChangeInfo *change = &ei->change_page[i];
6989 xx_current_change_page = i;
6991 xx_change = *change; /* copy change data into temporary buffer */
6994 setEventBitsFromEventFlags(change);
6996 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
6997 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7004 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7006 struct ElementInfo *ei = &element_info[element];
7007 struct ElementGroupInfo *group = ei->group;
7011 chunk_size += putFile16BitBE(file, element);
7013 xx_ei = *ei; /* copy element data into temporary buffer */
7014 xx_group = *group; /* copy group data into temporary buffer */
7016 /* set default description string for this specific element */
7017 strcpy(xx_default_description, getDefaultElementDescription(ei));
7019 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7020 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7025 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
7031 if (!(file = fopen(filename, MODE_WRITE)))
7033 Error(ERR_WARN, "cannot save level file '%s'", filename);
7037 level->file_version = FILE_VERSION_ACTUAL;
7038 level->game_version = GAME_VERSION_ACTUAL;
7040 level->creation_date = getCurrentDate();
7042 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7043 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7045 chunk_size = SaveLevel_VERS(NULL, level);
7046 putFileChunkBE(file, "VERS", chunk_size);
7047 SaveLevel_VERS(file, level);
7049 chunk_size = SaveLevel_DATE(NULL, level);
7050 putFileChunkBE(file, "DATE", chunk_size);
7051 SaveLevel_DATE(file, level);
7053 chunk_size = SaveLevel_NAME(NULL, level);
7054 putFileChunkBE(file, "NAME", chunk_size);
7055 SaveLevel_NAME(file, level);
7057 chunk_size = SaveLevel_AUTH(NULL, level);
7058 putFileChunkBE(file, "AUTH", chunk_size);
7059 SaveLevel_AUTH(file, level);
7061 chunk_size = SaveLevel_INFO(NULL, level);
7062 putFileChunkBE(file, "INFO", chunk_size);
7063 SaveLevel_INFO(file, level);
7065 chunk_size = SaveLevel_BODY(NULL, level);
7066 putFileChunkBE(file, "BODY", chunk_size);
7067 SaveLevel_BODY(file, level);
7069 chunk_size = SaveLevel_ELEM(NULL, level);
7070 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7072 putFileChunkBE(file, "ELEM", chunk_size);
7073 SaveLevel_ELEM(file, level);
7076 for (i = 0; i < NUM_ENVELOPES; i++)
7078 int element = EL_ENVELOPE_1 + i;
7080 chunk_size = SaveLevel_NOTE(NULL, level, element);
7081 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7083 putFileChunkBE(file, "NOTE", chunk_size);
7084 SaveLevel_NOTE(file, level, element);
7088 /* if not using template level, check for non-default custom/group elements */
7089 if (!level->use_custom_template)
7091 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7093 int element = EL_CUSTOM_START + i;
7095 chunk_size = SaveLevel_CUSX(NULL, level, element);
7096 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7098 putFileChunkBE(file, "CUSX", chunk_size);
7099 SaveLevel_CUSX(file, level, element);
7103 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7105 int element = EL_GROUP_START + i;
7107 chunk_size = SaveLevel_GRPX(NULL, level, element);
7108 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7110 putFileChunkBE(file, "GRPX", chunk_size);
7111 SaveLevel_GRPX(file, level, element);
7118 SetFilePermissions(filename, PERMS_PRIVATE);
7121 void SaveLevel(int nr)
7123 char *filename = getDefaultLevelFilename(nr);
7125 SaveLevelFromFilename(&level, filename);
7128 void SaveLevelTemplate()
7130 char *filename = getDefaultLevelFilename(-1);
7132 SaveLevelFromFilename(&level, filename);
7135 boolean SaveLevelChecked(int nr)
7137 char *filename = getDefaultLevelFilename(nr);
7138 boolean new_level = !fileExists(filename);
7139 boolean level_saved = FALSE;
7141 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7146 Request("Level saved!", REQ_CONFIRM);
7154 void DumpLevel(struct LevelInfo *level)
7156 if (level->no_valid_file)
7158 Error(ERR_WARN, "cannot dump -- no valid level file found");
7164 Print("Level xxx (file version %08d, game version %08d)\n",
7165 level->file_version, level->game_version);
7168 Print("Level author: '%s'\n", level->author);
7169 Print("Level title: '%s'\n", level->name);
7171 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7173 Print("Level time: %d seconds\n", level->time);
7174 Print("Gems needed: %d\n", level->gems_needed);
7176 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7177 Print("Time for wheel: %d seconds\n", level->time_wheel);
7178 Print("Time for light: %d seconds\n", level->time_light);
7179 Print("Time for timegate: %d seconds\n", level->time_timegate);
7181 Print("Amoeba speed: %d\n", level->amoeba_speed);
7184 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7185 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7186 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7187 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7188 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7194 /* ========================================================================= */
7195 /* tape file functions */
7196 /* ========================================================================= */
7198 static void setTapeInfoToDefaults()
7202 /* always start with reliable default values (empty tape) */
7205 /* default values (also for pre-1.2 tapes) with only the first player */
7206 tape.player_participates[0] = TRUE;
7207 for (i = 1; i < MAX_PLAYERS; i++)
7208 tape.player_participates[i] = FALSE;
7210 /* at least one (default: the first) player participates in every tape */
7211 tape.num_participating_players = 1;
7213 tape.level_nr = level_nr;
7215 tape.changed = FALSE;
7217 tape.recording = FALSE;
7218 tape.playing = FALSE;
7219 tape.pausing = FALSE;
7221 tape.no_valid_file = FALSE;
7224 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7226 tape->file_version = getFileVersion(file);
7227 tape->game_version = getFileVersion(file);
7232 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7236 tape->random_seed = getFile32BitBE(file);
7237 tape->date = getFile32BitBE(file);
7238 tape->length = getFile32BitBE(file);
7240 /* read header fields that are new since version 1.2 */
7241 if (tape->file_version >= FILE_VERSION_1_2)
7243 byte store_participating_players = getFile8Bit(file);
7246 /* since version 1.2, tapes store which players participate in the tape */
7247 tape->num_participating_players = 0;
7248 for (i = 0; i < MAX_PLAYERS; i++)
7250 tape->player_participates[i] = FALSE;
7252 if (store_participating_players & (1 << i))
7254 tape->player_participates[i] = TRUE;
7255 tape->num_participating_players++;
7259 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7261 engine_version = getFileVersion(file);
7262 if (engine_version > 0)
7263 tape->engine_version = engine_version;
7265 tape->engine_version = tape->game_version;
7271 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7273 int level_identifier_size;
7276 level_identifier_size = getFile16BitBE(file);
7278 tape->level_identifier =
7279 checked_realloc(tape->level_identifier, level_identifier_size);
7281 for (i = 0; i < level_identifier_size; i++)
7282 tape->level_identifier[i] = getFile8Bit(file);
7284 tape->level_nr = getFile16BitBE(file);
7286 chunk_size = 2 + level_identifier_size + 2;
7291 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7294 int chunk_size_expected =
7295 (tape->num_participating_players + 1) * tape->length;
7297 if (chunk_size_expected != chunk_size)
7299 ReadUnusedBytesFromFile(file, chunk_size);
7300 return chunk_size_expected;
7303 for (i = 0; i < tape->length; i++)
7305 if (i >= MAX_TAPE_LEN)
7308 for (j = 0; j < MAX_PLAYERS; j++)
7310 tape->pos[i].action[j] = MV_NONE;
7312 if (tape->player_participates[j])
7313 tape->pos[i].action[j] = getFile8Bit(file);
7316 tape->pos[i].delay = getFile8Bit(file);
7318 if (tape->file_version == FILE_VERSION_1_0)
7320 /* eliminate possible diagonal moves in old tapes */
7321 /* this is only for backward compatibility */
7323 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7324 byte action = tape->pos[i].action[0];
7325 int k, num_moves = 0;
7327 for (k = 0; k<4; k++)
7329 if (action & joy_dir[k])
7331 tape->pos[i + num_moves].action[0] = joy_dir[k];
7333 tape->pos[i + num_moves].delay = 0;
7342 tape->length += num_moves;
7345 else if (tape->file_version < FILE_VERSION_2_0)
7347 /* convert pre-2.0 tapes to new tape format */
7349 if (tape->pos[i].delay > 1)
7352 tape->pos[i + 1] = tape->pos[i];
7353 tape->pos[i + 1].delay = 1;
7356 for (j = 0; j < MAX_PLAYERS; j++)
7357 tape->pos[i].action[j] = MV_NONE;
7358 tape->pos[i].delay--;
7365 if (checkEndOfFile(file))
7369 if (i != tape->length)
7370 chunk_size = (tape->num_participating_players + 1) * i;
7375 void LoadTape_SokobanSolution(char *filename)
7378 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7380 if (!(file = openFile(filename, MODE_READ)))
7382 tape.no_valid_file = TRUE;
7387 while (!checkEndOfFile(file))
7389 unsigned char c = getByteFromFile(file);
7391 if (checkEndOfFile(file))
7398 tape.pos[tape.length].action[0] = MV_UP;
7399 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7405 tape.pos[tape.length].action[0] = MV_DOWN;
7406 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7412 tape.pos[tape.length].action[0] = MV_LEFT;
7413 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7419 tape.pos[tape.length].action[0] = MV_RIGHT;
7420 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7428 /* ignore white-space characters */
7432 tape.no_valid_file = TRUE;
7434 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7442 if (tape.no_valid_file)
7445 tape.length_frames = GetTapeLengthFrames();
7446 tape.length_seconds = GetTapeLengthSeconds();
7449 void LoadTapeFromFilename(char *filename)
7451 char cookie[MAX_LINE_LEN];
7452 char chunk_name[CHUNK_ID_LEN + 1];
7456 /* always start with reliable default values */
7457 setTapeInfoToDefaults();
7459 if (strSuffix(filename, ".sln"))
7461 LoadTape_SokobanSolution(filename);
7466 if (!(file = openFile(filename, MODE_READ)))
7468 tape.no_valid_file = TRUE;
7473 getFileChunkBE(file, chunk_name, NULL);
7474 if (strEqual(chunk_name, "RND1"))
7476 getFile32BitBE(file); /* not used */
7478 getFileChunkBE(file, chunk_name, NULL);
7479 if (!strEqual(chunk_name, "TAPE"))
7481 tape.no_valid_file = TRUE;
7483 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7490 else /* check for pre-2.0 file format with cookie string */
7492 strcpy(cookie, chunk_name);
7493 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7495 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7496 cookie[strlen(cookie) - 1] = '\0';
7498 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7500 tape.no_valid_file = TRUE;
7502 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7509 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7511 tape.no_valid_file = TRUE;
7513 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7520 /* pre-2.0 tape files have no game version, so use file version here */
7521 tape.game_version = tape.file_version;
7524 if (tape.file_version < FILE_VERSION_1_2)
7526 /* tape files from versions before 1.2.0 without chunk structure */
7527 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7528 LoadTape_BODY(file, 2 * tape.length, &tape);
7536 int (*loader)(File *, int, struct TapeInfo *);
7540 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7541 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7542 { "INFO", -1, LoadTape_INFO },
7543 { "BODY", -1, LoadTape_BODY },
7547 while (getFileChunkBE(file, chunk_name, &chunk_size))
7551 while (chunk_info[i].name != NULL &&
7552 !strEqual(chunk_name, chunk_info[i].name))
7555 if (chunk_info[i].name == NULL)
7557 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7558 chunk_name, filename);
7559 ReadUnusedBytesFromFile(file, chunk_size);
7561 else if (chunk_info[i].size != -1 &&
7562 chunk_info[i].size != chunk_size)
7564 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7565 chunk_size, chunk_name, filename);
7566 ReadUnusedBytesFromFile(file, chunk_size);
7570 /* call function to load this tape chunk */
7571 int chunk_size_expected =
7572 (chunk_info[i].loader)(file, chunk_size, &tape);
7574 /* the size of some chunks cannot be checked before reading other
7575 chunks first (like "HEAD" and "BODY") that contain some header
7576 information, so check them here */
7577 if (chunk_size_expected != chunk_size)
7579 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7580 chunk_size, chunk_name, filename);
7588 tape.length_frames = GetTapeLengthFrames();
7589 tape.length_seconds = GetTapeLengthSeconds();
7592 printf("::: tape file version: %d\n", tape.file_version);
7593 printf("::: tape game version: %d\n", tape.game_version);
7594 printf("::: tape engine version: %d\n", tape.engine_version);
7598 void LoadTape(int nr)
7600 char *filename = getTapeFilename(nr);
7602 LoadTapeFromFilename(filename);
7605 void LoadSolutionTape(int nr)
7607 char *filename = getSolutionTapeFilename(nr);
7609 LoadTapeFromFilename(filename);
7611 if (TAPE_IS_EMPTY(tape) &&
7612 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7613 level.native_sp_level->demo.is_available)
7614 CopyNativeTape_SP_to_RND(&level);
7617 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7619 putFileVersion(file, tape->file_version);
7620 putFileVersion(file, tape->game_version);
7623 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7626 byte store_participating_players = 0;
7628 /* set bits for participating players for compact storage */
7629 for (i = 0; i < MAX_PLAYERS; i++)
7630 if (tape->player_participates[i])
7631 store_participating_players |= (1 << i);
7633 putFile32BitBE(file, tape->random_seed);
7634 putFile32BitBE(file, tape->date);
7635 putFile32BitBE(file, tape->length);
7637 putFile8Bit(file, store_participating_players);
7639 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7640 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7642 putFileVersion(file, tape->engine_version);
7645 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7647 int level_identifier_size = strlen(tape->level_identifier) + 1;
7650 putFile16BitBE(file, level_identifier_size);
7652 for (i = 0; i < level_identifier_size; i++)
7653 putFile8Bit(file, tape->level_identifier[i]);
7655 putFile16BitBE(file, tape->level_nr);
7658 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7662 for (i = 0; i < tape->length; i++)
7664 for (j = 0; j < MAX_PLAYERS; j++)
7665 if (tape->player_participates[j])
7666 putFile8Bit(file, tape->pos[i].action[j]);
7668 putFile8Bit(file, tape->pos[i].delay);
7672 void SaveTape(int nr)
7674 char *filename = getTapeFilename(nr);
7676 int num_participating_players = 0;
7677 int info_chunk_size;
7678 int body_chunk_size;
7681 InitTapeDirectory(leveldir_current->subdir);
7683 if (!(file = fopen(filename, MODE_WRITE)))
7685 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7689 tape.file_version = FILE_VERSION_ACTUAL;
7690 tape.game_version = GAME_VERSION_ACTUAL;
7692 /* count number of participating players */
7693 for (i = 0; i < MAX_PLAYERS; i++)
7694 if (tape.player_participates[i])
7695 num_participating_players++;
7697 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7698 body_chunk_size = (num_participating_players + 1) * tape.length;
7700 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7701 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7703 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7704 SaveTape_VERS(file, &tape);
7706 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7707 SaveTape_HEAD(file, &tape);
7709 putFileChunkBE(file, "INFO", info_chunk_size);
7710 SaveTape_INFO(file, &tape);
7712 putFileChunkBE(file, "BODY", body_chunk_size);
7713 SaveTape_BODY(file, &tape);
7717 SetFilePermissions(filename, PERMS_PRIVATE);
7719 tape.changed = FALSE;
7722 boolean SaveTapeChecked(int nr)
7724 char *filename = getTapeFilename(nr);
7725 boolean new_tape = !fileExists(filename);
7726 boolean tape_saved = FALSE;
7728 if (new_tape || Request("Replace old tape?", REQ_ASK))
7733 Request("Tape saved!", REQ_CONFIRM);
7741 void DumpTape(struct TapeInfo *tape)
7743 int tape_frame_counter;
7746 if (tape->no_valid_file)
7748 Error(ERR_WARN, "cannot dump -- no valid tape file found");
7754 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
7755 tape->level_nr, tape->file_version, tape->game_version);
7756 Print(" (effective engine version %08d)\n",
7757 tape->engine_version);
7758 Print("Level series identifier: '%s'\n", tape->level_identifier);
7761 tape_frame_counter = 0;
7763 for (i = 0; i < tape->length; i++)
7765 if (i >= MAX_TAPE_LEN)
7770 for (j = 0; j < MAX_PLAYERS; j++)
7772 if (tape->player_participates[j])
7774 int action = tape->pos[i].action[j];
7776 Print("%d:%02x ", j, action);
7777 Print("[%c%c%c%c|%c%c] - ",
7778 (action & JOY_LEFT ? '<' : ' '),
7779 (action & JOY_RIGHT ? '>' : ' '),
7780 (action & JOY_UP ? '^' : ' '),
7781 (action & JOY_DOWN ? 'v' : ' '),
7782 (action & JOY_BUTTON_1 ? '1' : ' '),
7783 (action & JOY_BUTTON_2 ? '2' : ' '));
7787 Print("(%03d) ", tape->pos[i].delay);
7788 Print("[%05d]\n", tape_frame_counter);
7790 tape_frame_counter += tape->pos[i].delay;
7797 /* ========================================================================= */
7798 /* score file functions */
7799 /* ========================================================================= */
7801 void LoadScore(int nr)
7804 char *filename = getScoreFilename(nr);
7805 char cookie[MAX_LINE_LEN];
7806 char line[MAX_LINE_LEN];
7810 /* always start with reliable default values */
7811 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7813 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
7814 highscore[i].Score = 0;
7817 if (!(file = fopen(filename, MODE_READ)))
7820 /* check file identifier */
7821 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
7823 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7824 cookie[strlen(cookie) - 1] = '\0';
7826 if (!checkCookieString(cookie, SCORE_COOKIE))
7828 Error(ERR_WARN, "unknown format of score file '%s'", filename);
7833 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7835 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
7836 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
7837 if (fgets(line, MAX_LINE_LEN, file) == NULL)
7840 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
7841 line[strlen(line) - 1] = '\0';
7843 for (line_ptr = line; *line_ptr; line_ptr++)
7845 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
7847 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
7848 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
7857 void SaveScore(int nr)
7860 char *filename = getScoreFilename(nr);
7863 InitScoreDirectory(leveldir_current->subdir);
7865 if (!(file = fopen(filename, MODE_WRITE)))
7867 Error(ERR_WARN, "cannot save score for level %d", nr);
7871 fprintf(file, "%s\n\n", SCORE_COOKIE);
7873 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7874 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
7878 SetFilePermissions(filename, PERMS_PUBLIC);
7882 /* ========================================================================= */
7883 /* setup file functions */
7884 /* ========================================================================= */
7886 #define TOKEN_STR_PLAYER_PREFIX "player_"
7889 #define SETUP_TOKEN_PLAYER_NAME 0
7890 #define SETUP_TOKEN_SOUND 1
7891 #define SETUP_TOKEN_SOUND_LOOPS 2
7892 #define SETUP_TOKEN_SOUND_MUSIC 3
7893 #define SETUP_TOKEN_SOUND_SIMPLE 4
7894 #define SETUP_TOKEN_TOONS 5
7895 #define SETUP_TOKEN_SCROLL_DELAY 6
7896 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
7897 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE 8
7898 #define SETUP_TOKEN_FADE_SCREENS 9
7899 #define SETUP_TOKEN_AUTORECORD 10
7900 #define SETUP_TOKEN_SHOW_TITLESCREEN 11
7901 #define SETUP_TOKEN_QUICK_DOORS 12
7902 #define SETUP_TOKEN_TEAM_MODE 13
7903 #define SETUP_TOKEN_HANDICAP 14
7904 #define SETUP_TOKEN_SKIP_LEVELS 15
7905 #define SETUP_TOKEN_TIME_LIMIT 16
7906 #define SETUP_TOKEN_FULLSCREEN 17
7907 #define SETUP_TOKEN_FULLSCREEN_MODE 18
7908 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT 19
7909 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY 20
7910 #define SETUP_TOKEN_ASK_ON_ESCAPE 21
7911 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 22
7912 #define SETUP_TOKEN_QUICK_SWITCH 23
7913 #define SETUP_TOKEN_INPUT_ON_FOCUS 24
7914 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 25
7915 #define SETUP_TOKEN_GAME_FRAME_DELAY 26
7916 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 27
7917 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 28
7918 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS 29
7919 #define SETUP_TOKEN_GRAPHICS_SET 30
7920 #define SETUP_TOKEN_SOUNDS_SET 31
7921 #define SETUP_TOKEN_MUSIC_SET 32
7922 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 33
7923 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 34
7924 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 35
7925 #define SETUP_TOKEN_VOLUME_SIMPLE 36
7926 #define SETUP_TOKEN_VOLUME_LOOPS 37
7927 #define SETUP_TOKEN_VOLUME_MUSIC 38
7928 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE 39
7929 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 40
7930 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE 41
7932 #define NUM_GLOBAL_SETUP_TOKENS 42
7935 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
7936 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
7937 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
7938 #define SETUP_TOKEN_EDITOR_EL_MORE 3
7939 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 4
7940 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 5
7941 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 6
7942 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 7
7943 #define SETUP_TOKEN_EDITOR_EL_CHARS 8
7944 #define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS 9
7945 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 10
7946 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 11
7947 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 12
7948 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 13
7949 #define SETUP_TOKEN_EDITOR_EL_BY_GAME 14
7950 #define SETUP_TOKEN_EDITOR_EL_BY_TYPE 15
7951 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 16
7953 #define NUM_EDITOR_SETUP_TOKENS 17
7955 /* editor cascade setup */
7956 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
7957 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
7958 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
7959 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
7960 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
7961 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
7962 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
7963 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
7964 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
7965 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
7966 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
7967 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
7968 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
7969 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
7970 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
7972 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
7974 /* shortcut setup */
7975 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
7976 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
7977 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
7978 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
7979 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
7980 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
7981 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
7982 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
7983 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
7984 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
7985 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
7986 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
7987 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
7988 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
7989 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
7990 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
7991 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
7992 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
7993 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
7994 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
7995 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
7997 #define NUM_SHORTCUT_SETUP_TOKENS 21
8000 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
8001 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
8002 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
8003 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
8004 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
8005 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
8006 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
8007 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
8008 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
8009 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
8010 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
8011 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
8012 #define SETUP_TOKEN_PLAYER_KEY_UP 12
8013 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
8014 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8015 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8017 #define NUM_PLAYER_SETUP_TOKENS 16
8020 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8021 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8022 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8024 #define NUM_SYSTEM_SETUP_TOKENS 3
8026 /* internal setup */
8027 #define SETUP_TOKEN_INT_PROGRAM_TITLE 0
8028 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR 1
8029 #define SETUP_TOKEN_INT_PROGRAM_EMAIL 2
8030 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE 3
8031 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 4
8032 #define SETUP_TOKEN_INT_PROGRAM_COMPANY 5
8033 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 6
8034 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 7
8035 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 8
8036 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 9
8037 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 10
8038 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 11
8039 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 12
8040 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 13
8041 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 14
8043 #define NUM_INTERNAL_SETUP_TOKENS 15
8046 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8048 #define NUM_OPTIONS_SETUP_TOKENS 1
8051 static struct SetupInfo si;
8052 static struct SetupEditorInfo sei;
8053 static struct SetupEditorCascadeInfo seci;
8054 static struct SetupShortcutInfo ssi;
8055 static struct SetupInputInfo sii;
8056 static struct SetupSystemInfo syi;
8057 static struct SetupInternalInfo sxi;
8058 static struct OptionInfo soi;
8060 static struct TokenInfo global_setup_tokens[] =
8062 { TYPE_STRING, &si.player_name, "player_name" },
8063 { TYPE_SWITCH, &si.sound, "sound" },
8064 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8065 { TYPE_SWITCH, &si.sound_music, "background_music" },
8066 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8067 { TYPE_SWITCH, &si.toons, "toons" },
8068 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8069 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8070 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8071 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8072 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8073 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8074 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8075 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8076 { TYPE_SWITCH, &si.handicap, "handicap" },
8077 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8078 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8079 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8080 { TYPE_STRING, &si.fullscreen_mode, "fullscreen_mode" },
8081 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8082 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8083 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8084 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8085 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8086 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8087 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8088 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8089 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8090 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8091 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8092 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8093 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8094 { TYPE_STRING, &si.music_set, "music_set" },
8095 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8096 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8097 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8098 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8099 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8100 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8101 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8102 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8103 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8106 static struct TokenInfo editor_setup_tokens[] =
8108 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
8109 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
8110 { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
8111 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
8112 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
8113 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
8114 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
8115 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
8116 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
8117 { TYPE_SWITCH, &sei.el_steel_chars, "editor.el_steel_chars" },
8118 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8119 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8120 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8121 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8122 { TYPE_SWITCH, &sei.el_by_game, "editor.el_by_game" },
8123 { TYPE_SWITCH, &sei.el_by_type, "editor.el_by_type" },
8124 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8127 static struct TokenInfo editor_cascade_setup_tokens[] =
8129 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8130 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8131 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8132 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8133 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8134 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8135 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8136 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8137 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8138 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8139 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8140 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8141 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8142 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8143 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8146 static struct TokenInfo shortcut_setup_tokens[] =
8148 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8149 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8150 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8151 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8152 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8153 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8154 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8155 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8156 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8157 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8158 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8159 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8160 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8161 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8162 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8163 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8164 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8165 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8166 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8167 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8168 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8171 static struct TokenInfo player_setup_tokens[] =
8173 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8174 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8175 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8176 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8177 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8178 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8179 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8180 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8181 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8182 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8183 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8184 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8185 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8186 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8187 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8188 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8191 static struct TokenInfo system_setup_tokens[] =
8193 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8194 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8195 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8198 static struct TokenInfo internal_setup_tokens[] =
8200 { TYPE_STRING, &sxi.program_title, "program_title" },
8201 { TYPE_STRING, &sxi.program_author, "program_author" },
8202 { TYPE_STRING, &sxi.program_email, "program_email" },
8203 { TYPE_STRING, &sxi.program_website, "program_website" },
8204 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8205 { TYPE_STRING, &sxi.program_company, "program_company" },
8206 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8207 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8208 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8209 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8210 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8211 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8212 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8213 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8214 { TYPE_STRING, &sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8217 static struct TokenInfo options_setup_tokens[] =
8219 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8222 static char *get_corrected_login_name(char *login_name)
8224 /* needed because player name must be a fixed length string */
8225 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8227 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8228 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8230 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8231 if (strchr(login_name_new, ' '))
8232 *strchr(login_name_new, ' ') = '\0';
8234 return login_name_new;
8237 static void setSetupInfoToDefaults(struct SetupInfo *si)
8241 si->player_name = get_corrected_login_name(getLoginName());
8244 si->sound_loops = TRUE;
8245 si->sound_music = TRUE;
8246 si->sound_simple = TRUE;
8248 si->scroll_delay = TRUE;
8249 si->scroll_delay_value = STD_SCROLL_DELAY;
8250 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8251 si->fade_screens = TRUE;
8252 si->autorecord = TRUE;
8253 si->show_titlescreen = TRUE;
8254 si->quick_doors = FALSE;
8255 si->team_mode = FALSE;
8256 si->handicap = TRUE;
8257 si->skip_levels = TRUE;
8258 si->time_limit = TRUE;
8259 si->fullscreen = FALSE;
8260 si->fullscreen_mode = getStringCopy(DEFAULT_FULLSCREEN_MODE);
8261 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8262 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8263 si->ask_on_escape = TRUE;
8264 si->ask_on_escape_editor = TRUE;
8265 si->quick_switch = FALSE;
8266 si->input_on_focus = FALSE;
8267 si->prefer_aga_graphics = TRUE;
8268 si->game_frame_delay = GAME_FRAME_DELAY;
8269 si->sp_show_border_elements = FALSE;
8270 si->small_game_graphics = FALSE;
8271 si->show_snapshot_buttons = FALSE;
8273 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8274 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8275 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8277 si->override_level_graphics = FALSE;
8278 si->override_level_sounds = FALSE;
8279 si->override_level_music = FALSE;
8281 si->volume_simple = 100; /* percent */
8282 si->volume_loops = 100; /* percent */
8283 si->volume_music = 100; /* percent */
8285 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8286 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8287 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8289 si->editor.el_boulderdash = TRUE;
8290 si->editor.el_emerald_mine = TRUE;
8291 si->editor.el_emerald_mine_club = TRUE;
8292 si->editor.el_more = TRUE;
8293 si->editor.el_sokoban = TRUE;
8294 si->editor.el_supaplex = TRUE;
8295 si->editor.el_diamond_caves = TRUE;
8296 si->editor.el_dx_boulderdash = TRUE;
8297 si->editor.el_chars = TRUE;
8298 si->editor.el_steel_chars = TRUE;
8299 si->editor.el_custom = TRUE;
8301 si->editor.el_headlines = TRUE;
8302 si->editor.el_user_defined = FALSE;
8303 si->editor.el_dynamic = TRUE;
8305 si->editor.show_element_token = FALSE;
8307 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8308 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8309 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8311 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8312 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8313 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8314 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8315 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8317 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8318 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8319 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8320 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8321 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8322 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8324 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8325 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8326 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8328 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8329 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8330 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8331 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8333 for (i = 0; i < MAX_PLAYERS; i++)
8335 si->input[i].use_joystick = FALSE;
8336 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8337 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8338 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8339 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8340 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8341 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8342 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8343 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8344 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8345 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8346 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8347 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8348 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8349 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8350 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8353 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8354 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8355 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8357 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8358 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8359 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8360 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8361 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8362 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8364 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8366 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8367 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8368 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8370 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8371 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8372 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8374 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8375 si->internal.choose_from_top_leveldir = FALSE;
8377 si->options.verbose = FALSE;
8379 #if defined(PLATFORM_ANDROID)
8380 si->fullscreen = TRUE;
8384 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8386 si->editor_cascade.el_bd = TRUE;
8387 si->editor_cascade.el_em = TRUE;
8388 si->editor_cascade.el_emc = TRUE;
8389 si->editor_cascade.el_rnd = TRUE;
8390 si->editor_cascade.el_sb = TRUE;
8391 si->editor_cascade.el_sp = TRUE;
8392 si->editor_cascade.el_dc = TRUE;
8393 si->editor_cascade.el_dx = TRUE;
8395 si->editor_cascade.el_chars = FALSE;
8396 si->editor_cascade.el_steel_chars = FALSE;
8397 si->editor_cascade.el_ce = FALSE;
8398 si->editor_cascade.el_ge = FALSE;
8399 si->editor_cascade.el_ref = FALSE;
8400 si->editor_cascade.el_user = FALSE;
8401 si->editor_cascade.el_dynamic = FALSE;
8404 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8408 if (!setup_file_hash)
8413 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8414 setSetupInfo(global_setup_tokens, i,
8415 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
8420 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8421 setSetupInfo(editor_setup_tokens, i,
8422 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
8425 /* shortcut setup */
8426 ssi = setup.shortcut;
8427 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8428 setSetupInfo(shortcut_setup_tokens, i,
8429 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
8430 setup.shortcut = ssi;
8433 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8437 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8439 sii = setup.input[pnr];
8440 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8442 char full_token[100];
8444 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8445 setSetupInfo(player_setup_tokens, i,
8446 getHashEntry(setup_file_hash, full_token));
8448 setup.input[pnr] = sii;
8453 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8454 setSetupInfo(system_setup_tokens, i,
8455 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
8458 /* internal setup */
8459 sxi = setup.internal;
8460 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8461 setSetupInfo(internal_setup_tokens, i,
8462 getHashEntry(setup_file_hash, internal_setup_tokens[i].text));
8463 setup.internal = sxi;
8466 soi = setup.options;
8467 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8468 setSetupInfo(options_setup_tokens, i,
8469 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
8470 setup.options = soi;
8473 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8477 if (!setup_file_hash)
8480 /* editor cascade setup */
8481 seci = setup.editor_cascade;
8482 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8483 setSetupInfo(editor_cascade_setup_tokens, i,
8484 getHashEntry(setup_file_hash,
8485 editor_cascade_setup_tokens[i].text));
8486 setup.editor_cascade = seci;
8489 void LoadSetupFromFilename(char *filename)
8491 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
8493 if (setup_file_hash)
8495 decodeSetupFileHash(setup_file_hash);
8497 freeSetupFileHash(setup_file_hash);
8501 Error(ERR_WARN, "using default setup values");
8505 static void LoadSetup_SpecialPostProcessing()
8507 char *player_name_new;
8509 /* needed to work around problems with fixed length strings */
8510 player_name_new = get_corrected_login_name(setup.player_name);
8511 free(setup.player_name);
8512 setup.player_name = player_name_new;
8514 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
8515 if (setup.scroll_delay == FALSE)
8517 setup.scroll_delay_value = MIN_SCROLL_DELAY;
8518 setup.scroll_delay = TRUE; /* now always "on" */
8521 /* make sure that scroll delay value stays inside valid range */
8522 setup.scroll_delay_value =
8523 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
8530 /* always start with reliable default values */
8531 setSetupInfoToDefaults(&setup);
8533 /* try to load setup values from default setup file */
8534 filename = getDefaultSetupFilename();
8536 if (fileExists(filename))
8537 LoadSetupFromFilename(filename);
8539 /* try to load setup values from user setup file */
8540 filename = getSetupFilename();
8542 LoadSetupFromFilename(filename);
8544 LoadSetup_SpecialPostProcessing();
8547 void LoadSetup_EditorCascade()
8549 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8550 SetupFileHash *setup_file_hash = NULL;
8552 /* always start with reliable default values */
8553 setSetupInfoToDefaults_EditorCascade(&setup);
8555 setup_file_hash = loadSetupFileHash(filename);
8557 if (setup_file_hash)
8559 decodeSetupFileHash_EditorCascade(setup_file_hash);
8561 freeSetupFileHash(setup_file_hash);
8569 char *filename = getSetupFilename();
8573 InitUserDataDirectory();
8575 if (!(file = fopen(filename, MODE_WRITE)))
8577 Error(ERR_WARN, "cannot write setup file '%s'", filename);
8581 fprintFileHeader(file, SETUP_FILENAME);
8585 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8587 /* just to make things nicer :) */
8588 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
8589 i == SETUP_TOKEN_GRAPHICS_SET ||
8590 i == SETUP_TOKEN_VOLUME_SIMPLE ||
8591 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
8592 fprintf(file, "\n");
8594 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
8599 fprintf(file, "\n");
8600 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8601 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
8603 /* shortcut setup */
8604 ssi = setup.shortcut;
8605 fprintf(file, "\n");
8606 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8607 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
8610 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8614 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8615 fprintf(file, "\n");
8617 sii = setup.input[pnr];
8618 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8619 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
8624 fprintf(file, "\n");
8625 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8626 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
8628 /* internal setup */
8629 /* (internal setup values not saved to user setup file) */
8632 soi = setup.options;
8633 fprintf(file, "\n");
8634 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8635 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
8639 SetFilePermissions(filename, PERMS_PRIVATE);
8642 void SaveSetup_EditorCascade()
8644 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8648 InitUserDataDirectory();
8650 if (!(file = fopen(filename, MODE_WRITE)))
8652 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
8657 fprintFileHeader(file, EDITORCASCADE_FILENAME);
8659 seci = setup.editor_cascade;
8660 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8661 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
8665 SetFilePermissions(filename, PERMS_PRIVATE);
8670 void LoadCustomElementDescriptions()
8672 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8673 SetupFileHash *setup_file_hash;
8676 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8678 if (element_info[i].custom_description != NULL)
8680 free(element_info[i].custom_description);
8681 element_info[i].custom_description = NULL;
8685 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8688 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8690 char *token = getStringCat2(element_info[i].token_name, ".name");
8691 char *value = getHashEntry(setup_file_hash, token);
8694 element_info[i].custom_description = getStringCopy(value);
8699 freeSetupFileHash(setup_file_hash);
8702 static int getElementFromToken(char *token)
8704 char *value = getHashEntry(element_token_hash, token);
8709 Error(ERR_WARN, "unknown element token '%s'", token);
8711 return EL_UNDEFINED;
8714 static int get_token_parameter_value(char *token, char *value_raw)
8718 if (token == NULL || value_raw == NULL)
8719 return ARG_UNDEFINED_VALUE;
8721 suffix = strrchr(token, '.');
8725 if (strEqual(suffix, ".element"))
8726 return getElementFromToken(value_raw);
8728 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
8729 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
8732 void InitMenuDesignSettings_Static()
8736 /* always start with reliable default values from static default config */
8737 for (i = 0; image_config_vars[i].token != NULL; i++)
8739 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
8742 *image_config_vars[i].value =
8743 get_token_parameter_value(image_config_vars[i].token, value);
8747 static void InitMenuDesignSettings_SpecialPreProcessing()
8751 /* the following initializes hierarchical values from static configuration */
8753 /* special case: initialize "ARG_DEFAULT" values in static default config */
8754 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
8755 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
8756 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
8757 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
8758 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
8759 titlemessage_default.fade_mode = title_default.fade_mode;
8760 titlemessage_default.fade_delay = title_default.fade_delay;
8761 titlemessage_default.post_delay = title_default.post_delay;
8762 titlemessage_default.auto_delay = title_default.auto_delay;
8764 /* special case: initialize "ARG_DEFAULT" values in static default config */
8765 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
8766 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
8768 titlemessage_initial[i] = titlemessage_initial_default;
8769 titlemessage[i] = titlemessage_default;
8772 /* special case: initialize "ARG_DEFAULT" values in static default config */
8773 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
8774 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8776 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
8777 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
8780 /* special case: initialize "ARG_DEFAULT" values in static default config */
8781 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
8782 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8784 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
8785 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
8786 if (i != GFX_SPECIAL_ARG_EDITOR) /* editor value already initialized */
8787 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
8791 static void InitMenuDesignSettings_SpecialPostProcessing()
8795 struct XY *dst, *src;
8799 { &game.button.save, &game.button.stop },
8800 { &game.button.pause2, &game.button.pause },
8801 { &game.button.load, &game.button.play },
8802 { &game.button.undo, &game.button.stop },
8803 { &game.button.redo, &game.button.play },
8809 /* special case: initialize later added SETUP list size from LEVELS value */
8810 if (menu.list_size[GAME_MODE_SETUP] == -1)
8811 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
8813 /* set default position for snapshot buttons to stop/pause/play buttons */
8814 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
8816 if ((*game_buttons_xy[i].dst).x == -1 &&
8817 (*game_buttons_xy[i].dst).y == -1)
8819 (*game_buttons_xy[i].dst).x = (*game_buttons_xy[i].src).x;
8820 (*game_buttons_xy[i].dst).y = (*game_buttons_xy[i].src).y;
8825 static void LoadMenuDesignSettingsFromFilename(char *filename)
8827 static struct TitleMessageInfo tmi;
8828 static struct TokenInfo titlemessage_tokens[] =
8830 { TYPE_INTEGER, &tmi.x, ".x" },
8831 { TYPE_INTEGER, &tmi.y, ".y" },
8832 { TYPE_INTEGER, &tmi.width, ".width" },
8833 { TYPE_INTEGER, &tmi.height, ".height" },
8834 { TYPE_INTEGER, &tmi.chars, ".chars" },
8835 { TYPE_INTEGER, &tmi.lines, ".lines" },
8836 { TYPE_INTEGER, &tmi.align, ".align" },
8837 { TYPE_INTEGER, &tmi.valign, ".valign" },
8838 { TYPE_INTEGER, &tmi.font, ".font" },
8839 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
8840 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
8841 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
8842 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
8843 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
8844 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
8845 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
8846 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
8852 struct TitleMessageInfo *array;
8855 titlemessage_arrays[] =
8857 { titlemessage_initial, "[titlemessage_initial]" },
8858 { titlemessage, "[titlemessage]" },
8862 SetupFileHash *setup_file_hash;
8865 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8868 /* the following initializes hierarchical values from dynamic configuration */
8870 /* special case: initialize with default values that may be overwritten */
8871 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
8872 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8874 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
8875 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
8876 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
8878 if (value_1 != NULL)
8879 menu.draw_xoffset[i] = get_integer_from_string(value_1);
8880 if (value_2 != NULL)
8881 menu.draw_yoffset[i] = get_integer_from_string(value_2);
8882 if (value_3 != NULL)
8883 menu.list_size[i] = get_integer_from_string(value_3);
8886 /* special case: initialize with default values that may be overwritten */
8887 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
8888 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
8890 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
8891 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
8893 if (value_1 != NULL)
8894 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
8895 if (value_2 != NULL)
8896 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
8898 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
8900 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
8902 if (value_1 != NULL)
8903 menu.list_size_info[i] = get_integer_from_string(value_1);
8907 /* special case: initialize with default values that may be overwritten */
8908 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
8909 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
8911 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
8912 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
8914 if (value_1 != NULL)
8915 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
8916 if (value_2 != NULL)
8917 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
8920 /* special case: initialize with default values that may be overwritten */
8921 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
8922 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8924 char *token_1 = "menu.enter_screen.fade_mode";
8925 char *token_2 = "menu.enter_screen.fade_delay";
8926 char *token_3 = "menu.enter_screen.post_delay";
8927 char *token_4 = "menu.leave_screen.fade_mode";
8928 char *token_5 = "menu.leave_screen.fade_delay";
8929 char *token_6 = "menu.leave_screen.post_delay";
8930 char *value_1 = getHashEntry(setup_file_hash, token_1);
8931 char *value_2 = getHashEntry(setup_file_hash, token_2);
8932 char *value_3 = getHashEntry(setup_file_hash, token_3);
8933 char *value_4 = getHashEntry(setup_file_hash, token_4);
8934 char *value_5 = getHashEntry(setup_file_hash, token_5);
8935 char *value_6 = getHashEntry(setup_file_hash, token_6);
8937 if (value_1 != NULL)
8938 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
8940 if (value_2 != NULL)
8941 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
8943 if (value_3 != NULL)
8944 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
8946 if (value_4 != NULL)
8947 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
8949 if (value_5 != NULL)
8950 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
8952 if (value_6 != NULL)
8953 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
8957 /* special case: initialize with default values that may be overwritten */
8958 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
8959 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8961 char *token_01 = "viewport.playfield.x";
8962 char *token_02 = "viewport.playfield.y";
8963 char *token_03 = "viewport.playfield.width";
8964 char *token_04 = "viewport.playfield.height";
8965 char *token_05 = "viewport.playfield.border_size";
8966 char *token_06 = "viewport.door_1.x";
8967 char *token_07 = "viewport.door_1.y";
8968 char *token_08 = "viewport.door_1.width";
8969 char *token_09 = "viewport.door_1.height";
8970 char *token_10 = "viewport.door_1.border_size";
8971 char *token_11 = "viewport.door_2.x";
8972 char *token_12 = "viewport.door_2.y";
8973 char *token_13 = "viewport.door_2.width";
8974 char *token_14 = "viewport.door_2.height";
8975 char *token_15 = "viewport.door_2.border_size";
8976 char *value_01 = getHashEntry(setup_file_hash, token_01);
8977 char *value_02 = getHashEntry(setup_file_hash, token_02);
8978 char *value_03 = getHashEntry(setup_file_hash, token_03);
8979 char *value_04 = getHashEntry(setup_file_hash, token_04);
8980 char *value_05 = getHashEntry(setup_file_hash, token_05);
8981 char *value_06 = getHashEntry(setup_file_hash, token_06);
8982 char *value_07 = getHashEntry(setup_file_hash, token_07);
8983 char *value_08 = getHashEntry(setup_file_hash, token_08);
8984 char *value_09 = getHashEntry(setup_file_hash, token_09);
8985 char *value_10 = getHashEntry(setup_file_hash, token_10);
8986 char *value_11 = getHashEntry(setup_file_hash, token_11);
8987 char *value_12 = getHashEntry(setup_file_hash, token_12);
8988 char *value_13 = getHashEntry(setup_file_hash, token_13);
8989 char *value_14 = getHashEntry(setup_file_hash, token_14);
8990 char *value_15 = getHashEntry(setup_file_hash, token_15);
8992 if (value_01 != NULL)
8993 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
8994 if (value_02 != NULL)
8995 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
8996 if (value_03 != NULL)
8997 viewport.playfield[i].width = get_token_parameter_value(token_03,
8999 if (value_04 != NULL)
9000 viewport.playfield[i].height = get_token_parameter_value(token_04,
9002 if (value_05 != NULL)
9003 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9005 if (value_06 != NULL)
9006 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9007 if (value_07 != NULL)
9008 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9009 if (value_08 != NULL)
9010 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9011 if (value_09 != NULL)
9012 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9013 if (value_10 != NULL)
9014 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9016 if (value_11 != NULL)
9017 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9018 if (value_12 != NULL)
9019 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9020 if (value_13 != NULL)
9021 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9022 if (value_14 != NULL)
9023 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9024 if (value_15 != NULL)
9025 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9029 /* special case: initialize with default values that may be overwritten */
9030 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9031 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9033 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9034 char *base_token = titlemessage_arrays[i].text;
9036 for (j = 0; titlemessage_tokens[j].type != -1; j++)
9038 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9039 char *value = getHashEntry(setup_file_hash, token);
9043 int parameter_value = get_token_parameter_value(token, value);
9045 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9049 if (titlemessage_tokens[j].type == TYPE_INTEGER)
9050 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9052 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
9062 /* read (and overwrite with) values that may be specified in config file */
9063 for (i = 0; image_config_vars[i].token != NULL; i++)
9065 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9067 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9068 if (value != NULL && !strEqual(value, ARG_DEFAULT))
9069 *image_config_vars[i].value =
9070 get_token_parameter_value(image_config_vars[i].token, value);
9073 freeSetupFileHash(setup_file_hash);
9076 void LoadMenuDesignSettings()
9078 char *filename_base = UNDEFINED_FILENAME, *filename_local;
9080 InitMenuDesignSettings_Static();
9081 InitMenuDesignSettings_SpecialPreProcessing();
9083 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9085 /* first look for special settings configured in level series config */
9086 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9088 if (fileExists(filename_base))
9089 LoadMenuDesignSettingsFromFilename(filename_base);
9092 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9094 if (filename_local != NULL && !strEqual(filename_base, filename_local))
9095 LoadMenuDesignSettingsFromFilename(filename_local);
9097 InitMenuDesignSettings_SpecialPostProcessing();
9100 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9102 char *filename = getEditorSetupFilename();
9103 SetupFileList *setup_file_list, *list;
9104 SetupFileHash *element_hash;
9105 int num_unknown_tokens = 0;
9108 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9111 element_hash = newSetupFileHash();
9113 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9114 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9116 /* determined size may be larger than needed (due to unknown elements) */
9118 for (list = setup_file_list; list != NULL; list = list->next)
9121 /* add space for up to 3 more elements for padding that may be needed */
9124 /* free memory for old list of elements, if needed */
9125 checked_free(*elements);
9127 /* allocate memory for new list of elements */
9128 *elements = checked_malloc(*num_elements * sizeof(int));
9131 for (list = setup_file_list; list != NULL; list = list->next)
9133 char *value = getHashEntry(element_hash, list->token);
9135 if (value == NULL) /* try to find obsolete token mapping */
9137 char *mapped_token = get_mapped_token(list->token);
9139 if (mapped_token != NULL)
9141 value = getHashEntry(element_hash, mapped_token);
9149 (*elements)[(*num_elements)++] = atoi(value);
9153 if (num_unknown_tokens == 0)
9155 Error(ERR_INFO_LINE, "-");
9156 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9157 Error(ERR_INFO, "- config file: '%s'", filename);
9159 num_unknown_tokens++;
9162 Error(ERR_INFO, "- token: '%s'", list->token);
9166 if (num_unknown_tokens > 0)
9167 Error(ERR_INFO_LINE, "-");
9169 while (*num_elements % 4) /* pad with empty elements, if needed */
9170 (*elements)[(*num_elements)++] = EL_EMPTY;
9172 freeSetupFileList(setup_file_list);
9173 freeSetupFileHash(element_hash);
9176 for (i = 0; i < *num_elements; i++)
9177 printf("editor: element '%s' [%d]\n",
9178 element_info[(*elements)[i]].token_name, (*elements)[i]);
9182 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9185 SetupFileHash *setup_file_hash = NULL;
9186 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9187 char *filename_music, *filename_prefix, *filename_info;
9193 token_to_value_ptr[] =
9195 { "title_header", &tmp_music_file_info.title_header },
9196 { "artist_header", &tmp_music_file_info.artist_header },
9197 { "album_header", &tmp_music_file_info.album_header },
9198 { "year_header", &tmp_music_file_info.year_header },
9200 { "title", &tmp_music_file_info.title },
9201 { "artist", &tmp_music_file_info.artist },
9202 { "album", &tmp_music_file_info.album },
9203 { "year", &tmp_music_file_info.year },
9209 filename_music = (is_sound ? getCustomSoundFilename(basename) :
9210 getCustomMusicFilename(basename));
9212 if (filename_music == NULL)
9215 /* ---------- try to replace file extension ---------- */
9217 filename_prefix = getStringCopy(filename_music);
9218 if (strrchr(filename_prefix, '.') != NULL)
9219 *strrchr(filename_prefix, '.') = '\0';
9220 filename_info = getStringCat2(filename_prefix, ".txt");
9222 if (fileExists(filename_info))
9223 setup_file_hash = loadSetupFileHash(filename_info);
9225 free(filename_prefix);
9226 free(filename_info);
9228 if (setup_file_hash == NULL)
9230 /* ---------- try to add file extension ---------- */
9232 filename_prefix = getStringCopy(filename_music);
9233 filename_info = getStringCat2(filename_prefix, ".txt");
9235 if (fileExists(filename_info))
9236 setup_file_hash = loadSetupFileHash(filename_info);
9238 free(filename_prefix);
9239 free(filename_info);
9242 if (setup_file_hash == NULL)
9245 /* ---------- music file info found ---------- */
9247 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
9249 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
9251 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
9253 *token_to_value_ptr[i].value_ptr =
9254 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
9257 tmp_music_file_info.basename = getStringCopy(basename);
9258 tmp_music_file_info.music = music;
9259 tmp_music_file_info.is_sound = is_sound;
9261 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
9262 *new_music_file_info = tmp_music_file_info;
9264 return new_music_file_info;
9267 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
9269 return get_music_file_info_ext(basename, music, FALSE);
9272 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
9274 return get_music_file_info_ext(basename, sound, TRUE);
9277 static boolean music_info_listed_ext(struct MusicFileInfo *list,
9278 char *basename, boolean is_sound)
9280 for (; list != NULL; list = list->next)
9281 if (list->is_sound == is_sound && strEqual(list->basename, basename))
9287 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
9289 return music_info_listed_ext(list, basename, FALSE);
9292 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
9294 return music_info_listed_ext(list, basename, TRUE);
9297 void LoadMusicInfo()
9299 char *music_directory = getCustomMusicDirectory();
9300 int num_music = getMusicListSize();
9301 int num_music_noconf = 0;
9302 int num_sounds = getSoundListSize();
9304 DirectoryEntry *dir_entry;
9305 struct FileInfo *music, *sound;
9306 struct MusicFileInfo *next, **new;
9309 while (music_file_info != NULL)
9311 next = music_file_info->next;
9313 checked_free(music_file_info->basename);
9315 checked_free(music_file_info->title_header);
9316 checked_free(music_file_info->artist_header);
9317 checked_free(music_file_info->album_header);
9318 checked_free(music_file_info->year_header);
9320 checked_free(music_file_info->title);
9321 checked_free(music_file_info->artist);
9322 checked_free(music_file_info->album);
9323 checked_free(music_file_info->year);
9325 free(music_file_info);
9327 music_file_info = next;
9330 new = &music_file_info;
9332 for (i = 0; i < num_music; i++)
9334 music = getMusicListEntry(i);
9336 if (music->filename == NULL)
9339 if (strEqual(music->filename, UNDEFINED_FILENAME))
9342 /* a configured file may be not recognized as music */
9343 if (!FileIsMusic(music->filename))
9346 if (!music_info_listed(music_file_info, music->filename))
9348 *new = get_music_file_info(music->filename, i);
9351 new = &(*new)->next;
9355 if ((dir = openDirectory(music_directory)) == NULL)
9357 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
9361 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
9363 char *basename = dir_entry->basename;
9364 boolean music_already_used = FALSE;
9367 /* skip all music files that are configured in music config file */
9368 for (i = 0; i < num_music; i++)
9370 music = getMusicListEntry(i);
9372 if (music->filename == NULL)
9375 if (strEqual(basename, music->filename))
9377 music_already_used = TRUE;
9382 if (music_already_used)
9385 if (!FileIsMusic(dir_entry->filename))
9388 if (!music_info_listed(music_file_info, basename))
9390 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
9393 new = &(*new)->next;
9399 closeDirectory(dir);
9401 for (i = 0; i < num_sounds; i++)
9403 sound = getSoundListEntry(i);
9405 if (sound->filename == NULL)
9408 if (strEqual(sound->filename, UNDEFINED_FILENAME))
9411 /* a configured file may be not recognized as sound */
9412 if (!FileIsSound(sound->filename))
9415 if (!sound_info_listed(music_file_info, sound->filename))
9417 *new = get_sound_file_info(sound->filename, i);
9419 new = &(*new)->next;
9424 void add_helpanim_entry(int element, int action, int direction, int delay,
9425 int *num_list_entries)
9427 struct HelpAnimInfo *new_list_entry;
9428 (*num_list_entries)++;
9431 checked_realloc(helpanim_info,
9432 *num_list_entries * sizeof(struct HelpAnimInfo));
9433 new_list_entry = &helpanim_info[*num_list_entries - 1];
9435 new_list_entry->element = element;
9436 new_list_entry->action = action;
9437 new_list_entry->direction = direction;
9438 new_list_entry->delay = delay;
9441 void print_unknown_token(char *filename, char *token, int token_nr)
9445 Error(ERR_INFO_LINE, "-");
9446 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9447 Error(ERR_INFO, "- config file: '%s'", filename);
9450 Error(ERR_INFO, "- token: '%s'", token);
9453 void print_unknown_token_end(int token_nr)
9456 Error(ERR_INFO_LINE, "-");
9459 void LoadHelpAnimInfo()
9461 char *filename = getHelpAnimFilename();
9462 SetupFileList *setup_file_list = NULL, *list;
9463 SetupFileHash *element_hash, *action_hash, *direction_hash;
9464 int num_list_entries = 0;
9465 int num_unknown_tokens = 0;
9468 if (fileExists(filename))
9469 setup_file_list = loadSetupFileList(filename);
9471 if (setup_file_list == NULL)
9473 /* use reliable default values from static configuration */
9474 SetupFileList *insert_ptr;
9476 insert_ptr = setup_file_list =
9477 newSetupFileList(helpanim_config[0].token,
9478 helpanim_config[0].value);
9480 for (i = 1; helpanim_config[i].token; i++)
9481 insert_ptr = addListEntry(insert_ptr,
9482 helpanim_config[i].token,
9483 helpanim_config[i].value);
9486 element_hash = newSetupFileHash();
9487 action_hash = newSetupFileHash();
9488 direction_hash = newSetupFileHash();
9490 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
9491 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9493 for (i = 0; i < NUM_ACTIONS; i++)
9494 setHashEntry(action_hash, element_action_info[i].suffix,
9495 i_to_a(element_action_info[i].value));
9497 /* do not store direction index (bit) here, but direction value! */
9498 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
9499 setHashEntry(direction_hash, element_direction_info[i].suffix,
9500 i_to_a(1 << element_direction_info[i].value));
9502 for (list = setup_file_list; list != NULL; list = list->next)
9504 char *element_token, *action_token, *direction_token;
9505 char *element_value, *action_value, *direction_value;
9506 int delay = atoi(list->value);
9508 if (strEqual(list->token, "end"))
9510 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
9515 /* first try to break element into element/action/direction parts;
9516 if this does not work, also accept combined "element[.act][.dir]"
9517 elements (like "dynamite.active"), which are unique elements */
9519 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
9521 element_value = getHashEntry(element_hash, list->token);
9522 if (element_value != NULL) /* element found */
9523 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9527 /* no further suffixes found -- this is not an element */
9528 print_unknown_token(filename, list->token, num_unknown_tokens++);
9534 /* token has format "<prefix>.<something>" */
9536 action_token = strchr(list->token, '.'); /* suffix may be action ... */
9537 direction_token = action_token; /* ... or direction */
9539 element_token = getStringCopy(list->token);
9540 *strchr(element_token, '.') = '\0';
9542 element_value = getHashEntry(element_hash, element_token);
9544 if (element_value == NULL) /* this is no element */
9546 element_value = getHashEntry(element_hash, list->token);
9547 if (element_value != NULL) /* combined element found */
9548 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9551 print_unknown_token(filename, list->token, num_unknown_tokens++);
9553 free(element_token);
9558 action_value = getHashEntry(action_hash, action_token);
9560 if (action_value != NULL) /* action found */
9562 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
9565 free(element_token);
9570 direction_value = getHashEntry(direction_hash, direction_token);
9572 if (direction_value != NULL) /* direction found */
9574 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
9577 free(element_token);
9582 if (strchr(action_token + 1, '.') == NULL)
9584 /* no further suffixes found -- this is not an action nor direction */
9586 element_value = getHashEntry(element_hash, list->token);
9587 if (element_value != NULL) /* combined element found */
9588 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9591 print_unknown_token(filename, list->token, num_unknown_tokens++);
9593 free(element_token);
9598 /* token has format "<prefix>.<suffix>.<something>" */
9600 direction_token = strchr(action_token + 1, '.');
9602 action_token = getStringCopy(action_token);
9603 *strchr(action_token + 1, '.') = '\0';
9605 action_value = getHashEntry(action_hash, action_token);
9607 if (action_value == NULL) /* this is no action */
9609 element_value = getHashEntry(element_hash, list->token);
9610 if (element_value != NULL) /* combined element found */
9611 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9614 print_unknown_token(filename, list->token, num_unknown_tokens++);
9616 free(element_token);
9622 direction_value = getHashEntry(direction_hash, direction_token);
9624 if (direction_value != NULL) /* direction found */
9626 add_helpanim_entry(atoi(element_value), atoi(action_value),
9627 atoi(direction_value), delay, &num_list_entries);
9629 free(element_token);
9635 /* this is no direction */
9637 element_value = getHashEntry(element_hash, list->token);
9638 if (element_value != NULL) /* combined element found */
9639 add_helpanim_entry(atoi(element_value), -1, -1, delay,
9642 print_unknown_token(filename, list->token, num_unknown_tokens++);
9644 free(element_token);
9648 print_unknown_token_end(num_unknown_tokens);
9650 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
9651 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
9653 freeSetupFileList(setup_file_list);
9654 freeSetupFileHash(element_hash);
9655 freeSetupFileHash(action_hash);
9656 freeSetupFileHash(direction_hash);
9659 for (i = 0; i < num_list_entries; i++)
9660 printf("::: '%s': %d, %d, %d => %d\n",
9661 EL_NAME(helpanim_info[i].element),
9662 helpanim_info[i].element,
9663 helpanim_info[i].action,
9664 helpanim_info[i].direction,
9665 helpanim_info[i].delay);
9669 void LoadHelpTextInfo()
9671 char *filename = getHelpTextFilename();
9674 if (helptext_info != NULL)
9676 freeSetupFileHash(helptext_info);
9677 helptext_info = NULL;
9680 if (fileExists(filename))
9681 helptext_info = loadSetupFileHash(filename);
9683 if (helptext_info == NULL)
9685 /* use reliable default values from static configuration */
9686 helptext_info = newSetupFileHash();
9688 for (i = 0; helptext_config[i].token; i++)
9689 setHashEntry(helptext_info,
9690 helptext_config[i].token,
9691 helptext_config[i].value);
9695 BEGIN_HASH_ITERATION(helptext_info, itr)
9697 printf("::: '%s' => '%s'\n",
9698 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
9700 END_HASH_ITERATION(hash, itr)
9705 /* ------------------------------------------------------------------------- */
9706 /* convert levels */
9707 /* ------------------------------------------------------------------------- */
9709 #define MAX_NUM_CONVERT_LEVELS 1000
9711 void ConvertLevels()
9713 static LevelDirTree *convert_leveldir = NULL;
9714 static int convert_level_nr = -1;
9715 static int num_levels_handled = 0;
9716 static int num_levels_converted = 0;
9717 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
9720 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9721 global.convert_leveldir);
9723 if (convert_leveldir == NULL)
9724 Error(ERR_EXIT, "no such level identifier: '%s'",
9725 global.convert_leveldir);
9727 leveldir_current = convert_leveldir;
9729 if (global.convert_level_nr != -1)
9731 convert_leveldir->first_level = global.convert_level_nr;
9732 convert_leveldir->last_level = global.convert_level_nr;
9735 convert_level_nr = convert_leveldir->first_level;
9737 printf_line("=", 79);
9738 printf("Converting levels\n");
9739 printf_line("-", 79);
9740 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
9741 printf("Level series name: '%s'\n", convert_leveldir->name);
9742 printf("Level series author: '%s'\n", convert_leveldir->author);
9743 printf("Number of levels: %d\n", convert_leveldir->levels);
9744 printf_line("=", 79);
9747 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
9748 levels_failed[i] = FALSE;
9750 while (convert_level_nr <= convert_leveldir->last_level)
9752 char *level_filename;
9755 level_nr = convert_level_nr++;
9757 printf("Level %03d: ", level_nr);
9759 LoadLevel(level_nr);
9760 if (level.no_valid_file)
9762 printf("(no level)\n");
9766 printf("converting level ... ");
9768 level_filename = getDefaultLevelFilename(level_nr);
9769 new_level = !fileExists(level_filename);
9773 SaveLevel(level_nr);
9775 num_levels_converted++;
9777 printf("converted.\n");
9781 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
9782 levels_failed[level_nr] = TRUE;
9784 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
9787 num_levels_handled++;
9791 printf_line("=", 79);
9792 printf("Number of levels handled: %d\n", num_levels_handled);
9793 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
9794 (num_levels_handled ?
9795 num_levels_converted * 100 / num_levels_handled : 0));
9796 printf_line("-", 79);
9797 printf("Summary (for automatic parsing by scripts):\n");
9798 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
9799 convert_leveldir->identifier, num_levels_converted,
9801 (num_levels_handled ?
9802 num_levels_converted * 100 / num_levels_handled : 0));
9804 if (num_levels_handled != num_levels_converted)
9806 printf(", FAILED:");
9807 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
9808 if (levels_failed[i])
9813 printf_line("=", 79);
9819 /* ------------------------------------------------------------------------- */
9820 /* create and save images for use in level sketches (raw BMP format) */
9821 /* ------------------------------------------------------------------------- */
9823 void CreateLevelSketchImages()
9825 #if defined(TARGET_SDL)
9830 InitElementPropertiesGfxElement();
9832 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
9833 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
9835 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9839 int element = getMappedElement(i);
9840 int graphic = el2edimg(element);
9846 sprintf(basename1, "%03d.bmp", i);
9847 sprintf(basename2, "%03ds.bmp", i);
9849 filename1 = getPath2(global.create_images_dir, basename1);
9850 filename2 = getPath2(global.create_images_dir, basename2);
9852 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
9853 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
9856 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
9857 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
9859 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
9860 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
9862 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
9863 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
9869 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
9872 FreeBitmap(bitmap1);
9873 FreeBitmap(bitmap2);
9878 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
9885 /* ------------------------------------------------------------------------- */
9886 /* create and save images for custom and group elements (raw BMP format) */
9887 /* ------------------------------------------------------------------------- */
9889 void CreateCustomElementImages(char *directory)
9891 #if defined(TARGET_SDL)
9892 char *src_basename = "RocksCE-template.ilbm";
9893 char *dst_basename = "RocksCE.bmp";
9894 char *src_filename = getPath2(directory, src_basename);
9895 char *dst_filename = getPath2(directory, dst_basename);
9899 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
9902 SDLInitVideoDisplay();
9904 src_bitmap = LoadImage(src_filename);
9906 bitmap = CreateBitmap(TILEX * 16 * 2,
9907 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
9910 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9917 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
9918 TILEX * x, TILEY * y + yoffset_ce);
9920 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
9922 TILEX * x + TILEX * 16,
9923 TILEY * y + yoffset_ce);
9925 for (j = 2; j >= 0; j--)
9929 BlitBitmap(src_bitmap, bitmap,
9930 TILEX + c * 7, 0, 6, 10,
9931 TILEX * x + 6 + j * 7,
9932 TILEY * y + 11 + yoffset_ce);
9934 BlitBitmap(src_bitmap, bitmap,
9935 TILEX + c * 8, TILEY, 6, 10,
9936 TILEX * 16 + TILEX * x + 6 + j * 8,
9937 TILEY * y + 10 + yoffset_ce);
9943 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
9950 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
9951 TILEX * x, TILEY * y + yoffset_ge);
9953 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
9955 TILEX * x + TILEX * 16,
9956 TILEY * y + yoffset_ge);
9958 for (j = 1; j >= 0; j--)
9962 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
9963 TILEX * x + 6 + j * 10,
9964 TILEY * y + 11 + yoffset_ge);
9966 BlitBitmap(src_bitmap, bitmap,
9967 TILEX + c * 8, TILEY + 12, 6, 10,
9968 TILEX * 16 + TILEX * x + 10 + j * 8,
9969 TILEY * y + 10 + yoffset_ge);
9975 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
9976 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);