1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
26 #define ENABLE_UNUSED_CODE 0 // currently unused functions
27 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
28 #define ENABLE_RESERVED_CODE 0 // reserved for later use
30 #define CHUNK_ID_LEN 4 // IFF style chunk id length
31 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
32 #define CHUNK_SIZE_NONE -1 // do not write chunk size
34 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
35 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
37 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
38 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
39 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
40 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
41 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
42 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
43 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
44 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
45 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
46 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
47 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
49 // (element number, number of change pages, change page number)
50 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
52 // (element number only)
53 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
54 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
56 // (nothing at all if unchanged)
57 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
59 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
60 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
61 #define TAPE_CHUNK_HEAD_UNUSED 1 // unused tape header bytes
62 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
64 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
65 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
66 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
68 // file identifier strings
69 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
70 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
71 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
73 // values for deciding when (not) to save configuration data
74 #define SAVE_CONF_NEVER 0
75 #define SAVE_CONF_ALWAYS 1
76 #define SAVE_CONF_WHEN_CHANGED -1
78 // values for chunks using micro chunks
79 #define CONF_MASK_1_BYTE 0x00
80 #define CONF_MASK_2_BYTE 0x40
81 #define CONF_MASK_4_BYTE 0x80
82 #define CONF_MASK_MULTI_BYTES 0xc0
84 #define CONF_MASK_BYTES 0xc0
85 #define CONF_MASK_TOKEN 0x3f
87 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
88 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
89 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
90 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
92 // these definitions are just for convenience of use and readability
93 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
94 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
95 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
96 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
98 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
99 (x) == CONF_MASK_2_BYTE ? 2 : \
100 (x) == CONF_MASK_4_BYTE ? 4 : 0)
102 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
103 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
104 #define CONF_ELEMENT_NUM_BYTES (2)
106 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
107 (t) == TYPE_ELEMENT_LIST ? \
108 CONF_ELEMENT_NUM_BYTES : \
109 (t) == TYPE_CONTENT || \
110 (t) == TYPE_CONTENT_LIST ? \
111 CONF_CONTENT_NUM_BYTES : 1)
113 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
114 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
115 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
117 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
119 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
120 CONF_ELEMENT_NUM_BYTES)
121 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
122 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
124 // temporary variables used to store pointers to structure members
125 static struct LevelInfo li;
126 static struct ElementInfo xx_ei, yy_ei;
127 static struct ElementChangeInfo xx_change;
128 static struct ElementGroupInfo xx_group;
129 static struct EnvelopeInfo xx_envelope;
130 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
131 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
132 static int xx_num_contents;
133 static int xx_current_change_page;
134 static char xx_default_string_empty[1] = "";
135 static int xx_string_length_unused;
137 struct LevelFileConfigInfo
139 int element; // element for which data is to be stored
140 int save_type; // save data always, never or when changed
141 int data_type; // data type (used internally, not stored)
142 int conf_type; // micro chunk identifier (stored in file)
145 void *value; // variable that holds the data to be stored
146 int default_value; // initial default value for this variable
149 void *value_copy; // variable that holds the data to be copied
150 void *num_entities; // number of entities for multi-byte data
151 int default_num_entities; // default number of entities for this data
152 int max_num_entities; // maximal number of entities for this data
153 char *default_string; // optional default string for string data
156 static struct LevelFileConfigInfo chunk_config_INFO[] =
158 // ---------- values not related to single elements -------------------------
161 -1, SAVE_CONF_ALWAYS,
162 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
163 &li.game_engine_type, GAME_ENGINE_TYPE_RND
167 -1, SAVE_CONF_ALWAYS,
168 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
169 &li.fieldx, STD_LEV_FIELDX
172 -1, SAVE_CONF_ALWAYS,
173 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
174 &li.fieldy, STD_LEV_FIELDY
178 -1, SAVE_CONF_ALWAYS,
179 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
191 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
197 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
198 &li.use_step_counter, FALSE
203 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
204 &li.wind_direction_initial, MV_NONE
209 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
210 &li.em_slippery_gems, FALSE
215 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
216 &li.use_custom_template, FALSE
221 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
222 &li.can_move_into_acid_bits, ~0 // default: everything can
227 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
228 &li.dont_collide_with_bits, ~0 // default: always deadly
233 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
234 &li.em_explodes_by_fire, FALSE
239 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
240 &li.score[SC_TIME_BONUS], 1
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
246 &li.auto_exit_sokoban, FALSE
251 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
252 &li.auto_count_gems, FALSE
257 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
258 &li.solved_by_one_player, FALSE
268 static struct LevelFileConfigInfo chunk_config_ELEM[] =
270 // (these values are the same for each player)
273 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
274 &li.block_last_field, FALSE // default case for EM levels
278 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
279 &li.sp_block_last_field, TRUE // default case for SP levels
283 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
284 &li.instant_relocation, FALSE
288 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
289 &li.can_pass_to_walkable, FALSE
293 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
294 &li.block_snap_field, TRUE
298 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
299 &li.continuous_snapping, TRUE
303 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
304 &li.shifted_relocation, FALSE
308 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
309 &li.lazy_relocation, FALSE
313 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
314 &li.finish_dig_collect, TRUE
317 // (these values are different for each player)
320 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
321 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
325 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
326 &li.initial_player_gravity[0], FALSE
330 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
331 &li.use_start_element[0], FALSE
335 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
336 &li.start_element[0], EL_PLAYER_1
340 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
341 &li.use_artwork_element[0], FALSE
345 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
346 &li.artwork_element[0], EL_PLAYER_1
350 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
351 &li.use_explosion_element[0], FALSE
355 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
356 &li.explosion_element[0], EL_PLAYER_1
360 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
361 &li.use_initial_inventory[0], FALSE
365 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
366 &li.initial_inventory_size[0], 1
370 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
371 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
372 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
377 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
378 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
382 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
383 &li.initial_player_gravity[1], FALSE
387 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
388 &li.use_start_element[1], FALSE
392 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
393 &li.start_element[1], EL_PLAYER_2
397 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
398 &li.use_artwork_element[1], FALSE
402 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
403 &li.artwork_element[1], EL_PLAYER_2
407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
408 &li.use_explosion_element[1], FALSE
412 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
413 &li.explosion_element[1], EL_PLAYER_2
417 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
418 &li.use_initial_inventory[1], FALSE
422 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
423 &li.initial_inventory_size[1], 1
427 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
428 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
429 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
434 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
435 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
439 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
440 &li.initial_player_gravity[2], FALSE
444 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
445 &li.use_start_element[2], FALSE
449 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
450 &li.start_element[2], EL_PLAYER_3
454 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
455 &li.use_artwork_element[2], FALSE
459 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
460 &li.artwork_element[2], EL_PLAYER_3
464 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
465 &li.use_explosion_element[2], FALSE
469 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
470 &li.explosion_element[2], EL_PLAYER_3
474 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
475 &li.use_initial_inventory[2], FALSE
479 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
480 &li.initial_inventory_size[2], 1
484 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
485 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
486 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
491 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
492 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
496 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
497 &li.initial_player_gravity[3], FALSE
501 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
502 &li.use_start_element[3], FALSE
506 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
507 &li.start_element[3], EL_PLAYER_4
511 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
512 &li.use_artwork_element[3], FALSE
516 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
517 &li.artwork_element[3], EL_PLAYER_4
521 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
522 &li.use_explosion_element[3], FALSE
526 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
527 &li.explosion_element[3], EL_PLAYER_4
531 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
532 &li.use_initial_inventory[3], FALSE
536 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
537 &li.initial_inventory_size[3], 1
541 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
542 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
543 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
548 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
549 &li.score[SC_EMERALD], 10
554 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
555 &li.score[SC_DIAMOND], 10
560 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
561 &li.score[SC_BUG], 10
566 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
567 &li.score[SC_SPACESHIP], 10
572 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
573 &li.score[SC_PACMAN], 10
578 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
579 &li.score[SC_NUT], 10
584 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
585 &li.score[SC_DYNAMITE], 10
590 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
591 &li.score[SC_KEY], 10
596 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
597 &li.score[SC_PEARL], 10
602 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
603 &li.score[SC_CRYSTAL], 10
608 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
609 &li.amoeba_content, EL_DIAMOND
613 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
618 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
619 &li.grow_into_diggable, TRUE
624 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
625 &li.yamyam_content, EL_ROCK, NULL,
626 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
630 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
631 &li.score[SC_YAMYAM], 10
636 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
637 &li.score[SC_ROBOT], 10
641 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
647 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
653 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
654 &li.time_magic_wall, 10
659 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
660 &li.game_of_life[0], 2
664 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
665 &li.game_of_life[1], 3
669 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
670 &li.game_of_life[2], 3
674 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
675 &li.game_of_life[3], 3
679 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
680 &li.use_life_bugs, FALSE
685 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
690 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
695 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
700 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
705 EL_TIMEGATE_SWITCH, -1,
706 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
707 &li.time_timegate, 10
711 EL_LIGHT_SWITCH_ACTIVE, -1,
712 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
717 EL_SHIELD_NORMAL, -1,
718 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
719 &li.shield_normal_time, 10
722 EL_SHIELD_NORMAL, -1,
723 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
724 &li.score[SC_SHIELD], 10
728 EL_SHIELD_DEADLY, -1,
729 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
730 &li.shield_deadly_time, 10
733 EL_SHIELD_DEADLY, -1,
734 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
735 &li.score[SC_SHIELD], 10
740 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
745 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
746 &li.extra_time_score, 10
750 EL_TIME_ORB_FULL, -1,
751 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
752 &li.time_orb_time, 10
755 EL_TIME_ORB_FULL, -1,
756 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
757 &li.use_time_orb_bug, FALSE
762 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
763 &li.use_spring_bug, FALSE
768 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
769 &li.android_move_time, 10
773 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
774 &li.android_clone_time, 10
777 EL_EMC_ANDROID, SAVE_CONF_NEVER,
778 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
779 &li.android_clone_element[0], EL_EMPTY, NULL,
780 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
784 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
785 &li.android_clone_element[0], EL_EMPTY, NULL,
786 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
791 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
796 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
801 EL_EMC_MAGNIFIER, -1,
802 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
803 &li.magnify_score, 10
806 EL_EMC_MAGNIFIER, -1,
807 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
812 EL_EMC_MAGIC_BALL, -1,
813 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
817 EL_EMC_MAGIC_BALL, -1,
818 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
819 &li.ball_random, FALSE
822 EL_EMC_MAGIC_BALL, -1,
823 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
824 &li.ball_active_initial, FALSE
827 EL_EMC_MAGIC_BALL, -1,
828 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
829 &li.ball_content, EL_EMPTY, NULL,
830 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
834 EL_SOKOBAN_FIELD_EMPTY, -1,
835 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
836 &li.sb_fields_needed, TRUE
840 EL_SOKOBAN_OBJECT, -1,
841 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
842 &li.sb_objects_needed, TRUE
847 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
848 &li.mm_laser_red, FALSE
852 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
853 &li.mm_laser_green, FALSE
857 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
858 &li.mm_laser_blue, TRUE
863 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
864 &li.df_laser_red, TRUE
868 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
869 &li.df_laser_green, TRUE
873 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
874 &li.df_laser_blue, FALSE
878 EL_MM_FUSE_ACTIVE, -1,
879 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
884 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
889 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
893 EL_MM_STEEL_BLOCK, -1,
894 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
895 &li.mm_time_block, 75
899 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
900 &li.score[SC_ELEM_BONUS], 10
903 // ---------- unused values -------------------------------------------------
906 EL_UNKNOWN, SAVE_CONF_NEVER,
907 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
908 &li.score[SC_UNKNOWN_15], 10
918 static struct LevelFileConfigInfo chunk_config_NOTE[] =
922 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
923 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
927 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
928 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
933 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
934 &xx_envelope.autowrap, FALSE
938 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
939 &xx_envelope.centered, FALSE
944 TYPE_STRING, CONF_VALUE_BYTES(1),
945 &xx_envelope.text, -1, NULL,
946 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
947 &xx_default_string_empty[0]
957 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
961 TYPE_STRING, CONF_VALUE_BYTES(1),
962 &xx_ei.description[0], -1,
963 &yy_ei.description[0],
964 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
965 &xx_default_description[0]
970 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
971 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
972 &yy_ei.properties[EP_BITFIELD_BASE_NR]
974 #if ENABLE_RESERVED_CODE
975 // (reserved for later use)
978 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
979 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
980 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
986 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
987 &xx_ei.use_gfx_element, FALSE,
988 &yy_ei.use_gfx_element
992 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
993 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
994 &yy_ei.gfx_element_initial
999 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1000 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1001 &yy_ei.access_direction
1006 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1007 &xx_ei.collect_score_initial, 10,
1008 &yy_ei.collect_score_initial
1012 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1013 &xx_ei.collect_count_initial, 1,
1014 &yy_ei.collect_count_initial
1019 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1020 &xx_ei.ce_value_fixed_initial, 0,
1021 &yy_ei.ce_value_fixed_initial
1025 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1026 &xx_ei.ce_value_random_initial, 0,
1027 &yy_ei.ce_value_random_initial
1031 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1032 &xx_ei.use_last_ce_value, FALSE,
1033 &yy_ei.use_last_ce_value
1038 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1039 &xx_ei.push_delay_fixed, 8,
1040 &yy_ei.push_delay_fixed
1044 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1045 &xx_ei.push_delay_random, 8,
1046 &yy_ei.push_delay_random
1050 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1051 &xx_ei.drop_delay_fixed, 0,
1052 &yy_ei.drop_delay_fixed
1056 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1057 &xx_ei.drop_delay_random, 0,
1058 &yy_ei.drop_delay_random
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1063 &xx_ei.move_delay_fixed, 0,
1064 &yy_ei.move_delay_fixed
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1069 &xx_ei.move_delay_random, 0,
1070 &yy_ei.move_delay_random
1075 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1076 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1081 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1082 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1083 &yy_ei.move_direction_initial
1087 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1088 &xx_ei.move_stepsize, TILEX / 8,
1089 &yy_ei.move_stepsize
1094 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1095 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1096 &yy_ei.move_enter_element
1100 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1101 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1102 &yy_ei.move_leave_element
1106 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1107 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1108 &yy_ei.move_leave_type
1113 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1114 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1115 &yy_ei.slippery_type
1120 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1121 &xx_ei.explosion_type, EXPLODES_3X3,
1122 &yy_ei.explosion_type
1126 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1127 &xx_ei.explosion_delay, 16,
1128 &yy_ei.explosion_delay
1132 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1133 &xx_ei.ignition_delay, 8,
1134 &yy_ei.ignition_delay
1139 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1140 &xx_ei.content, EL_EMPTY_SPACE,
1142 &xx_num_contents, 1, 1
1145 // ---------- "num_change_pages" must be the last entry ---------------------
1148 -1, SAVE_CONF_ALWAYS,
1149 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1150 &xx_ei.num_change_pages, 1,
1151 &yy_ei.num_change_pages
1162 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1164 // ---------- "current_change_page" must be the first entry -----------------
1167 -1, SAVE_CONF_ALWAYS,
1168 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1169 &xx_current_change_page, -1
1172 // ---------- (the remaining entries can be in any order) -------------------
1176 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1177 &xx_change.can_change, FALSE
1182 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1183 &xx_event_bits[0], 0
1187 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1188 &xx_event_bits[1], 0
1193 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1194 &xx_change.trigger_player, CH_PLAYER_ANY
1198 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1199 &xx_change.trigger_side, CH_SIDE_ANY
1203 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1204 &xx_change.trigger_page, CH_PAGE_ANY
1209 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1210 &xx_change.target_element, EL_EMPTY_SPACE
1215 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1216 &xx_change.delay_fixed, 0
1220 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1221 &xx_change.delay_random, 0
1225 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1226 &xx_change.delay_frames, FRAMES_PER_SECOND
1231 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1232 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1237 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1238 &xx_change.explode, FALSE
1242 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1243 &xx_change.use_target_content, FALSE
1247 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1248 &xx_change.only_if_complete, FALSE
1252 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1253 &xx_change.use_random_replace, FALSE
1257 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1258 &xx_change.random_percentage, 100
1262 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1263 &xx_change.replace_when, CP_WHEN_EMPTY
1268 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1269 &xx_change.has_action, FALSE
1273 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1274 &xx_change.action_type, CA_NO_ACTION
1278 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1279 &xx_change.action_mode, CA_MODE_UNDEFINED
1283 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1284 &xx_change.action_arg, CA_ARG_UNDEFINED
1289 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1290 &xx_change.action_element, EL_EMPTY_SPACE
1295 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1296 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1297 &xx_num_contents, 1, 1
1307 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1311 TYPE_STRING, CONF_VALUE_BYTES(1),
1312 &xx_ei.description[0], -1, NULL,
1313 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1314 &xx_default_description[0]
1319 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1320 &xx_ei.use_gfx_element, FALSE
1324 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1325 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1330 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1331 &xx_group.choice_mode, ANIM_RANDOM
1336 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1337 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1338 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1348 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1352 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1353 &li.block_snap_field, TRUE
1357 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1358 &li.continuous_snapping, TRUE
1362 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1363 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1367 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1368 &li.use_start_element[0], FALSE
1372 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1373 &li.start_element[0], EL_PLAYER_1
1377 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1378 &li.use_artwork_element[0], FALSE
1382 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1383 &li.artwork_element[0], EL_PLAYER_1
1387 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1388 &li.use_explosion_element[0], FALSE
1392 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1393 &li.explosion_element[0], EL_PLAYER_1
1408 filetype_id_list[] =
1410 { LEVEL_FILE_TYPE_RND, "RND" },
1411 { LEVEL_FILE_TYPE_BD, "BD" },
1412 { LEVEL_FILE_TYPE_EM, "EM" },
1413 { LEVEL_FILE_TYPE_SP, "SP" },
1414 { LEVEL_FILE_TYPE_DX, "DX" },
1415 { LEVEL_FILE_TYPE_SB, "SB" },
1416 { LEVEL_FILE_TYPE_DC, "DC" },
1417 { LEVEL_FILE_TYPE_MM, "MM" },
1418 { LEVEL_FILE_TYPE_MM, "DF" },
1423 // ============================================================================
1424 // level file functions
1425 // ============================================================================
1427 static boolean check_special_flags(char *flag)
1429 if (strEqual(options.special_flags, flag) ||
1430 strEqual(leveldir_current->special_flags, flag))
1436 static struct DateInfo getCurrentDate(void)
1438 time_t epoch_seconds = time(NULL);
1439 struct tm *now = localtime(&epoch_seconds);
1440 struct DateInfo date;
1442 date.year = now->tm_year + 1900;
1443 date.month = now->tm_mon + 1;
1444 date.day = now->tm_mday;
1446 date.src = DATE_SRC_CLOCK;
1451 static void resetEventFlags(struct ElementChangeInfo *change)
1455 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1456 change->has_event[i] = FALSE;
1459 static void resetEventBits(void)
1463 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1464 xx_event_bits[i] = 0;
1467 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1471 /* important: only change event flag if corresponding event bit is set
1472 (this is because all xx_event_bits[] values are loaded separately,
1473 and all xx_event_bits[] values are set back to zero before loading
1474 another value xx_event_bits[x] (each value representing 32 flags)) */
1476 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1477 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1478 change->has_event[i] = TRUE;
1481 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1485 /* in contrast to the above function setEventFlagsFromEventBits(), it
1486 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1487 depending on the corresponding change->has_event[i] values here, as
1488 all xx_event_bits[] values are reset in resetEventBits() before */
1490 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1491 if (change->has_event[i])
1492 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1495 static char *getDefaultElementDescription(struct ElementInfo *ei)
1497 static char description[MAX_ELEMENT_NAME_LEN + 1];
1498 char *default_description = (ei->custom_description != NULL ?
1499 ei->custom_description :
1500 ei->editor_description);
1503 // always start with reliable default values
1504 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1505 description[i] = '\0';
1507 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1508 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1510 return &description[0];
1513 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1515 char *default_description = getDefaultElementDescription(ei);
1518 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1519 ei->description[i] = default_description[i];
1522 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1526 for (i = 0; conf[i].data_type != -1; i++)
1528 int default_value = conf[i].default_value;
1529 int data_type = conf[i].data_type;
1530 int conf_type = conf[i].conf_type;
1531 int byte_mask = conf_type & CONF_MASK_BYTES;
1533 if (byte_mask == CONF_MASK_MULTI_BYTES)
1535 int default_num_entities = conf[i].default_num_entities;
1536 int max_num_entities = conf[i].max_num_entities;
1538 *(int *)(conf[i].num_entities) = default_num_entities;
1540 if (data_type == TYPE_STRING)
1542 char *default_string = conf[i].default_string;
1543 char *string = (char *)(conf[i].value);
1545 strncpy(string, default_string, max_num_entities);
1547 else if (data_type == TYPE_ELEMENT_LIST)
1549 int *element_array = (int *)(conf[i].value);
1552 for (j = 0; j < max_num_entities; j++)
1553 element_array[j] = default_value;
1555 else if (data_type == TYPE_CONTENT_LIST)
1557 struct Content *content = (struct Content *)(conf[i].value);
1560 for (c = 0; c < max_num_entities; c++)
1561 for (y = 0; y < 3; y++)
1562 for (x = 0; x < 3; x++)
1563 content[c].e[x][y] = default_value;
1566 else // constant size configuration data (1, 2 or 4 bytes)
1568 if (data_type == TYPE_BOOLEAN)
1569 *(boolean *)(conf[i].value) = default_value;
1571 *(int *) (conf[i].value) = default_value;
1576 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1580 for (i = 0; conf[i].data_type != -1; i++)
1582 int data_type = conf[i].data_type;
1583 int conf_type = conf[i].conf_type;
1584 int byte_mask = conf_type & CONF_MASK_BYTES;
1586 if (byte_mask == CONF_MASK_MULTI_BYTES)
1588 int max_num_entities = conf[i].max_num_entities;
1590 if (data_type == TYPE_STRING)
1592 char *string = (char *)(conf[i].value);
1593 char *string_copy = (char *)(conf[i].value_copy);
1595 strncpy(string_copy, string, max_num_entities);
1597 else if (data_type == TYPE_ELEMENT_LIST)
1599 int *element_array = (int *)(conf[i].value);
1600 int *element_array_copy = (int *)(conf[i].value_copy);
1603 for (j = 0; j < max_num_entities; j++)
1604 element_array_copy[j] = element_array[j];
1606 else if (data_type == TYPE_CONTENT_LIST)
1608 struct Content *content = (struct Content *)(conf[i].value);
1609 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1612 for (c = 0; c < max_num_entities; c++)
1613 for (y = 0; y < 3; y++)
1614 for (x = 0; x < 3; x++)
1615 content_copy[c].e[x][y] = content[c].e[x][y];
1618 else // constant size configuration data (1, 2 or 4 bytes)
1620 if (data_type == TYPE_BOOLEAN)
1621 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1623 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1628 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1632 xx_ei = *ei_from; // copy element data into temporary buffer
1633 yy_ei = *ei_to; // copy element data into temporary buffer
1635 copyConfigFromConfigList(chunk_config_CUSX_base);
1640 // ---------- reinitialize and copy change pages ----------
1642 ei_to->num_change_pages = ei_from->num_change_pages;
1643 ei_to->current_change_page = ei_from->current_change_page;
1645 setElementChangePages(ei_to, ei_to->num_change_pages);
1647 for (i = 0; i < ei_to->num_change_pages; i++)
1648 ei_to->change_page[i] = ei_from->change_page[i];
1650 // ---------- copy group element info ----------
1651 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1652 *ei_to->group = *ei_from->group;
1654 // mark this custom element as modified
1655 ei_to->modified_settings = TRUE;
1658 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1660 int change_page_size = sizeof(struct ElementChangeInfo);
1662 ei->num_change_pages = MAX(1, change_pages);
1665 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1667 if (ei->current_change_page >= ei->num_change_pages)
1668 ei->current_change_page = ei->num_change_pages - 1;
1670 ei->change = &ei->change_page[ei->current_change_page];
1673 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1675 xx_change = *change; // copy change data into temporary buffer
1677 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1679 *change = xx_change;
1681 resetEventFlags(change);
1683 change->direct_action = 0;
1684 change->other_action = 0;
1686 change->pre_change_function = NULL;
1687 change->change_function = NULL;
1688 change->post_change_function = NULL;
1691 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1695 li = *level; // copy level data into temporary buffer
1696 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1697 *level = li; // copy temporary buffer back to level data
1699 setLevelInfoToDefaults_EM();
1700 setLevelInfoToDefaults_SP();
1701 setLevelInfoToDefaults_MM();
1703 level->native_em_level = &native_em_level;
1704 level->native_sp_level = &native_sp_level;
1705 level->native_mm_level = &native_mm_level;
1707 level->file_version = FILE_VERSION_ACTUAL;
1708 level->game_version = GAME_VERSION_ACTUAL;
1710 level->creation_date = getCurrentDate();
1712 level->encoding_16bit_field = TRUE;
1713 level->encoding_16bit_yamyam = TRUE;
1714 level->encoding_16bit_amoeba = TRUE;
1716 // clear level name and level author string buffers
1717 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1718 level->name[i] = '\0';
1719 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1720 level->author[i] = '\0';
1722 // set level name and level author to default values
1723 strcpy(level->name, NAMELESS_LEVEL_NAME);
1724 strcpy(level->author, ANONYMOUS_NAME);
1726 // set level playfield to playable default level with player and exit
1727 for (x = 0; x < MAX_LEV_FIELDX; x++)
1728 for (y = 0; y < MAX_LEV_FIELDY; y++)
1729 level->field[x][y] = EL_SAND;
1731 level->field[0][0] = EL_PLAYER_1;
1732 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1734 BorderElement = EL_STEELWALL;
1736 // detect custom elements when loading them
1737 level->file_has_custom_elements = FALSE;
1739 // set all bug compatibility flags to "false" => do not emulate this bug
1740 level->use_action_after_change_bug = FALSE;
1742 if (leveldir_current)
1744 // try to determine better author name than 'anonymous'
1745 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1747 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1748 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1752 switch (LEVELCLASS(leveldir_current))
1754 case LEVELCLASS_TUTORIAL:
1755 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1758 case LEVELCLASS_CONTRIB:
1759 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1760 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1763 case LEVELCLASS_PRIVATE:
1764 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1765 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1769 // keep default value
1776 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1778 static boolean clipboard_elements_initialized = FALSE;
1781 InitElementPropertiesStatic();
1783 li = *level; // copy level data into temporary buffer
1784 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1785 *level = li; // copy temporary buffer back to level data
1787 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1790 struct ElementInfo *ei = &element_info[element];
1792 // never initialize clipboard elements after the very first time
1793 // (to be able to use clipboard elements between several levels)
1794 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1797 if (IS_ENVELOPE(element))
1799 int envelope_nr = element - EL_ENVELOPE_1;
1801 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1803 level->envelope[envelope_nr] = xx_envelope;
1806 if (IS_CUSTOM_ELEMENT(element) ||
1807 IS_GROUP_ELEMENT(element) ||
1808 IS_INTERNAL_ELEMENT(element))
1810 xx_ei = *ei; // copy element data into temporary buffer
1812 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1817 setElementChangePages(ei, 1);
1818 setElementChangeInfoToDefaults(ei->change);
1820 if (IS_CUSTOM_ELEMENT(element) ||
1821 IS_GROUP_ELEMENT(element) ||
1822 IS_INTERNAL_ELEMENT(element))
1824 setElementDescriptionToDefault(ei);
1826 ei->modified_settings = FALSE;
1829 if (IS_CUSTOM_ELEMENT(element) ||
1830 IS_INTERNAL_ELEMENT(element))
1832 // internal values used in level editor
1834 ei->access_type = 0;
1835 ei->access_layer = 0;
1836 ei->access_protected = 0;
1837 ei->walk_to_action = 0;
1838 ei->smash_targets = 0;
1841 ei->can_explode_by_fire = FALSE;
1842 ei->can_explode_smashed = FALSE;
1843 ei->can_explode_impact = FALSE;
1845 ei->current_change_page = 0;
1848 if (IS_GROUP_ELEMENT(element) ||
1849 IS_INTERNAL_ELEMENT(element))
1851 struct ElementGroupInfo *group;
1853 // initialize memory for list of elements in group
1854 if (ei->group == NULL)
1855 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1859 xx_group = *group; // copy group data into temporary buffer
1861 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1867 clipboard_elements_initialized = TRUE;
1870 static void setLevelInfoToDefaults(struct LevelInfo *level,
1871 boolean level_info_only,
1872 boolean reset_file_status)
1874 setLevelInfoToDefaults_Level(level);
1876 if (!level_info_only)
1877 setLevelInfoToDefaults_Elements(level);
1879 if (reset_file_status)
1881 level->no_valid_file = FALSE;
1882 level->no_level_file = FALSE;
1885 level->changed = FALSE;
1888 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1890 level_file_info->nr = 0;
1891 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1892 level_file_info->packed = FALSE;
1894 setString(&level_file_info->basename, NULL);
1895 setString(&level_file_info->filename, NULL);
1898 int getMappedElement_SB(int, boolean);
1900 static void ActivateLevelTemplate(void)
1904 if (check_special_flags("load_xsb_to_ces"))
1906 // fill smaller playfields with padding "beyond border wall" elements
1907 if (level.fieldx < level_template.fieldx ||
1908 level.fieldy < level_template.fieldy)
1910 short field[level.fieldx][level.fieldy];
1911 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1912 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1913 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1914 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1916 // copy old playfield (which is smaller than the visible area)
1917 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1918 field[x][y] = level.field[x][y];
1920 // fill new, larger playfield with "beyond border wall" elements
1921 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1922 level.field[x][y] = getMappedElement_SB('_', TRUE);
1924 // copy the old playfield to the middle of the new playfield
1925 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1926 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1928 level.fieldx = new_fieldx;
1929 level.fieldy = new_fieldy;
1933 // Currently there is no special action needed to activate the template
1934 // data, because 'element_info' property settings overwrite the original
1935 // level data, while all other variables do not change.
1937 // Exception: 'from_level_template' elements in the original level playfield
1938 // are overwritten with the corresponding elements at the same position in
1939 // playfield from the level template.
1941 for (x = 0; x < level.fieldx; x++)
1942 for (y = 0; y < level.fieldy; y++)
1943 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1944 level.field[x][y] = level_template.field[x][y];
1946 if (check_special_flags("load_xsb_to_ces"))
1948 struct LevelInfo level_backup = level;
1950 // overwrite all individual level settings from template level settings
1951 level = level_template;
1953 // restore level file info
1954 level.file_info = level_backup.file_info;
1956 // restore playfield size
1957 level.fieldx = level_backup.fieldx;
1958 level.fieldy = level_backup.fieldy;
1960 // restore playfield content
1961 for (x = 0; x < level.fieldx; x++)
1962 for (y = 0; y < level.fieldy; y++)
1963 level.field[x][y] = level_backup.field[x][y];
1965 // restore name and author from individual level
1966 strcpy(level.name, level_backup.name);
1967 strcpy(level.author, level_backup.author);
1969 // restore flag "use_custom_template"
1970 level.use_custom_template = level_backup.use_custom_template;
1974 static char *getLevelFilenameFromBasename(char *basename)
1976 static char *filename = NULL;
1978 checked_free(filename);
1980 filename = getPath2(getCurrentLevelDir(), basename);
1985 static int getFileTypeFromBasename(char *basename)
1987 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
1989 static char *filename = NULL;
1990 struct stat file_status;
1992 // ---------- try to determine file type from filename ----------
1994 // check for typical filename of a Supaplex level package file
1995 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1996 return LEVEL_FILE_TYPE_SP;
1998 // check for typical filename of a Diamond Caves II level package file
1999 if (strSuffixLower(basename, ".dc") ||
2000 strSuffixLower(basename, ".dc2"))
2001 return LEVEL_FILE_TYPE_DC;
2003 // check for typical filename of a Sokoban level package file
2004 if (strSuffixLower(basename, ".xsb") &&
2005 strchr(basename, '%') == NULL)
2006 return LEVEL_FILE_TYPE_SB;
2008 // ---------- try to determine file type from filesize ----------
2010 checked_free(filename);
2011 filename = getPath2(getCurrentLevelDir(), basename);
2013 if (stat(filename, &file_status) == 0)
2015 // check for typical filesize of a Supaplex level package file
2016 if (file_status.st_size == 170496)
2017 return LEVEL_FILE_TYPE_SP;
2020 return LEVEL_FILE_TYPE_UNKNOWN;
2023 static int getFileTypeFromMagicBytes(char *filename, int type)
2027 if ((file = openFile(filename, MODE_READ)))
2029 char chunk_name[CHUNK_ID_LEN + 1];
2031 getFileChunkBE(file, chunk_name, NULL);
2033 if (strEqual(chunk_name, "MMII") ||
2034 strEqual(chunk_name, "MIRR"))
2035 type = LEVEL_FILE_TYPE_MM;
2043 static boolean checkForPackageFromBasename(char *basename)
2045 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2046 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2048 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2051 static char *getSingleLevelBasenameExt(int nr, char *extension)
2053 static char basename[MAX_FILENAME_LEN];
2056 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2058 sprintf(basename, "%03d.%s", nr, extension);
2063 static char *getSingleLevelBasename(int nr)
2065 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2068 static char *getPackedLevelBasename(int type)
2070 static char basename[MAX_FILENAME_LEN];
2071 char *directory = getCurrentLevelDir();
2073 DirectoryEntry *dir_entry;
2075 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2077 if ((dir = openDirectory(directory)) == NULL)
2079 Warn("cannot read current level directory '%s'", directory);
2084 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2086 char *entry_basename = dir_entry->basename;
2087 int entry_type = getFileTypeFromBasename(entry_basename);
2089 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2091 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2094 strcpy(basename, entry_basename);
2101 closeDirectory(dir);
2106 static char *getSingleLevelFilename(int nr)
2108 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2111 #if ENABLE_UNUSED_CODE
2112 static char *getPackedLevelFilename(int type)
2114 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2118 char *getDefaultLevelFilename(int nr)
2120 return getSingleLevelFilename(nr);
2123 #if ENABLE_UNUSED_CODE
2124 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2128 lfi->packed = FALSE;
2130 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2131 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2135 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2136 int type, char *format, ...)
2138 static char basename[MAX_FILENAME_LEN];
2141 va_start(ap, format);
2142 vsprintf(basename, format, ap);
2146 lfi->packed = FALSE;
2148 setString(&lfi->basename, basename);
2149 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2152 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2158 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2159 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2162 static int getFiletypeFromID(char *filetype_id)
2164 char *filetype_id_lower;
2165 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2168 if (filetype_id == NULL)
2169 return LEVEL_FILE_TYPE_UNKNOWN;
2171 filetype_id_lower = getStringToLower(filetype_id);
2173 for (i = 0; filetype_id_list[i].id != NULL; i++)
2175 char *id_lower = getStringToLower(filetype_id_list[i].id);
2177 if (strEqual(filetype_id_lower, id_lower))
2178 filetype = filetype_id_list[i].filetype;
2182 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2186 free(filetype_id_lower);
2191 char *getLocalLevelTemplateFilename(void)
2193 return getDefaultLevelFilename(-1);
2196 char *getGlobalLevelTemplateFilename(void)
2198 // global variable "leveldir_current" must be modified in the loop below
2199 LevelDirTree *leveldir_current_last = leveldir_current;
2200 char *filename = NULL;
2202 // check for template level in path from current to topmost tree node
2204 while (leveldir_current != NULL)
2206 filename = getDefaultLevelFilename(-1);
2208 if (fileExists(filename))
2211 leveldir_current = leveldir_current->node_parent;
2214 // restore global variable "leveldir_current" modified in above loop
2215 leveldir_current = leveldir_current_last;
2220 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2224 // special case: level number is negative => check for level template file
2227 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2228 getSingleLevelBasename(-1));
2230 // replace local level template filename with global template filename
2231 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2233 // no fallback if template file not existing
2237 // special case: check for file name/pattern specified in "levelinfo.conf"
2238 if (leveldir_current->level_filename != NULL)
2240 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2242 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2243 leveldir_current->level_filename, nr);
2245 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2247 if (fileExists(lfi->filename))
2250 else if (leveldir_current->level_filetype != NULL)
2252 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2254 // check for specified native level file with standard file name
2255 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2256 "%03d.%s", nr, LEVELFILE_EXTENSION);
2257 if (fileExists(lfi->filename))
2261 // check for native Rocks'n'Diamonds level file
2262 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2263 "%03d.%s", nr, LEVELFILE_EXTENSION);
2264 if (fileExists(lfi->filename))
2267 // check for Emerald Mine level file (V1)
2268 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2269 'a' + (nr / 10) % 26, '0' + nr % 10);
2270 if (fileExists(lfi->filename))
2272 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2273 'A' + (nr / 10) % 26, '0' + nr % 10);
2274 if (fileExists(lfi->filename))
2277 // check for Emerald Mine level file (V2 to V5)
2278 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2279 if (fileExists(lfi->filename))
2282 // check for Emerald Mine level file (V6 / single mode)
2283 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2284 if (fileExists(lfi->filename))
2286 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2287 if (fileExists(lfi->filename))
2290 // check for Emerald Mine level file (V6 / teamwork mode)
2291 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2292 if (fileExists(lfi->filename))
2294 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2295 if (fileExists(lfi->filename))
2298 // check for various packed level file formats
2299 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2300 if (fileExists(lfi->filename))
2303 // no known level file found -- use default values (and fail later)
2304 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2305 "%03d.%s", nr, LEVELFILE_EXTENSION);
2308 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2310 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2311 lfi->type = getFileTypeFromBasename(lfi->basename);
2313 if (lfi->type == LEVEL_FILE_TYPE_RND)
2314 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2317 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2319 // always start with reliable default values
2320 setFileInfoToDefaults(level_file_info);
2322 level_file_info->nr = nr; // set requested level number
2324 determineLevelFileInfo_Filename(level_file_info);
2325 determineLevelFileInfo_Filetype(level_file_info);
2328 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2329 struct LevelFileInfo *lfi_to)
2331 lfi_to->nr = lfi_from->nr;
2332 lfi_to->type = lfi_from->type;
2333 lfi_to->packed = lfi_from->packed;
2335 setString(&lfi_to->basename, lfi_from->basename);
2336 setString(&lfi_to->filename, lfi_from->filename);
2339 // ----------------------------------------------------------------------------
2340 // functions for loading R'n'D level
2341 // ----------------------------------------------------------------------------
2343 int getMappedElement(int element)
2345 // remap some (historic, now obsolete) elements
2349 case EL_PLAYER_OBSOLETE:
2350 element = EL_PLAYER_1;
2353 case EL_KEY_OBSOLETE:
2357 case EL_EM_KEY_1_FILE_OBSOLETE:
2358 element = EL_EM_KEY_1;
2361 case EL_EM_KEY_2_FILE_OBSOLETE:
2362 element = EL_EM_KEY_2;
2365 case EL_EM_KEY_3_FILE_OBSOLETE:
2366 element = EL_EM_KEY_3;
2369 case EL_EM_KEY_4_FILE_OBSOLETE:
2370 element = EL_EM_KEY_4;
2373 case EL_ENVELOPE_OBSOLETE:
2374 element = EL_ENVELOPE_1;
2382 if (element >= NUM_FILE_ELEMENTS)
2384 Warn("invalid level element %d", element);
2386 element = EL_UNKNOWN;
2394 static int getMappedElementByVersion(int element, int game_version)
2396 // remap some elements due to certain game version
2398 if (game_version <= VERSION_IDENT(2,2,0,0))
2400 // map game font elements
2401 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2402 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2403 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2404 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2407 if (game_version < VERSION_IDENT(3,0,0,0))
2409 // map Supaplex gravity tube elements
2410 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2411 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2412 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2413 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2420 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2422 level->file_version = getFileVersion(file);
2423 level->game_version = getFileVersion(file);
2428 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2430 level->creation_date.year = getFile16BitBE(file);
2431 level->creation_date.month = getFile8Bit(file);
2432 level->creation_date.day = getFile8Bit(file);
2434 level->creation_date.src = DATE_SRC_LEVELFILE;
2439 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2441 int initial_player_stepsize;
2442 int initial_player_gravity;
2445 level->fieldx = getFile8Bit(file);
2446 level->fieldy = getFile8Bit(file);
2448 level->time = getFile16BitBE(file);
2449 level->gems_needed = getFile16BitBE(file);
2451 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2452 level->name[i] = getFile8Bit(file);
2453 level->name[MAX_LEVEL_NAME_LEN] = 0;
2455 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2456 level->score[i] = getFile8Bit(file);
2458 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2459 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2460 for (y = 0; y < 3; y++)
2461 for (x = 0; x < 3; x++)
2462 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2464 level->amoeba_speed = getFile8Bit(file);
2465 level->time_magic_wall = getFile8Bit(file);
2466 level->time_wheel = getFile8Bit(file);
2467 level->amoeba_content = getMappedElement(getFile8Bit(file));
2469 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2472 for (i = 0; i < MAX_PLAYERS; i++)
2473 level->initial_player_stepsize[i] = initial_player_stepsize;
2475 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2477 for (i = 0; i < MAX_PLAYERS; i++)
2478 level->initial_player_gravity[i] = initial_player_gravity;
2480 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2481 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2483 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2485 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2486 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2487 level->can_move_into_acid_bits = getFile32BitBE(file);
2488 level->dont_collide_with_bits = getFile8Bit(file);
2490 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2491 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2493 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2494 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2495 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2497 level->game_engine_type = getFile8Bit(file);
2499 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2504 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2508 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2509 level->name[i] = getFile8Bit(file);
2510 level->name[MAX_LEVEL_NAME_LEN] = 0;
2515 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2519 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2520 level->author[i] = getFile8Bit(file);
2521 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2526 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2529 int chunk_size_expected = level->fieldx * level->fieldy;
2531 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2532 stored with 16-bit encoding (and should be twice as big then).
2533 Even worse, playfield data was stored 16-bit when only yamyam content
2534 contained 16-bit elements and vice versa. */
2536 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2537 chunk_size_expected *= 2;
2539 if (chunk_size_expected != chunk_size)
2541 ReadUnusedBytesFromFile(file, chunk_size);
2542 return chunk_size_expected;
2545 for (y = 0; y < level->fieldy; y++)
2546 for (x = 0; x < level->fieldx; x++)
2547 level->field[x][y] =
2548 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2553 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2556 int header_size = 4;
2557 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2558 int chunk_size_expected = header_size + content_size;
2560 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2561 stored with 16-bit encoding (and should be twice as big then).
2562 Even worse, playfield data was stored 16-bit when only yamyam content
2563 contained 16-bit elements and vice versa. */
2565 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2566 chunk_size_expected += content_size;
2568 if (chunk_size_expected != chunk_size)
2570 ReadUnusedBytesFromFile(file, chunk_size);
2571 return chunk_size_expected;
2575 level->num_yamyam_contents = getFile8Bit(file);
2579 // correct invalid number of content fields -- should never happen
2580 if (level->num_yamyam_contents < 1 ||
2581 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2582 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2584 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2585 for (y = 0; y < 3; y++)
2586 for (x = 0; x < 3; x++)
2587 level->yamyam_content[i].e[x][y] =
2588 getMappedElement(level->encoding_16bit_field ?
2589 getFile16BitBE(file) : getFile8Bit(file));
2593 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2598 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2600 element = getMappedElement(getFile16BitBE(file));
2601 num_contents = getFile8Bit(file);
2603 getFile8Bit(file); // content x size (unused)
2604 getFile8Bit(file); // content y size (unused)
2606 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2608 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2609 for (y = 0; y < 3; y++)
2610 for (x = 0; x < 3; x++)
2611 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2613 // correct invalid number of content fields -- should never happen
2614 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2615 num_contents = STD_ELEMENT_CONTENTS;
2617 if (element == EL_YAMYAM)
2619 level->num_yamyam_contents = num_contents;
2621 for (i = 0; i < num_contents; i++)
2622 for (y = 0; y < 3; y++)
2623 for (x = 0; x < 3; x++)
2624 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2626 else if (element == EL_BD_AMOEBA)
2628 level->amoeba_content = content_array[0][0][0];
2632 Warn("cannot load content for element '%d'", element);
2638 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2644 int chunk_size_expected;
2646 element = getMappedElement(getFile16BitBE(file));
2647 if (!IS_ENVELOPE(element))
2648 element = EL_ENVELOPE_1;
2650 envelope_nr = element - EL_ENVELOPE_1;
2652 envelope_len = getFile16BitBE(file);
2654 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2655 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2657 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2659 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2660 if (chunk_size_expected != chunk_size)
2662 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2663 return chunk_size_expected;
2666 for (i = 0; i < envelope_len; i++)
2667 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2672 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2674 int num_changed_custom_elements = getFile16BitBE(file);
2675 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2678 if (chunk_size_expected != chunk_size)
2680 ReadUnusedBytesFromFile(file, chunk_size - 2);
2681 return chunk_size_expected;
2684 for (i = 0; i < num_changed_custom_elements; i++)
2686 int element = getMappedElement(getFile16BitBE(file));
2687 int properties = getFile32BitBE(file);
2689 if (IS_CUSTOM_ELEMENT(element))
2690 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2692 Warn("invalid custom element number %d", element);
2694 // older game versions that wrote level files with CUS1 chunks used
2695 // different default push delay values (not yet stored in level file)
2696 element_info[element].push_delay_fixed = 2;
2697 element_info[element].push_delay_random = 8;
2700 level->file_has_custom_elements = TRUE;
2705 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2707 int num_changed_custom_elements = getFile16BitBE(file);
2708 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2711 if (chunk_size_expected != chunk_size)
2713 ReadUnusedBytesFromFile(file, chunk_size - 2);
2714 return chunk_size_expected;
2717 for (i = 0; i < num_changed_custom_elements; i++)
2719 int element = getMappedElement(getFile16BitBE(file));
2720 int custom_target_element = getMappedElement(getFile16BitBE(file));
2722 if (IS_CUSTOM_ELEMENT(element))
2723 element_info[element].change->target_element = custom_target_element;
2725 Warn("invalid custom element number %d", element);
2728 level->file_has_custom_elements = TRUE;
2733 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2735 int num_changed_custom_elements = getFile16BitBE(file);
2736 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2739 if (chunk_size_expected != chunk_size)
2741 ReadUnusedBytesFromFile(file, chunk_size - 2);
2742 return chunk_size_expected;
2745 for (i = 0; i < num_changed_custom_elements; i++)
2747 int element = getMappedElement(getFile16BitBE(file));
2748 struct ElementInfo *ei = &element_info[element];
2749 unsigned int event_bits;
2751 if (!IS_CUSTOM_ELEMENT(element))
2753 Warn("invalid custom element number %d", element);
2755 element = EL_INTERNAL_DUMMY;
2758 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2759 ei->description[j] = getFile8Bit(file);
2760 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2762 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2764 // some free bytes for future properties and padding
2765 ReadUnusedBytesFromFile(file, 7);
2767 ei->use_gfx_element = getFile8Bit(file);
2768 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2770 ei->collect_score_initial = getFile8Bit(file);
2771 ei->collect_count_initial = getFile8Bit(file);
2773 ei->push_delay_fixed = getFile16BitBE(file);
2774 ei->push_delay_random = getFile16BitBE(file);
2775 ei->move_delay_fixed = getFile16BitBE(file);
2776 ei->move_delay_random = getFile16BitBE(file);
2778 ei->move_pattern = getFile16BitBE(file);
2779 ei->move_direction_initial = getFile8Bit(file);
2780 ei->move_stepsize = getFile8Bit(file);
2782 for (y = 0; y < 3; y++)
2783 for (x = 0; x < 3; x++)
2784 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2786 event_bits = getFile32BitBE(file);
2787 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2788 if (event_bits & (1 << j))
2789 ei->change->has_event[j] = TRUE;
2791 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2793 ei->change->delay_fixed = getFile16BitBE(file);
2794 ei->change->delay_random = getFile16BitBE(file);
2795 ei->change->delay_frames = getFile16BitBE(file);
2797 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2799 ei->change->explode = getFile8Bit(file);
2800 ei->change->use_target_content = getFile8Bit(file);
2801 ei->change->only_if_complete = getFile8Bit(file);
2802 ei->change->use_random_replace = getFile8Bit(file);
2804 ei->change->random_percentage = getFile8Bit(file);
2805 ei->change->replace_when = getFile8Bit(file);
2807 for (y = 0; y < 3; y++)
2808 for (x = 0; x < 3; x++)
2809 ei->change->target_content.e[x][y] =
2810 getMappedElement(getFile16BitBE(file));
2812 ei->slippery_type = getFile8Bit(file);
2814 // some free bytes for future properties and padding
2815 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2817 // mark that this custom element has been modified
2818 ei->modified_settings = TRUE;
2821 level->file_has_custom_elements = TRUE;
2826 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2828 struct ElementInfo *ei;
2829 int chunk_size_expected;
2833 // ---------- custom element base property values (96 bytes) ----------------
2835 element = getMappedElement(getFile16BitBE(file));
2837 if (!IS_CUSTOM_ELEMENT(element))
2839 Warn("invalid custom element number %d", element);
2841 ReadUnusedBytesFromFile(file, chunk_size - 2);
2846 ei = &element_info[element];
2848 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2849 ei->description[i] = getFile8Bit(file);
2850 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2852 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2854 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2856 ei->num_change_pages = getFile8Bit(file);
2858 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2859 if (chunk_size_expected != chunk_size)
2861 ReadUnusedBytesFromFile(file, chunk_size - 43);
2862 return chunk_size_expected;
2865 ei->ce_value_fixed_initial = getFile16BitBE(file);
2866 ei->ce_value_random_initial = getFile16BitBE(file);
2867 ei->use_last_ce_value = getFile8Bit(file);
2869 ei->use_gfx_element = getFile8Bit(file);
2870 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2872 ei->collect_score_initial = getFile8Bit(file);
2873 ei->collect_count_initial = getFile8Bit(file);
2875 ei->drop_delay_fixed = getFile8Bit(file);
2876 ei->push_delay_fixed = getFile8Bit(file);
2877 ei->drop_delay_random = getFile8Bit(file);
2878 ei->push_delay_random = getFile8Bit(file);
2879 ei->move_delay_fixed = getFile16BitBE(file);
2880 ei->move_delay_random = getFile16BitBE(file);
2882 // bits 0 - 15 of "move_pattern" ...
2883 ei->move_pattern = getFile16BitBE(file);
2884 ei->move_direction_initial = getFile8Bit(file);
2885 ei->move_stepsize = getFile8Bit(file);
2887 ei->slippery_type = getFile8Bit(file);
2889 for (y = 0; y < 3; y++)
2890 for (x = 0; x < 3; x++)
2891 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2893 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2894 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2895 ei->move_leave_type = getFile8Bit(file);
2897 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2898 ei->move_pattern |= (getFile16BitBE(file) << 16);
2900 ei->access_direction = getFile8Bit(file);
2902 ei->explosion_delay = getFile8Bit(file);
2903 ei->ignition_delay = getFile8Bit(file);
2904 ei->explosion_type = getFile8Bit(file);
2906 // some free bytes for future custom property values and padding
2907 ReadUnusedBytesFromFile(file, 1);
2909 // ---------- change page property values (48 bytes) ------------------------
2911 setElementChangePages(ei, ei->num_change_pages);
2913 for (i = 0; i < ei->num_change_pages; i++)
2915 struct ElementChangeInfo *change = &ei->change_page[i];
2916 unsigned int event_bits;
2918 // always start with reliable default values
2919 setElementChangeInfoToDefaults(change);
2921 // bits 0 - 31 of "has_event[]" ...
2922 event_bits = getFile32BitBE(file);
2923 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2924 if (event_bits & (1 << j))
2925 change->has_event[j] = TRUE;
2927 change->target_element = getMappedElement(getFile16BitBE(file));
2929 change->delay_fixed = getFile16BitBE(file);
2930 change->delay_random = getFile16BitBE(file);
2931 change->delay_frames = getFile16BitBE(file);
2933 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2935 change->explode = getFile8Bit(file);
2936 change->use_target_content = getFile8Bit(file);
2937 change->only_if_complete = getFile8Bit(file);
2938 change->use_random_replace = getFile8Bit(file);
2940 change->random_percentage = getFile8Bit(file);
2941 change->replace_when = getFile8Bit(file);
2943 for (y = 0; y < 3; y++)
2944 for (x = 0; x < 3; x++)
2945 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2947 change->can_change = getFile8Bit(file);
2949 change->trigger_side = getFile8Bit(file);
2951 change->trigger_player = getFile8Bit(file);
2952 change->trigger_page = getFile8Bit(file);
2954 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2955 CH_PAGE_ANY : (1 << change->trigger_page));
2957 change->has_action = getFile8Bit(file);
2958 change->action_type = getFile8Bit(file);
2959 change->action_mode = getFile8Bit(file);
2960 change->action_arg = getFile16BitBE(file);
2962 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
2963 event_bits = getFile8Bit(file);
2964 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2965 if (event_bits & (1 << (j - 32)))
2966 change->has_event[j] = TRUE;
2969 // mark this custom element as modified
2970 ei->modified_settings = TRUE;
2972 level->file_has_custom_elements = TRUE;
2977 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2979 struct ElementInfo *ei;
2980 struct ElementGroupInfo *group;
2984 element = getMappedElement(getFile16BitBE(file));
2986 if (!IS_GROUP_ELEMENT(element))
2988 Warn("invalid group element number %d", element);
2990 ReadUnusedBytesFromFile(file, chunk_size - 2);
2995 ei = &element_info[element];
2997 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2998 ei->description[i] = getFile8Bit(file);
2999 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3001 group = element_info[element].group;
3003 group->num_elements = getFile8Bit(file);
3005 ei->use_gfx_element = getFile8Bit(file);
3006 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3008 group->choice_mode = getFile8Bit(file);
3010 // some free bytes for future values and padding
3011 ReadUnusedBytesFromFile(file, 3);
3013 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3014 group->element[i] = getMappedElement(getFile16BitBE(file));
3016 // mark this group element as modified
3017 element_info[element].modified_settings = TRUE;
3019 level->file_has_custom_elements = TRUE;
3024 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3025 int element, int real_element)
3027 int micro_chunk_size = 0;
3028 int conf_type = getFile8Bit(file);
3029 int byte_mask = conf_type & CONF_MASK_BYTES;
3030 boolean element_found = FALSE;
3033 micro_chunk_size += 1;
3035 if (byte_mask == CONF_MASK_MULTI_BYTES)
3037 int num_bytes = getFile16BitBE(file);
3038 byte *buffer = checked_malloc(num_bytes);
3040 ReadBytesFromFile(file, buffer, num_bytes);
3042 for (i = 0; conf[i].data_type != -1; i++)
3044 if (conf[i].element == element &&
3045 conf[i].conf_type == conf_type)
3047 int data_type = conf[i].data_type;
3048 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3049 int max_num_entities = conf[i].max_num_entities;
3051 if (num_entities > max_num_entities)
3053 Warn("truncating number of entities for element %d from %d to %d",
3054 element, num_entities, max_num_entities);
3056 num_entities = max_num_entities;
3059 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3060 data_type == TYPE_CONTENT_LIST))
3062 // for element and content lists, zero entities are not allowed
3063 Warn("found empty list of entities for element %d", element);
3065 // do not set "num_entities" here to prevent reading behind buffer
3067 *(int *)(conf[i].num_entities) = 1; // at least one is required
3071 *(int *)(conf[i].num_entities) = num_entities;
3074 element_found = TRUE;
3076 if (data_type == TYPE_STRING)
3078 char *string = (char *)(conf[i].value);
3081 for (j = 0; j < max_num_entities; j++)
3082 string[j] = (j < num_entities ? buffer[j] : '\0');
3084 else if (data_type == TYPE_ELEMENT_LIST)
3086 int *element_array = (int *)(conf[i].value);
3089 for (j = 0; j < num_entities; j++)
3091 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3093 else if (data_type == TYPE_CONTENT_LIST)
3095 struct Content *content= (struct Content *)(conf[i].value);
3098 for (c = 0; c < num_entities; c++)
3099 for (y = 0; y < 3; y++)
3100 for (x = 0; x < 3; x++)
3101 content[c].e[x][y] =
3102 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3105 element_found = FALSE;
3111 checked_free(buffer);
3113 micro_chunk_size += 2 + num_bytes;
3115 else // constant size configuration data (1, 2 or 4 bytes)
3117 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3118 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3119 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3121 for (i = 0; conf[i].data_type != -1; i++)
3123 if (conf[i].element == element &&
3124 conf[i].conf_type == conf_type)
3126 int data_type = conf[i].data_type;
3128 if (data_type == TYPE_ELEMENT)
3129 value = getMappedElement(value);
3131 if (data_type == TYPE_BOOLEAN)
3132 *(boolean *)(conf[i].value) = value;
3134 *(int *) (conf[i].value) = value;
3136 element_found = TRUE;
3142 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3147 char *error_conf_chunk_bytes =
3148 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3149 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3150 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3151 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3152 int error_element = real_element;
3154 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3155 error_conf_chunk_bytes, error_conf_chunk_token,
3156 error_element, EL_NAME(error_element));
3159 return micro_chunk_size;
3162 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3164 int real_chunk_size = 0;
3166 li = *level; // copy level data into temporary buffer
3168 while (!checkEndOfFile(file))
3170 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3172 if (real_chunk_size >= chunk_size)
3176 *level = li; // copy temporary buffer back to level data
3178 return real_chunk_size;
3181 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3183 int real_chunk_size = 0;
3185 li = *level; // copy level data into temporary buffer
3187 while (!checkEndOfFile(file))
3189 int element = getMappedElement(getFile16BitBE(file));
3191 real_chunk_size += 2;
3192 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3194 if (real_chunk_size >= chunk_size)
3198 *level = li; // copy temporary buffer back to level data
3200 return real_chunk_size;
3203 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3205 int real_chunk_size = 0;
3207 li = *level; // copy level data into temporary buffer
3209 while (!checkEndOfFile(file))
3211 int element = getMappedElement(getFile16BitBE(file));
3213 real_chunk_size += 2;
3214 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3216 if (real_chunk_size >= chunk_size)
3220 *level = li; // copy temporary buffer back to level data
3222 return real_chunk_size;
3225 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3227 int element = getMappedElement(getFile16BitBE(file));
3228 int envelope_nr = element - EL_ENVELOPE_1;
3229 int real_chunk_size = 2;
3231 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3233 while (!checkEndOfFile(file))
3235 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3238 if (real_chunk_size >= chunk_size)
3242 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3244 return real_chunk_size;
3247 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3249 int element = getMappedElement(getFile16BitBE(file));
3250 int real_chunk_size = 2;
3251 struct ElementInfo *ei = &element_info[element];
3254 xx_ei = *ei; // copy element data into temporary buffer
3256 xx_ei.num_change_pages = -1;
3258 while (!checkEndOfFile(file))
3260 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3262 if (xx_ei.num_change_pages != -1)
3265 if (real_chunk_size >= chunk_size)
3271 if (ei->num_change_pages == -1)
3273 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3276 ei->num_change_pages = 1;
3278 setElementChangePages(ei, 1);
3279 setElementChangeInfoToDefaults(ei->change);
3281 return real_chunk_size;
3284 // initialize number of change pages stored for this custom element
3285 setElementChangePages(ei, ei->num_change_pages);
3286 for (i = 0; i < ei->num_change_pages; i++)
3287 setElementChangeInfoToDefaults(&ei->change_page[i]);
3289 // start with reading properties for the first change page
3290 xx_current_change_page = 0;
3292 while (!checkEndOfFile(file))
3294 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3296 xx_change = *change; // copy change data into temporary buffer
3298 resetEventBits(); // reset bits; change page might have changed
3300 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3303 *change = xx_change;
3305 setEventFlagsFromEventBits(change);
3307 if (real_chunk_size >= chunk_size)
3311 level->file_has_custom_elements = TRUE;
3313 return real_chunk_size;
3316 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3318 int element = getMappedElement(getFile16BitBE(file));
3319 int real_chunk_size = 2;
3320 struct ElementInfo *ei = &element_info[element];
3321 struct ElementGroupInfo *group = ei->group;
3323 xx_ei = *ei; // copy element data into temporary buffer
3324 xx_group = *group; // copy group data into temporary buffer
3326 while (!checkEndOfFile(file))
3328 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3331 if (real_chunk_size >= chunk_size)
3338 level->file_has_custom_elements = TRUE;
3340 return real_chunk_size;
3343 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3344 struct LevelFileInfo *level_file_info,
3345 boolean level_info_only)
3347 char *filename = level_file_info->filename;
3348 char cookie[MAX_LINE_LEN];
3349 char chunk_name[CHUNK_ID_LEN + 1];
3353 if (!(file = openFile(filename, MODE_READ)))
3355 level->no_valid_file = TRUE;
3356 level->no_level_file = TRUE;
3358 if (level_info_only)
3361 Warn("cannot read level '%s' -- using empty level", filename);
3363 if (!setup.editor.use_template_for_new_levels)
3366 // if level file not found, try to initialize level data from template
3367 filename = getGlobalLevelTemplateFilename();
3369 if (!(file = openFile(filename, MODE_READ)))
3372 // default: for empty levels, use level template for custom elements
3373 level->use_custom_template = TRUE;
3375 level->no_valid_file = FALSE;
3378 getFileChunkBE(file, chunk_name, NULL);
3379 if (strEqual(chunk_name, "RND1"))
3381 getFile32BitBE(file); // not used
3383 getFileChunkBE(file, chunk_name, NULL);
3384 if (!strEqual(chunk_name, "CAVE"))
3386 level->no_valid_file = TRUE;
3388 Warn("unknown format of level file '%s'", filename);
3395 else // check for pre-2.0 file format with cookie string
3397 strcpy(cookie, chunk_name);
3398 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3400 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3401 cookie[strlen(cookie) - 1] = '\0';
3403 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3405 level->no_valid_file = TRUE;
3407 Warn("unknown format of level file '%s'", filename);
3414 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3416 level->no_valid_file = TRUE;
3418 Warn("unsupported version of level file '%s'", filename);
3425 // pre-2.0 level files have no game version, so use file version here
3426 level->game_version = level->file_version;
3429 if (level->file_version < FILE_VERSION_1_2)
3431 // level files from versions before 1.2.0 without chunk structure
3432 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3433 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3441 int (*loader)(File *, int, struct LevelInfo *);
3445 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3446 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3447 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3448 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3449 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3450 { "INFO", -1, LoadLevel_INFO },
3451 { "BODY", -1, LoadLevel_BODY },
3452 { "CONT", -1, LoadLevel_CONT },
3453 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3454 { "CNT3", -1, LoadLevel_CNT3 },
3455 { "CUS1", -1, LoadLevel_CUS1 },
3456 { "CUS2", -1, LoadLevel_CUS2 },
3457 { "CUS3", -1, LoadLevel_CUS3 },
3458 { "CUS4", -1, LoadLevel_CUS4 },
3459 { "GRP1", -1, LoadLevel_GRP1 },
3460 { "CONF", -1, LoadLevel_CONF },
3461 { "ELEM", -1, LoadLevel_ELEM },
3462 { "NOTE", -1, LoadLevel_NOTE },
3463 { "CUSX", -1, LoadLevel_CUSX },
3464 { "GRPX", -1, LoadLevel_GRPX },
3469 while (getFileChunkBE(file, chunk_name, &chunk_size))
3473 while (chunk_info[i].name != NULL &&
3474 !strEqual(chunk_name, chunk_info[i].name))
3477 if (chunk_info[i].name == NULL)
3479 Warn("unknown chunk '%s' in level file '%s'",
3480 chunk_name, filename);
3482 ReadUnusedBytesFromFile(file, chunk_size);
3484 else if (chunk_info[i].size != -1 &&
3485 chunk_info[i].size != chunk_size)
3487 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3488 chunk_size, chunk_name, filename);
3490 ReadUnusedBytesFromFile(file, chunk_size);
3494 // call function to load this level chunk
3495 int chunk_size_expected =
3496 (chunk_info[i].loader)(file, chunk_size, level);
3498 // the size of some chunks cannot be checked before reading other
3499 // chunks first (like "HEAD" and "BODY") that contain some header
3500 // information, so check them here
3501 if (chunk_size_expected != chunk_size)
3503 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3504 chunk_size, chunk_name, filename);
3514 // ----------------------------------------------------------------------------
3515 // functions for loading EM level
3516 // ----------------------------------------------------------------------------
3518 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3520 static int ball_xy[8][2] =
3531 struct LevelInfo_EM *level_em = level->native_em_level;
3532 struct CAVE *cav = level_em->cav;
3535 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3536 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3538 cav->time_seconds = level->time;
3539 cav->gems_needed = level->gems_needed;
3541 cav->emerald_score = level->score[SC_EMERALD];
3542 cav->diamond_score = level->score[SC_DIAMOND];
3543 cav->alien_score = level->score[SC_ROBOT];
3544 cav->tank_score = level->score[SC_SPACESHIP];
3545 cav->bug_score = level->score[SC_BUG];
3546 cav->eater_score = level->score[SC_YAMYAM];
3547 cav->nut_score = level->score[SC_NUT];
3548 cav->dynamite_score = level->score[SC_DYNAMITE];
3549 cav->key_score = level->score[SC_KEY];
3550 cav->exit_score = level->score[SC_TIME_BONUS];
3552 cav->num_eater_arrays = level->num_yamyam_contents;
3554 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3555 for (y = 0; y < 3; y++)
3556 for (x = 0; x < 3; x++)
3557 cav->eater_array[i][y * 3 + x] =
3558 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3560 cav->amoeba_time = level->amoeba_speed;
3561 cav->wonderwall_time = level->time_magic_wall;
3562 cav->wheel_time = level->time_wheel;
3564 cav->android_move_time = level->android_move_time;
3565 cav->android_clone_time = level->android_clone_time;
3566 cav->ball_random = level->ball_random;
3567 cav->ball_active = level->ball_active_initial;
3568 cav->ball_time = level->ball_time;
3569 cav->num_ball_arrays = level->num_ball_contents;
3571 cav->lenses_score = level->lenses_score;
3572 cav->magnify_score = level->magnify_score;
3573 cav->slurp_score = level->slurp_score;
3575 cav->lenses_time = level->lenses_time;
3576 cav->magnify_time = level->magnify_time;
3578 cav->wind_direction =
3579 map_direction_RND_to_EM(level->wind_direction_initial);
3581 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3582 for (j = 0; j < 8; j++)
3583 cav->ball_array[i][j] =
3584 map_element_RND_to_EM_cave(level->ball_content[i].
3585 e[ball_xy[j][0]][ball_xy[j][1]]);
3587 map_android_clone_elements_RND_to_EM(level);
3589 // first fill the complete playfield with the empty space element
3590 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3591 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3592 cav->cave[x][y] = Cblank;
3594 // then copy the real level contents from level file into the playfield
3595 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3597 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3599 if (level->field[x][y] == EL_AMOEBA_DEAD)
3600 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3602 cav->cave[x][y] = new_element;
3605 for (i = 0; i < MAX_PLAYERS; i++)
3607 cav->player_x[i] = -1;
3608 cav->player_y[i] = -1;
3611 // initialize player positions and delete players from the playfield
3612 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3614 if (ELEM_IS_PLAYER(level->field[x][y]))
3616 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3618 cav->player_x[player_nr] = x;
3619 cav->player_y[player_nr] = y;
3621 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3626 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3628 static int ball_xy[8][2] =
3639 struct LevelInfo_EM *level_em = level->native_em_level;
3640 struct CAVE *cav = level_em->cav;
3643 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3644 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3646 level->time = cav->time_seconds;
3647 level->gems_needed = cav->gems_needed;
3649 sprintf(level->name, "Level %d", level->file_info.nr);
3651 level->score[SC_EMERALD] = cav->emerald_score;
3652 level->score[SC_DIAMOND] = cav->diamond_score;
3653 level->score[SC_ROBOT] = cav->alien_score;
3654 level->score[SC_SPACESHIP] = cav->tank_score;
3655 level->score[SC_BUG] = cav->bug_score;
3656 level->score[SC_YAMYAM] = cav->eater_score;
3657 level->score[SC_NUT] = cav->nut_score;
3658 level->score[SC_DYNAMITE] = cav->dynamite_score;
3659 level->score[SC_KEY] = cav->key_score;
3660 level->score[SC_TIME_BONUS] = cav->exit_score;
3662 level->num_yamyam_contents = cav->num_eater_arrays;
3664 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3665 for (y = 0; y < 3; y++)
3666 for (x = 0; x < 3; x++)
3667 level->yamyam_content[i].e[x][y] =
3668 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3670 level->amoeba_speed = cav->amoeba_time;
3671 level->time_magic_wall = cav->wonderwall_time;
3672 level->time_wheel = cav->wheel_time;
3674 level->android_move_time = cav->android_move_time;
3675 level->android_clone_time = cav->android_clone_time;
3676 level->ball_random = cav->ball_random;
3677 level->ball_active_initial = cav->ball_active;
3678 level->ball_time = cav->ball_time;
3679 level->num_ball_contents = cav->num_ball_arrays;
3681 level->lenses_score = cav->lenses_score;
3682 level->magnify_score = cav->magnify_score;
3683 level->slurp_score = cav->slurp_score;
3685 level->lenses_time = cav->lenses_time;
3686 level->magnify_time = cav->magnify_time;
3688 level->wind_direction_initial =
3689 map_direction_EM_to_RND(cav->wind_direction);
3691 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3692 for (j = 0; j < 8; j++)
3693 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3694 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3696 map_android_clone_elements_EM_to_RND(level);
3698 // convert the playfield (some elements need special treatment)
3699 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3701 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3703 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3704 new_element = EL_AMOEBA_DEAD;
3706 level->field[x][y] = new_element;
3709 for (i = 0; i < MAX_PLAYERS; i++)
3711 // in case of all players set to the same field, use the first player
3712 int nr = MAX_PLAYERS - i - 1;
3713 int jx = cav->player_x[nr];
3714 int jy = cav->player_y[nr];
3716 if (jx != -1 && jy != -1)
3717 level->field[jx][jy] = EL_PLAYER_1 + nr;
3722 // ----------------------------------------------------------------------------
3723 // functions for loading SP level
3724 // ----------------------------------------------------------------------------
3726 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3728 struct LevelInfo_SP *level_sp = level->native_sp_level;
3729 LevelInfoType *header = &level_sp->header;
3732 level_sp->width = level->fieldx;
3733 level_sp->height = level->fieldy;
3735 for (x = 0; x < level->fieldx; x++)
3736 for (y = 0; y < level->fieldy; y++)
3737 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3739 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3741 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3742 header->LevelTitle[i] = level->name[i];
3743 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3745 header->InfotronsNeeded = level->gems_needed;
3747 header->SpecialPortCount = 0;
3749 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3751 boolean gravity_port_found = FALSE;
3752 boolean gravity_port_valid = FALSE;
3753 int gravity_port_flag;
3754 int gravity_port_base_element;
3755 int element = level->field[x][y];
3757 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3758 element <= EL_SP_GRAVITY_ON_PORT_UP)
3760 gravity_port_found = TRUE;
3761 gravity_port_valid = TRUE;
3762 gravity_port_flag = 1;
3763 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3765 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3766 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3768 gravity_port_found = TRUE;
3769 gravity_port_valid = TRUE;
3770 gravity_port_flag = 0;
3771 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3773 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3774 element <= EL_SP_GRAVITY_PORT_UP)
3776 // change R'n'D style gravity inverting special port to normal port
3777 // (there are no gravity inverting ports in native Supaplex engine)
3779 gravity_port_found = TRUE;
3780 gravity_port_valid = FALSE;
3781 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3784 if (gravity_port_found)
3786 if (gravity_port_valid &&
3787 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3789 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3791 port->PortLocation = (y * level->fieldx + x) * 2;
3792 port->Gravity = gravity_port_flag;
3794 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3796 header->SpecialPortCount++;
3800 // change special gravity port to normal port
3802 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3805 level_sp->playfield[x][y] = element - EL_SP_START;
3810 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3812 struct LevelInfo_SP *level_sp = level->native_sp_level;
3813 LevelInfoType *header = &level_sp->header;
3814 boolean num_invalid_elements = 0;
3817 level->fieldx = level_sp->width;
3818 level->fieldy = level_sp->height;
3820 for (x = 0; x < level->fieldx; x++)
3822 for (y = 0; y < level->fieldy; y++)
3824 int element_old = level_sp->playfield[x][y];
3825 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3827 if (element_new == EL_UNKNOWN)
3829 num_invalid_elements++;
3831 Debug("level:native:SP", "invalid element %d at position %d, %d",
3835 level->field[x][y] = element_new;
3839 if (num_invalid_elements > 0)
3840 Warn("found %d invalid elements%s", num_invalid_elements,
3841 (!options.debug ? " (use '--debug' for more details)" : ""));
3843 for (i = 0; i < MAX_PLAYERS; i++)
3844 level->initial_player_gravity[i] =
3845 (header->InitialGravity == 1 ? TRUE : FALSE);
3847 // skip leading spaces
3848 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3849 if (header->LevelTitle[i] != ' ')
3853 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3854 level->name[j] = header->LevelTitle[i];
3855 level->name[j] = '\0';
3857 // cut trailing spaces
3859 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3860 level->name[j - 1] = '\0';
3862 level->gems_needed = header->InfotronsNeeded;
3864 for (i = 0; i < header->SpecialPortCount; i++)
3866 SpecialPortType *port = &header->SpecialPort[i];
3867 int port_location = port->PortLocation;
3868 int gravity = port->Gravity;
3869 int port_x, port_y, port_element;
3871 port_x = (port_location / 2) % level->fieldx;
3872 port_y = (port_location / 2) / level->fieldx;
3874 if (port_x < 0 || port_x >= level->fieldx ||
3875 port_y < 0 || port_y >= level->fieldy)
3877 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3882 port_element = level->field[port_x][port_y];
3884 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3885 port_element > EL_SP_GRAVITY_PORT_UP)
3887 Warn("no special port at position (%d, %d)", port_x, port_y);
3892 // change previous (wrong) gravity inverting special port to either
3893 // gravity enabling special port or gravity disabling special port
3894 level->field[port_x][port_y] +=
3895 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3896 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3899 // change special gravity ports without database entries to normal ports
3900 for (x = 0; x < level->fieldx; x++)
3901 for (y = 0; y < level->fieldy; y++)
3902 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3903 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3904 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3906 level->time = 0; // no time limit
3907 level->amoeba_speed = 0;
3908 level->time_magic_wall = 0;
3909 level->time_wheel = 0;
3910 level->amoeba_content = EL_EMPTY;
3913 // original Supaplex does not use score values -- use default values
3915 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3916 level->score[i] = 0;
3919 // there are no yamyams in supaplex levels
3920 for (i = 0; i < level->num_yamyam_contents; i++)
3921 for (x = 0; x < 3; x++)
3922 for (y = 0; y < 3; y++)
3923 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3926 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3928 struct LevelInfo_SP *level_sp = level->native_sp_level;
3929 struct DemoInfo_SP *demo = &level_sp->demo;
3932 // always start with reliable default values
3933 demo->is_available = FALSE;
3936 if (TAPE_IS_EMPTY(tape))
3939 demo->level_nr = tape.level_nr; // (currently not used)
3941 level_sp->header.DemoRandomSeed = tape.random_seed;
3945 for (i = 0; i < tape.length; i++)
3947 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3948 int demo_repeat = tape.pos[i].delay;
3949 int demo_entries = (demo_repeat + 15) / 16;
3951 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3953 Warn("tape truncated: size exceeds maximum SP demo size %d",
3959 for (j = 0; j < demo_repeat / 16; j++)
3960 demo->data[demo->length++] = 0xf0 | demo_action;
3962 if (demo_repeat % 16)
3963 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3966 demo->is_available = TRUE;
3969 static void setTapeInfoToDefaults(void);
3971 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3973 struct LevelInfo_SP *level_sp = level->native_sp_level;
3974 struct DemoInfo_SP *demo = &level_sp->demo;
3975 char *filename = level->file_info.filename;
3978 // always start with reliable default values
3979 setTapeInfoToDefaults();
3981 if (!demo->is_available)
3984 tape.level_nr = demo->level_nr; // (currently not used)
3985 tape.random_seed = level_sp->header.DemoRandomSeed;
3987 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3990 tape.pos[tape.counter].delay = 0;
3992 for (i = 0; i < demo->length; i++)
3994 int demo_action = demo->data[i] & 0x0f;
3995 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3996 int tape_action = map_key_SP_to_RND(demo_action);
3997 int tape_repeat = demo_repeat + 1;
3998 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3999 boolean success = 0;
4002 for (j = 0; j < tape_repeat; j++)
4003 success = TapeAddAction(action);
4007 Warn("SP demo truncated: size exceeds maximum tape size %d",
4014 TapeHaltRecording();
4018 // ----------------------------------------------------------------------------
4019 // functions for loading MM level
4020 // ----------------------------------------------------------------------------
4022 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4024 struct LevelInfo_MM *level_mm = level->native_mm_level;
4027 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4028 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4030 level_mm->time = level->time;
4031 level_mm->kettles_needed = level->gems_needed;
4032 level_mm->auto_count_kettles = level->auto_count_gems;
4034 level_mm->laser_red = level->mm_laser_red;
4035 level_mm->laser_green = level->mm_laser_green;
4036 level_mm->laser_blue = level->mm_laser_blue;
4038 strcpy(level_mm->name, level->name);
4039 strcpy(level_mm->author, level->author);
4041 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4042 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4043 level_mm->score[SC_KEY] = level->score[SC_KEY];
4044 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4045 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4047 level_mm->amoeba_speed = level->amoeba_speed;
4048 level_mm->time_fuse = level->mm_time_fuse;
4049 level_mm->time_bomb = level->mm_time_bomb;
4050 level_mm->time_ball = level->mm_time_ball;
4051 level_mm->time_block = level->mm_time_block;
4053 for (x = 0; x < level->fieldx; x++)
4054 for (y = 0; y < level->fieldy; y++)
4056 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4059 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4061 struct LevelInfo_MM *level_mm = level->native_mm_level;
4064 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4065 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4067 level->time = level_mm->time;
4068 level->gems_needed = level_mm->kettles_needed;
4069 level->auto_count_gems = level_mm->auto_count_kettles;
4071 level->mm_laser_red = level_mm->laser_red;
4072 level->mm_laser_green = level_mm->laser_green;
4073 level->mm_laser_blue = level_mm->laser_blue;
4075 strcpy(level->name, level_mm->name);
4077 // only overwrite author from 'levelinfo.conf' if author defined in level
4078 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4079 strcpy(level->author, level_mm->author);
4081 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4082 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4083 level->score[SC_KEY] = level_mm->score[SC_KEY];
4084 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4085 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4087 level->amoeba_speed = level_mm->amoeba_speed;
4088 level->mm_time_fuse = level_mm->time_fuse;
4089 level->mm_time_bomb = level_mm->time_bomb;
4090 level->mm_time_ball = level_mm->time_ball;
4091 level->mm_time_block = level_mm->time_block;
4093 for (x = 0; x < level->fieldx; x++)
4094 for (y = 0; y < level->fieldy; y++)
4095 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4099 // ----------------------------------------------------------------------------
4100 // functions for loading DC level
4101 // ----------------------------------------------------------------------------
4103 #define DC_LEVEL_HEADER_SIZE 344
4105 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4108 static int last_data_encoded;
4112 int diff_hi, diff_lo;
4113 int data_hi, data_lo;
4114 unsigned short data_decoded;
4118 last_data_encoded = 0;
4125 diff = data_encoded - last_data_encoded;
4126 diff_hi = diff & ~0xff;
4127 diff_lo = diff & 0xff;
4131 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4132 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4133 data_hi = data_hi & 0xff00;
4135 data_decoded = data_hi | data_lo;
4137 last_data_encoded = data_encoded;
4139 offset1 = (offset1 + 1) % 31;
4140 offset2 = offset2 & 0xff;
4142 return data_decoded;
4145 static int getMappedElement_DC(int element)
4153 // 0x0117 - 0x036e: (?)
4156 // 0x042d - 0x0684: (?)
4172 element = EL_CRYSTAL;
4175 case 0x0e77: // quicksand (boulder)
4176 element = EL_QUICKSAND_FAST_FULL;
4179 case 0x0e99: // slow quicksand (boulder)
4180 element = EL_QUICKSAND_FULL;
4184 element = EL_EM_EXIT_OPEN;
4188 element = EL_EM_EXIT_CLOSED;
4192 element = EL_EM_STEEL_EXIT_OPEN;
4196 element = EL_EM_STEEL_EXIT_CLOSED;
4199 case 0x0f4f: // dynamite (lit 1)
4200 element = EL_EM_DYNAMITE_ACTIVE;
4203 case 0x0f57: // dynamite (lit 2)
4204 element = EL_EM_DYNAMITE_ACTIVE;
4207 case 0x0f5f: // dynamite (lit 3)
4208 element = EL_EM_DYNAMITE_ACTIVE;
4211 case 0x0f67: // dynamite (lit 4)
4212 element = EL_EM_DYNAMITE_ACTIVE;
4219 element = EL_AMOEBA_WET;
4223 element = EL_AMOEBA_DROP;
4227 element = EL_DC_MAGIC_WALL;
4231 element = EL_SPACESHIP_UP;
4235 element = EL_SPACESHIP_DOWN;
4239 element = EL_SPACESHIP_LEFT;
4243 element = EL_SPACESHIP_RIGHT;
4247 element = EL_BUG_UP;
4251 element = EL_BUG_DOWN;
4255 element = EL_BUG_LEFT;
4259 element = EL_BUG_RIGHT;
4263 element = EL_MOLE_UP;
4267 element = EL_MOLE_DOWN;
4271 element = EL_MOLE_LEFT;
4275 element = EL_MOLE_RIGHT;
4283 element = EL_YAMYAM_UP;
4287 element = EL_SWITCHGATE_OPEN;
4291 element = EL_SWITCHGATE_CLOSED;
4295 element = EL_DC_SWITCHGATE_SWITCH_UP;
4299 element = EL_TIMEGATE_CLOSED;
4302 case 0x144c: // conveyor belt switch (green)
4303 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4306 case 0x144f: // conveyor belt switch (red)
4307 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4310 case 0x1452: // conveyor belt switch (blue)
4311 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4315 element = EL_CONVEYOR_BELT_3_MIDDLE;
4319 element = EL_CONVEYOR_BELT_3_LEFT;
4323 element = EL_CONVEYOR_BELT_3_RIGHT;
4327 element = EL_CONVEYOR_BELT_1_MIDDLE;
4331 element = EL_CONVEYOR_BELT_1_LEFT;
4335 element = EL_CONVEYOR_BELT_1_RIGHT;
4339 element = EL_CONVEYOR_BELT_4_MIDDLE;
4343 element = EL_CONVEYOR_BELT_4_LEFT;
4347 element = EL_CONVEYOR_BELT_4_RIGHT;
4351 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4355 element = EL_EXPANDABLE_WALL_VERTICAL;
4359 element = EL_EXPANDABLE_WALL_ANY;
4362 case 0x14ce: // growing steel wall (left/right)
4363 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4366 case 0x14df: // growing steel wall (up/down)
4367 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4370 case 0x14e8: // growing steel wall (up/down/left/right)
4371 element = EL_EXPANDABLE_STEELWALL_ANY;
4375 element = EL_SHIELD_DEADLY;
4379 element = EL_EXTRA_TIME;
4387 element = EL_EMPTY_SPACE;
4390 case 0x1578: // quicksand (empty)
4391 element = EL_QUICKSAND_FAST_EMPTY;
4394 case 0x1579: // slow quicksand (empty)
4395 element = EL_QUICKSAND_EMPTY;
4405 element = EL_EM_DYNAMITE;
4408 case 0x15a1: // key (red)
4409 element = EL_EM_KEY_1;
4412 case 0x15a2: // key (yellow)
4413 element = EL_EM_KEY_2;
4416 case 0x15a3: // key (blue)
4417 element = EL_EM_KEY_4;
4420 case 0x15a4: // key (green)
4421 element = EL_EM_KEY_3;
4424 case 0x15a5: // key (white)
4425 element = EL_DC_KEY_WHITE;
4429 element = EL_WALL_SLIPPERY;
4436 case 0x15a8: // wall (not round)
4440 case 0x15a9: // (blue)
4441 element = EL_CHAR_A;
4444 case 0x15aa: // (blue)
4445 element = EL_CHAR_B;
4448 case 0x15ab: // (blue)
4449 element = EL_CHAR_C;
4452 case 0x15ac: // (blue)
4453 element = EL_CHAR_D;
4456 case 0x15ad: // (blue)
4457 element = EL_CHAR_E;
4460 case 0x15ae: // (blue)
4461 element = EL_CHAR_F;
4464 case 0x15af: // (blue)
4465 element = EL_CHAR_G;
4468 case 0x15b0: // (blue)
4469 element = EL_CHAR_H;
4472 case 0x15b1: // (blue)
4473 element = EL_CHAR_I;
4476 case 0x15b2: // (blue)
4477 element = EL_CHAR_J;
4480 case 0x15b3: // (blue)
4481 element = EL_CHAR_K;
4484 case 0x15b4: // (blue)
4485 element = EL_CHAR_L;
4488 case 0x15b5: // (blue)
4489 element = EL_CHAR_M;
4492 case 0x15b6: // (blue)
4493 element = EL_CHAR_N;
4496 case 0x15b7: // (blue)
4497 element = EL_CHAR_O;
4500 case 0x15b8: // (blue)
4501 element = EL_CHAR_P;
4504 case 0x15b9: // (blue)
4505 element = EL_CHAR_Q;
4508 case 0x15ba: // (blue)
4509 element = EL_CHAR_R;
4512 case 0x15bb: // (blue)
4513 element = EL_CHAR_S;
4516 case 0x15bc: // (blue)
4517 element = EL_CHAR_T;
4520 case 0x15bd: // (blue)
4521 element = EL_CHAR_U;
4524 case 0x15be: // (blue)
4525 element = EL_CHAR_V;
4528 case 0x15bf: // (blue)
4529 element = EL_CHAR_W;
4532 case 0x15c0: // (blue)
4533 element = EL_CHAR_X;
4536 case 0x15c1: // (blue)
4537 element = EL_CHAR_Y;
4540 case 0x15c2: // (blue)
4541 element = EL_CHAR_Z;
4544 case 0x15c3: // (blue)
4545 element = EL_CHAR_AUMLAUT;
4548 case 0x15c4: // (blue)
4549 element = EL_CHAR_OUMLAUT;
4552 case 0x15c5: // (blue)
4553 element = EL_CHAR_UUMLAUT;
4556 case 0x15c6: // (blue)
4557 element = EL_CHAR_0;
4560 case 0x15c7: // (blue)
4561 element = EL_CHAR_1;
4564 case 0x15c8: // (blue)
4565 element = EL_CHAR_2;
4568 case 0x15c9: // (blue)
4569 element = EL_CHAR_3;
4572 case 0x15ca: // (blue)
4573 element = EL_CHAR_4;
4576 case 0x15cb: // (blue)
4577 element = EL_CHAR_5;
4580 case 0x15cc: // (blue)
4581 element = EL_CHAR_6;
4584 case 0x15cd: // (blue)
4585 element = EL_CHAR_7;
4588 case 0x15ce: // (blue)
4589 element = EL_CHAR_8;
4592 case 0x15cf: // (blue)
4593 element = EL_CHAR_9;
4596 case 0x15d0: // (blue)
4597 element = EL_CHAR_PERIOD;
4600 case 0x15d1: // (blue)
4601 element = EL_CHAR_EXCLAM;
4604 case 0x15d2: // (blue)
4605 element = EL_CHAR_COLON;
4608 case 0x15d3: // (blue)
4609 element = EL_CHAR_LESS;
4612 case 0x15d4: // (blue)
4613 element = EL_CHAR_GREATER;
4616 case 0x15d5: // (blue)
4617 element = EL_CHAR_QUESTION;
4620 case 0x15d6: // (blue)
4621 element = EL_CHAR_COPYRIGHT;
4624 case 0x15d7: // (blue)
4625 element = EL_CHAR_UP;
4628 case 0x15d8: // (blue)
4629 element = EL_CHAR_DOWN;
4632 case 0x15d9: // (blue)
4633 element = EL_CHAR_BUTTON;
4636 case 0x15da: // (blue)
4637 element = EL_CHAR_PLUS;
4640 case 0x15db: // (blue)
4641 element = EL_CHAR_MINUS;
4644 case 0x15dc: // (blue)
4645 element = EL_CHAR_APOSTROPHE;
4648 case 0x15dd: // (blue)
4649 element = EL_CHAR_PARENLEFT;
4652 case 0x15de: // (blue)
4653 element = EL_CHAR_PARENRIGHT;
4656 case 0x15df: // (green)
4657 element = EL_CHAR_A;
4660 case 0x15e0: // (green)
4661 element = EL_CHAR_B;
4664 case 0x15e1: // (green)
4665 element = EL_CHAR_C;
4668 case 0x15e2: // (green)
4669 element = EL_CHAR_D;
4672 case 0x15e3: // (green)
4673 element = EL_CHAR_E;
4676 case 0x15e4: // (green)
4677 element = EL_CHAR_F;
4680 case 0x15e5: // (green)
4681 element = EL_CHAR_G;
4684 case 0x15e6: // (green)
4685 element = EL_CHAR_H;
4688 case 0x15e7: // (green)
4689 element = EL_CHAR_I;
4692 case 0x15e8: // (green)
4693 element = EL_CHAR_J;
4696 case 0x15e9: // (green)
4697 element = EL_CHAR_K;
4700 case 0x15ea: // (green)
4701 element = EL_CHAR_L;
4704 case 0x15eb: // (green)
4705 element = EL_CHAR_M;
4708 case 0x15ec: // (green)
4709 element = EL_CHAR_N;
4712 case 0x15ed: // (green)
4713 element = EL_CHAR_O;
4716 case 0x15ee: // (green)
4717 element = EL_CHAR_P;
4720 case 0x15ef: // (green)
4721 element = EL_CHAR_Q;
4724 case 0x15f0: // (green)
4725 element = EL_CHAR_R;
4728 case 0x15f1: // (green)
4729 element = EL_CHAR_S;
4732 case 0x15f2: // (green)
4733 element = EL_CHAR_T;
4736 case 0x15f3: // (green)
4737 element = EL_CHAR_U;
4740 case 0x15f4: // (green)
4741 element = EL_CHAR_V;
4744 case 0x15f5: // (green)
4745 element = EL_CHAR_W;
4748 case 0x15f6: // (green)
4749 element = EL_CHAR_X;
4752 case 0x15f7: // (green)
4753 element = EL_CHAR_Y;
4756 case 0x15f8: // (green)
4757 element = EL_CHAR_Z;
4760 case 0x15f9: // (green)
4761 element = EL_CHAR_AUMLAUT;
4764 case 0x15fa: // (green)
4765 element = EL_CHAR_OUMLAUT;
4768 case 0x15fb: // (green)
4769 element = EL_CHAR_UUMLAUT;
4772 case 0x15fc: // (green)
4773 element = EL_CHAR_0;
4776 case 0x15fd: // (green)
4777 element = EL_CHAR_1;
4780 case 0x15fe: // (green)
4781 element = EL_CHAR_2;
4784 case 0x15ff: // (green)
4785 element = EL_CHAR_3;
4788 case 0x1600: // (green)
4789 element = EL_CHAR_4;
4792 case 0x1601: // (green)
4793 element = EL_CHAR_5;
4796 case 0x1602: // (green)
4797 element = EL_CHAR_6;
4800 case 0x1603: // (green)
4801 element = EL_CHAR_7;
4804 case 0x1604: // (green)
4805 element = EL_CHAR_8;
4808 case 0x1605: // (green)
4809 element = EL_CHAR_9;
4812 case 0x1606: // (green)
4813 element = EL_CHAR_PERIOD;
4816 case 0x1607: // (green)
4817 element = EL_CHAR_EXCLAM;
4820 case 0x1608: // (green)
4821 element = EL_CHAR_COLON;
4824 case 0x1609: // (green)
4825 element = EL_CHAR_LESS;
4828 case 0x160a: // (green)
4829 element = EL_CHAR_GREATER;
4832 case 0x160b: // (green)
4833 element = EL_CHAR_QUESTION;
4836 case 0x160c: // (green)
4837 element = EL_CHAR_COPYRIGHT;
4840 case 0x160d: // (green)
4841 element = EL_CHAR_UP;
4844 case 0x160e: // (green)
4845 element = EL_CHAR_DOWN;
4848 case 0x160f: // (green)
4849 element = EL_CHAR_BUTTON;
4852 case 0x1610: // (green)
4853 element = EL_CHAR_PLUS;
4856 case 0x1611: // (green)
4857 element = EL_CHAR_MINUS;
4860 case 0x1612: // (green)
4861 element = EL_CHAR_APOSTROPHE;
4864 case 0x1613: // (green)
4865 element = EL_CHAR_PARENLEFT;
4868 case 0x1614: // (green)
4869 element = EL_CHAR_PARENRIGHT;
4872 case 0x1615: // (blue steel)
4873 element = EL_STEEL_CHAR_A;
4876 case 0x1616: // (blue steel)
4877 element = EL_STEEL_CHAR_B;
4880 case 0x1617: // (blue steel)
4881 element = EL_STEEL_CHAR_C;
4884 case 0x1618: // (blue steel)
4885 element = EL_STEEL_CHAR_D;
4888 case 0x1619: // (blue steel)
4889 element = EL_STEEL_CHAR_E;
4892 case 0x161a: // (blue steel)
4893 element = EL_STEEL_CHAR_F;
4896 case 0x161b: // (blue steel)
4897 element = EL_STEEL_CHAR_G;
4900 case 0x161c: // (blue steel)
4901 element = EL_STEEL_CHAR_H;
4904 case 0x161d: // (blue steel)
4905 element = EL_STEEL_CHAR_I;
4908 case 0x161e: // (blue steel)
4909 element = EL_STEEL_CHAR_J;
4912 case 0x161f: // (blue steel)
4913 element = EL_STEEL_CHAR_K;
4916 case 0x1620: // (blue steel)
4917 element = EL_STEEL_CHAR_L;
4920 case 0x1621: // (blue steel)
4921 element = EL_STEEL_CHAR_M;
4924 case 0x1622: // (blue steel)
4925 element = EL_STEEL_CHAR_N;
4928 case 0x1623: // (blue steel)
4929 element = EL_STEEL_CHAR_O;
4932 case 0x1624: // (blue steel)
4933 element = EL_STEEL_CHAR_P;
4936 case 0x1625: // (blue steel)
4937 element = EL_STEEL_CHAR_Q;
4940 case 0x1626: // (blue steel)
4941 element = EL_STEEL_CHAR_R;
4944 case 0x1627: // (blue steel)
4945 element = EL_STEEL_CHAR_S;
4948 case 0x1628: // (blue steel)
4949 element = EL_STEEL_CHAR_T;
4952 case 0x1629: // (blue steel)
4953 element = EL_STEEL_CHAR_U;
4956 case 0x162a: // (blue steel)
4957 element = EL_STEEL_CHAR_V;
4960 case 0x162b: // (blue steel)
4961 element = EL_STEEL_CHAR_W;
4964 case 0x162c: // (blue steel)
4965 element = EL_STEEL_CHAR_X;
4968 case 0x162d: // (blue steel)
4969 element = EL_STEEL_CHAR_Y;
4972 case 0x162e: // (blue steel)
4973 element = EL_STEEL_CHAR_Z;
4976 case 0x162f: // (blue steel)
4977 element = EL_STEEL_CHAR_AUMLAUT;
4980 case 0x1630: // (blue steel)
4981 element = EL_STEEL_CHAR_OUMLAUT;
4984 case 0x1631: // (blue steel)
4985 element = EL_STEEL_CHAR_UUMLAUT;
4988 case 0x1632: // (blue steel)
4989 element = EL_STEEL_CHAR_0;
4992 case 0x1633: // (blue steel)
4993 element = EL_STEEL_CHAR_1;
4996 case 0x1634: // (blue steel)
4997 element = EL_STEEL_CHAR_2;
5000 case 0x1635: // (blue steel)
5001 element = EL_STEEL_CHAR_3;
5004 case 0x1636: // (blue steel)
5005 element = EL_STEEL_CHAR_4;
5008 case 0x1637: // (blue steel)
5009 element = EL_STEEL_CHAR_5;
5012 case 0x1638: // (blue steel)
5013 element = EL_STEEL_CHAR_6;
5016 case 0x1639: // (blue steel)
5017 element = EL_STEEL_CHAR_7;
5020 case 0x163a: // (blue steel)
5021 element = EL_STEEL_CHAR_8;
5024 case 0x163b: // (blue steel)
5025 element = EL_STEEL_CHAR_9;
5028 case 0x163c: // (blue steel)
5029 element = EL_STEEL_CHAR_PERIOD;
5032 case 0x163d: // (blue steel)
5033 element = EL_STEEL_CHAR_EXCLAM;
5036 case 0x163e: // (blue steel)
5037 element = EL_STEEL_CHAR_COLON;
5040 case 0x163f: // (blue steel)
5041 element = EL_STEEL_CHAR_LESS;
5044 case 0x1640: // (blue steel)
5045 element = EL_STEEL_CHAR_GREATER;
5048 case 0x1641: // (blue steel)
5049 element = EL_STEEL_CHAR_QUESTION;
5052 case 0x1642: // (blue steel)
5053 element = EL_STEEL_CHAR_COPYRIGHT;
5056 case 0x1643: // (blue steel)
5057 element = EL_STEEL_CHAR_UP;
5060 case 0x1644: // (blue steel)
5061 element = EL_STEEL_CHAR_DOWN;
5064 case 0x1645: // (blue steel)
5065 element = EL_STEEL_CHAR_BUTTON;
5068 case 0x1646: // (blue steel)
5069 element = EL_STEEL_CHAR_PLUS;
5072 case 0x1647: // (blue steel)
5073 element = EL_STEEL_CHAR_MINUS;
5076 case 0x1648: // (blue steel)
5077 element = EL_STEEL_CHAR_APOSTROPHE;
5080 case 0x1649: // (blue steel)
5081 element = EL_STEEL_CHAR_PARENLEFT;
5084 case 0x164a: // (blue steel)
5085 element = EL_STEEL_CHAR_PARENRIGHT;
5088 case 0x164b: // (green steel)
5089 element = EL_STEEL_CHAR_A;
5092 case 0x164c: // (green steel)
5093 element = EL_STEEL_CHAR_B;
5096 case 0x164d: // (green steel)
5097 element = EL_STEEL_CHAR_C;
5100 case 0x164e: // (green steel)
5101 element = EL_STEEL_CHAR_D;
5104 case 0x164f: // (green steel)
5105 element = EL_STEEL_CHAR_E;
5108 case 0x1650: // (green steel)
5109 element = EL_STEEL_CHAR_F;
5112 case 0x1651: // (green steel)
5113 element = EL_STEEL_CHAR_G;
5116 case 0x1652: // (green steel)
5117 element = EL_STEEL_CHAR_H;
5120 case 0x1653: // (green steel)
5121 element = EL_STEEL_CHAR_I;
5124 case 0x1654: // (green steel)
5125 element = EL_STEEL_CHAR_J;
5128 case 0x1655: // (green steel)
5129 element = EL_STEEL_CHAR_K;
5132 case 0x1656: // (green steel)
5133 element = EL_STEEL_CHAR_L;
5136 case 0x1657: // (green steel)
5137 element = EL_STEEL_CHAR_M;
5140 case 0x1658: // (green steel)
5141 element = EL_STEEL_CHAR_N;
5144 case 0x1659: // (green steel)
5145 element = EL_STEEL_CHAR_O;
5148 case 0x165a: // (green steel)
5149 element = EL_STEEL_CHAR_P;
5152 case 0x165b: // (green steel)
5153 element = EL_STEEL_CHAR_Q;
5156 case 0x165c: // (green steel)
5157 element = EL_STEEL_CHAR_R;
5160 case 0x165d: // (green steel)
5161 element = EL_STEEL_CHAR_S;
5164 case 0x165e: // (green steel)
5165 element = EL_STEEL_CHAR_T;
5168 case 0x165f: // (green steel)
5169 element = EL_STEEL_CHAR_U;
5172 case 0x1660: // (green steel)
5173 element = EL_STEEL_CHAR_V;
5176 case 0x1661: // (green steel)
5177 element = EL_STEEL_CHAR_W;
5180 case 0x1662: // (green steel)
5181 element = EL_STEEL_CHAR_X;
5184 case 0x1663: // (green steel)
5185 element = EL_STEEL_CHAR_Y;
5188 case 0x1664: // (green steel)
5189 element = EL_STEEL_CHAR_Z;
5192 case 0x1665: // (green steel)
5193 element = EL_STEEL_CHAR_AUMLAUT;
5196 case 0x1666: // (green steel)
5197 element = EL_STEEL_CHAR_OUMLAUT;
5200 case 0x1667: // (green steel)
5201 element = EL_STEEL_CHAR_UUMLAUT;
5204 case 0x1668: // (green steel)
5205 element = EL_STEEL_CHAR_0;
5208 case 0x1669: // (green steel)
5209 element = EL_STEEL_CHAR_1;
5212 case 0x166a: // (green steel)
5213 element = EL_STEEL_CHAR_2;
5216 case 0x166b: // (green steel)
5217 element = EL_STEEL_CHAR_3;
5220 case 0x166c: // (green steel)
5221 element = EL_STEEL_CHAR_4;
5224 case 0x166d: // (green steel)
5225 element = EL_STEEL_CHAR_5;
5228 case 0x166e: // (green steel)
5229 element = EL_STEEL_CHAR_6;
5232 case 0x166f: // (green steel)
5233 element = EL_STEEL_CHAR_7;
5236 case 0x1670: // (green steel)
5237 element = EL_STEEL_CHAR_8;
5240 case 0x1671: // (green steel)
5241 element = EL_STEEL_CHAR_9;
5244 case 0x1672: // (green steel)
5245 element = EL_STEEL_CHAR_PERIOD;
5248 case 0x1673: // (green steel)
5249 element = EL_STEEL_CHAR_EXCLAM;
5252 case 0x1674: // (green steel)
5253 element = EL_STEEL_CHAR_COLON;
5256 case 0x1675: // (green steel)
5257 element = EL_STEEL_CHAR_LESS;
5260 case 0x1676: // (green steel)
5261 element = EL_STEEL_CHAR_GREATER;
5264 case 0x1677: // (green steel)
5265 element = EL_STEEL_CHAR_QUESTION;
5268 case 0x1678: // (green steel)
5269 element = EL_STEEL_CHAR_COPYRIGHT;
5272 case 0x1679: // (green steel)
5273 element = EL_STEEL_CHAR_UP;
5276 case 0x167a: // (green steel)
5277 element = EL_STEEL_CHAR_DOWN;
5280 case 0x167b: // (green steel)
5281 element = EL_STEEL_CHAR_BUTTON;
5284 case 0x167c: // (green steel)
5285 element = EL_STEEL_CHAR_PLUS;
5288 case 0x167d: // (green steel)
5289 element = EL_STEEL_CHAR_MINUS;
5292 case 0x167e: // (green steel)
5293 element = EL_STEEL_CHAR_APOSTROPHE;
5296 case 0x167f: // (green steel)
5297 element = EL_STEEL_CHAR_PARENLEFT;
5300 case 0x1680: // (green steel)
5301 element = EL_STEEL_CHAR_PARENRIGHT;
5304 case 0x1681: // gate (red)
5305 element = EL_EM_GATE_1;
5308 case 0x1682: // secret gate (red)
5309 element = EL_EM_GATE_1_GRAY;
5312 case 0x1683: // gate (yellow)
5313 element = EL_EM_GATE_2;
5316 case 0x1684: // secret gate (yellow)
5317 element = EL_EM_GATE_2_GRAY;
5320 case 0x1685: // gate (blue)
5321 element = EL_EM_GATE_4;
5324 case 0x1686: // secret gate (blue)
5325 element = EL_EM_GATE_4_GRAY;
5328 case 0x1687: // gate (green)
5329 element = EL_EM_GATE_3;
5332 case 0x1688: // secret gate (green)
5333 element = EL_EM_GATE_3_GRAY;
5336 case 0x1689: // gate (white)
5337 element = EL_DC_GATE_WHITE;
5340 case 0x168a: // secret gate (white)
5341 element = EL_DC_GATE_WHITE_GRAY;
5344 case 0x168b: // secret gate (no key)
5345 element = EL_DC_GATE_FAKE_GRAY;
5349 element = EL_ROBOT_WHEEL;
5353 element = EL_DC_TIMEGATE_SWITCH;
5357 element = EL_ACID_POOL_BOTTOM;
5361 element = EL_ACID_POOL_TOPLEFT;
5365 element = EL_ACID_POOL_TOPRIGHT;
5369 element = EL_ACID_POOL_BOTTOMLEFT;
5373 element = EL_ACID_POOL_BOTTOMRIGHT;
5377 element = EL_STEELWALL;
5381 element = EL_STEELWALL_SLIPPERY;
5384 case 0x1695: // steel wall (not round)
5385 element = EL_STEELWALL;
5388 case 0x1696: // steel wall (left)
5389 element = EL_DC_STEELWALL_1_LEFT;
5392 case 0x1697: // steel wall (bottom)
5393 element = EL_DC_STEELWALL_1_BOTTOM;
5396 case 0x1698: // steel wall (right)
5397 element = EL_DC_STEELWALL_1_RIGHT;
5400 case 0x1699: // steel wall (top)
5401 element = EL_DC_STEELWALL_1_TOP;
5404 case 0x169a: // steel wall (left/bottom)
5405 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5408 case 0x169b: // steel wall (right/bottom)
5409 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5412 case 0x169c: // steel wall (right/top)
5413 element = EL_DC_STEELWALL_1_TOPRIGHT;
5416 case 0x169d: // steel wall (left/top)
5417 element = EL_DC_STEELWALL_1_TOPLEFT;
5420 case 0x169e: // steel wall (right/bottom small)
5421 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5424 case 0x169f: // steel wall (left/bottom small)
5425 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5428 case 0x16a0: // steel wall (right/top small)
5429 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5432 case 0x16a1: // steel wall (left/top small)
5433 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5436 case 0x16a2: // steel wall (left/right)
5437 element = EL_DC_STEELWALL_1_VERTICAL;
5440 case 0x16a3: // steel wall (top/bottom)
5441 element = EL_DC_STEELWALL_1_HORIZONTAL;
5444 case 0x16a4: // steel wall 2 (left end)
5445 element = EL_DC_STEELWALL_2_LEFT;
5448 case 0x16a5: // steel wall 2 (right end)
5449 element = EL_DC_STEELWALL_2_RIGHT;
5452 case 0x16a6: // steel wall 2 (top end)
5453 element = EL_DC_STEELWALL_2_TOP;
5456 case 0x16a7: // steel wall 2 (bottom end)
5457 element = EL_DC_STEELWALL_2_BOTTOM;
5460 case 0x16a8: // steel wall 2 (left/right)
5461 element = EL_DC_STEELWALL_2_HORIZONTAL;
5464 case 0x16a9: // steel wall 2 (up/down)
5465 element = EL_DC_STEELWALL_2_VERTICAL;
5468 case 0x16aa: // steel wall 2 (mid)
5469 element = EL_DC_STEELWALL_2_MIDDLE;
5473 element = EL_SIGN_EXCLAMATION;
5477 element = EL_SIGN_RADIOACTIVITY;
5481 element = EL_SIGN_STOP;
5485 element = EL_SIGN_WHEELCHAIR;
5489 element = EL_SIGN_PARKING;
5493 element = EL_SIGN_NO_ENTRY;
5497 element = EL_SIGN_HEART;
5501 element = EL_SIGN_GIVE_WAY;
5505 element = EL_SIGN_ENTRY_FORBIDDEN;
5509 element = EL_SIGN_EMERGENCY_EXIT;
5513 element = EL_SIGN_YIN_YANG;
5517 element = EL_WALL_EMERALD;
5521 element = EL_WALL_DIAMOND;
5525 element = EL_WALL_PEARL;
5529 element = EL_WALL_CRYSTAL;
5533 element = EL_INVISIBLE_WALL;
5537 element = EL_INVISIBLE_STEELWALL;
5541 // EL_INVISIBLE_SAND
5544 element = EL_LIGHT_SWITCH;
5548 element = EL_ENVELOPE_1;
5552 if (element >= 0x0117 && element <= 0x036e) // (?)
5553 element = EL_DIAMOND;
5554 else if (element >= 0x042d && element <= 0x0684) // (?)
5555 element = EL_EMERALD;
5556 else if (element >= 0x157c && element <= 0x158b)
5558 else if (element >= 0x1590 && element <= 0x159f)
5559 element = EL_DC_LANDMINE;
5560 else if (element >= 0x16bc && element <= 0x16cb)
5561 element = EL_INVISIBLE_SAND;
5564 Warn("unknown Diamond Caves element 0x%04x", element);
5566 element = EL_UNKNOWN;
5571 return getMappedElement(element);
5574 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5577 byte header[DC_LEVEL_HEADER_SIZE];
5579 int envelope_header_pos = 62;
5580 int envelope_content_pos = 94;
5581 int level_name_pos = 251;
5582 int level_author_pos = 292;
5583 int envelope_header_len;
5584 int envelope_content_len;
5586 int level_author_len;
5588 int num_yamyam_contents;
5591 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5593 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5595 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5597 header[i * 2 + 0] = header_word >> 8;
5598 header[i * 2 + 1] = header_word & 0xff;
5601 // read some values from level header to check level decoding integrity
5602 fieldx = header[6] | (header[7] << 8);
5603 fieldy = header[8] | (header[9] << 8);
5604 num_yamyam_contents = header[60] | (header[61] << 8);
5606 // do some simple sanity checks to ensure that level was correctly decoded
5607 if (fieldx < 1 || fieldx > 256 ||
5608 fieldy < 1 || fieldy > 256 ||
5609 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5611 level->no_valid_file = TRUE;
5613 Warn("cannot decode level from stream -- using empty level");
5618 // maximum envelope header size is 31 bytes
5619 envelope_header_len = header[envelope_header_pos];
5620 // maximum envelope content size is 110 (156?) bytes
5621 envelope_content_len = header[envelope_content_pos];
5623 // maximum level title size is 40 bytes
5624 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5625 // maximum level author size is 30 (51?) bytes
5626 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5630 for (i = 0; i < envelope_header_len; i++)
5631 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5632 level->envelope[0].text[envelope_size++] =
5633 header[envelope_header_pos + 1 + i];
5635 if (envelope_header_len > 0 && envelope_content_len > 0)
5637 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5638 level->envelope[0].text[envelope_size++] = '\n';
5639 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5640 level->envelope[0].text[envelope_size++] = '\n';
5643 for (i = 0; i < envelope_content_len; i++)
5644 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5645 level->envelope[0].text[envelope_size++] =
5646 header[envelope_content_pos + 1 + i];
5648 level->envelope[0].text[envelope_size] = '\0';
5650 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5651 level->envelope[0].ysize = 10;
5652 level->envelope[0].autowrap = TRUE;
5653 level->envelope[0].centered = TRUE;
5655 for (i = 0; i < level_name_len; i++)
5656 level->name[i] = header[level_name_pos + 1 + i];
5657 level->name[level_name_len] = '\0';
5659 for (i = 0; i < level_author_len; i++)
5660 level->author[i] = header[level_author_pos + 1 + i];
5661 level->author[level_author_len] = '\0';
5663 num_yamyam_contents = header[60] | (header[61] << 8);
5664 level->num_yamyam_contents =
5665 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5667 for (i = 0; i < num_yamyam_contents; i++)
5669 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5671 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5672 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5674 if (i < MAX_ELEMENT_CONTENTS)
5675 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5679 fieldx = header[6] | (header[7] << 8);
5680 fieldy = header[8] | (header[9] << 8);
5681 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5682 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5684 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5686 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5687 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5689 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5690 level->field[x][y] = getMappedElement_DC(element_dc);
5693 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5694 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5695 level->field[x][y] = EL_PLAYER_1;
5697 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5698 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5699 level->field[x][y] = EL_PLAYER_2;
5701 level->gems_needed = header[18] | (header[19] << 8);
5703 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5704 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5705 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5706 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5707 level->score[SC_NUT] = header[28] | (header[29] << 8);
5708 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5709 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5710 level->score[SC_BUG] = header[34] | (header[35] << 8);
5711 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5712 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5713 level->score[SC_KEY] = header[40] | (header[41] << 8);
5714 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5716 level->time = header[44] | (header[45] << 8);
5718 level->amoeba_speed = header[46] | (header[47] << 8);
5719 level->time_light = header[48] | (header[49] << 8);
5720 level->time_timegate = header[50] | (header[51] << 8);
5721 level->time_wheel = header[52] | (header[53] << 8);
5722 level->time_magic_wall = header[54] | (header[55] << 8);
5723 level->extra_time = header[56] | (header[57] << 8);
5724 level->shield_normal_time = header[58] | (header[59] << 8);
5726 // shield and extra time elements do not have a score
5727 level->score[SC_SHIELD] = 0;
5728 level->extra_time_score = 0;
5730 // set time for normal and deadly shields to the same value
5731 level->shield_deadly_time = level->shield_normal_time;
5733 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5734 // can slip down from flat walls, like normal walls and steel walls
5735 level->em_slippery_gems = TRUE;
5738 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5739 struct LevelFileInfo *level_file_info,
5740 boolean level_info_only)
5742 char *filename = level_file_info->filename;
5744 int num_magic_bytes = 8;
5745 char magic_bytes[num_magic_bytes + 1];
5746 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5748 if (!(file = openFile(filename, MODE_READ)))
5750 level->no_valid_file = TRUE;
5752 if (!level_info_only)
5753 Warn("cannot read level '%s' -- using empty level", filename);
5758 // fseek(file, 0x0000, SEEK_SET);
5760 if (level_file_info->packed)
5762 // read "magic bytes" from start of file
5763 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5764 magic_bytes[0] = '\0';
5766 // check "magic bytes" for correct file format
5767 if (!strPrefix(magic_bytes, "DC2"))
5769 level->no_valid_file = TRUE;
5771 Warn("unknown DC level file '%s' -- using empty level", filename);
5776 if (strPrefix(magic_bytes, "DC2Win95") ||
5777 strPrefix(magic_bytes, "DC2Win98"))
5779 int position_first_level = 0x00fa;
5780 int extra_bytes = 4;
5783 // advance file stream to first level inside the level package
5784 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5786 // each block of level data is followed by block of non-level data
5787 num_levels_to_skip *= 2;
5789 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5790 while (num_levels_to_skip >= 0)
5792 // advance file stream to next level inside the level package
5793 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5795 level->no_valid_file = TRUE;
5797 Warn("cannot fseek in file '%s' -- using empty level", filename);
5802 // skip apparently unused extra bytes following each level
5803 ReadUnusedBytesFromFile(file, extra_bytes);
5805 // read size of next level in level package
5806 skip_bytes = getFile32BitLE(file);
5808 num_levels_to_skip--;
5813 level->no_valid_file = TRUE;
5815 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5821 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5827 // ----------------------------------------------------------------------------
5828 // functions for loading SB level
5829 // ----------------------------------------------------------------------------
5831 int getMappedElement_SB(int element_ascii, boolean use_ces)
5839 sb_element_mapping[] =
5841 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5842 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5843 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5844 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5845 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5846 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5847 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5848 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5855 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5856 if (element_ascii == sb_element_mapping[i].ascii)
5857 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5859 return EL_UNDEFINED;
5862 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5863 struct LevelFileInfo *level_file_info,
5864 boolean level_info_only)
5866 char *filename = level_file_info->filename;
5867 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5868 char last_comment[MAX_LINE_LEN];
5869 char level_name[MAX_LINE_LEN];
5872 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5873 boolean read_continued_line = FALSE;
5874 boolean reading_playfield = FALSE;
5875 boolean got_valid_playfield_line = FALSE;
5876 boolean invalid_playfield_char = FALSE;
5877 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5878 int file_level_nr = 0;
5880 int x = 0, y = 0; // initialized to make compilers happy
5882 last_comment[0] = '\0';
5883 level_name[0] = '\0';
5885 if (!(file = openFile(filename, MODE_READ)))
5887 level->no_valid_file = TRUE;
5889 if (!level_info_only)
5890 Warn("cannot read level '%s' -- using empty level", filename);
5895 while (!checkEndOfFile(file))
5897 // level successfully read, but next level may follow here
5898 if (!got_valid_playfield_line && reading_playfield)
5900 // read playfield from single level file -- skip remaining file
5901 if (!level_file_info->packed)
5904 if (file_level_nr >= num_levels_to_skip)
5909 last_comment[0] = '\0';
5910 level_name[0] = '\0';
5912 reading_playfield = FALSE;
5915 got_valid_playfield_line = FALSE;
5917 // read next line of input file
5918 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5921 // check if line was completely read and is terminated by line break
5922 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5925 // cut trailing line break (this can be newline and/or carriage return)
5926 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5927 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5930 // copy raw input line for later use (mainly debugging output)
5931 strcpy(line_raw, line);
5933 if (read_continued_line)
5935 // append new line to existing line, if there is enough space
5936 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5937 strcat(previous_line, line_ptr);
5939 strcpy(line, previous_line); // copy storage buffer to line
5941 read_continued_line = FALSE;
5944 // if the last character is '\', continue at next line
5945 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5947 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
5948 strcpy(previous_line, line); // copy line to storage buffer
5950 read_continued_line = TRUE;
5956 if (line[0] == '\0')
5959 // extract comment text from comment line
5962 for (line_ptr = line; *line_ptr; line_ptr++)
5963 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5966 strcpy(last_comment, line_ptr);
5971 // extract level title text from line containing level title
5972 if (line[0] == '\'')
5974 strcpy(level_name, &line[1]);
5976 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5977 level_name[strlen(level_name) - 1] = '\0';
5982 // skip lines containing only spaces (or empty lines)
5983 for (line_ptr = line; *line_ptr; line_ptr++)
5984 if (*line_ptr != ' ')
5986 if (*line_ptr == '\0')
5989 // at this point, we have found a line containing part of a playfield
5991 got_valid_playfield_line = TRUE;
5993 if (!reading_playfield)
5995 reading_playfield = TRUE;
5996 invalid_playfield_char = FALSE;
5998 for (x = 0; x < MAX_LEV_FIELDX; x++)
5999 for (y = 0; y < MAX_LEV_FIELDY; y++)
6000 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6005 // start with topmost tile row
6009 // skip playfield line if larger row than allowed
6010 if (y >= MAX_LEV_FIELDY)
6013 // start with leftmost tile column
6016 // read playfield elements from line
6017 for (line_ptr = line; *line_ptr; line_ptr++)
6019 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6021 // stop parsing playfield line if larger column than allowed
6022 if (x >= MAX_LEV_FIELDX)
6025 if (mapped_sb_element == EL_UNDEFINED)
6027 invalid_playfield_char = TRUE;
6032 level->field[x][y] = mapped_sb_element;
6034 // continue with next tile column
6037 level->fieldx = MAX(x, level->fieldx);
6040 if (invalid_playfield_char)
6042 // if first playfield line, treat invalid lines as comment lines
6044 reading_playfield = FALSE;
6049 // continue with next tile row
6057 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6058 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6060 if (!reading_playfield)
6062 level->no_valid_file = TRUE;
6064 Warn("cannot read level '%s' -- using empty level", filename);
6069 if (*level_name != '\0')
6071 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6072 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6074 else if (*last_comment != '\0')
6076 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6077 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6081 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6084 // set all empty fields beyond the border walls to invisible steel wall
6085 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6087 if ((x == 0 || x == level->fieldx - 1 ||
6088 y == 0 || y == level->fieldy - 1) &&
6089 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6090 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6091 level->field, level->fieldx, level->fieldy);
6094 // set special level settings for Sokoban levels
6097 level->use_step_counter = TRUE;
6099 if (load_xsb_to_ces)
6101 // special global settings can now be set in level template
6103 level->use_custom_template = TRUE;
6108 // -------------------------------------------------------------------------
6109 // functions for handling native levels
6110 // -------------------------------------------------------------------------
6112 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6113 struct LevelFileInfo *level_file_info,
6114 boolean level_info_only)
6116 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6117 level->no_valid_file = TRUE;
6120 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6121 struct LevelFileInfo *level_file_info,
6122 boolean level_info_only)
6126 // determine position of requested level inside level package
6127 if (level_file_info->packed)
6128 pos = level_file_info->nr - leveldir_current->first_level;
6130 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6131 level->no_valid_file = TRUE;
6134 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6135 struct LevelFileInfo *level_file_info,
6136 boolean level_info_only)
6138 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6139 level->no_valid_file = TRUE;
6142 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6144 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6145 CopyNativeLevel_RND_to_EM(level);
6146 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6147 CopyNativeLevel_RND_to_SP(level);
6148 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6149 CopyNativeLevel_RND_to_MM(level);
6152 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6154 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6155 CopyNativeLevel_EM_to_RND(level);
6156 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6157 CopyNativeLevel_SP_to_RND(level);
6158 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6159 CopyNativeLevel_MM_to_RND(level);
6162 void SaveNativeLevel(struct LevelInfo *level)
6164 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6166 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6167 char *filename = getLevelFilenameFromBasename(basename);
6169 CopyNativeLevel_RND_to_SP(level);
6170 CopyNativeTape_RND_to_SP(level);
6172 SaveNativeLevel_SP(filename);
6177 // ----------------------------------------------------------------------------
6178 // functions for loading generic level
6179 // ----------------------------------------------------------------------------
6181 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6182 struct LevelFileInfo *level_file_info,
6183 boolean level_info_only)
6185 // always start with reliable default values
6186 setLevelInfoToDefaults(level, level_info_only, TRUE);
6188 switch (level_file_info->type)
6190 case LEVEL_FILE_TYPE_RND:
6191 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6194 case LEVEL_FILE_TYPE_EM:
6195 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6196 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6199 case LEVEL_FILE_TYPE_SP:
6200 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6201 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6204 case LEVEL_FILE_TYPE_MM:
6205 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6206 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6209 case LEVEL_FILE_TYPE_DC:
6210 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6213 case LEVEL_FILE_TYPE_SB:
6214 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6218 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6222 // if level file is invalid, restore level structure to default values
6223 if (level->no_valid_file)
6224 setLevelInfoToDefaults(level, level_info_only, FALSE);
6226 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6227 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6229 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6230 CopyNativeLevel_Native_to_RND(level);
6233 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6235 static struct LevelFileInfo level_file_info;
6237 // always start with reliable default values
6238 setFileInfoToDefaults(&level_file_info);
6240 level_file_info.nr = 0; // unknown level number
6241 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6243 setString(&level_file_info.filename, filename);
6245 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6248 static void LoadLevel_InitVersion(struct LevelInfo *level)
6252 if (leveldir_current == NULL) // only when dumping level
6255 // all engine modifications also valid for levels which use latest engine
6256 if (level->game_version < VERSION_IDENT(3,2,0,5))
6258 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6259 level->score[SC_TIME_BONUS] /= 10;
6262 if (leveldir_current->latest_engine)
6264 // ---------- use latest game engine --------------------------------------
6266 /* For all levels which are forced to use the latest game engine version
6267 (normally all but user contributed, private and undefined levels), set
6268 the game engine version to the actual version; this allows for actual
6269 corrections in the game engine to take effect for existing, converted
6270 levels (from "classic" or other existing games) to make the emulation
6271 of the corresponding game more accurate, while (hopefully) not breaking
6272 existing levels created from other players. */
6274 level->game_version = GAME_VERSION_ACTUAL;
6276 /* Set special EM style gems behaviour: EM style gems slip down from
6277 normal, steel and growing wall. As this is a more fundamental change,
6278 it seems better to set the default behaviour to "off" (as it is more
6279 natural) and make it configurable in the level editor (as a property
6280 of gem style elements). Already existing converted levels (neither
6281 private nor contributed levels) are changed to the new behaviour. */
6283 if (level->file_version < FILE_VERSION_2_0)
6284 level->em_slippery_gems = TRUE;
6289 // ---------- use game engine the level was created with --------------------
6291 /* For all levels which are not forced to use the latest game engine
6292 version (normally user contributed, private and undefined levels),
6293 use the version of the game engine the levels were created for.
6295 Since 2.0.1, the game engine version is now directly stored
6296 in the level file (chunk "VERS"), so there is no need anymore
6297 to set the game version from the file version (except for old,
6298 pre-2.0 levels, where the game version is still taken from the
6299 file format version used to store the level -- see above). */
6301 // player was faster than enemies in 1.0.0 and before
6302 if (level->file_version == FILE_VERSION_1_0)
6303 for (i = 0; i < MAX_PLAYERS; i++)
6304 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6306 // default behaviour for EM style gems was "slippery" only in 2.0.1
6307 if (level->game_version == VERSION_IDENT(2,0,1,0))
6308 level->em_slippery_gems = TRUE;
6310 // springs could be pushed over pits before (pre-release version) 2.2.0
6311 if (level->game_version < VERSION_IDENT(2,2,0,0))
6312 level->use_spring_bug = TRUE;
6314 if (level->game_version < VERSION_IDENT(3,2,0,5))
6316 // time orb caused limited time in endless time levels before 3.2.0-5
6317 level->use_time_orb_bug = TRUE;
6319 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6320 level->block_snap_field = FALSE;
6322 // extra time score was same value as time left score before 3.2.0-5
6323 level->extra_time_score = level->score[SC_TIME_BONUS];
6326 if (level->game_version < VERSION_IDENT(3,2,0,7))
6328 // default behaviour for snapping was "not continuous" before 3.2.0-7
6329 level->continuous_snapping = FALSE;
6332 // only few elements were able to actively move into acid before 3.1.0
6333 // trigger settings did not exist before 3.1.0; set to default "any"
6334 if (level->game_version < VERSION_IDENT(3,1,0,0))
6336 // correct "can move into acid" settings (all zero in old levels)
6338 level->can_move_into_acid_bits = 0; // nothing can move into acid
6339 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6341 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6342 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6343 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6344 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6346 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6347 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6349 // correct trigger settings (stored as zero == "none" in old levels)
6351 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6353 int element = EL_CUSTOM_START + i;
6354 struct ElementInfo *ei = &element_info[element];
6356 for (j = 0; j < ei->num_change_pages; j++)
6358 struct ElementChangeInfo *change = &ei->change_page[j];
6360 change->trigger_player = CH_PLAYER_ANY;
6361 change->trigger_page = CH_PAGE_ANY;
6366 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6368 int element = EL_CUSTOM_256;
6369 struct ElementInfo *ei = &element_info[element];
6370 struct ElementChangeInfo *change = &ei->change_page[0];
6372 /* This is needed to fix a problem that was caused by a bugfix in function
6373 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6374 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6375 not replace walkable elements, but instead just placed the player on it,
6376 without placing the Sokoban field under the player). Unfortunately, this
6377 breaks "Snake Bite" style levels when the snake is halfway through a door
6378 that just closes (the snake head is still alive and can be moved in this
6379 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6380 player (without Sokoban element) which then gets killed as designed). */
6382 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6383 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6384 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6385 change->target_element = EL_PLAYER_1;
6388 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6389 if (level->game_version < VERSION_IDENT(3,2,5,0))
6391 /* This is needed to fix a problem that was caused by a bugfix in function
6392 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6393 corrects the behaviour when a custom element changes to another custom
6394 element with a higher element number that has change actions defined.
6395 Normally, only one change per frame is allowed for custom elements.
6396 Therefore, it is checked if a custom element already changed in the
6397 current frame; if it did, subsequent changes are suppressed.
6398 Unfortunately, this is only checked for element changes, but not for
6399 change actions, which are still executed. As the function above loops
6400 through all custom elements from lower to higher, an element change
6401 resulting in a lower CE number won't be checked again, while a target
6402 element with a higher number will also be checked, and potential change
6403 actions will get executed for this CE, too (which is wrong), while
6404 further changes are ignored (which is correct). As this bugfix breaks
6405 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6406 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6407 behaviour for existing levels and tapes that make use of this bug */
6409 level->use_action_after_change_bug = TRUE;
6412 // not centering level after relocating player was default only in 3.2.3
6413 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6414 level->shifted_relocation = TRUE;
6416 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6417 if (level->game_version < VERSION_IDENT(3,2,6,0))
6418 level->em_explodes_by_fire = TRUE;
6420 // levels were solved by the first player entering an exit up to 4.1.0.0
6421 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6422 level->solved_by_one_player = TRUE;
6424 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6425 if (level->game_version < VERSION_IDENT(4,1,1,1))
6426 level->use_life_bugs = TRUE;
6428 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6429 if (level->game_version < VERSION_IDENT(4,1,1,1))
6430 level->sb_objects_needed = FALSE;
6432 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6433 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6434 level->finish_dig_collect = FALSE;
6437 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6441 // map elements that have changed in newer versions
6442 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6443 level->game_version);
6444 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6445 for (x = 0; x < 3; x++)
6446 for (y = 0; y < 3; y++)
6447 level->yamyam_content[i].e[x][y] =
6448 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6449 level->game_version);
6453 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6457 // map custom element change events that have changed in newer versions
6458 // (these following values were accidentally changed in version 3.0.1)
6459 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6460 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6462 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6464 int element = EL_CUSTOM_START + i;
6466 // order of checking and copying events to be mapped is important
6467 // (do not change the start and end value -- they are constant)
6468 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6470 if (HAS_CHANGE_EVENT(element, j - 2))
6472 SET_CHANGE_EVENT(element, j - 2, FALSE);
6473 SET_CHANGE_EVENT(element, j, TRUE);
6477 // order of checking and copying events to be mapped is important
6478 // (do not change the start and end value -- they are constant)
6479 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6481 if (HAS_CHANGE_EVENT(element, j - 1))
6483 SET_CHANGE_EVENT(element, j - 1, FALSE);
6484 SET_CHANGE_EVENT(element, j, TRUE);
6490 // initialize "can_change" field for old levels with only one change page
6491 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6493 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6495 int element = EL_CUSTOM_START + i;
6497 if (CAN_CHANGE(element))
6498 element_info[element].change->can_change = TRUE;
6502 // correct custom element values (for old levels without these options)
6503 if (level->game_version < VERSION_IDENT(3,1,1,0))
6505 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6507 int element = EL_CUSTOM_START + i;
6508 struct ElementInfo *ei = &element_info[element];
6510 if (ei->access_direction == MV_NO_DIRECTION)
6511 ei->access_direction = MV_ALL_DIRECTIONS;
6515 // correct custom element values (fix invalid values for all versions)
6518 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6520 int element = EL_CUSTOM_START + i;
6521 struct ElementInfo *ei = &element_info[element];
6523 for (j = 0; j < ei->num_change_pages; j++)
6525 struct ElementChangeInfo *change = &ei->change_page[j];
6527 if (change->trigger_player == CH_PLAYER_NONE)
6528 change->trigger_player = CH_PLAYER_ANY;
6530 if (change->trigger_side == CH_SIDE_NONE)
6531 change->trigger_side = CH_SIDE_ANY;
6536 // initialize "can_explode" field for old levels which did not store this
6537 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6538 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6540 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6542 int element = EL_CUSTOM_START + i;
6544 if (EXPLODES_1X1_OLD(element))
6545 element_info[element].explosion_type = EXPLODES_1X1;
6547 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6548 EXPLODES_SMASHED(element) ||
6549 EXPLODES_IMPACT(element)));
6553 // correct previously hard-coded move delay values for maze runner style
6554 if (level->game_version < VERSION_IDENT(3,1,1,0))
6556 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6558 int element = EL_CUSTOM_START + i;
6560 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6562 // previously hard-coded and therefore ignored
6563 element_info[element].move_delay_fixed = 9;
6564 element_info[element].move_delay_random = 0;
6569 // set some other uninitialized values of custom elements in older levels
6570 if (level->game_version < VERSION_IDENT(3,1,0,0))
6572 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6574 int element = EL_CUSTOM_START + i;
6576 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6578 element_info[element].explosion_delay = 17;
6579 element_info[element].ignition_delay = 8;
6584 static void LoadLevel_InitElements(struct LevelInfo *level)
6586 LoadLevel_InitStandardElements(level);
6588 if (level->file_has_custom_elements)
6589 LoadLevel_InitCustomElements(level);
6591 // initialize element properties for level editor etc.
6592 InitElementPropertiesEngine(level->game_version);
6593 InitElementPropertiesGfxElement();
6596 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6600 // map elements that have changed in newer versions
6601 for (y = 0; y < level->fieldy; y++)
6602 for (x = 0; x < level->fieldx; x++)
6603 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6604 level->game_version);
6606 // clear unused playfield data (nicer if level gets resized in editor)
6607 for (x = 0; x < MAX_LEV_FIELDX; x++)
6608 for (y = 0; y < MAX_LEV_FIELDY; y++)
6609 if (x >= level->fieldx || y >= level->fieldy)
6610 level->field[x][y] = EL_EMPTY;
6612 // copy elements to runtime playfield array
6613 for (x = 0; x < MAX_LEV_FIELDX; x++)
6614 for (y = 0; y < MAX_LEV_FIELDY; y++)
6615 Tile[x][y] = level->field[x][y];
6617 // initialize level size variables for faster access
6618 lev_fieldx = level->fieldx;
6619 lev_fieldy = level->fieldy;
6621 // determine border element for this level
6622 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6623 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6628 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6630 struct LevelFileInfo *level_file_info = &level->file_info;
6632 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6633 CopyNativeLevel_RND_to_Native(level);
6636 static void LoadLevelTemplate_LoadAndInit(void)
6638 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6640 LoadLevel_InitVersion(&level_template);
6641 LoadLevel_InitElements(&level_template);
6643 ActivateLevelTemplate();
6646 void LoadLevelTemplate(int nr)
6648 if (!fileExists(getGlobalLevelTemplateFilename()))
6650 Warn("no level template found for this level");
6655 setLevelFileInfo(&level_template.file_info, nr);
6657 LoadLevelTemplate_LoadAndInit();
6660 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6662 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6664 LoadLevelTemplate_LoadAndInit();
6667 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6669 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6671 if (level.use_custom_template)
6673 if (network_level != NULL)
6674 LoadNetworkLevelTemplate(network_level);
6676 LoadLevelTemplate(-1);
6679 LoadLevel_InitVersion(&level);
6680 LoadLevel_InitElements(&level);
6681 LoadLevel_InitPlayfield(&level);
6683 LoadLevel_InitNativeEngines(&level);
6686 void LoadLevel(int nr)
6688 SetLevelSetInfo(leveldir_current->identifier, nr);
6690 setLevelFileInfo(&level.file_info, nr);
6692 LoadLevel_LoadAndInit(NULL);
6695 void LoadLevelInfoOnly(int nr)
6697 setLevelFileInfo(&level.file_info, nr);
6699 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6702 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6704 SetLevelSetInfo(network_level->leveldir_identifier,
6705 network_level->file_info.nr);
6707 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6709 LoadLevel_LoadAndInit(network_level);
6712 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6716 chunk_size += putFileVersion(file, level->file_version);
6717 chunk_size += putFileVersion(file, level->game_version);
6722 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6726 chunk_size += putFile16BitBE(file, level->creation_date.year);
6727 chunk_size += putFile8Bit(file, level->creation_date.month);
6728 chunk_size += putFile8Bit(file, level->creation_date.day);
6733 #if ENABLE_HISTORIC_CHUNKS
6734 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6738 putFile8Bit(file, level->fieldx);
6739 putFile8Bit(file, level->fieldy);
6741 putFile16BitBE(file, level->time);
6742 putFile16BitBE(file, level->gems_needed);
6744 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6745 putFile8Bit(file, level->name[i]);
6747 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6748 putFile8Bit(file, level->score[i]);
6750 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6751 for (y = 0; y < 3; y++)
6752 for (x = 0; x < 3; x++)
6753 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6754 level->yamyam_content[i].e[x][y]));
6755 putFile8Bit(file, level->amoeba_speed);
6756 putFile8Bit(file, level->time_magic_wall);
6757 putFile8Bit(file, level->time_wheel);
6758 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6759 level->amoeba_content));
6760 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6761 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6762 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6763 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6765 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6767 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6768 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6769 putFile32BitBE(file, level->can_move_into_acid_bits);
6770 putFile8Bit(file, level->dont_collide_with_bits);
6772 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6773 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6775 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6776 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6777 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6779 putFile8Bit(file, level->game_engine_type);
6781 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6785 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6790 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6791 chunk_size += putFile8Bit(file, level->name[i]);
6796 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6801 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6802 chunk_size += putFile8Bit(file, level->author[i]);
6807 #if ENABLE_HISTORIC_CHUNKS
6808 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6813 for (y = 0; y < level->fieldy; y++)
6814 for (x = 0; x < level->fieldx; x++)
6815 if (level->encoding_16bit_field)
6816 chunk_size += putFile16BitBE(file, level->field[x][y]);
6818 chunk_size += putFile8Bit(file, level->field[x][y]);
6824 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6829 for (y = 0; y < level->fieldy; y++)
6830 for (x = 0; x < level->fieldx; x++)
6831 chunk_size += putFile16BitBE(file, level->field[x][y]);
6836 #if ENABLE_HISTORIC_CHUNKS
6837 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6841 putFile8Bit(file, EL_YAMYAM);
6842 putFile8Bit(file, level->num_yamyam_contents);
6843 putFile8Bit(file, 0);
6844 putFile8Bit(file, 0);
6846 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6847 for (y = 0; y < 3; y++)
6848 for (x = 0; x < 3; x++)
6849 if (level->encoding_16bit_field)
6850 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6852 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6856 #if ENABLE_HISTORIC_CHUNKS
6857 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6860 int num_contents, content_xsize, content_ysize;
6861 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6863 if (element == EL_YAMYAM)
6865 num_contents = level->num_yamyam_contents;
6869 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6870 for (y = 0; y < 3; y++)
6871 for (x = 0; x < 3; x++)
6872 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6874 else if (element == EL_BD_AMOEBA)
6880 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6881 for (y = 0; y < 3; y++)
6882 for (x = 0; x < 3; x++)
6883 content_array[i][x][y] = EL_EMPTY;
6884 content_array[0][0][0] = level->amoeba_content;
6888 // chunk header already written -- write empty chunk data
6889 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6891 Warn("cannot save content for element '%d'", element);
6896 putFile16BitBE(file, element);
6897 putFile8Bit(file, num_contents);
6898 putFile8Bit(file, content_xsize);
6899 putFile8Bit(file, content_ysize);
6901 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6903 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6904 for (y = 0; y < 3; y++)
6905 for (x = 0; x < 3; x++)
6906 putFile16BitBE(file, content_array[i][x][y]);
6910 #if ENABLE_HISTORIC_CHUNKS
6911 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6913 int envelope_nr = element - EL_ENVELOPE_1;
6914 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6918 chunk_size += putFile16BitBE(file, element);
6919 chunk_size += putFile16BitBE(file, envelope_len);
6920 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6921 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6923 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6924 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6926 for (i = 0; i < envelope_len; i++)
6927 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6933 #if ENABLE_HISTORIC_CHUNKS
6934 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6935 int num_changed_custom_elements)
6939 putFile16BitBE(file, num_changed_custom_elements);
6941 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6943 int element = EL_CUSTOM_START + i;
6945 struct ElementInfo *ei = &element_info[element];
6947 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6949 if (check < num_changed_custom_elements)
6951 putFile16BitBE(file, element);
6952 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6959 if (check != num_changed_custom_elements) // should not happen
6960 Warn("inconsistent number of custom element properties");
6964 #if ENABLE_HISTORIC_CHUNKS
6965 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6966 int num_changed_custom_elements)
6970 putFile16BitBE(file, num_changed_custom_elements);
6972 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6974 int element = EL_CUSTOM_START + i;
6976 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6978 if (check < num_changed_custom_elements)
6980 putFile16BitBE(file, element);
6981 putFile16BitBE(file, element_info[element].change->target_element);
6988 if (check != num_changed_custom_elements) // should not happen
6989 Warn("inconsistent number of custom target elements");
6993 #if ENABLE_HISTORIC_CHUNKS
6994 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6995 int num_changed_custom_elements)
6997 int i, j, x, y, check = 0;
6999 putFile16BitBE(file, num_changed_custom_elements);
7001 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7003 int element = EL_CUSTOM_START + i;
7004 struct ElementInfo *ei = &element_info[element];
7006 if (ei->modified_settings)
7008 if (check < num_changed_custom_elements)
7010 putFile16BitBE(file, element);
7012 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7013 putFile8Bit(file, ei->description[j]);
7015 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7017 // some free bytes for future properties and padding
7018 WriteUnusedBytesToFile(file, 7);
7020 putFile8Bit(file, ei->use_gfx_element);
7021 putFile16BitBE(file, ei->gfx_element_initial);
7023 putFile8Bit(file, ei->collect_score_initial);
7024 putFile8Bit(file, ei->collect_count_initial);
7026 putFile16BitBE(file, ei->push_delay_fixed);
7027 putFile16BitBE(file, ei->push_delay_random);
7028 putFile16BitBE(file, ei->move_delay_fixed);
7029 putFile16BitBE(file, ei->move_delay_random);
7031 putFile16BitBE(file, ei->move_pattern);
7032 putFile8Bit(file, ei->move_direction_initial);
7033 putFile8Bit(file, ei->move_stepsize);
7035 for (y = 0; y < 3; y++)
7036 for (x = 0; x < 3; x++)
7037 putFile16BitBE(file, ei->content.e[x][y]);
7039 putFile32BitBE(file, ei->change->events);
7041 putFile16BitBE(file, ei->change->target_element);
7043 putFile16BitBE(file, ei->change->delay_fixed);
7044 putFile16BitBE(file, ei->change->delay_random);
7045 putFile16BitBE(file, ei->change->delay_frames);
7047 putFile16BitBE(file, ei->change->initial_trigger_element);
7049 putFile8Bit(file, ei->change->explode);
7050 putFile8Bit(file, ei->change->use_target_content);
7051 putFile8Bit(file, ei->change->only_if_complete);
7052 putFile8Bit(file, ei->change->use_random_replace);
7054 putFile8Bit(file, ei->change->random_percentage);
7055 putFile8Bit(file, ei->change->replace_when);
7057 for (y = 0; y < 3; y++)
7058 for (x = 0; x < 3; x++)
7059 putFile16BitBE(file, ei->change->content.e[x][y]);
7061 putFile8Bit(file, ei->slippery_type);
7063 // some free bytes for future properties and padding
7064 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7071 if (check != num_changed_custom_elements) // should not happen
7072 Warn("inconsistent number of custom element properties");
7076 #if ENABLE_HISTORIC_CHUNKS
7077 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7079 struct ElementInfo *ei = &element_info[element];
7082 // ---------- custom element base property values (96 bytes) ----------------
7084 putFile16BitBE(file, element);
7086 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7087 putFile8Bit(file, ei->description[i]);
7089 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7091 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7093 putFile8Bit(file, ei->num_change_pages);
7095 putFile16BitBE(file, ei->ce_value_fixed_initial);
7096 putFile16BitBE(file, ei->ce_value_random_initial);
7097 putFile8Bit(file, ei->use_last_ce_value);
7099 putFile8Bit(file, ei->use_gfx_element);
7100 putFile16BitBE(file, ei->gfx_element_initial);
7102 putFile8Bit(file, ei->collect_score_initial);
7103 putFile8Bit(file, ei->collect_count_initial);
7105 putFile8Bit(file, ei->drop_delay_fixed);
7106 putFile8Bit(file, ei->push_delay_fixed);
7107 putFile8Bit(file, ei->drop_delay_random);
7108 putFile8Bit(file, ei->push_delay_random);
7109 putFile16BitBE(file, ei->move_delay_fixed);
7110 putFile16BitBE(file, ei->move_delay_random);
7112 // bits 0 - 15 of "move_pattern" ...
7113 putFile16BitBE(file, ei->move_pattern & 0xffff);
7114 putFile8Bit(file, ei->move_direction_initial);
7115 putFile8Bit(file, ei->move_stepsize);
7117 putFile8Bit(file, ei->slippery_type);
7119 for (y = 0; y < 3; y++)
7120 for (x = 0; x < 3; x++)
7121 putFile16BitBE(file, ei->content.e[x][y]);
7123 putFile16BitBE(file, ei->move_enter_element);
7124 putFile16BitBE(file, ei->move_leave_element);
7125 putFile8Bit(file, ei->move_leave_type);
7127 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7128 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7130 putFile8Bit(file, ei->access_direction);
7132 putFile8Bit(file, ei->explosion_delay);
7133 putFile8Bit(file, ei->ignition_delay);
7134 putFile8Bit(file, ei->explosion_type);
7136 // some free bytes for future custom property values and padding
7137 WriteUnusedBytesToFile(file, 1);
7139 // ---------- change page property values (48 bytes) ------------------------
7141 for (i = 0; i < ei->num_change_pages; i++)
7143 struct ElementChangeInfo *change = &ei->change_page[i];
7144 unsigned int event_bits;
7146 // bits 0 - 31 of "has_event[]" ...
7148 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7149 if (change->has_event[j])
7150 event_bits |= (1 << j);
7151 putFile32BitBE(file, event_bits);
7153 putFile16BitBE(file, change->target_element);
7155 putFile16BitBE(file, change->delay_fixed);
7156 putFile16BitBE(file, change->delay_random);
7157 putFile16BitBE(file, change->delay_frames);
7159 putFile16BitBE(file, change->initial_trigger_element);
7161 putFile8Bit(file, change->explode);
7162 putFile8Bit(file, change->use_target_content);
7163 putFile8Bit(file, change->only_if_complete);
7164 putFile8Bit(file, change->use_random_replace);
7166 putFile8Bit(file, change->random_percentage);
7167 putFile8Bit(file, change->replace_when);
7169 for (y = 0; y < 3; y++)
7170 for (x = 0; x < 3; x++)
7171 putFile16BitBE(file, change->target_content.e[x][y]);
7173 putFile8Bit(file, change->can_change);
7175 putFile8Bit(file, change->trigger_side);
7177 putFile8Bit(file, change->trigger_player);
7178 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7179 log_2(change->trigger_page)));
7181 putFile8Bit(file, change->has_action);
7182 putFile8Bit(file, change->action_type);
7183 putFile8Bit(file, change->action_mode);
7184 putFile16BitBE(file, change->action_arg);
7186 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7188 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7189 if (change->has_event[j])
7190 event_bits |= (1 << (j - 32));
7191 putFile8Bit(file, event_bits);
7196 #if ENABLE_HISTORIC_CHUNKS
7197 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7199 struct ElementInfo *ei = &element_info[element];
7200 struct ElementGroupInfo *group = ei->group;
7203 putFile16BitBE(file, element);
7205 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7206 putFile8Bit(file, ei->description[i]);
7208 putFile8Bit(file, group->num_elements);
7210 putFile8Bit(file, ei->use_gfx_element);
7211 putFile16BitBE(file, ei->gfx_element_initial);
7213 putFile8Bit(file, group->choice_mode);
7215 // some free bytes for future values and padding
7216 WriteUnusedBytesToFile(file, 3);
7218 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7219 putFile16BitBE(file, group->element[i]);
7223 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7224 boolean write_element)
7226 int save_type = entry->save_type;
7227 int data_type = entry->data_type;
7228 int conf_type = entry->conf_type;
7229 int byte_mask = conf_type & CONF_MASK_BYTES;
7230 int element = entry->element;
7231 int default_value = entry->default_value;
7233 boolean modified = FALSE;
7235 if (byte_mask != CONF_MASK_MULTI_BYTES)
7237 void *value_ptr = entry->value;
7238 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7241 // check if any settings have been modified before saving them
7242 if (value != default_value)
7245 // do not save if explicitly told or if unmodified default settings
7246 if ((save_type == SAVE_CONF_NEVER) ||
7247 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7251 num_bytes += putFile16BitBE(file, element);
7253 num_bytes += putFile8Bit(file, conf_type);
7254 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7255 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7256 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7259 else if (data_type == TYPE_STRING)
7261 char *default_string = entry->default_string;
7262 char *string = (char *)(entry->value);
7263 int string_length = strlen(string);
7266 // check if any settings have been modified before saving them
7267 if (!strEqual(string, default_string))
7270 // do not save if explicitly told or if unmodified default settings
7271 if ((save_type == SAVE_CONF_NEVER) ||
7272 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7276 num_bytes += putFile16BitBE(file, element);
7278 num_bytes += putFile8Bit(file, conf_type);
7279 num_bytes += putFile16BitBE(file, string_length);
7281 for (i = 0; i < string_length; i++)
7282 num_bytes += putFile8Bit(file, string[i]);
7284 else if (data_type == TYPE_ELEMENT_LIST)
7286 int *element_array = (int *)(entry->value);
7287 int num_elements = *(int *)(entry->num_entities);
7290 // check if any settings have been modified before saving them
7291 for (i = 0; i < num_elements; i++)
7292 if (element_array[i] != default_value)
7295 // do not save if explicitly told or if unmodified default settings
7296 if ((save_type == SAVE_CONF_NEVER) ||
7297 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7301 num_bytes += putFile16BitBE(file, element);
7303 num_bytes += putFile8Bit(file, conf_type);
7304 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7306 for (i = 0; i < num_elements; i++)
7307 num_bytes += putFile16BitBE(file, element_array[i]);
7309 else if (data_type == TYPE_CONTENT_LIST)
7311 struct Content *content = (struct Content *)(entry->value);
7312 int num_contents = *(int *)(entry->num_entities);
7315 // check if any settings have been modified before saving them
7316 for (i = 0; i < num_contents; i++)
7317 for (y = 0; y < 3; y++)
7318 for (x = 0; x < 3; x++)
7319 if (content[i].e[x][y] != default_value)
7322 // do not save if explicitly told or if unmodified default settings
7323 if ((save_type == SAVE_CONF_NEVER) ||
7324 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7328 num_bytes += putFile16BitBE(file, element);
7330 num_bytes += putFile8Bit(file, conf_type);
7331 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7333 for (i = 0; i < num_contents; i++)
7334 for (y = 0; y < 3; y++)
7335 for (x = 0; x < 3; x++)
7336 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7342 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7347 li = *level; // copy level data into temporary buffer
7349 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7350 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7355 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7360 li = *level; // copy level data into temporary buffer
7362 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7363 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7368 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7370 int envelope_nr = element - EL_ENVELOPE_1;
7374 chunk_size += putFile16BitBE(file, element);
7376 // copy envelope data into temporary buffer
7377 xx_envelope = level->envelope[envelope_nr];
7379 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7380 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7385 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7387 struct ElementInfo *ei = &element_info[element];
7391 chunk_size += putFile16BitBE(file, element);
7393 xx_ei = *ei; // copy element data into temporary buffer
7395 // set default description string for this specific element
7396 strcpy(xx_default_description, getDefaultElementDescription(ei));
7398 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7399 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7401 for (i = 0; i < ei->num_change_pages; i++)
7403 struct ElementChangeInfo *change = &ei->change_page[i];
7405 xx_current_change_page = i;
7407 xx_change = *change; // copy change data into temporary buffer
7410 setEventBitsFromEventFlags(change);
7412 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7413 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7420 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7422 struct ElementInfo *ei = &element_info[element];
7423 struct ElementGroupInfo *group = ei->group;
7427 chunk_size += putFile16BitBE(file, element);
7429 xx_ei = *ei; // copy element data into temporary buffer
7430 xx_group = *group; // copy group data into temporary buffer
7432 // set default description string for this specific element
7433 strcpy(xx_default_description, getDefaultElementDescription(ei));
7435 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7436 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7441 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7442 boolean save_as_template)
7448 if (!(file = fopen(filename, MODE_WRITE)))
7450 Warn("cannot save level file '%s'", filename);
7455 level->file_version = FILE_VERSION_ACTUAL;
7456 level->game_version = GAME_VERSION_ACTUAL;
7458 level->creation_date = getCurrentDate();
7460 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7461 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7463 chunk_size = SaveLevel_VERS(NULL, level);
7464 putFileChunkBE(file, "VERS", chunk_size);
7465 SaveLevel_VERS(file, level);
7467 chunk_size = SaveLevel_DATE(NULL, level);
7468 putFileChunkBE(file, "DATE", chunk_size);
7469 SaveLevel_DATE(file, level);
7471 chunk_size = SaveLevel_NAME(NULL, level);
7472 putFileChunkBE(file, "NAME", chunk_size);
7473 SaveLevel_NAME(file, level);
7475 chunk_size = SaveLevel_AUTH(NULL, level);
7476 putFileChunkBE(file, "AUTH", chunk_size);
7477 SaveLevel_AUTH(file, level);
7479 chunk_size = SaveLevel_INFO(NULL, level);
7480 putFileChunkBE(file, "INFO", chunk_size);
7481 SaveLevel_INFO(file, level);
7483 chunk_size = SaveLevel_BODY(NULL, level);
7484 putFileChunkBE(file, "BODY", chunk_size);
7485 SaveLevel_BODY(file, level);
7487 chunk_size = SaveLevel_ELEM(NULL, level);
7488 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7490 putFileChunkBE(file, "ELEM", chunk_size);
7491 SaveLevel_ELEM(file, level);
7494 for (i = 0; i < NUM_ENVELOPES; i++)
7496 int element = EL_ENVELOPE_1 + i;
7498 chunk_size = SaveLevel_NOTE(NULL, level, element);
7499 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7501 putFileChunkBE(file, "NOTE", chunk_size);
7502 SaveLevel_NOTE(file, level, element);
7506 // if not using template level, check for non-default custom/group elements
7507 if (!level->use_custom_template || save_as_template)
7509 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7511 int element = EL_CUSTOM_START + i;
7513 chunk_size = SaveLevel_CUSX(NULL, level, element);
7514 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7516 putFileChunkBE(file, "CUSX", chunk_size);
7517 SaveLevel_CUSX(file, level, element);
7521 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7523 int element = EL_GROUP_START + i;
7525 chunk_size = SaveLevel_GRPX(NULL, level, element);
7526 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7528 putFileChunkBE(file, "GRPX", chunk_size);
7529 SaveLevel_GRPX(file, level, element);
7536 SetFilePermissions(filename, PERMS_PRIVATE);
7539 void SaveLevel(int nr)
7541 char *filename = getDefaultLevelFilename(nr);
7543 SaveLevelFromFilename(&level, filename, FALSE);
7546 void SaveLevelTemplate(void)
7548 char *filename = getLocalLevelTemplateFilename();
7550 SaveLevelFromFilename(&level, filename, TRUE);
7553 boolean SaveLevelChecked(int nr)
7555 char *filename = getDefaultLevelFilename(nr);
7556 boolean new_level = !fileExists(filename);
7557 boolean level_saved = FALSE;
7559 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7564 Request("Level saved!", REQ_CONFIRM);
7572 void DumpLevel(struct LevelInfo *level)
7574 if (level->no_level_file || level->no_valid_file)
7576 Warn("cannot dump -- no valid level file found");
7582 Print("Level xxx (file version %08d, game version %08d)\n",
7583 level->file_version, level->game_version);
7586 Print("Level author: '%s'\n", level->author);
7587 Print("Level title: '%s'\n", level->name);
7589 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7591 Print("Level time: %d seconds\n", level->time);
7592 Print("Gems needed: %d\n", level->gems_needed);
7594 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7595 Print("Time for wheel: %d seconds\n", level->time_wheel);
7596 Print("Time for light: %d seconds\n", level->time_light);
7597 Print("Time for timegate: %d seconds\n", level->time_timegate);
7599 Print("Amoeba speed: %d\n", level->amoeba_speed);
7602 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7603 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7604 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7605 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7606 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7612 // ============================================================================
7613 // tape file functions
7614 // ============================================================================
7616 static void setTapeInfoToDefaults(void)
7620 // always start with reliable default values (empty tape)
7623 // default values (also for pre-1.2 tapes) with only the first player
7624 tape.player_participates[0] = TRUE;
7625 for (i = 1; i < MAX_PLAYERS; i++)
7626 tape.player_participates[i] = FALSE;
7628 // at least one (default: the first) player participates in every tape
7629 tape.num_participating_players = 1;
7631 tape.property_bits = TAPE_PROPERTY_NONE;
7633 tape.level_nr = level_nr;
7635 tape.changed = FALSE;
7637 tape.recording = FALSE;
7638 tape.playing = FALSE;
7639 tape.pausing = FALSE;
7641 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7642 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7644 tape.no_valid_file = FALSE;
7647 static int getTapePosSize(struct TapeInfo *tape)
7649 int tape_pos_size = 0;
7651 if (tape->use_key_actions)
7652 tape_pos_size += tape->num_participating_players;
7654 if (tape->use_mouse_actions)
7655 tape_pos_size += 3; // x and y position and mouse button mask
7657 tape_pos_size += 1; // tape action delay value
7659 return tape_pos_size;
7662 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7664 tape->use_key_actions = FALSE;
7665 tape->use_mouse_actions = FALSE;
7667 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7668 tape->use_key_actions = TRUE;
7670 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7671 tape->use_mouse_actions = TRUE;
7674 static int getTapeActionValue(struct TapeInfo *tape)
7676 return (tape->use_key_actions &&
7677 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7678 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7679 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7680 TAPE_ACTIONS_DEFAULT);
7683 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7685 tape->file_version = getFileVersion(file);
7686 tape->game_version = getFileVersion(file);
7691 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7695 tape->random_seed = getFile32BitBE(file);
7696 tape->date = getFile32BitBE(file);
7697 tape->length = getFile32BitBE(file);
7699 // read header fields that are new since version 1.2
7700 if (tape->file_version >= FILE_VERSION_1_2)
7702 byte store_participating_players = getFile8Bit(file);
7705 // since version 1.2, tapes store which players participate in the tape
7706 tape->num_participating_players = 0;
7707 for (i = 0; i < MAX_PLAYERS; i++)
7709 tape->player_participates[i] = FALSE;
7711 if (store_participating_players & (1 << i))
7713 tape->player_participates[i] = TRUE;
7714 tape->num_participating_players++;
7718 setTapeActionFlags(tape, getFile8Bit(file));
7720 tape->property_bits = getFile8Bit(file);
7722 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7724 engine_version = getFileVersion(file);
7725 if (engine_version > 0)
7726 tape->engine_version = engine_version;
7728 tape->engine_version = tape->game_version;
7734 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7736 tape->scr_fieldx = getFile8Bit(file);
7737 tape->scr_fieldy = getFile8Bit(file);
7742 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7744 int level_identifier_size;
7747 level_identifier_size = getFile16BitBE(file);
7749 tape->level_identifier =
7750 checked_realloc(tape->level_identifier, level_identifier_size);
7752 for (i = 0; i < level_identifier_size; i++)
7753 tape->level_identifier[i] = getFile8Bit(file);
7755 tape->level_nr = getFile16BitBE(file);
7757 chunk_size = 2 + level_identifier_size + 2;
7762 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7765 int tape_pos_size = getTapePosSize(tape);
7766 int chunk_size_expected = tape_pos_size * tape->length;
7768 if (chunk_size_expected != chunk_size)
7770 ReadUnusedBytesFromFile(file, chunk_size);
7771 return chunk_size_expected;
7774 for (i = 0; i < tape->length; i++)
7776 if (i >= MAX_TAPE_LEN)
7778 Warn("tape truncated -- size exceeds maximum tape size %d",
7781 // tape too large; read and ignore remaining tape data from this chunk
7782 for (;i < tape->length; i++)
7783 ReadUnusedBytesFromFile(file, tape_pos_size);
7788 if (tape->use_key_actions)
7790 for (j = 0; j < MAX_PLAYERS; j++)
7792 tape->pos[i].action[j] = MV_NONE;
7794 if (tape->player_participates[j])
7795 tape->pos[i].action[j] = getFile8Bit(file);
7799 if (tape->use_mouse_actions)
7801 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7802 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7803 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7806 tape->pos[i].delay = getFile8Bit(file);
7808 if (tape->file_version == FILE_VERSION_1_0)
7810 // eliminate possible diagonal moves in old tapes
7811 // this is only for backward compatibility
7813 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7814 byte action = tape->pos[i].action[0];
7815 int k, num_moves = 0;
7817 for (k = 0; k<4; k++)
7819 if (action & joy_dir[k])
7821 tape->pos[i + num_moves].action[0] = joy_dir[k];
7823 tape->pos[i + num_moves].delay = 0;
7832 tape->length += num_moves;
7835 else if (tape->file_version < FILE_VERSION_2_0)
7837 // convert pre-2.0 tapes to new tape format
7839 if (tape->pos[i].delay > 1)
7842 tape->pos[i + 1] = tape->pos[i];
7843 tape->pos[i + 1].delay = 1;
7846 for (j = 0; j < MAX_PLAYERS; j++)
7847 tape->pos[i].action[j] = MV_NONE;
7848 tape->pos[i].delay--;
7855 if (checkEndOfFile(file))
7859 if (i != tape->length)
7860 chunk_size = tape_pos_size * i;
7865 static void LoadTape_SokobanSolution(char *filename)
7868 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7870 if (!(file = openFile(filename, MODE_READ)))
7872 tape.no_valid_file = TRUE;
7877 while (!checkEndOfFile(file))
7879 unsigned char c = getByteFromFile(file);
7881 if (checkEndOfFile(file))
7888 tape.pos[tape.length].action[0] = MV_UP;
7889 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7895 tape.pos[tape.length].action[0] = MV_DOWN;
7896 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7902 tape.pos[tape.length].action[0] = MV_LEFT;
7903 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7909 tape.pos[tape.length].action[0] = MV_RIGHT;
7910 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7918 // ignore white-space characters
7922 tape.no_valid_file = TRUE;
7924 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
7932 if (tape.no_valid_file)
7935 tape.length_frames = GetTapeLengthFrames();
7936 tape.length_seconds = GetTapeLengthSeconds();
7939 void LoadTapeFromFilename(char *filename)
7941 char cookie[MAX_LINE_LEN];
7942 char chunk_name[CHUNK_ID_LEN + 1];
7946 // always start with reliable default values
7947 setTapeInfoToDefaults();
7949 if (strSuffix(filename, ".sln"))
7951 LoadTape_SokobanSolution(filename);
7956 if (!(file = openFile(filename, MODE_READ)))
7958 tape.no_valid_file = TRUE;
7963 getFileChunkBE(file, chunk_name, NULL);
7964 if (strEqual(chunk_name, "RND1"))
7966 getFile32BitBE(file); // not used
7968 getFileChunkBE(file, chunk_name, NULL);
7969 if (!strEqual(chunk_name, "TAPE"))
7971 tape.no_valid_file = TRUE;
7973 Warn("unknown format of tape file '%s'", filename);
7980 else // check for pre-2.0 file format with cookie string
7982 strcpy(cookie, chunk_name);
7983 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7985 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7986 cookie[strlen(cookie) - 1] = '\0';
7988 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7990 tape.no_valid_file = TRUE;
7992 Warn("unknown format of tape file '%s'", filename);
7999 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8001 tape.no_valid_file = TRUE;
8003 Warn("unsupported version of tape file '%s'", filename);
8010 // pre-2.0 tape files have no game version, so use file version here
8011 tape.game_version = tape.file_version;
8014 if (tape.file_version < FILE_VERSION_1_2)
8016 // tape files from versions before 1.2.0 without chunk structure
8017 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8018 LoadTape_BODY(file, 2 * tape.length, &tape);
8026 int (*loader)(File *, int, struct TapeInfo *);
8030 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8031 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8032 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8033 { "INFO", -1, LoadTape_INFO },
8034 { "BODY", -1, LoadTape_BODY },
8038 while (getFileChunkBE(file, chunk_name, &chunk_size))
8042 while (chunk_info[i].name != NULL &&
8043 !strEqual(chunk_name, chunk_info[i].name))
8046 if (chunk_info[i].name == NULL)
8048 Warn("unknown chunk '%s' in tape file '%s'",
8049 chunk_name, filename);
8051 ReadUnusedBytesFromFile(file, chunk_size);
8053 else if (chunk_info[i].size != -1 &&
8054 chunk_info[i].size != chunk_size)
8056 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8057 chunk_size, chunk_name, filename);
8059 ReadUnusedBytesFromFile(file, chunk_size);
8063 // call function to load this tape chunk
8064 int chunk_size_expected =
8065 (chunk_info[i].loader)(file, chunk_size, &tape);
8067 // the size of some chunks cannot be checked before reading other
8068 // chunks first (like "HEAD" and "BODY") that contain some header
8069 // information, so check them here
8070 if (chunk_size_expected != chunk_size)
8072 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8073 chunk_size, chunk_name, filename);
8081 tape.length_frames = GetTapeLengthFrames();
8082 tape.length_seconds = GetTapeLengthSeconds();
8085 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8087 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8089 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8090 tape.engine_version);
8094 void LoadTape(int nr)
8096 char *filename = getTapeFilename(nr);
8098 LoadTapeFromFilename(filename);
8101 void LoadSolutionTape(int nr)
8103 char *filename = getSolutionTapeFilename(nr);
8105 LoadTapeFromFilename(filename);
8107 if (TAPE_IS_EMPTY(tape) &&
8108 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8109 level.native_sp_level->demo.is_available)
8110 CopyNativeTape_SP_to_RND(&level);
8113 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8115 // chunk required for team mode tapes with non-default screen size
8116 return (tape->num_participating_players > 1 &&
8117 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8118 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8121 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8123 putFileVersion(file, tape->file_version);
8124 putFileVersion(file, tape->game_version);
8127 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8130 byte store_participating_players = 0;
8132 // set bits for participating players for compact storage
8133 for (i = 0; i < MAX_PLAYERS; i++)
8134 if (tape->player_participates[i])
8135 store_participating_players |= (1 << i);
8137 putFile32BitBE(file, tape->random_seed);
8138 putFile32BitBE(file, tape->date);
8139 putFile32BitBE(file, tape->length);
8141 putFile8Bit(file, store_participating_players);
8143 putFile8Bit(file, getTapeActionValue(tape));
8145 putFile8Bit(file, tape->property_bits);
8147 // unused bytes not at the end here for 4-byte alignment of engine_version
8148 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8150 putFileVersion(file, tape->engine_version);
8153 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8155 putFile8Bit(file, tape->scr_fieldx);
8156 putFile8Bit(file, tape->scr_fieldy);
8159 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8161 int level_identifier_size = strlen(tape->level_identifier) + 1;
8164 putFile16BitBE(file, level_identifier_size);
8166 for (i = 0; i < level_identifier_size; i++)
8167 putFile8Bit(file, tape->level_identifier[i]);
8169 putFile16BitBE(file, tape->level_nr);
8172 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8176 for (i = 0; i < tape->length; i++)
8178 if (tape->use_key_actions)
8180 for (j = 0; j < MAX_PLAYERS; j++)
8181 if (tape->player_participates[j])
8182 putFile8Bit(file, tape->pos[i].action[j]);
8185 if (tape->use_mouse_actions)
8187 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8188 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8189 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8192 putFile8Bit(file, tape->pos[i].delay);
8196 void SaveTapeToFilename(char *filename)
8200 int info_chunk_size;
8201 int body_chunk_size;
8203 if (!(file = fopen(filename, MODE_WRITE)))
8205 Warn("cannot save level recording file '%s'", filename);
8210 tape_pos_size = getTapePosSize(&tape);
8212 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8213 body_chunk_size = tape_pos_size * tape.length;
8215 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8216 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8218 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8219 SaveTape_VERS(file, &tape);
8221 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8222 SaveTape_HEAD(file, &tape);
8224 if (checkSaveTape_SCRN(&tape))
8226 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8227 SaveTape_SCRN(file, &tape);
8230 putFileChunkBE(file, "INFO", info_chunk_size);
8231 SaveTape_INFO(file, &tape);
8233 putFileChunkBE(file, "BODY", body_chunk_size);
8234 SaveTape_BODY(file, &tape);
8238 SetFilePermissions(filename, PERMS_PRIVATE);
8241 void SaveTape(int nr)
8243 char *filename = getTapeFilename(nr);
8246 InitTapeDirectory(leveldir_current->subdir);
8248 tape.file_version = FILE_VERSION_ACTUAL;
8249 tape.game_version = GAME_VERSION_ACTUAL;
8251 tape.num_participating_players = 0;
8253 // count number of participating players
8254 for (i = 0; i < MAX_PLAYERS; i++)
8255 if (tape.player_participates[i])
8256 tape.num_participating_players++;
8258 SaveTapeToFilename(filename);
8260 tape.changed = FALSE;
8263 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8264 unsigned int req_state_added)
8266 char *filename = getTapeFilename(nr);
8267 boolean new_tape = !fileExists(filename);
8268 boolean tape_saved = FALSE;
8270 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8275 Request(msg_saved, REQ_CONFIRM | req_state_added);
8283 boolean SaveTapeChecked(int nr)
8285 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8288 boolean SaveTapeChecked_LevelSolved(int nr)
8290 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8291 "Level solved! Tape saved!", REQ_STAY_OPEN);
8294 void DumpTape(struct TapeInfo *tape)
8296 int tape_frame_counter;
8299 if (tape->no_valid_file)
8301 Warn("cannot dump -- no valid tape file found");
8307 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8308 tape->level_nr, tape->file_version, tape->game_version);
8309 Print(" (effective engine version %08d)\n",
8310 tape->engine_version);
8311 Print("Level series identifier: '%s'\n", tape->level_identifier);
8314 tape_frame_counter = 0;
8316 for (i = 0; i < tape->length; i++)
8318 if (i >= MAX_TAPE_LEN)
8323 for (j = 0; j < MAX_PLAYERS; j++)
8325 if (tape->player_participates[j])
8327 int action = tape->pos[i].action[j];
8329 Print("%d:%02x ", j, action);
8330 Print("[%c%c%c%c|%c%c] - ",
8331 (action & JOY_LEFT ? '<' : ' '),
8332 (action & JOY_RIGHT ? '>' : ' '),
8333 (action & JOY_UP ? '^' : ' '),
8334 (action & JOY_DOWN ? 'v' : ' '),
8335 (action & JOY_BUTTON_1 ? '1' : ' '),
8336 (action & JOY_BUTTON_2 ? '2' : ' '));
8340 Print("(%03d) ", tape->pos[i].delay);
8341 Print("[%05d]\n", tape_frame_counter);
8343 tape_frame_counter += tape->pos[i].delay;
8350 // ============================================================================
8351 // score file functions
8352 // ============================================================================
8354 void LoadScore(int nr)
8357 char *filename = getScoreFilename(nr);
8358 char cookie[MAX_LINE_LEN];
8359 char line[MAX_LINE_LEN];
8363 // always start with reliable default values
8364 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8366 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8367 highscore[i].Score = 0;
8370 if (!(file = fopen(filename, MODE_READ)))
8373 // check file identifier
8374 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8376 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8377 cookie[strlen(cookie) - 1] = '\0';
8379 if (!checkCookieString(cookie, SCORE_COOKIE))
8381 Warn("unknown format of score file '%s'", filename);
8388 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8390 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8391 Warn("fscanf() failed; %s", strerror(errno));
8393 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8396 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8397 line[strlen(line) - 1] = '\0';
8399 for (line_ptr = line; *line_ptr; line_ptr++)
8401 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8403 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8404 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8413 void SaveScore(int nr)
8416 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8417 char *filename = getScoreFilename(nr);
8420 // used instead of "leveldir_current->subdir" (for network games)
8421 InitScoreDirectory(levelset.identifier);
8423 if (!(file = fopen(filename, MODE_WRITE)))
8425 Warn("cannot save score for level %d", nr);
8430 fprintf(file, "%s\n\n", SCORE_COOKIE);
8432 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8433 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8437 SetFilePermissions(filename, permissions);
8441 // ============================================================================
8442 // setup file functions
8443 // ============================================================================
8445 #define TOKEN_STR_PLAYER_PREFIX "player_"
8448 static struct TokenInfo global_setup_tokens[] =
8452 &setup.player_name, "player_name"
8456 &setup.multiple_users, "multiple_users"
8460 &setup.sound, "sound"
8464 &setup.sound_loops, "repeating_sound_loops"
8468 &setup.sound_music, "background_music"
8472 &setup.sound_simple, "simple_sound_effects"
8476 &setup.toons, "toons"
8480 &setup.scroll_delay, "scroll_delay"
8484 &setup.forced_scroll_delay, "forced_scroll_delay"
8488 &setup.scroll_delay_value, "scroll_delay_value"
8492 &setup.engine_snapshot_mode, "engine_snapshot_mode"
8496 &setup.engine_snapshot_memory, "engine_snapshot_memory"
8500 &setup.fade_screens, "fade_screens"
8504 &setup.autorecord, "automatic_tape_recording"
8508 &setup.show_titlescreen, "show_titlescreen"
8512 &setup.quick_doors, "quick_doors"
8516 &setup.team_mode, "team_mode"
8520 &setup.handicap, "handicap"
8524 &setup.skip_levels, "skip_levels"
8528 &setup.increment_levels, "increment_levels"
8532 &setup.auto_play_next_level, "auto_play_next_level"
8536 &setup.skip_scores_after_game, "skip_scores_after_game"
8540 &setup.time_limit, "time_limit"
8544 &setup.fullscreen, "fullscreen"
8548 &setup.window_scaling_percent, "window_scaling_percent"
8552 &setup.window_scaling_quality, "window_scaling_quality"
8556 &setup.screen_rendering_mode, "screen_rendering_mode"
8560 &setup.vsync_mode, "vsync_mode"
8564 &setup.ask_on_escape, "ask_on_escape"
8568 &setup.ask_on_escape_editor, "ask_on_escape_editor"
8572 &setup.ask_on_game_over, "ask_on_game_over"
8576 &setup.quick_switch, "quick_player_switch"
8580 &setup.input_on_focus, "input_on_focus"
8584 &setup.prefer_aga_graphics, "prefer_aga_graphics"
8588 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
8592 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
8596 &setup.game_speed_extended, "game_speed_extended"
8600 &setup.game_frame_delay, "game_frame_delay"
8604 &setup.sp_show_border_elements, "sp_show_border_elements"
8608 &setup.small_game_graphics, "small_game_graphics"
8612 &setup.show_snapshot_buttons, "show_snapshot_buttons"
8616 &setup.graphics_set, "graphics_set"
8620 &setup.sounds_set, "sounds_set"
8624 &setup.music_set, "music_set"
8628 &setup.override_level_graphics, "override_level_graphics"
8632 &setup.override_level_sounds, "override_level_sounds"
8636 &setup.override_level_music, "override_level_music"
8640 &setup.volume_simple, "volume_simple"
8644 &setup.volume_loops, "volume_loops"
8648 &setup.volume_music, "volume_music"
8652 &setup.network_mode, "network_mode"
8656 &setup.network_player_nr, "network_player"
8660 &setup.network_server_hostname, "network_server_hostname"
8664 &setup.touch.control_type, "touch.control_type"
8668 &setup.touch.move_distance, "touch.move_distance"
8672 &setup.touch.drop_distance, "touch.drop_distance"
8676 &setup.touch.transparency, "touch.transparency"
8680 &setup.touch.draw_outlined, "touch.draw_outlined"
8684 &setup.touch.draw_pressed, "touch.draw_pressed"
8688 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
8692 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
8696 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
8700 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
8704 static struct TokenInfo auto_setup_tokens[] =
8708 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
8712 static struct TokenInfo editor_setup_tokens[] =
8716 &setup.editor.el_classic, "editor.el_classic"
8720 &setup.editor.el_custom, "editor.el_custom"
8724 &setup.editor.el_user_defined, "editor.el_user_defined"
8728 &setup.editor.el_dynamic, "editor.el_dynamic"
8732 &setup.editor.el_headlines, "editor.el_headlines"
8736 &setup.editor.show_element_token, "editor.show_element_token"
8740 static struct TokenInfo editor_cascade_setup_tokens[] =
8744 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
8748 &setup.editor_cascade.el_em, "editor.cascade.el_em"
8752 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
8756 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
8760 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
8764 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
8768 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
8772 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
8776 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
8780 &setup.editor_cascade.el_df, "editor.cascade.el_df"
8784 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
8788 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
8792 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
8796 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
8800 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
8804 &setup.editor_cascade.el_user, "editor.cascade.el_user"
8808 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
8812 static struct TokenInfo shortcut_setup_tokens[] =
8816 &setup.shortcut.save_game, "shortcut.save_game"
8820 &setup.shortcut.load_game, "shortcut.load_game"
8824 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
8828 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
8832 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
8836 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
8840 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
8844 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
8848 &setup.shortcut.tape_eject, "shortcut.tape_eject"
8852 &setup.shortcut.tape_extra, "shortcut.tape_extra"
8856 &setup.shortcut.tape_stop, "shortcut.tape_stop"
8860 &setup.shortcut.tape_pause, "shortcut.tape_pause"
8864 &setup.shortcut.tape_record, "shortcut.tape_record"
8868 &setup.shortcut.tape_play, "shortcut.tape_play"
8872 &setup.shortcut.sound_simple, "shortcut.sound_simple"
8876 &setup.shortcut.sound_loops, "shortcut.sound_loops"
8880 &setup.shortcut.sound_music, "shortcut.sound_music"
8884 &setup.shortcut.snap_left, "shortcut.snap_left"
8888 &setup.shortcut.snap_right, "shortcut.snap_right"
8892 &setup.shortcut.snap_up, "shortcut.snap_up"
8896 &setup.shortcut.snap_down, "shortcut.snap_down"
8900 static struct SetupInputInfo setup_input;
8901 static struct TokenInfo player_setup_tokens[] =
8905 &setup_input.use_joystick, ".use_joystick"
8909 &setup_input.joy.device_name, ".joy.device_name"
8913 &setup_input.joy.xleft, ".joy.xleft"
8917 &setup_input.joy.xmiddle, ".joy.xmiddle"
8921 &setup_input.joy.xright, ".joy.xright"
8925 &setup_input.joy.yupper, ".joy.yupper"
8929 &setup_input.joy.ymiddle, ".joy.ymiddle"
8933 &setup_input.joy.ylower, ".joy.ylower"
8937 &setup_input.joy.snap, ".joy.snap_field"
8941 &setup_input.joy.drop, ".joy.place_bomb"
8945 &setup_input.key.left, ".key.move_left"
8949 &setup_input.key.right, ".key.move_right"
8953 &setup_input.key.up, ".key.move_up"
8957 &setup_input.key.down, ".key.move_down"
8961 &setup_input.key.snap, ".key.snap_field"
8965 &setup_input.key.drop, ".key.place_bomb"
8969 static struct TokenInfo system_setup_tokens[] =
8973 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
8977 &setup.system.sdl_videodriver, "system.sdl_videodriver"
8981 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
8985 &setup.system.audio_fragment_size, "system.audio_fragment_size"
8989 static struct TokenInfo internal_setup_tokens[] =
8993 &setup.internal.program_title, "program_title"
8997 &setup.internal.program_version, "program_version"
9001 &setup.internal.program_author, "program_author"
9005 &setup.internal.program_email, "program_email"
9009 &setup.internal.program_website, "program_website"
9013 &setup.internal.program_copyright, "program_copyright"
9017 &setup.internal.program_company, "program_company"
9021 &setup.internal.program_icon_file, "program_icon_file"
9025 &setup.internal.default_graphics_set, "default_graphics_set"
9029 &setup.internal.default_sounds_set, "default_sounds_set"
9033 &setup.internal.default_music_set, "default_music_set"
9037 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
9041 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
9045 &setup.internal.fallback_music_file, "fallback_music_file"
9049 &setup.internal.default_level_series, "default_level_series"
9053 &setup.internal.default_window_width, "default_window_width"
9057 &setup.internal.default_window_height, "default_window_height"
9061 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
9065 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
9069 &setup.internal.create_user_levelset, "create_user_levelset"
9073 &setup.internal.menu_game, "menu_game"
9077 &setup.internal.menu_editor, "menu_editor"
9081 &setup.internal.menu_graphics, "menu_graphics"
9085 &setup.internal.menu_sound, "menu_sound"
9089 &setup.internal.menu_artwork, "menu_artwork"
9093 &setup.internal.menu_input, "menu_input"
9097 &setup.internal.menu_touch, "menu_touch"
9101 &setup.internal.menu_shortcuts, "menu_shortcuts"
9105 &setup.internal.menu_exit, "menu_exit"
9109 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
9113 static struct TokenInfo debug_setup_tokens[] =
9117 &setup.debug.frame_delay[0], "debug.frame_delay_0"
9121 &setup.debug.frame_delay[1], "debug.frame_delay_1"
9125 &setup.debug.frame_delay[2], "debug.frame_delay_2"
9129 &setup.debug.frame_delay[3], "debug.frame_delay_3"
9133 &setup.debug.frame_delay[4], "debug.frame_delay_4"
9137 &setup.debug.frame_delay[5], "debug.frame_delay_5"
9141 &setup.debug.frame_delay[6], "debug.frame_delay_6"
9145 &setup.debug.frame_delay[7], "debug.frame_delay_7"
9149 &setup.debug.frame_delay[8], "debug.frame_delay_8"
9153 &setup.debug.frame_delay[9], "debug.frame_delay_9"
9157 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
9161 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
9165 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
9169 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
9173 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
9177 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
9181 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
9185 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
9189 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
9193 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
9197 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
9200 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
9204 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
9208 &setup.debug.xsn_mode, "debug.xsn_mode"
9212 &setup.debug.xsn_percent, "debug.xsn_percent"
9216 static struct TokenInfo options_setup_tokens[] =
9220 &setup.options.verbose, "options.verbose"
9224 static void setSetupInfoToDefaults(struct SetupInfo *si)
9228 si->player_name = getStringCopy(getDefaultUserName(user.nr));
9230 si->multiple_users = TRUE;
9233 si->sound_loops = TRUE;
9234 si->sound_music = TRUE;
9235 si->sound_simple = TRUE;
9237 si->scroll_delay = TRUE;
9238 si->forced_scroll_delay = FALSE;
9239 si->scroll_delay_value = STD_SCROLL_DELAY;
9240 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9241 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9242 si->fade_screens = TRUE;
9243 si->autorecord = TRUE;
9244 si->show_titlescreen = TRUE;
9245 si->quick_doors = FALSE;
9246 si->team_mode = FALSE;
9247 si->handicap = TRUE;
9248 si->skip_levels = TRUE;
9249 si->increment_levels = TRUE;
9250 si->auto_play_next_level = TRUE;
9251 si->skip_scores_after_game = FALSE;
9252 si->time_limit = TRUE;
9253 si->fullscreen = FALSE;
9254 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9255 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9256 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9257 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9258 si->ask_on_escape = TRUE;
9259 si->ask_on_escape_editor = TRUE;
9260 si->ask_on_game_over = TRUE;
9261 si->quick_switch = FALSE;
9262 si->input_on_focus = FALSE;
9263 si->prefer_aga_graphics = TRUE;
9264 si->prefer_lowpass_sounds = FALSE;
9265 si->prefer_extra_panel_items = TRUE;
9266 si->game_speed_extended = FALSE;
9267 si->game_frame_delay = GAME_FRAME_DELAY;
9268 si->sp_show_border_elements = FALSE;
9269 si->small_game_graphics = FALSE;
9270 si->show_snapshot_buttons = FALSE;
9272 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9273 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9274 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9276 si->override_level_graphics = FALSE;
9277 si->override_level_sounds = FALSE;
9278 si->override_level_music = FALSE;
9280 si->volume_simple = 100; // percent
9281 si->volume_loops = 100; // percent
9282 si->volume_music = 100; // percent
9284 si->network_mode = FALSE;
9285 si->network_player_nr = 0; // first player
9286 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9288 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9289 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
9290 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
9291 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
9292 si->touch.draw_outlined = TRUE;
9293 si->touch.draw_pressed = TRUE;
9295 for (i = 0; i < 2; i++)
9297 char *default_grid_button[6][2] =
9303 { "111222", " vv " },
9304 { "111222", " vv " }
9306 int grid_xsize = DEFAULT_GRID_XSIZE(i);
9307 int grid_ysize = DEFAULT_GRID_YSIZE(i);
9308 int min_xsize = MIN(6, grid_xsize);
9309 int min_ysize = MIN(6, grid_ysize);
9310 int startx = grid_xsize - min_xsize;
9311 int starty = grid_ysize - min_ysize;
9314 // virtual buttons grid can only be set to defaults if video is initialized
9315 // (this will be repeated if virtual buttons are not loaded from setup file)
9316 if (video.initialized)
9318 si->touch.grid_xsize[i] = grid_xsize;
9319 si->touch.grid_ysize[i] = grid_ysize;
9323 si->touch.grid_xsize[i] = -1;
9324 si->touch.grid_ysize[i] = -1;
9327 for (x = 0; x < MAX_GRID_XSIZE; x++)
9328 for (y = 0; y < MAX_GRID_YSIZE; y++)
9329 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9331 for (x = 0; x < min_xsize; x++)
9332 for (y = 0; y < min_ysize; y++)
9333 si->touch.grid_button[i][x][starty + y] =
9334 default_grid_button[y][0][x];
9336 for (x = 0; x < min_xsize; x++)
9337 for (y = 0; y < min_ysize; y++)
9338 si->touch.grid_button[i][startx + x][starty + y] =
9339 default_grid_button[y][1][x];
9342 si->touch.grid_initialized = video.initialized;
9344 si->editor.el_boulderdash = TRUE;
9345 si->editor.el_emerald_mine = TRUE;
9346 si->editor.el_emerald_mine_club = TRUE;
9347 si->editor.el_more = TRUE;
9348 si->editor.el_sokoban = TRUE;
9349 si->editor.el_supaplex = TRUE;
9350 si->editor.el_diamond_caves = TRUE;
9351 si->editor.el_dx_boulderdash = TRUE;
9353 si->editor.el_mirror_magic = TRUE;
9354 si->editor.el_deflektor = TRUE;
9356 si->editor.el_chars = TRUE;
9357 si->editor.el_steel_chars = TRUE;
9359 si->editor.el_classic = TRUE;
9360 si->editor.el_custom = TRUE;
9362 si->editor.el_user_defined = FALSE;
9363 si->editor.el_dynamic = TRUE;
9365 si->editor.el_headlines = TRUE;
9367 si->editor.show_element_token = FALSE;
9369 si->editor.use_template_for_new_levels = TRUE;
9371 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
9372 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
9373 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
9375 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
9376 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
9377 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
9378 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
9379 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9381 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
9382 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
9383 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
9384 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
9385 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
9386 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
9388 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
9389 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
9390 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
9392 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
9393 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
9394 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
9395 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
9397 for (i = 0; i < MAX_PLAYERS; i++)
9399 si->input[i].use_joystick = FALSE;
9400 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9401 si->input[i].joy.xleft = JOYSTICK_XLEFT;
9402 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9403 si->input[i].joy.xright = JOYSTICK_XRIGHT;
9404 si->input[i].joy.yupper = JOYSTICK_YUPPER;
9405 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9406 si->input[i].joy.ylower = JOYSTICK_YLOWER;
9407 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
9408 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
9409 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
9410 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9411 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
9412 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
9413 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
9414 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
9417 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
9418 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9419 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9420 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9422 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
9423 si->internal.program_version = getStringCopy(getProgramRealVersionString());
9424 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
9425 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
9426 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
9427 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9428 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
9430 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9432 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9433 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9434 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9436 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9437 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
9438 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
9440 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9441 si->internal.choose_from_top_leveldir = FALSE;
9442 si->internal.show_scaling_in_title = TRUE;
9443 si->internal.create_user_levelset = TRUE;
9445 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
9446 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9448 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9449 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9450 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9451 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9452 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9453 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9454 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9455 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9456 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9457 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9459 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9460 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9461 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9462 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9463 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9464 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9465 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9466 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9467 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9468 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9470 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9471 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9473 si->debug.show_frames_per_second = FALSE;
9475 si->debug.xsn_mode = AUTO;
9476 si->debug.xsn_percent = 0;
9478 si->options.verbose = FALSE;
9480 #if defined(PLATFORM_ANDROID)
9481 si->fullscreen = TRUE;
9484 setHideSetupEntry(&setup.debug.xsn_mode);
9487 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9489 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9492 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9494 si->editor_cascade.el_bd = TRUE;
9495 si->editor_cascade.el_em = TRUE;
9496 si->editor_cascade.el_emc = TRUE;
9497 si->editor_cascade.el_rnd = TRUE;
9498 si->editor_cascade.el_sb = TRUE;
9499 si->editor_cascade.el_sp = TRUE;
9500 si->editor_cascade.el_dc = TRUE;
9501 si->editor_cascade.el_dx = TRUE;
9503 si->editor_cascade.el_mm = TRUE;
9504 si->editor_cascade.el_df = TRUE;
9506 si->editor_cascade.el_chars = FALSE;
9507 si->editor_cascade.el_steel_chars = FALSE;
9508 si->editor_cascade.el_ce = FALSE;
9509 si->editor_cascade.el_ge = FALSE;
9510 si->editor_cascade.el_ref = FALSE;
9511 si->editor_cascade.el_user = FALSE;
9512 si->editor_cascade.el_dynamic = FALSE;
9515 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9517 static char *getHideSetupToken(void *setup_value)
9519 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9521 if (setup_value != NULL)
9522 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9524 return hide_setup_token;
9527 void setHideSetupEntry(void *setup_value)
9529 char *hide_setup_token = getHideSetupToken(setup_value);
9531 if (hide_setup_hash == NULL)
9532 hide_setup_hash = newSetupFileHash();
9534 if (setup_value != NULL)
9535 setHashEntry(hide_setup_hash, hide_setup_token, "");
9538 void removeHideSetupEntry(void *setup_value)
9540 char *hide_setup_token = getHideSetupToken(setup_value);
9542 if (setup_value != NULL)
9543 removeHashEntry(hide_setup_hash, hide_setup_token);
9546 boolean hideSetupEntry(void *setup_value)
9548 char *hide_setup_token = getHideSetupToken(setup_value);
9550 return (setup_value != NULL &&
9551 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9554 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9555 struct TokenInfo *token_info,
9556 int token_nr, char *token_text)
9558 char *token_hide_text = getStringCat2(token_text, ".hide");
9559 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9561 // set the value of this setup option in the setup option structure
9562 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9564 // check if this setup option should be hidden in the setup menu
9565 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9566 setHideSetupEntry(token_info[token_nr].value);
9568 free(token_hide_text);
9571 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9572 struct TokenInfo *token_info,
9575 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9576 token_info[token_nr].text);
9579 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9583 if (!setup_file_hash)
9586 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9587 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9589 setup.touch.grid_initialized = TRUE;
9590 for (i = 0; i < 2; i++)
9592 int grid_xsize = setup.touch.grid_xsize[i];
9593 int grid_ysize = setup.touch.grid_ysize[i];
9596 // if virtual buttons are not loaded from setup file, repeat initializing
9597 // virtual buttons grid with default values later when video is initialized
9598 if (grid_xsize == -1 ||
9601 setup.touch.grid_initialized = FALSE;
9606 for (y = 0; y < grid_ysize; y++)
9608 char token_string[MAX_LINE_LEN];
9610 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9612 char *value_string = getHashEntry(setup_file_hash, token_string);
9614 if (value_string == NULL)
9617 for (x = 0; x < grid_xsize; x++)
9619 char c = value_string[x];
9621 setup.touch.grid_button[i][x][y] =
9622 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9627 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9628 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9630 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9631 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9633 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9637 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9639 setup_input = setup.input[pnr];
9640 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9642 char full_token[100];
9644 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9645 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9648 setup.input[pnr] = setup_input;
9651 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9652 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9654 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
9655 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9657 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9658 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9660 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9661 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9663 setHideRelatedSetupEntries();
9666 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9670 if (!setup_file_hash)
9673 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9674 setSetupInfo(auto_setup_tokens, i,
9675 getHashEntry(setup_file_hash,
9676 auto_setup_tokens[i].text));
9679 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9683 if (!setup_file_hash)
9686 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9687 setSetupInfo(editor_cascade_setup_tokens, i,
9688 getHashEntry(setup_file_hash,
9689 editor_cascade_setup_tokens[i].text));
9692 void LoadUserNames(void)
9694 int last_user_nr = user.nr;
9697 if (global.user_names != NULL)
9699 for (i = 0; i < MAX_PLAYER_NAMES; i++)
9700 checked_free(global.user_names[i]);
9702 checked_free(global.user_names);
9705 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
9707 for (i = 0; i < MAX_PLAYER_NAMES; i++)
9711 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
9713 if (setup_file_hash)
9715 char *player_name = getHashEntry(setup_file_hash, "player_name");
9717 global.user_names[i] = getFixedUserName(player_name);
9719 freeSetupFileHash(setup_file_hash);
9722 if (global.user_names[i] == NULL)
9723 global.user_names[i] = getStringCopy(getDefaultUserName(i));
9726 user.nr = last_user_nr;
9729 void LoadSetupFromFilename(char *filename)
9731 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9733 if (setup_file_hash)
9735 decodeSetupFileHash(setup_file_hash);
9737 freeSetupFileHash(setup_file_hash);
9741 Debug("setup", "using default setup values");
9745 static void LoadSetup_SpecialPostProcessing(void)
9747 char *player_name_new;
9749 // needed to work around problems with fixed length strings
9750 player_name_new = getFixedUserName(setup.player_name);
9751 free(setup.player_name);
9752 setup.player_name = player_name_new;
9754 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
9755 if (setup.scroll_delay == FALSE)
9757 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9758 setup.scroll_delay = TRUE; // now always "on"
9761 // make sure that scroll delay value stays inside valid range
9762 setup.scroll_delay_value =
9763 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9766 void LoadSetup(void)
9770 // always start with reliable default values
9771 setSetupInfoToDefaults(&setup);
9773 // try to load setup values from default setup file
9774 filename = getDefaultSetupFilename();
9776 if (fileExists(filename))
9777 LoadSetupFromFilename(filename);
9779 // try to load setup values from user setup file
9780 filename = getSetupFilename();
9782 LoadSetupFromFilename(filename);
9784 LoadSetup_SpecialPostProcessing();
9787 void LoadSetup_AutoSetup(void)
9789 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9790 SetupFileHash *setup_file_hash = NULL;
9792 // always start with reliable default values
9793 setSetupInfoToDefaults_AutoSetup(&setup);
9795 setup_file_hash = loadSetupFileHash(filename);
9797 if (setup_file_hash)
9799 decodeSetupFileHash_AutoSetup(setup_file_hash);
9801 freeSetupFileHash(setup_file_hash);
9807 void LoadSetup_EditorCascade(void)
9809 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9810 SetupFileHash *setup_file_hash = NULL;
9812 // always start with reliable default values
9813 setSetupInfoToDefaults_EditorCascade(&setup);
9815 setup_file_hash = loadSetupFileHash(filename);
9817 if (setup_file_hash)
9819 decodeSetupFileHash_EditorCascade(setup_file_hash);
9821 freeSetupFileHash(setup_file_hash);
9827 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9830 char mapping_guid[MAX_LINE_LEN];
9831 char *mapping_start, *mapping_end;
9833 // get GUID from game controller mapping line: copy complete line
9834 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9835 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9837 // get GUID from game controller mapping line: cut after GUID part
9838 mapping_start = strchr(mapping_guid, ',');
9839 if (mapping_start != NULL)
9840 *mapping_start = '\0';
9842 // cut newline from game controller mapping line
9843 mapping_end = strchr(mapping_line, '\n');
9844 if (mapping_end != NULL)
9845 *mapping_end = '\0';
9847 // add mapping entry to game controller mappings hash
9848 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9851 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9856 if (!(file = fopen(filename, MODE_READ)))
9858 Warn("cannot read game controller mappings file '%s'", filename);
9865 char line[MAX_LINE_LEN];
9867 if (!fgets(line, MAX_LINE_LEN, file))
9870 addGameControllerMappingToHash(mappings_hash, line);
9876 void SaveSetup(void)
9878 char *filename = getSetupFilename();
9882 InitUserDataDirectory();
9884 if (!(file = fopen(filename, MODE_WRITE)))
9886 Warn("cannot write setup file '%s'", filename);
9891 fprintFileHeader(file, SETUP_FILENAME);
9893 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9895 // just to make things nicer :)
9896 if (global_setup_tokens[i].value == &setup.multiple_users ||
9897 global_setup_tokens[i].value == &setup.sound ||
9898 global_setup_tokens[i].value == &setup.graphics_set ||
9899 global_setup_tokens[i].value == &setup.volume_simple ||
9900 global_setup_tokens[i].value == &setup.network_mode ||
9901 global_setup_tokens[i].value == &setup.touch.control_type ||
9902 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
9903 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
9904 fprintf(file, "\n");
9906 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9909 for (i = 0; i < 2; i++)
9911 int grid_xsize = setup.touch.grid_xsize[i];
9912 int grid_ysize = setup.touch.grid_ysize[i];
9915 fprintf(file, "\n");
9917 for (y = 0; y < grid_ysize; y++)
9919 char token_string[MAX_LINE_LEN];
9920 char value_string[MAX_LINE_LEN];
9922 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9924 for (x = 0; x < grid_xsize; x++)
9926 char c = setup.touch.grid_button[i][x][y];
9928 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9931 value_string[grid_xsize] = '\0';
9933 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9937 fprintf(file, "\n");
9938 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9939 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9941 fprintf(file, "\n");
9942 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9943 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9945 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9949 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9950 fprintf(file, "\n");
9952 setup_input = setup.input[pnr];
9953 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9954 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9957 fprintf(file, "\n");
9958 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9959 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9961 // (internal setup values not saved to user setup file)
9963 fprintf(file, "\n");
9964 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9965 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
9966 setup.debug.xsn_mode != AUTO)
9967 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9969 fprintf(file, "\n");
9970 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9971 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9975 SetFilePermissions(filename, PERMS_PRIVATE);
9978 void SaveSetup_AutoSetup(void)
9980 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9984 InitUserDataDirectory();
9986 if (!(file = fopen(filename, MODE_WRITE)))
9988 Warn("cannot write auto setup file '%s'", filename);
9995 fprintFileHeader(file, AUTOSETUP_FILENAME);
9997 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9998 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
10002 SetFilePermissions(filename, PERMS_PRIVATE);
10007 void SaveSetup_EditorCascade(void)
10009 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10013 InitUserDataDirectory();
10015 if (!(file = fopen(filename, MODE_WRITE)))
10017 Warn("cannot write editor cascade state file '%s'", filename);
10024 fprintFileHeader(file, EDITORCASCADE_FILENAME);
10026 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10027 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
10031 SetFilePermissions(filename, PERMS_PRIVATE);
10036 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
10041 if (!(file = fopen(filename, MODE_WRITE)))
10043 Warn("cannot write game controller mappings file '%s'", filename);
10048 BEGIN_HASH_ITERATION(mappings_hash, itr)
10050 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
10052 END_HASH_ITERATION(mappings_hash, itr)
10057 void SaveSetup_AddGameControllerMapping(char *mapping)
10059 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
10060 SetupFileHash *mappings_hash = newSetupFileHash();
10062 InitUserDataDirectory();
10064 // load existing personal game controller mappings
10065 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
10067 // add new mapping to personal game controller mappings
10068 addGameControllerMappingToHash(mappings_hash, mapping);
10070 // save updated personal game controller mappings
10071 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
10073 freeSetupFileHash(mappings_hash);
10077 void LoadCustomElementDescriptions(void)
10079 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10080 SetupFileHash *setup_file_hash;
10083 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10085 if (element_info[i].custom_description != NULL)
10087 free(element_info[i].custom_description);
10088 element_info[i].custom_description = NULL;
10092 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10095 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10097 char *token = getStringCat2(element_info[i].token_name, ".name");
10098 char *value = getHashEntry(setup_file_hash, token);
10101 element_info[i].custom_description = getStringCopy(value);
10106 freeSetupFileHash(setup_file_hash);
10109 static int getElementFromToken(char *token)
10111 char *value = getHashEntry(element_token_hash, token);
10114 return atoi(value);
10116 Warn("unknown element token '%s'", token);
10118 return EL_UNDEFINED;
10121 void FreeGlobalAnimEventInfo(void)
10123 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10125 if (gaei->event_list == NULL)
10130 for (i = 0; i < gaei->num_event_lists; i++)
10132 checked_free(gaei->event_list[i]->event_value);
10133 checked_free(gaei->event_list[i]);
10136 checked_free(gaei->event_list);
10138 gaei->event_list = NULL;
10139 gaei->num_event_lists = 0;
10142 static int AddGlobalAnimEventList(void)
10144 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10145 int list_pos = gaei->num_event_lists++;
10147 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
10148 sizeof(struct GlobalAnimEventListInfo *));
10150 gaei->event_list[list_pos] =
10151 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
10153 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10155 gaeli->event_value = NULL;
10156 gaeli->num_event_values = 0;
10161 static int AddGlobalAnimEventValue(int list_pos, int event_value)
10163 // do not add empty global animation events
10164 if (event_value == ANIM_EVENT_NONE)
10167 // if list position is undefined, create new list
10168 if (list_pos == ANIM_EVENT_UNDEFINED)
10169 list_pos = AddGlobalAnimEventList();
10171 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10172 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10173 int value_pos = gaeli->num_event_values++;
10175 gaeli->event_value = checked_realloc(gaeli->event_value,
10176 gaeli->num_event_values * sizeof(int *));
10178 gaeli->event_value[value_pos] = event_value;
10183 int GetGlobalAnimEventValue(int list_pos, int value_pos)
10185 if (list_pos == ANIM_EVENT_UNDEFINED)
10186 return ANIM_EVENT_NONE;
10188 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10189 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10191 return gaeli->event_value[value_pos];
10194 int GetGlobalAnimEventValueCount(int list_pos)
10196 if (list_pos == ANIM_EVENT_UNDEFINED)
10199 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10200 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10202 return gaeli->num_event_values;
10205 // This function checks if a string <s> of the format "string1, string2, ..."
10206 // exactly contains a string <s_contained>.
10208 static boolean string_has_parameter(char *s, char *s_contained)
10212 if (s == NULL || s_contained == NULL)
10215 if (strlen(s_contained) > strlen(s))
10218 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10220 char next_char = s[strlen(s_contained)];
10222 // check if next character is delimiter or whitespace
10223 return (next_char == ',' || next_char == '\0' ||
10224 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10227 // check if string contains another parameter string after a comma
10228 substring = strchr(s, ',');
10229 if (substring == NULL) // string does not contain a comma
10232 // advance string pointer to next character after the comma
10235 // skip potential whitespaces after the comma
10236 while (*substring == ' ' || *substring == '\t')
10239 return string_has_parameter(substring, s_contained);
10242 static int get_anim_parameter_value(char *s)
10244 int event_value[] =
10252 char *pattern_1[] =
10260 char *pattern_2 = ".part_";
10261 char *matching_char = NULL;
10263 int pattern_1_len = 0;
10264 int result = ANIM_EVENT_NONE;
10267 for (i = 0; i < ARRAY_SIZE(event_value); i++)
10269 matching_char = strstr(s_ptr, pattern_1[i]);
10270 pattern_1_len = strlen(pattern_1[i]);
10271 result = event_value[i];
10273 if (matching_char != NULL)
10277 if (matching_char == NULL)
10278 return ANIM_EVENT_NONE;
10280 s_ptr = matching_char + pattern_1_len;
10282 // check for main animation number ("anim_X" or "anim_XX")
10283 if (*s_ptr >= '0' && *s_ptr <= '9')
10285 int gic_anim_nr = (*s_ptr++ - '0');
10287 if (*s_ptr >= '0' && *s_ptr <= '9')
10288 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10290 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10291 return ANIM_EVENT_NONE;
10293 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10297 // invalid main animation number specified
10299 return ANIM_EVENT_NONE;
10302 // check for animation part number ("part_X" or "part_XX") (optional)
10303 if (strPrefix(s_ptr, pattern_2))
10305 s_ptr += strlen(pattern_2);
10307 if (*s_ptr >= '0' && *s_ptr <= '9')
10309 int gic_part_nr = (*s_ptr++ - '0');
10311 if (*s_ptr >= '0' && *s_ptr <= '9')
10312 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10314 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10315 return ANIM_EVENT_NONE;
10317 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10321 // invalid animation part number specified
10323 return ANIM_EVENT_NONE;
10327 // discard result if next character is neither delimiter nor whitespace
10328 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10329 *s_ptr == ' ' || *s_ptr == '\t'))
10330 return ANIM_EVENT_NONE;
10335 static int get_anim_parameter_values(char *s)
10337 int list_pos = ANIM_EVENT_UNDEFINED;
10338 int event_value = ANIM_EVENT_DEFAULT;
10340 if (string_has_parameter(s, "any"))
10341 event_value |= ANIM_EVENT_ANY;
10343 if (string_has_parameter(s, "click:self") ||
10344 string_has_parameter(s, "click") ||
10345 string_has_parameter(s, "self"))
10346 event_value |= ANIM_EVENT_SELF;
10348 if (string_has_parameter(s, "unclick:any"))
10349 event_value |= ANIM_EVENT_UNCLICK_ANY;
10351 // if animation event found, add it to global animation event list
10352 if (event_value != ANIM_EVENT_NONE)
10353 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10357 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10358 event_value = get_anim_parameter_value(s);
10360 // if animation event found, add it to global animation event list
10361 if (event_value != ANIM_EVENT_NONE)
10362 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10364 // continue with next part of the string, starting with next comma
10365 s = strchr(s + 1, ',');
10371 static int get_anim_action_parameter_value(char *token)
10373 // check most common default case first to massively speed things up
10374 if (strEqual(token, ARG_UNDEFINED))
10375 return ANIM_EVENT_ACTION_NONE;
10377 int result = getImageIDFromToken(token);
10381 char *gfx_token = getStringCat2("gfx.", token);
10383 result = getImageIDFromToken(gfx_token);
10385 checked_free(gfx_token);
10390 Key key = getKeyFromX11KeyName(token);
10392 if (key != KSYM_UNDEFINED)
10393 result = -(int)key;
10397 result = ANIM_EVENT_ACTION_NONE;
10402 int get_parameter_value(char *value_raw, char *suffix, int type)
10404 char *value = getStringToLower(value_raw);
10405 int result = 0; // probably a save default value
10407 if (strEqual(suffix, ".direction"))
10409 result = (strEqual(value, "left") ? MV_LEFT :
10410 strEqual(value, "right") ? MV_RIGHT :
10411 strEqual(value, "up") ? MV_UP :
10412 strEqual(value, "down") ? MV_DOWN : MV_NONE);
10414 else if (strEqual(suffix, ".position"))
10416 result = (strEqual(value, "left") ? POS_LEFT :
10417 strEqual(value, "right") ? POS_RIGHT :
10418 strEqual(value, "top") ? POS_TOP :
10419 strEqual(value, "upper") ? POS_UPPER :
10420 strEqual(value, "middle") ? POS_MIDDLE :
10421 strEqual(value, "lower") ? POS_LOWER :
10422 strEqual(value, "bottom") ? POS_BOTTOM :
10423 strEqual(value, "any") ? POS_ANY :
10424 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
10426 else if (strEqual(suffix, ".align"))
10428 result = (strEqual(value, "left") ? ALIGN_LEFT :
10429 strEqual(value, "right") ? ALIGN_RIGHT :
10430 strEqual(value, "center") ? ALIGN_CENTER :
10431 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10433 else if (strEqual(suffix, ".valign"))
10435 result = (strEqual(value, "top") ? VALIGN_TOP :
10436 strEqual(value, "bottom") ? VALIGN_BOTTOM :
10437 strEqual(value, "middle") ? VALIGN_MIDDLE :
10438 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10440 else if (strEqual(suffix, ".anim_mode"))
10442 result = (string_has_parameter(value, "none") ? ANIM_NONE :
10443 string_has_parameter(value, "loop") ? ANIM_LOOP :
10444 string_has_parameter(value, "linear") ? ANIM_LINEAR :
10445 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
10446 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
10447 string_has_parameter(value, "random") ? ANIM_RANDOM :
10448 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
10449 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
10450 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
10451 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10452 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
10453 string_has_parameter(value, "centered") ? ANIM_CENTERED :
10454 string_has_parameter(value, "all") ? ANIM_ALL :
10457 if (string_has_parameter(value, "once"))
10458 result |= ANIM_ONCE;
10460 if (string_has_parameter(value, "reverse"))
10461 result |= ANIM_REVERSE;
10463 if (string_has_parameter(value, "opaque_player"))
10464 result |= ANIM_OPAQUE_PLAYER;
10466 if (string_has_parameter(value, "static_panel"))
10467 result |= ANIM_STATIC_PANEL;
10469 else if (strEqual(suffix, ".init_event") ||
10470 strEqual(suffix, ".anim_event"))
10472 result = get_anim_parameter_values(value);
10474 else if (strEqual(suffix, ".init_delay_action") ||
10475 strEqual(suffix, ".anim_delay_action") ||
10476 strEqual(suffix, ".post_delay_action") ||
10477 strEqual(suffix, ".init_event_action") ||
10478 strEqual(suffix, ".anim_event_action"))
10480 result = get_anim_action_parameter_value(value_raw);
10482 else if (strEqual(suffix, ".class"))
10484 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10485 get_hash_from_key(value));
10487 else if (strEqual(suffix, ".style"))
10489 result = STYLE_DEFAULT;
10491 if (string_has_parameter(value, "accurate_borders"))
10492 result |= STYLE_ACCURATE_BORDERS;
10494 if (string_has_parameter(value, "inner_corners"))
10495 result |= STYLE_INNER_CORNERS;
10497 if (string_has_parameter(value, "reverse"))
10498 result |= STYLE_REVERSE;
10500 if (string_has_parameter(value, "leftmost_position"))
10501 result |= STYLE_LEFTMOST_POSITION;
10503 if (string_has_parameter(value, "block_clicks"))
10504 result |= STYLE_BLOCK;
10506 if (string_has_parameter(value, "passthrough_clicks"))
10507 result |= STYLE_PASSTHROUGH;
10509 if (string_has_parameter(value, "multiple_actions"))
10510 result |= STYLE_MULTIPLE_ACTIONS;
10512 else if (strEqual(suffix, ".fade_mode"))
10514 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
10515 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
10516 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
10517 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
10518 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
10519 FADE_MODE_DEFAULT);
10521 else if (strEqual(suffix, ".auto_delay_unit"))
10523 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
10524 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10525 AUTO_DELAY_UNIT_DEFAULT);
10527 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
10529 result = gfx.get_font_from_token_function(value);
10531 else // generic parameter of type integer or boolean
10533 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10534 type == TYPE_INTEGER ? get_integer_from_string(value) :
10535 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10536 ARG_UNDEFINED_VALUE);
10544 static int get_token_parameter_value(char *token, char *value_raw)
10548 if (token == NULL || value_raw == NULL)
10549 return ARG_UNDEFINED_VALUE;
10551 suffix = strrchr(token, '.');
10552 if (suffix == NULL)
10555 if (strEqual(suffix, ".element"))
10556 return getElementFromToken(value_raw);
10558 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10559 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10562 void InitMenuDesignSettings_Static(void)
10566 // always start with reliable default values from static default config
10567 for (i = 0; image_config_vars[i].token != NULL; i++)
10569 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10572 *image_config_vars[i].value =
10573 get_token_parameter_value(image_config_vars[i].token, value);
10577 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10581 // the following initializes hierarchical values from static configuration
10583 // special case: initialize "ARG_DEFAULT" values in static default config
10584 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10585 titlescreen_initial_first_default.fade_mode =
10586 title_initial_first_default.fade_mode;
10587 titlescreen_initial_first_default.fade_delay =
10588 title_initial_first_default.fade_delay;
10589 titlescreen_initial_first_default.post_delay =
10590 title_initial_first_default.post_delay;
10591 titlescreen_initial_first_default.auto_delay =
10592 title_initial_first_default.auto_delay;
10593 titlescreen_initial_first_default.auto_delay_unit =
10594 title_initial_first_default.auto_delay_unit;
10595 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
10596 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10597 titlescreen_first_default.post_delay = title_first_default.post_delay;
10598 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10599 titlescreen_first_default.auto_delay_unit =
10600 title_first_default.auto_delay_unit;
10601 titlemessage_initial_first_default.fade_mode =
10602 title_initial_first_default.fade_mode;
10603 titlemessage_initial_first_default.fade_delay =
10604 title_initial_first_default.fade_delay;
10605 titlemessage_initial_first_default.post_delay =
10606 title_initial_first_default.post_delay;
10607 titlemessage_initial_first_default.auto_delay =
10608 title_initial_first_default.auto_delay;
10609 titlemessage_initial_first_default.auto_delay_unit =
10610 title_initial_first_default.auto_delay_unit;
10611 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
10612 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10613 titlemessage_first_default.post_delay = title_first_default.post_delay;
10614 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10615 titlemessage_first_default.auto_delay_unit =
10616 title_first_default.auto_delay_unit;
10618 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
10619 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10620 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10621 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10622 titlescreen_initial_default.auto_delay_unit =
10623 title_initial_default.auto_delay_unit;
10624 titlescreen_default.fade_mode = title_default.fade_mode;
10625 titlescreen_default.fade_delay = title_default.fade_delay;
10626 titlescreen_default.post_delay = title_default.post_delay;
10627 titlescreen_default.auto_delay = title_default.auto_delay;
10628 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
10629 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
10630 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10631 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10632 titlemessage_initial_default.auto_delay_unit =
10633 title_initial_default.auto_delay_unit;
10634 titlemessage_default.fade_mode = title_default.fade_mode;
10635 titlemessage_default.fade_delay = title_default.fade_delay;
10636 titlemessage_default.post_delay = title_default.post_delay;
10637 titlemessage_default.auto_delay = title_default.auto_delay;
10638 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
10640 // special case: initialize "ARG_DEFAULT" values in static default config
10641 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
10642 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10644 titlescreen_initial_first[i] = titlescreen_initial_first_default;
10645 titlescreen_first[i] = titlescreen_first_default;
10646 titlemessage_initial_first[i] = titlemessage_initial_first_default;
10647 titlemessage_first[i] = titlemessage_first_default;
10649 titlescreen_initial[i] = titlescreen_initial_default;
10650 titlescreen[i] = titlescreen_default;
10651 titlemessage_initial[i] = titlemessage_initial_default;
10652 titlemessage[i] = titlemessage_default;
10655 // special case: initialize "ARG_DEFAULT" values in static default config
10656 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
10657 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10659 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
10662 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
10663 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
10664 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
10667 // special case: initialize "ARG_DEFAULT" values in static default config
10668 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
10669 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10671 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
10672 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
10673 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
10675 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
10678 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
10682 static void InitMenuDesignSettings_SpecialPostProcessing(void)
10686 struct XY *dst, *src;
10688 game_buttons_xy[] =
10690 { &game.button.save, &game.button.stop },
10691 { &game.button.pause2, &game.button.pause },
10692 { &game.button.load, &game.button.play },
10693 { &game.button.undo, &game.button.stop },
10694 { &game.button.redo, &game.button.play },
10700 // special case: initialize later added SETUP list size from LEVELS value
10701 if (menu.list_size[GAME_MODE_SETUP] == -1)
10702 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
10704 // set default position for snapshot buttons to stop/pause/play buttons
10705 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
10706 if ((*game_buttons_xy[i].dst).x == -1 &&
10707 (*game_buttons_xy[i].dst).y == -1)
10708 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
10710 // --------------------------------------------------------------------------
10711 // dynamic viewports (including playfield margins, borders and alignments)
10712 // --------------------------------------------------------------------------
10714 // dynamic viewports currently only supported for landscape mode
10715 int display_width = MAX(video.display_width, video.display_height);
10716 int display_height = MIN(video.display_width, video.display_height);
10718 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10720 struct RectWithBorder *vp_window = &viewport.window[i];
10721 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
10722 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
10723 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
10724 boolean dynamic_window_width = (vp_window->min_width != -1);
10725 boolean dynamic_window_height = (vp_window->min_height != -1);
10726 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
10727 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
10729 // adjust window size if min/max width/height is specified
10731 if (vp_window->min_width != -1)
10733 int window_width = display_width;
10735 // when using static window height, use aspect ratio of display
10736 if (vp_window->min_height == -1)
10737 window_width = vp_window->height * display_width / display_height;
10739 vp_window->width = MAX(vp_window->min_width, window_width);
10742 if (vp_window->min_height != -1)
10744 int window_height = display_height;
10746 // when using static window width, use aspect ratio of display
10747 if (vp_window->min_width == -1)
10748 window_height = vp_window->width * display_height / display_width;
10750 vp_window->height = MAX(vp_window->min_height, window_height);
10753 if (vp_window->max_width != -1)
10754 vp_window->width = MIN(vp_window->width, vp_window->max_width);
10756 if (vp_window->max_height != -1)
10757 vp_window->height = MIN(vp_window->height, vp_window->max_height);
10759 int playfield_width = vp_window->width;
10760 int playfield_height = vp_window->height;
10762 // adjust playfield size and position according to specified margins
10764 playfield_width -= vp_playfield->margin_left;
10765 playfield_width -= vp_playfield->margin_right;
10767 playfield_height -= vp_playfield->margin_top;
10768 playfield_height -= vp_playfield->margin_bottom;
10770 // adjust playfield size if min/max width/height is specified
10772 if (vp_playfield->min_width != -1)
10773 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
10775 if (vp_playfield->min_height != -1)
10776 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
10778 if (vp_playfield->max_width != -1)
10779 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
10781 if (vp_playfield->max_height != -1)
10782 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
10784 // adjust playfield position according to specified alignment
10786 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
10787 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
10788 else if (vp_playfield->align == ALIGN_CENTER)
10789 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
10790 else if (vp_playfield->align == ALIGN_RIGHT)
10791 vp_playfield->x += playfield_width - vp_playfield->width;
10793 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
10794 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
10795 else if (vp_playfield->valign == VALIGN_MIDDLE)
10796 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
10797 else if (vp_playfield->valign == VALIGN_BOTTOM)
10798 vp_playfield->y += playfield_height - vp_playfield->height;
10800 vp_playfield->x += vp_playfield->margin_left;
10801 vp_playfield->y += vp_playfield->margin_top;
10803 // adjust individual playfield borders if only default border is specified
10805 if (vp_playfield->border_left == -1)
10806 vp_playfield->border_left = vp_playfield->border_size;
10807 if (vp_playfield->border_right == -1)
10808 vp_playfield->border_right = vp_playfield->border_size;
10809 if (vp_playfield->border_top == -1)
10810 vp_playfield->border_top = vp_playfield->border_size;
10811 if (vp_playfield->border_bottom == -1)
10812 vp_playfield->border_bottom = vp_playfield->border_size;
10814 // set dynamic playfield borders if borders are specified as undefined
10815 // (but only if window size was dynamic and playfield size was static)
10817 if (dynamic_window_width && !dynamic_playfield_width)
10819 if (vp_playfield->border_left == -1)
10821 vp_playfield->border_left = (vp_playfield->x -
10822 vp_playfield->margin_left);
10823 vp_playfield->x -= vp_playfield->border_left;
10824 vp_playfield->width += vp_playfield->border_left;
10827 if (vp_playfield->border_right == -1)
10829 vp_playfield->border_right = (vp_window->width -
10831 vp_playfield->width -
10832 vp_playfield->margin_right);
10833 vp_playfield->width += vp_playfield->border_right;
10837 if (dynamic_window_height && !dynamic_playfield_height)
10839 if (vp_playfield->border_top == -1)
10841 vp_playfield->border_top = (vp_playfield->y -
10842 vp_playfield->margin_top);
10843 vp_playfield->y -= vp_playfield->border_top;
10844 vp_playfield->height += vp_playfield->border_top;
10847 if (vp_playfield->border_bottom == -1)
10849 vp_playfield->border_bottom = (vp_window->height -
10851 vp_playfield->height -
10852 vp_playfield->margin_bottom);
10853 vp_playfield->height += vp_playfield->border_bottom;
10857 // adjust playfield size to be a multiple of a defined alignment tile size
10859 int align_size = vp_playfield->align_size;
10860 int playfield_xtiles = vp_playfield->width / align_size;
10861 int playfield_ytiles = vp_playfield->height / align_size;
10862 int playfield_width_corrected = playfield_xtiles * align_size;
10863 int playfield_height_corrected = playfield_ytiles * align_size;
10864 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
10865 i == GFX_SPECIAL_ARG_EDITOR);
10867 if (is_playfield_mode &&
10868 dynamic_playfield_width &&
10869 vp_playfield->width != playfield_width_corrected)
10871 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
10873 vp_playfield->width = playfield_width_corrected;
10875 if (vp_playfield->align == ALIGN_LEFT)
10877 vp_playfield->border_left += playfield_xdiff;
10879 else if (vp_playfield->align == ALIGN_RIGHT)
10881 vp_playfield->border_right += playfield_xdiff;
10883 else if (vp_playfield->align == ALIGN_CENTER)
10885 int border_left_diff = playfield_xdiff / 2;
10886 int border_right_diff = playfield_xdiff - border_left_diff;
10888 vp_playfield->border_left += border_left_diff;
10889 vp_playfield->border_right += border_right_diff;
10893 if (is_playfield_mode &&
10894 dynamic_playfield_height &&
10895 vp_playfield->height != playfield_height_corrected)
10897 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
10899 vp_playfield->height = playfield_height_corrected;
10901 if (vp_playfield->valign == VALIGN_TOP)
10903 vp_playfield->border_top += playfield_ydiff;
10905 else if (vp_playfield->align == VALIGN_BOTTOM)
10907 vp_playfield->border_right += playfield_ydiff;
10909 else if (vp_playfield->align == VALIGN_MIDDLE)
10911 int border_top_diff = playfield_ydiff / 2;
10912 int border_bottom_diff = playfield_ydiff - border_top_diff;
10914 vp_playfield->border_top += border_top_diff;
10915 vp_playfield->border_bottom += border_bottom_diff;
10919 // adjust door positions according to specified alignment
10921 for (j = 0; j < 2; j++)
10923 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
10925 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
10926 vp_door->x = ALIGNED_VP_XPOS(vp_door);
10927 else if (vp_door->align == ALIGN_CENTER)
10928 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
10929 else if (vp_door->align == ALIGN_RIGHT)
10930 vp_door->x += vp_window->width - vp_door->width;
10932 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
10933 vp_door->y = ALIGNED_VP_YPOS(vp_door);
10934 else if (vp_door->valign == VALIGN_MIDDLE)
10935 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
10936 else if (vp_door->valign == VALIGN_BOTTOM)
10937 vp_door->y += vp_window->height - vp_door->height;
10942 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
10946 struct XYTileSize *dst, *src;
10949 editor_buttons_xy[] =
10952 &editor.button.element_left, &editor.palette.element_left,
10953 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
10956 &editor.button.element_middle, &editor.palette.element_middle,
10957 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
10960 &editor.button.element_right, &editor.palette.element_right,
10961 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
10968 // set default position for element buttons to element graphics
10969 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
10971 if ((*editor_buttons_xy[i].dst).x == -1 &&
10972 (*editor_buttons_xy[i].dst).y == -1)
10974 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
10976 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
10978 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
10982 // adjust editor palette rows and columns if specified to be dynamic
10984 if (editor.palette.cols == -1)
10986 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
10987 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
10988 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
10990 editor.palette.cols = (vp_width - sc_width) / bt_width;
10992 if (editor.palette.x == -1)
10994 int palette_width = editor.palette.cols * bt_width + sc_width;
10996 editor.palette.x = (vp_width - palette_width) / 2;
11000 if (editor.palette.rows == -1)
11002 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
11003 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
11004 int tx_height = getFontHeight(FONT_TEXT_2);
11006 editor.palette.rows = (vp_height - tx_height) / bt_height;
11008 if (editor.palette.y == -1)
11010 int palette_height = editor.palette.rows * bt_height + tx_height;
11012 editor.palette.y = (vp_height - palette_height) / 2;
11017 static void LoadMenuDesignSettingsFromFilename(char *filename)
11019 static struct TitleFadingInfo tfi;
11020 static struct TitleMessageInfo tmi;
11021 static struct TokenInfo title_tokens[] =
11023 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
11024 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
11025 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
11026 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
11027 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
11031 static struct TokenInfo titlemessage_tokens[] =
11033 { TYPE_INTEGER, &tmi.x, ".x" },
11034 { TYPE_INTEGER, &tmi.y, ".y" },
11035 { TYPE_INTEGER, &tmi.width, ".width" },
11036 { TYPE_INTEGER, &tmi.height, ".height" },
11037 { TYPE_INTEGER, &tmi.chars, ".chars" },
11038 { TYPE_INTEGER, &tmi.lines, ".lines" },
11039 { TYPE_INTEGER, &tmi.align, ".align" },
11040 { TYPE_INTEGER, &tmi.valign, ".valign" },
11041 { TYPE_INTEGER, &tmi.font, ".font" },
11042 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
11043 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
11044 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
11045 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
11046 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
11047 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
11048 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
11049 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
11050 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
11056 struct TitleFadingInfo *info;
11061 // initialize first titles from "enter screen" definitions, if defined
11062 { &title_initial_first_default, "menu.enter_screen.TITLE" },
11063 { &title_first_default, "menu.enter_screen.TITLE" },
11065 // initialize title screens from "next screen" definitions, if defined
11066 { &title_initial_default, "menu.next_screen.TITLE" },
11067 { &title_default, "menu.next_screen.TITLE" },
11073 struct TitleMessageInfo *array;
11076 titlemessage_arrays[] =
11078 // initialize first titles from "enter screen" definitions, if defined
11079 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
11080 { titlescreen_first, "menu.enter_screen.TITLE" },
11081 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
11082 { titlemessage_first, "menu.enter_screen.TITLE" },
11084 // initialize titles from "next screen" definitions, if defined
11085 { titlescreen_initial, "menu.next_screen.TITLE" },
11086 { titlescreen, "menu.next_screen.TITLE" },
11087 { titlemessage_initial, "menu.next_screen.TITLE" },
11088 { titlemessage, "menu.next_screen.TITLE" },
11090 // overwrite titles with title definitions, if defined
11091 { titlescreen_initial_first, "[title_initial]" },
11092 { titlescreen_first, "[title]" },
11093 { titlemessage_initial_first, "[title_initial]" },
11094 { titlemessage_first, "[title]" },
11096 { titlescreen_initial, "[title_initial]" },
11097 { titlescreen, "[title]" },
11098 { titlemessage_initial, "[title_initial]" },
11099 { titlemessage, "[title]" },
11101 // overwrite titles with title screen/message definitions, if defined
11102 { titlescreen_initial_first, "[titlescreen_initial]" },
11103 { titlescreen_first, "[titlescreen]" },
11104 { titlemessage_initial_first, "[titlemessage_initial]" },
11105 { titlemessage_first, "[titlemessage]" },
11107 { titlescreen_initial, "[titlescreen_initial]" },
11108 { titlescreen, "[titlescreen]" },
11109 { titlemessage_initial, "[titlemessage_initial]" },
11110 { titlemessage, "[titlemessage]" },
11114 SetupFileHash *setup_file_hash;
11117 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11120 // the following initializes hierarchical values from dynamic configuration
11122 // special case: initialize with default values that may be overwritten
11123 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
11124 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11126 struct TokenIntPtrInfo menu_config[] =
11128 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
11129 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
11130 { "menu.list_size", &menu.list_size[i] }
11133 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11135 char *token = menu_config[j].token;
11136 char *value = getHashEntry(setup_file_hash, token);
11139 *menu_config[j].value = get_integer_from_string(value);
11143 // special case: initialize with default values that may be overwritten
11144 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
11145 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11147 struct TokenIntPtrInfo menu_config[] =
11149 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
11150 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
11151 { "menu.list_size.INFO", &menu.list_size_info[i] }
11154 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11156 char *token = menu_config[j].token;
11157 char *value = getHashEntry(setup_file_hash, token);
11160 *menu_config[j].value = get_integer_from_string(value);
11164 // special case: initialize with default values that may be overwritten
11165 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
11166 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
11168 struct TokenIntPtrInfo menu_config[] =
11170 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
11171 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
11174 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11176 char *token = menu_config[j].token;
11177 char *value = getHashEntry(setup_file_hash, token);
11180 *menu_config[j].value = get_integer_from_string(value);
11184 // special case: initialize with default values that may be overwritten
11185 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
11186 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11188 struct TokenIntPtrInfo menu_config[] =
11190 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
11191 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
11192 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
11193 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
11194 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
11195 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
11196 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
11197 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
11198 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
11201 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11203 char *token = menu_config[j].token;
11204 char *value = getHashEntry(setup_file_hash, token);
11207 *menu_config[j].value = get_integer_from_string(value);
11211 // special case: initialize with default values that may be overwritten
11212 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11213 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11215 struct TokenIntPtrInfo menu_config[] =
11217 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
11218 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11219 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11220 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
11221 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11222 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11223 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
11224 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
11225 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
11228 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11230 char *token = menu_config[j].token;
11231 char *value = getHashEntry(setup_file_hash, token);
11234 *menu_config[j].value = get_token_parameter_value(token, value);
11238 // special case: initialize with default values that may be overwritten
11239 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11240 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11244 char *token_prefix;
11245 struct RectWithBorder *struct_ptr;
11249 { "viewport.window", &viewport.window[i] },
11250 { "viewport.playfield", &viewport.playfield[i] },
11251 { "viewport.door_1", &viewport.door_1[i] },
11252 { "viewport.door_2", &viewport.door_2[i] }
11255 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11257 struct TokenIntPtrInfo vp_config[] =
11259 { ".x", &vp_struct[j].struct_ptr->x },
11260 { ".y", &vp_struct[j].struct_ptr->y },
11261 { ".width", &vp_struct[j].struct_ptr->width },
11262 { ".height", &vp_struct[j].struct_ptr->height },
11263 { ".min_width", &vp_struct[j].struct_ptr->min_width },
11264 { ".min_height", &vp_struct[j].struct_ptr->min_height },
11265 { ".max_width", &vp_struct[j].struct_ptr->max_width },
11266 { ".max_height", &vp_struct[j].struct_ptr->max_height },
11267 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
11268 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
11269 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
11270 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
11271 { ".border_left", &vp_struct[j].struct_ptr->border_left },
11272 { ".border_right", &vp_struct[j].struct_ptr->border_right },
11273 { ".border_top", &vp_struct[j].struct_ptr->border_top },
11274 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
11275 { ".border_size", &vp_struct[j].struct_ptr->border_size },
11276 { ".align_size", &vp_struct[j].struct_ptr->align_size },
11277 { ".align", &vp_struct[j].struct_ptr->align },
11278 { ".valign", &vp_struct[j].struct_ptr->valign }
11281 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11283 char *token = getStringCat2(vp_struct[j].token_prefix,
11284 vp_config[k].token);
11285 char *value = getHashEntry(setup_file_hash, token);
11288 *vp_config[k].value = get_token_parameter_value(token, value);
11295 // special case: initialize with default values that may be overwritten
11296 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11297 for (i = 0; title_info[i].info != NULL; i++)
11299 struct TitleFadingInfo *info = title_info[i].info;
11300 char *base_token = title_info[i].text;
11302 for (j = 0; title_tokens[j].type != -1; j++)
11304 char *token = getStringCat2(base_token, title_tokens[j].text);
11305 char *value = getHashEntry(setup_file_hash, token);
11309 int parameter_value = get_token_parameter_value(token, value);
11313 *(int *)title_tokens[j].value = (int)parameter_value;
11322 // special case: initialize with default values that may be overwritten
11323 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11324 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11326 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11327 char *base_token = titlemessage_arrays[i].text;
11329 for (j = 0; titlemessage_tokens[j].type != -1; j++)
11331 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11332 char *value = getHashEntry(setup_file_hash, token);
11336 int parameter_value = get_token_parameter_value(token, value);
11338 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11342 if (titlemessage_tokens[j].type == TYPE_INTEGER)
11343 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
11345 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11355 // special case: check if network and preview player positions are redefined,
11356 // to compare this later against the main menu level preview being redefined
11357 struct TokenIntPtrInfo menu_config_players[] =
11359 { "main.network_players.x", &menu.main.network_players.redefined },
11360 { "main.network_players.y", &menu.main.network_players.redefined },
11361 { "main.preview_players.x", &menu.main.preview_players.redefined },
11362 { "main.preview_players.y", &menu.main.preview_players.redefined },
11363 { "preview.x", &preview.redefined },
11364 { "preview.y", &preview.redefined }
11367 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11368 *menu_config_players[i].value = FALSE;
11370 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11371 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11372 *menu_config_players[i].value = TRUE;
11374 // read (and overwrite with) values that may be specified in config file
11375 for (i = 0; image_config_vars[i].token != NULL; i++)
11377 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11379 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11380 if (value != NULL && !strEqual(value, ARG_DEFAULT))
11381 *image_config_vars[i].value =
11382 get_token_parameter_value(image_config_vars[i].token, value);
11385 freeSetupFileHash(setup_file_hash);
11388 void LoadMenuDesignSettings(void)
11390 char *filename_base = UNDEFINED_FILENAME, *filename_local;
11392 InitMenuDesignSettings_Static();
11393 InitMenuDesignSettings_SpecialPreProcessing();
11395 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11397 // first look for special settings configured in level series config
11398 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11400 if (fileExists(filename_base))
11401 LoadMenuDesignSettingsFromFilename(filename_base);
11404 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11406 if (filename_local != NULL && !strEqual(filename_base, filename_local))
11407 LoadMenuDesignSettingsFromFilename(filename_local);
11409 InitMenuDesignSettings_SpecialPostProcessing();
11412 void LoadMenuDesignSettings_AfterGraphics(void)
11414 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11417 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11419 char *filename = getEditorSetupFilename();
11420 SetupFileList *setup_file_list, *list;
11421 SetupFileHash *element_hash;
11422 int num_unknown_tokens = 0;
11425 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11428 element_hash = newSetupFileHash();
11430 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11431 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11433 // determined size may be larger than needed (due to unknown elements)
11435 for (list = setup_file_list; list != NULL; list = list->next)
11438 // add space for up to 3 more elements for padding that may be needed
11439 *num_elements += 3;
11441 // free memory for old list of elements, if needed
11442 checked_free(*elements);
11444 // allocate memory for new list of elements
11445 *elements = checked_malloc(*num_elements * sizeof(int));
11448 for (list = setup_file_list; list != NULL; list = list->next)
11450 char *value = getHashEntry(element_hash, list->token);
11452 if (value == NULL) // try to find obsolete token mapping
11454 char *mapped_token = get_mapped_token(list->token);
11456 if (mapped_token != NULL)
11458 value = getHashEntry(element_hash, mapped_token);
11460 free(mapped_token);
11466 (*elements)[(*num_elements)++] = atoi(value);
11470 if (num_unknown_tokens == 0)
11473 Warn("unknown token(s) found in config file:");
11474 Warn("- config file: '%s'", filename);
11476 num_unknown_tokens++;
11479 Warn("- token: '%s'", list->token);
11483 if (num_unknown_tokens > 0)
11486 while (*num_elements % 4) // pad with empty elements, if needed
11487 (*elements)[(*num_elements)++] = EL_EMPTY;
11489 freeSetupFileList(setup_file_list);
11490 freeSetupFileHash(element_hash);
11493 for (i = 0; i < *num_elements; i++)
11494 Debug("editor", "element '%s' [%d]\n",
11495 element_info[(*elements)[i]].token_name, (*elements)[i]);
11499 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11502 SetupFileHash *setup_file_hash = NULL;
11503 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11504 char *filename_music, *filename_prefix, *filename_info;
11510 token_to_value_ptr[] =
11512 { "title_header", &tmp_music_file_info.title_header },
11513 { "artist_header", &tmp_music_file_info.artist_header },
11514 { "album_header", &tmp_music_file_info.album_header },
11515 { "year_header", &tmp_music_file_info.year_header },
11517 { "title", &tmp_music_file_info.title },
11518 { "artist", &tmp_music_file_info.artist },
11519 { "album", &tmp_music_file_info.album },
11520 { "year", &tmp_music_file_info.year },
11526 filename_music = (is_sound ? getCustomSoundFilename(basename) :
11527 getCustomMusicFilename(basename));
11529 if (filename_music == NULL)
11532 // ---------- try to replace file extension ----------
11534 filename_prefix = getStringCopy(filename_music);
11535 if (strrchr(filename_prefix, '.') != NULL)
11536 *strrchr(filename_prefix, '.') = '\0';
11537 filename_info = getStringCat2(filename_prefix, ".txt");
11539 if (fileExists(filename_info))
11540 setup_file_hash = loadSetupFileHash(filename_info);
11542 free(filename_prefix);
11543 free(filename_info);
11545 if (setup_file_hash == NULL)
11547 // ---------- try to add file extension ----------
11549 filename_prefix = getStringCopy(filename_music);
11550 filename_info = getStringCat2(filename_prefix, ".txt");
11552 if (fileExists(filename_info))
11553 setup_file_hash = loadSetupFileHash(filename_info);
11555 free(filename_prefix);
11556 free(filename_info);
11559 if (setup_file_hash == NULL)
11562 // ---------- music file info found ----------
11564 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11566 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11568 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11570 *token_to_value_ptr[i].value_ptr =
11571 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11574 tmp_music_file_info.basename = getStringCopy(basename);
11575 tmp_music_file_info.music = music;
11576 tmp_music_file_info.is_sound = is_sound;
11578 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11579 *new_music_file_info = tmp_music_file_info;
11581 return new_music_file_info;
11584 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11586 return get_music_file_info_ext(basename, music, FALSE);
11589 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11591 return get_music_file_info_ext(basename, sound, TRUE);
11594 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11595 char *basename, boolean is_sound)
11597 for (; list != NULL; list = list->next)
11598 if (list->is_sound == is_sound && strEqual(list->basename, basename))
11604 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11606 return music_info_listed_ext(list, basename, FALSE);
11609 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11611 return music_info_listed_ext(list, basename, TRUE);
11614 void LoadMusicInfo(void)
11616 char *music_directory = getCustomMusicDirectory();
11617 int num_music = getMusicListSize();
11618 int num_music_noconf = 0;
11619 int num_sounds = getSoundListSize();
11621 DirectoryEntry *dir_entry;
11622 struct FileInfo *music, *sound;
11623 struct MusicFileInfo *next, **new;
11626 while (music_file_info != NULL)
11628 next = music_file_info->next;
11630 checked_free(music_file_info->basename);
11632 checked_free(music_file_info->title_header);
11633 checked_free(music_file_info->artist_header);
11634 checked_free(music_file_info->album_header);
11635 checked_free(music_file_info->year_header);
11637 checked_free(music_file_info->title);
11638 checked_free(music_file_info->artist);
11639 checked_free(music_file_info->album);
11640 checked_free(music_file_info->year);
11642 free(music_file_info);
11644 music_file_info = next;
11647 new = &music_file_info;
11649 for (i = 0; i < num_music; i++)
11651 music = getMusicListEntry(i);
11653 if (music->filename == NULL)
11656 if (strEqual(music->filename, UNDEFINED_FILENAME))
11659 // a configured file may be not recognized as music
11660 if (!FileIsMusic(music->filename))
11663 if (!music_info_listed(music_file_info, music->filename))
11665 *new = get_music_file_info(music->filename, i);
11668 new = &(*new)->next;
11672 if ((dir = openDirectory(music_directory)) == NULL)
11674 Warn("cannot read music directory '%s'", music_directory);
11679 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
11681 char *basename = dir_entry->basename;
11682 boolean music_already_used = FALSE;
11685 // skip all music files that are configured in music config file
11686 for (i = 0; i < num_music; i++)
11688 music = getMusicListEntry(i);
11690 if (music->filename == NULL)
11693 if (strEqual(basename, music->filename))
11695 music_already_used = TRUE;
11700 if (music_already_used)
11703 if (!FileIsMusic(dir_entry->filename))
11706 if (!music_info_listed(music_file_info, basename))
11708 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
11711 new = &(*new)->next;
11714 num_music_noconf++;
11717 closeDirectory(dir);
11719 for (i = 0; i < num_sounds; i++)
11721 sound = getSoundListEntry(i);
11723 if (sound->filename == NULL)
11726 if (strEqual(sound->filename, UNDEFINED_FILENAME))
11729 // a configured file may be not recognized as sound
11730 if (!FileIsSound(sound->filename))
11733 if (!sound_info_listed(music_file_info, sound->filename))
11735 *new = get_sound_file_info(sound->filename, i);
11737 new = &(*new)->next;
11742 static void add_helpanim_entry(int element, int action, int direction,
11743 int delay, int *num_list_entries)
11745 struct HelpAnimInfo *new_list_entry;
11746 (*num_list_entries)++;
11749 checked_realloc(helpanim_info,
11750 *num_list_entries * sizeof(struct HelpAnimInfo));
11751 new_list_entry = &helpanim_info[*num_list_entries - 1];
11753 new_list_entry->element = element;
11754 new_list_entry->action = action;
11755 new_list_entry->direction = direction;
11756 new_list_entry->delay = delay;
11759 static void print_unknown_token(char *filename, char *token, int token_nr)
11764 Warn("unknown token(s) found in config file:");
11765 Warn("- config file: '%s'", filename);
11768 Warn("- token: '%s'", token);
11771 static void print_unknown_token_end(int token_nr)
11777 void LoadHelpAnimInfo(void)
11779 char *filename = getHelpAnimFilename();
11780 SetupFileList *setup_file_list = NULL, *list;
11781 SetupFileHash *element_hash, *action_hash, *direction_hash;
11782 int num_list_entries = 0;
11783 int num_unknown_tokens = 0;
11786 if (fileExists(filename))
11787 setup_file_list = loadSetupFileList(filename);
11789 if (setup_file_list == NULL)
11791 // use reliable default values from static configuration
11792 SetupFileList *insert_ptr;
11794 insert_ptr = setup_file_list =
11795 newSetupFileList(helpanim_config[0].token,
11796 helpanim_config[0].value);
11798 for (i = 1; helpanim_config[i].token; i++)
11799 insert_ptr = addListEntry(insert_ptr,
11800 helpanim_config[i].token,
11801 helpanim_config[i].value);
11804 element_hash = newSetupFileHash();
11805 action_hash = newSetupFileHash();
11806 direction_hash = newSetupFileHash();
11808 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
11809 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11811 for (i = 0; i < NUM_ACTIONS; i++)
11812 setHashEntry(action_hash, element_action_info[i].suffix,
11813 i_to_a(element_action_info[i].value));
11815 // do not store direction index (bit) here, but direction value!
11816 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
11817 setHashEntry(direction_hash, element_direction_info[i].suffix,
11818 i_to_a(1 << element_direction_info[i].value));
11820 for (list = setup_file_list; list != NULL; list = list->next)
11822 char *element_token, *action_token, *direction_token;
11823 char *element_value, *action_value, *direction_value;
11824 int delay = atoi(list->value);
11826 if (strEqual(list->token, "end"))
11828 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11833 /* first try to break element into element/action/direction parts;
11834 if this does not work, also accept combined "element[.act][.dir]"
11835 elements (like "dynamite.active"), which are unique elements */
11837 if (strchr(list->token, '.') == NULL) // token contains no '.'
11839 element_value = getHashEntry(element_hash, list->token);
11840 if (element_value != NULL) // element found
11841 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11842 &num_list_entries);
11845 // no further suffixes found -- this is not an element
11846 print_unknown_token(filename, list->token, num_unknown_tokens++);
11852 // token has format "<prefix>.<something>"
11854 action_token = strchr(list->token, '.'); // suffix may be action ...
11855 direction_token = action_token; // ... or direction
11857 element_token = getStringCopy(list->token);
11858 *strchr(element_token, '.') = '\0';
11860 element_value = getHashEntry(element_hash, element_token);
11862 if (element_value == NULL) // this is no element
11864 element_value = getHashEntry(element_hash, list->token);
11865 if (element_value != NULL) // combined element found
11866 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11867 &num_list_entries);
11869 print_unknown_token(filename, list->token, num_unknown_tokens++);
11871 free(element_token);
11876 action_value = getHashEntry(action_hash, action_token);
11878 if (action_value != NULL) // action found
11880 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
11881 &num_list_entries);
11883 free(element_token);
11888 direction_value = getHashEntry(direction_hash, direction_token);
11890 if (direction_value != NULL) // direction found
11892 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
11893 &num_list_entries);
11895 free(element_token);
11900 if (strchr(action_token + 1, '.') == NULL)
11902 // no further suffixes found -- this is not an action nor direction
11904 element_value = getHashEntry(element_hash, list->token);
11905 if (element_value != NULL) // combined element found
11906 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11907 &num_list_entries);
11909 print_unknown_token(filename, list->token, num_unknown_tokens++);
11911 free(element_token);
11916 // token has format "<prefix>.<suffix>.<something>"
11918 direction_token = strchr(action_token + 1, '.');
11920 action_token = getStringCopy(action_token);
11921 *strchr(action_token + 1, '.') = '\0';
11923 action_value = getHashEntry(action_hash, action_token);
11925 if (action_value == NULL) // this is no action
11927 element_value = getHashEntry(element_hash, list->token);
11928 if (element_value != NULL) // combined element found
11929 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11930 &num_list_entries);
11932 print_unknown_token(filename, list->token, num_unknown_tokens++);
11934 free(element_token);
11935 free(action_token);
11940 direction_value = getHashEntry(direction_hash, direction_token);
11942 if (direction_value != NULL) // direction found
11944 add_helpanim_entry(atoi(element_value), atoi(action_value),
11945 atoi(direction_value), delay, &num_list_entries);
11947 free(element_token);
11948 free(action_token);
11953 // this is no direction
11955 element_value = getHashEntry(element_hash, list->token);
11956 if (element_value != NULL) // combined element found
11957 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11958 &num_list_entries);
11960 print_unknown_token(filename, list->token, num_unknown_tokens++);
11962 free(element_token);
11963 free(action_token);
11966 print_unknown_token_end(num_unknown_tokens);
11968 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11969 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
11971 freeSetupFileList(setup_file_list);
11972 freeSetupFileHash(element_hash);
11973 freeSetupFileHash(action_hash);
11974 freeSetupFileHash(direction_hash);
11977 for (i = 0; i < num_list_entries; i++)
11978 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
11979 EL_NAME(helpanim_info[i].element),
11980 helpanim_info[i].element,
11981 helpanim_info[i].action,
11982 helpanim_info[i].direction,
11983 helpanim_info[i].delay);
11987 void LoadHelpTextInfo(void)
11989 char *filename = getHelpTextFilename();
11992 if (helptext_info != NULL)
11994 freeSetupFileHash(helptext_info);
11995 helptext_info = NULL;
11998 if (fileExists(filename))
11999 helptext_info = loadSetupFileHash(filename);
12001 if (helptext_info == NULL)
12003 // use reliable default values from static configuration
12004 helptext_info = newSetupFileHash();
12006 for (i = 0; helptext_config[i].token; i++)
12007 setHashEntry(helptext_info,
12008 helptext_config[i].token,
12009 helptext_config[i].value);
12013 BEGIN_HASH_ITERATION(helptext_info, itr)
12015 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
12016 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
12018 END_HASH_ITERATION(hash, itr)
12023 // ----------------------------------------------------------------------------
12025 // ----------------------------------------------------------------------------
12027 #define MAX_NUM_CONVERT_LEVELS 1000
12029 void ConvertLevels(void)
12031 static LevelDirTree *convert_leveldir = NULL;
12032 static int convert_level_nr = -1;
12033 static int num_levels_handled = 0;
12034 static int num_levels_converted = 0;
12035 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
12038 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
12039 global.convert_leveldir);
12041 if (convert_leveldir == NULL)
12042 Fail("no such level identifier: '%s'", global.convert_leveldir);
12044 leveldir_current = convert_leveldir;
12046 if (global.convert_level_nr != -1)
12048 convert_leveldir->first_level = global.convert_level_nr;
12049 convert_leveldir->last_level = global.convert_level_nr;
12052 convert_level_nr = convert_leveldir->first_level;
12054 PrintLine("=", 79);
12055 Print("Converting levels\n");
12056 PrintLine("-", 79);
12057 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
12058 Print("Level series name: '%s'\n", convert_leveldir->name);
12059 Print("Level series author: '%s'\n", convert_leveldir->author);
12060 Print("Number of levels: %d\n", convert_leveldir->levels);
12061 PrintLine("=", 79);
12064 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12065 levels_failed[i] = FALSE;
12067 while (convert_level_nr <= convert_leveldir->last_level)
12069 char *level_filename;
12072 level_nr = convert_level_nr++;
12074 Print("Level %03d: ", level_nr);
12076 LoadLevel(level_nr);
12077 if (level.no_level_file || level.no_valid_file)
12079 Print("(no level)\n");
12083 Print("converting level ... ");
12085 level_filename = getDefaultLevelFilename(level_nr);
12086 new_level = !fileExists(level_filename);
12090 SaveLevel(level_nr);
12092 num_levels_converted++;
12094 Print("converted.\n");
12098 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
12099 levels_failed[level_nr] = TRUE;
12101 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
12104 num_levels_handled++;
12108 PrintLine("=", 79);
12109 Print("Number of levels handled: %d\n", num_levels_handled);
12110 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
12111 (num_levels_handled ?
12112 num_levels_converted * 100 / num_levels_handled : 0));
12113 PrintLine("-", 79);
12114 Print("Summary (for automatic parsing by scripts):\n");
12115 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
12116 convert_leveldir->identifier, num_levels_converted,
12117 num_levels_handled,
12118 (num_levels_handled ?
12119 num_levels_converted * 100 / num_levels_handled : 0));
12121 if (num_levels_handled != num_levels_converted)
12123 Print(", FAILED:");
12124 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12125 if (levels_failed[i])
12130 PrintLine("=", 79);
12132 CloseAllAndExit(0);
12136 // ----------------------------------------------------------------------------
12137 // create and save images for use in level sketches (raw BMP format)
12138 // ----------------------------------------------------------------------------
12140 void CreateLevelSketchImages(void)
12146 InitElementPropertiesGfxElement();
12148 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
12149 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
12151 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12153 int element = getMappedElement(i);
12154 char basename1[16];
12155 char basename2[16];
12159 sprintf(basename1, "%04d.bmp", i);
12160 sprintf(basename2, "%04ds.bmp", i);
12162 filename1 = getPath2(global.create_images_dir, basename1);
12163 filename2 = getPath2(global.create_images_dir, basename2);
12165 DrawSizedElement(0, 0, element, TILESIZE);
12166 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
12168 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
12169 Fail("cannot save level sketch image file '%s'", filename1);
12171 DrawSizedElement(0, 0, element, MINI_TILESIZE);
12172 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
12174 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
12175 Fail("cannot save level sketch image file '%s'", filename2);
12180 // create corresponding SQL statements (for normal and small images)
12183 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12184 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12187 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12188 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12190 // optional: create content for forum level sketch demonstration post
12192 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
12195 FreeBitmap(bitmap1);
12196 FreeBitmap(bitmap2);
12199 fprintf(stderr, "\n");
12201 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
12203 CloseAllAndExit(0);
12207 // ----------------------------------------------------------------------------
12208 // create and save images for custom and group elements (raw BMP format)
12209 // ----------------------------------------------------------------------------
12211 void CreateCustomElementImages(char *directory)
12213 char *src_basename = "RocksCE-template.ilbm";
12214 char *dst_basename = "RocksCE.bmp";
12215 char *src_filename = getPath2(directory, src_basename);
12216 char *dst_filename = getPath2(directory, dst_basename);
12217 Bitmap *src_bitmap;
12219 int yoffset_ce = 0;
12220 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12223 InitVideoDefaults();
12225 ReCreateBitmap(&backbuffer, video.width, video.height);
12227 src_bitmap = LoadImage(src_filename);
12229 bitmap = CreateBitmap(TILEX * 16 * 2,
12230 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12233 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12240 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12241 TILEX * x, TILEY * y + yoffset_ce);
12243 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12245 TILEX * x + TILEX * 16,
12246 TILEY * y + yoffset_ce);
12248 for (j = 2; j >= 0; j--)
12252 BlitBitmap(src_bitmap, bitmap,
12253 TILEX + c * 7, 0, 6, 10,
12254 TILEX * x + 6 + j * 7,
12255 TILEY * y + 11 + yoffset_ce);
12257 BlitBitmap(src_bitmap, bitmap,
12258 TILEX + c * 8, TILEY, 6, 10,
12259 TILEX * 16 + TILEX * x + 6 + j * 8,
12260 TILEY * y + 10 + yoffset_ce);
12266 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12273 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12274 TILEX * x, TILEY * y + yoffset_ge);
12276 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12278 TILEX * x + TILEX * 16,
12279 TILEY * y + yoffset_ge);
12281 for (j = 1; j >= 0; j--)
12285 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12286 TILEX * x + 6 + j * 10,
12287 TILEY * y + 11 + yoffset_ge);
12289 BlitBitmap(src_bitmap, bitmap,
12290 TILEX + c * 8, TILEY + 12, 6, 10,
12291 TILEX * 16 + TILEX * x + 10 + j * 8,
12292 TILEY * y + 10 + yoffset_ge);
12298 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12299 Fail("cannot save CE graphics file '%s'", dst_filename);
12301 FreeBitmap(bitmap);
12303 CloseAllAndExit(0);