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
312 // (these values are different for each player)
315 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
316 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
320 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
321 &li.initial_player_gravity[0], FALSE
325 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
326 &li.use_start_element[0], FALSE
330 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
331 &li.start_element[0], EL_PLAYER_1
335 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
336 &li.use_artwork_element[0], FALSE
340 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
341 &li.artwork_element[0], EL_PLAYER_1
345 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
346 &li.use_explosion_element[0], FALSE
350 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
351 &li.explosion_element[0], EL_PLAYER_1
355 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
356 &li.use_initial_inventory[0], FALSE
360 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
361 &li.initial_inventory_size[0], 1
365 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
366 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
367 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
372 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
373 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
377 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
378 &li.initial_player_gravity[1], FALSE
382 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
383 &li.use_start_element[1], FALSE
387 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
388 &li.start_element[1], EL_PLAYER_2
392 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
393 &li.use_artwork_element[1], FALSE
397 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
398 &li.artwork_element[1], EL_PLAYER_2
402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
403 &li.use_explosion_element[1], FALSE
407 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
408 &li.explosion_element[1], EL_PLAYER_2
412 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
413 &li.use_initial_inventory[1], FALSE
417 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
418 &li.initial_inventory_size[1], 1
422 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
423 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
424 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
429 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
430 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
434 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
435 &li.initial_player_gravity[2], FALSE
439 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
440 &li.use_start_element[2], FALSE
444 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
445 &li.start_element[2], EL_PLAYER_3
449 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
450 &li.use_artwork_element[2], FALSE
454 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
455 &li.artwork_element[2], EL_PLAYER_3
459 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
460 &li.use_explosion_element[2], FALSE
464 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
465 &li.explosion_element[2], EL_PLAYER_3
469 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
470 &li.use_initial_inventory[2], FALSE
474 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
475 &li.initial_inventory_size[2], 1
479 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
480 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
481 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
486 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
487 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
491 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
492 &li.initial_player_gravity[3], FALSE
496 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
497 &li.use_start_element[3], FALSE
501 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
502 &li.start_element[3], EL_PLAYER_4
506 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
507 &li.use_artwork_element[3], FALSE
511 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
512 &li.artwork_element[3], EL_PLAYER_4
516 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
517 &li.use_explosion_element[3], FALSE
521 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
522 &li.explosion_element[3], EL_PLAYER_4
526 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
527 &li.use_initial_inventory[3], FALSE
531 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
532 &li.initial_inventory_size[3], 1
536 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
537 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
538 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
543 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
544 &li.score[SC_EMERALD], 10
549 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
550 &li.score[SC_DIAMOND], 10
555 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
556 &li.score[SC_BUG], 10
561 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
562 &li.score[SC_SPACESHIP], 10
567 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
568 &li.score[SC_PACMAN], 10
573 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
574 &li.score[SC_NUT], 10
579 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
580 &li.score[SC_DYNAMITE], 10
585 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
586 &li.score[SC_KEY], 10
591 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
592 &li.score[SC_PEARL], 10
597 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
598 &li.score[SC_CRYSTAL], 10
603 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
604 &li.amoeba_content, EL_DIAMOND
608 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
614 &li.grow_into_diggable, TRUE
619 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
620 &li.yamyam_content, EL_ROCK, NULL,
621 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
625 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
626 &li.score[SC_YAMYAM], 10
631 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
632 &li.score[SC_ROBOT], 10
636 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
642 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
648 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
649 &li.time_magic_wall, 10
654 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
655 &li.game_of_life[0], 2
659 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
660 &li.game_of_life[1], 3
664 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
665 &li.game_of_life[2], 3
669 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
670 &li.game_of_life[3], 3
674 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
675 &li.use_life_bugs, FALSE
680 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
685 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
690 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
695 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
700 EL_TIMEGATE_SWITCH, -1,
701 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
702 &li.time_timegate, 10
706 EL_LIGHT_SWITCH_ACTIVE, -1,
707 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
712 EL_SHIELD_NORMAL, -1,
713 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
714 &li.shield_normal_time, 10
717 EL_SHIELD_NORMAL, -1,
718 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
719 &li.score[SC_SHIELD], 10
723 EL_SHIELD_DEADLY, -1,
724 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
725 &li.shield_deadly_time, 10
728 EL_SHIELD_DEADLY, -1,
729 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
730 &li.score[SC_SHIELD], 10
735 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
740 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
741 &li.extra_time_score, 10
745 EL_TIME_ORB_FULL, -1,
746 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
747 &li.time_orb_time, 10
750 EL_TIME_ORB_FULL, -1,
751 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
752 &li.use_time_orb_bug, FALSE
757 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
758 &li.use_spring_bug, FALSE
763 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
764 &li.android_move_time, 10
768 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
769 &li.android_clone_time, 10
772 EL_EMC_ANDROID, SAVE_CONF_NEVER,
773 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
774 &li.android_clone_element[0], EL_EMPTY, NULL,
775 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
779 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
780 &li.android_clone_element[0], EL_EMPTY, NULL,
781 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
786 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
791 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
796 EL_EMC_MAGNIFIER, -1,
797 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
798 &li.magnify_score, 10
801 EL_EMC_MAGNIFIER, -1,
802 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
807 EL_EMC_MAGIC_BALL, -1,
808 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
812 EL_EMC_MAGIC_BALL, -1,
813 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
814 &li.ball_random, FALSE
817 EL_EMC_MAGIC_BALL, -1,
818 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
819 &li.ball_active_initial, FALSE
822 EL_EMC_MAGIC_BALL, -1,
823 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
824 &li.ball_content, EL_EMPTY, NULL,
825 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
829 EL_SOKOBAN_FIELD_EMPTY, -1,
830 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
831 &li.sb_fields_needed, TRUE
835 EL_SOKOBAN_OBJECT, -1,
836 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
837 &li.sb_objects_needed, TRUE
842 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
843 &li.mm_laser_red, FALSE
847 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
848 &li.mm_laser_green, FALSE
852 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
853 &li.mm_laser_blue, TRUE
858 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
859 &li.df_laser_red, TRUE
863 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
864 &li.df_laser_green, TRUE
868 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
869 &li.df_laser_blue, FALSE
873 EL_MM_FUSE_ACTIVE, -1,
874 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
879 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
884 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
888 EL_MM_STEEL_BLOCK, -1,
889 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
890 &li.mm_time_block, 75
894 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
895 &li.score[SC_ELEM_BONUS], 10
898 // ---------- unused values -------------------------------------------------
901 EL_UNKNOWN, SAVE_CONF_NEVER,
902 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
903 &li.score[SC_UNKNOWN_15], 10
913 static struct LevelFileConfigInfo chunk_config_NOTE[] =
917 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
918 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
922 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
923 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
928 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
929 &xx_envelope.autowrap, FALSE
933 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
934 &xx_envelope.centered, FALSE
939 TYPE_STRING, CONF_VALUE_BYTES(1),
940 &xx_envelope.text, -1, NULL,
941 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
942 &xx_default_string_empty[0]
952 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
956 TYPE_STRING, CONF_VALUE_BYTES(1),
957 &xx_ei.description[0], -1,
958 &yy_ei.description[0],
959 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
960 &xx_default_description[0]
965 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
966 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
967 &yy_ei.properties[EP_BITFIELD_BASE_NR]
969 #if ENABLE_RESERVED_CODE
970 // (reserved for later use)
973 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
974 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
975 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
981 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
982 &xx_ei.use_gfx_element, FALSE,
983 &yy_ei.use_gfx_element
987 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
988 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
989 &yy_ei.gfx_element_initial
994 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
995 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
996 &yy_ei.access_direction
1001 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1002 &xx_ei.collect_score_initial, 10,
1003 &yy_ei.collect_score_initial
1007 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1008 &xx_ei.collect_count_initial, 1,
1009 &yy_ei.collect_count_initial
1014 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1015 &xx_ei.ce_value_fixed_initial, 0,
1016 &yy_ei.ce_value_fixed_initial
1020 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1021 &xx_ei.ce_value_random_initial, 0,
1022 &yy_ei.ce_value_random_initial
1026 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1027 &xx_ei.use_last_ce_value, FALSE,
1028 &yy_ei.use_last_ce_value
1033 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1034 &xx_ei.push_delay_fixed, 8,
1035 &yy_ei.push_delay_fixed
1039 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1040 &xx_ei.push_delay_random, 8,
1041 &yy_ei.push_delay_random
1045 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1046 &xx_ei.drop_delay_fixed, 0,
1047 &yy_ei.drop_delay_fixed
1051 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1052 &xx_ei.drop_delay_random, 0,
1053 &yy_ei.drop_delay_random
1057 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1058 &xx_ei.move_delay_fixed, 0,
1059 &yy_ei.move_delay_fixed
1063 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1064 &xx_ei.move_delay_random, 0,
1065 &yy_ei.move_delay_random
1070 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1071 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1076 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1077 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1078 &yy_ei.move_direction_initial
1082 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1083 &xx_ei.move_stepsize, TILEX / 8,
1084 &yy_ei.move_stepsize
1089 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1090 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1091 &yy_ei.move_enter_element
1095 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1096 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1097 &yy_ei.move_leave_element
1101 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1102 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1103 &yy_ei.move_leave_type
1108 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1109 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1110 &yy_ei.slippery_type
1115 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1116 &xx_ei.explosion_type, EXPLODES_3X3,
1117 &yy_ei.explosion_type
1121 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1122 &xx_ei.explosion_delay, 16,
1123 &yy_ei.explosion_delay
1127 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1128 &xx_ei.ignition_delay, 8,
1129 &yy_ei.ignition_delay
1134 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1135 &xx_ei.content, EL_EMPTY_SPACE,
1137 &xx_num_contents, 1, 1
1140 // ---------- "num_change_pages" must be the last entry ---------------------
1143 -1, SAVE_CONF_ALWAYS,
1144 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1145 &xx_ei.num_change_pages, 1,
1146 &yy_ei.num_change_pages
1157 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1159 // ---------- "current_change_page" must be the first entry -----------------
1162 -1, SAVE_CONF_ALWAYS,
1163 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1164 &xx_current_change_page, -1
1167 // ---------- (the remaining entries can be in any order) -------------------
1171 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1172 &xx_change.can_change, FALSE
1177 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1178 &xx_event_bits[0], 0
1182 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1183 &xx_event_bits[1], 0
1188 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1189 &xx_change.trigger_player, CH_PLAYER_ANY
1193 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1194 &xx_change.trigger_side, CH_SIDE_ANY
1198 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1199 &xx_change.trigger_page, CH_PAGE_ANY
1204 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1205 &xx_change.target_element, EL_EMPTY_SPACE
1210 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1211 &xx_change.delay_fixed, 0
1215 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1216 &xx_change.delay_random, 0
1220 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1221 &xx_change.delay_frames, FRAMES_PER_SECOND
1226 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1227 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1232 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1233 &xx_change.explode, FALSE
1237 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1238 &xx_change.use_target_content, FALSE
1242 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1243 &xx_change.only_if_complete, FALSE
1247 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1248 &xx_change.use_random_replace, FALSE
1252 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1253 &xx_change.random_percentage, 100
1257 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1258 &xx_change.replace_when, CP_WHEN_EMPTY
1263 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1264 &xx_change.has_action, FALSE
1268 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1269 &xx_change.action_type, CA_NO_ACTION
1273 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1274 &xx_change.action_mode, CA_MODE_UNDEFINED
1278 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1279 &xx_change.action_arg, CA_ARG_UNDEFINED
1284 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1285 &xx_change.action_element, EL_EMPTY_SPACE
1290 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1291 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1292 &xx_num_contents, 1, 1
1302 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1306 TYPE_STRING, CONF_VALUE_BYTES(1),
1307 &xx_ei.description[0], -1, NULL,
1308 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1309 &xx_default_description[0]
1314 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1315 &xx_ei.use_gfx_element, FALSE
1319 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1320 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1325 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1326 &xx_group.choice_mode, ANIM_RANDOM
1331 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1332 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1333 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1343 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1347 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1348 &li.block_snap_field, TRUE
1352 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1353 &li.continuous_snapping, TRUE
1357 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1358 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1362 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1363 &li.use_start_element[0], FALSE
1367 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1368 &li.start_element[0], EL_PLAYER_1
1372 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1373 &li.use_artwork_element[0], FALSE
1377 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1378 &li.artwork_element[0], EL_PLAYER_1
1382 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1383 &li.use_explosion_element[0], FALSE
1387 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1388 &li.explosion_element[0], EL_PLAYER_1
1403 filetype_id_list[] =
1405 { LEVEL_FILE_TYPE_RND, "RND" },
1406 { LEVEL_FILE_TYPE_BD, "BD" },
1407 { LEVEL_FILE_TYPE_EM, "EM" },
1408 { LEVEL_FILE_TYPE_SP, "SP" },
1409 { LEVEL_FILE_TYPE_DX, "DX" },
1410 { LEVEL_FILE_TYPE_SB, "SB" },
1411 { LEVEL_FILE_TYPE_DC, "DC" },
1412 { LEVEL_FILE_TYPE_MM, "MM" },
1413 { LEVEL_FILE_TYPE_MM, "DF" },
1418 // ============================================================================
1419 // level file functions
1420 // ============================================================================
1422 static boolean check_special_flags(char *flag)
1424 if (strEqual(options.special_flags, flag) ||
1425 strEqual(leveldir_current->special_flags, flag))
1431 static struct DateInfo getCurrentDate(void)
1433 time_t epoch_seconds = time(NULL);
1434 struct tm *now = localtime(&epoch_seconds);
1435 struct DateInfo date;
1437 date.year = now->tm_year + 1900;
1438 date.month = now->tm_mon + 1;
1439 date.day = now->tm_mday;
1441 date.src = DATE_SRC_CLOCK;
1446 static void resetEventFlags(struct ElementChangeInfo *change)
1450 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1451 change->has_event[i] = FALSE;
1454 static void resetEventBits(void)
1458 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1459 xx_event_bits[i] = 0;
1462 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1466 /* important: only change event flag if corresponding event bit is set
1467 (this is because all xx_event_bits[] values are loaded separately,
1468 and all xx_event_bits[] values are set back to zero before loading
1469 another value xx_event_bits[x] (each value representing 32 flags)) */
1471 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1472 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1473 change->has_event[i] = TRUE;
1476 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1480 /* in contrast to the above function setEventFlagsFromEventBits(), it
1481 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1482 depending on the corresponding change->has_event[i] values here, as
1483 all xx_event_bits[] values are reset in resetEventBits() before */
1485 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1486 if (change->has_event[i])
1487 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1490 static char *getDefaultElementDescription(struct ElementInfo *ei)
1492 static char description[MAX_ELEMENT_NAME_LEN + 1];
1493 char *default_description = (ei->custom_description != NULL ?
1494 ei->custom_description :
1495 ei->editor_description);
1498 // always start with reliable default values
1499 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1500 description[i] = '\0';
1502 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1503 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1505 return &description[0];
1508 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1510 char *default_description = getDefaultElementDescription(ei);
1513 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1514 ei->description[i] = default_description[i];
1517 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1521 for (i = 0; conf[i].data_type != -1; i++)
1523 int default_value = conf[i].default_value;
1524 int data_type = conf[i].data_type;
1525 int conf_type = conf[i].conf_type;
1526 int byte_mask = conf_type & CONF_MASK_BYTES;
1528 if (byte_mask == CONF_MASK_MULTI_BYTES)
1530 int default_num_entities = conf[i].default_num_entities;
1531 int max_num_entities = conf[i].max_num_entities;
1533 *(int *)(conf[i].num_entities) = default_num_entities;
1535 if (data_type == TYPE_STRING)
1537 char *default_string = conf[i].default_string;
1538 char *string = (char *)(conf[i].value);
1540 strncpy(string, default_string, max_num_entities);
1542 else if (data_type == TYPE_ELEMENT_LIST)
1544 int *element_array = (int *)(conf[i].value);
1547 for (j = 0; j < max_num_entities; j++)
1548 element_array[j] = default_value;
1550 else if (data_type == TYPE_CONTENT_LIST)
1552 struct Content *content = (struct Content *)(conf[i].value);
1555 for (c = 0; c < max_num_entities; c++)
1556 for (y = 0; y < 3; y++)
1557 for (x = 0; x < 3; x++)
1558 content[c].e[x][y] = default_value;
1561 else // constant size configuration data (1, 2 or 4 bytes)
1563 if (data_type == TYPE_BOOLEAN)
1564 *(boolean *)(conf[i].value) = default_value;
1566 *(int *) (conf[i].value) = default_value;
1571 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1575 for (i = 0; conf[i].data_type != -1; i++)
1577 int data_type = conf[i].data_type;
1578 int conf_type = conf[i].conf_type;
1579 int byte_mask = conf_type & CONF_MASK_BYTES;
1581 if (byte_mask == CONF_MASK_MULTI_BYTES)
1583 int max_num_entities = conf[i].max_num_entities;
1585 if (data_type == TYPE_STRING)
1587 char *string = (char *)(conf[i].value);
1588 char *string_copy = (char *)(conf[i].value_copy);
1590 strncpy(string_copy, string, max_num_entities);
1592 else if (data_type == TYPE_ELEMENT_LIST)
1594 int *element_array = (int *)(conf[i].value);
1595 int *element_array_copy = (int *)(conf[i].value_copy);
1598 for (j = 0; j < max_num_entities; j++)
1599 element_array_copy[j] = element_array[j];
1601 else if (data_type == TYPE_CONTENT_LIST)
1603 struct Content *content = (struct Content *)(conf[i].value);
1604 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1607 for (c = 0; c < max_num_entities; c++)
1608 for (y = 0; y < 3; y++)
1609 for (x = 0; x < 3; x++)
1610 content_copy[c].e[x][y] = content[c].e[x][y];
1613 else // constant size configuration data (1, 2 or 4 bytes)
1615 if (data_type == TYPE_BOOLEAN)
1616 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1618 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1623 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1627 xx_ei = *ei_from; // copy element data into temporary buffer
1628 yy_ei = *ei_to; // copy element data into temporary buffer
1630 copyConfigFromConfigList(chunk_config_CUSX_base);
1635 // ---------- reinitialize and copy change pages ----------
1637 ei_to->num_change_pages = ei_from->num_change_pages;
1638 ei_to->current_change_page = ei_from->current_change_page;
1640 setElementChangePages(ei_to, ei_to->num_change_pages);
1642 for (i = 0; i < ei_to->num_change_pages; i++)
1643 ei_to->change_page[i] = ei_from->change_page[i];
1645 // ---------- copy group element info ----------
1646 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1647 *ei_to->group = *ei_from->group;
1649 // mark this custom element as modified
1650 ei_to->modified_settings = TRUE;
1653 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1655 int change_page_size = sizeof(struct ElementChangeInfo);
1657 ei->num_change_pages = MAX(1, change_pages);
1660 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1662 if (ei->current_change_page >= ei->num_change_pages)
1663 ei->current_change_page = ei->num_change_pages - 1;
1665 ei->change = &ei->change_page[ei->current_change_page];
1668 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1670 xx_change = *change; // copy change data into temporary buffer
1672 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1674 *change = xx_change;
1676 resetEventFlags(change);
1678 change->direct_action = 0;
1679 change->other_action = 0;
1681 change->pre_change_function = NULL;
1682 change->change_function = NULL;
1683 change->post_change_function = NULL;
1686 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1690 li = *level; // copy level data into temporary buffer
1691 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1692 *level = li; // copy temporary buffer back to level data
1694 setLevelInfoToDefaults_EM();
1695 setLevelInfoToDefaults_SP();
1696 setLevelInfoToDefaults_MM();
1698 level->native_em_level = &native_em_level;
1699 level->native_sp_level = &native_sp_level;
1700 level->native_mm_level = &native_mm_level;
1702 level->file_version = FILE_VERSION_ACTUAL;
1703 level->game_version = GAME_VERSION_ACTUAL;
1705 level->creation_date = getCurrentDate();
1707 level->encoding_16bit_field = TRUE;
1708 level->encoding_16bit_yamyam = TRUE;
1709 level->encoding_16bit_amoeba = TRUE;
1711 // clear level name and level author string buffers
1712 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1713 level->name[i] = '\0';
1714 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1715 level->author[i] = '\0';
1717 // set level name and level author to default values
1718 strcpy(level->name, NAMELESS_LEVEL_NAME);
1719 strcpy(level->author, ANONYMOUS_NAME);
1721 // set level playfield to playable default level with player and exit
1722 for (x = 0; x < MAX_LEV_FIELDX; x++)
1723 for (y = 0; y < MAX_LEV_FIELDY; y++)
1724 level->field[x][y] = EL_SAND;
1726 level->field[0][0] = EL_PLAYER_1;
1727 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1729 BorderElement = EL_STEELWALL;
1731 // detect custom elements when loading them
1732 level->file_has_custom_elements = FALSE;
1734 // set all bug compatibility flags to "false" => do not emulate this bug
1735 level->use_action_after_change_bug = FALSE;
1737 if (leveldir_current)
1739 // try to determine better author name than 'anonymous'
1740 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1742 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1743 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1747 switch (LEVELCLASS(leveldir_current))
1749 case LEVELCLASS_TUTORIAL:
1750 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1753 case LEVELCLASS_CONTRIB:
1754 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1755 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1758 case LEVELCLASS_PRIVATE:
1759 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1760 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1764 // keep default value
1771 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1773 static boolean clipboard_elements_initialized = FALSE;
1776 InitElementPropertiesStatic();
1778 li = *level; // copy level data into temporary buffer
1779 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1780 *level = li; // copy temporary buffer back to level data
1782 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1785 struct ElementInfo *ei = &element_info[element];
1787 // never initialize clipboard elements after the very first time
1788 // (to be able to use clipboard elements between several levels)
1789 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1792 if (IS_ENVELOPE(element))
1794 int envelope_nr = element - EL_ENVELOPE_1;
1796 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1798 level->envelope[envelope_nr] = xx_envelope;
1801 if (IS_CUSTOM_ELEMENT(element) ||
1802 IS_GROUP_ELEMENT(element) ||
1803 IS_INTERNAL_ELEMENT(element))
1805 xx_ei = *ei; // copy element data into temporary buffer
1807 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1812 setElementChangePages(ei, 1);
1813 setElementChangeInfoToDefaults(ei->change);
1815 if (IS_CUSTOM_ELEMENT(element) ||
1816 IS_GROUP_ELEMENT(element) ||
1817 IS_INTERNAL_ELEMENT(element))
1819 setElementDescriptionToDefault(ei);
1821 ei->modified_settings = FALSE;
1824 if (IS_CUSTOM_ELEMENT(element) ||
1825 IS_INTERNAL_ELEMENT(element))
1827 // internal values used in level editor
1829 ei->access_type = 0;
1830 ei->access_layer = 0;
1831 ei->access_protected = 0;
1832 ei->walk_to_action = 0;
1833 ei->smash_targets = 0;
1836 ei->can_explode_by_fire = FALSE;
1837 ei->can_explode_smashed = FALSE;
1838 ei->can_explode_impact = FALSE;
1840 ei->current_change_page = 0;
1843 if (IS_GROUP_ELEMENT(element) ||
1844 IS_INTERNAL_ELEMENT(element))
1846 struct ElementGroupInfo *group;
1848 // initialize memory for list of elements in group
1849 if (ei->group == NULL)
1850 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1854 xx_group = *group; // copy group data into temporary buffer
1856 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1862 clipboard_elements_initialized = TRUE;
1865 static void setLevelInfoToDefaults(struct LevelInfo *level,
1866 boolean level_info_only,
1867 boolean reset_file_status)
1869 setLevelInfoToDefaults_Level(level);
1871 if (!level_info_only)
1872 setLevelInfoToDefaults_Elements(level);
1874 if (reset_file_status)
1876 level->no_valid_file = FALSE;
1877 level->no_level_file = FALSE;
1880 level->changed = FALSE;
1883 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1885 level_file_info->nr = 0;
1886 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1887 level_file_info->packed = FALSE;
1889 setString(&level_file_info->basename, NULL);
1890 setString(&level_file_info->filename, NULL);
1893 int getMappedElement_SB(int, boolean);
1895 static void ActivateLevelTemplate(void)
1899 if (check_special_flags("load_xsb_to_ces"))
1901 // fill smaller playfields with padding "beyond border wall" elements
1902 if (level.fieldx < level_template.fieldx ||
1903 level.fieldy < level_template.fieldy)
1905 short field[level.fieldx][level.fieldy];
1906 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1907 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1908 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1909 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1911 // copy old playfield (which is smaller than the visible area)
1912 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1913 field[x][y] = level.field[x][y];
1915 // fill new, larger playfield with "beyond border wall" elements
1916 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1917 level.field[x][y] = getMappedElement_SB('_', TRUE);
1919 // copy the old playfield to the middle of the new playfield
1920 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1921 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1923 level.fieldx = new_fieldx;
1924 level.fieldy = new_fieldy;
1928 // Currently there is no special action needed to activate the template
1929 // data, because 'element_info' property settings overwrite the original
1930 // level data, while all other variables do not change.
1932 // Exception: 'from_level_template' elements in the original level playfield
1933 // are overwritten with the corresponding elements at the same position in
1934 // playfield from the level template.
1936 for (x = 0; x < level.fieldx; x++)
1937 for (y = 0; y < level.fieldy; y++)
1938 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1939 level.field[x][y] = level_template.field[x][y];
1941 if (check_special_flags("load_xsb_to_ces"))
1943 struct LevelInfo level_backup = level;
1945 // overwrite all individual level settings from template level settings
1946 level = level_template;
1948 // restore level file info
1949 level.file_info = level_backup.file_info;
1951 // restore playfield size
1952 level.fieldx = level_backup.fieldx;
1953 level.fieldy = level_backup.fieldy;
1955 // restore playfield content
1956 for (x = 0; x < level.fieldx; x++)
1957 for (y = 0; y < level.fieldy; y++)
1958 level.field[x][y] = level_backup.field[x][y];
1960 // restore name and author from individual level
1961 strcpy(level.name, level_backup.name);
1962 strcpy(level.author, level_backup.author);
1964 // restore flag "use_custom_template"
1965 level.use_custom_template = level_backup.use_custom_template;
1969 static char *getLevelFilenameFromBasename(char *basename)
1971 static char *filename = NULL;
1973 checked_free(filename);
1975 filename = getPath2(getCurrentLevelDir(), basename);
1980 static int getFileTypeFromBasename(char *basename)
1982 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
1984 static char *filename = NULL;
1985 struct stat file_status;
1987 // ---------- try to determine file type from filename ----------
1989 // check for typical filename of a Supaplex level package file
1990 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1991 return LEVEL_FILE_TYPE_SP;
1993 // check for typical filename of a Diamond Caves II level package file
1994 if (strSuffixLower(basename, ".dc") ||
1995 strSuffixLower(basename, ".dc2"))
1996 return LEVEL_FILE_TYPE_DC;
1998 // check for typical filename of a Sokoban level package file
1999 if (strSuffixLower(basename, ".xsb") &&
2000 strchr(basename, '%') == NULL)
2001 return LEVEL_FILE_TYPE_SB;
2003 // ---------- try to determine file type from filesize ----------
2005 checked_free(filename);
2006 filename = getPath2(getCurrentLevelDir(), basename);
2008 if (stat(filename, &file_status) == 0)
2010 // check for typical filesize of a Supaplex level package file
2011 if (file_status.st_size == 170496)
2012 return LEVEL_FILE_TYPE_SP;
2015 return LEVEL_FILE_TYPE_UNKNOWN;
2018 static int getFileTypeFromMagicBytes(char *filename, int type)
2022 if ((file = openFile(filename, MODE_READ)))
2024 char chunk_name[CHUNK_ID_LEN + 1];
2026 getFileChunkBE(file, chunk_name, NULL);
2028 if (strEqual(chunk_name, "MMII") ||
2029 strEqual(chunk_name, "MIRR"))
2030 type = LEVEL_FILE_TYPE_MM;
2038 static boolean checkForPackageFromBasename(char *basename)
2040 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2041 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2043 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2046 static char *getSingleLevelBasenameExt(int nr, char *extension)
2048 static char basename[MAX_FILENAME_LEN];
2051 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2053 sprintf(basename, "%03d.%s", nr, extension);
2058 static char *getSingleLevelBasename(int nr)
2060 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2063 static char *getPackedLevelBasename(int type)
2065 static char basename[MAX_FILENAME_LEN];
2066 char *directory = getCurrentLevelDir();
2068 DirectoryEntry *dir_entry;
2070 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2072 if ((dir = openDirectory(directory)) == NULL)
2074 Warn("cannot read current level directory '%s'", directory);
2079 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2081 char *entry_basename = dir_entry->basename;
2082 int entry_type = getFileTypeFromBasename(entry_basename);
2084 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2086 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2089 strcpy(basename, entry_basename);
2096 closeDirectory(dir);
2101 static char *getSingleLevelFilename(int nr)
2103 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2106 #if ENABLE_UNUSED_CODE
2107 static char *getPackedLevelFilename(int type)
2109 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2113 char *getDefaultLevelFilename(int nr)
2115 return getSingleLevelFilename(nr);
2118 #if ENABLE_UNUSED_CODE
2119 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2123 lfi->packed = FALSE;
2125 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2126 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2130 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2131 int type, char *format, ...)
2133 static char basename[MAX_FILENAME_LEN];
2136 va_start(ap, format);
2137 vsprintf(basename, format, ap);
2141 lfi->packed = FALSE;
2143 setString(&lfi->basename, basename);
2144 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2147 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2153 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2154 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2157 static int getFiletypeFromID(char *filetype_id)
2159 char *filetype_id_lower;
2160 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2163 if (filetype_id == NULL)
2164 return LEVEL_FILE_TYPE_UNKNOWN;
2166 filetype_id_lower = getStringToLower(filetype_id);
2168 for (i = 0; filetype_id_list[i].id != NULL; i++)
2170 char *id_lower = getStringToLower(filetype_id_list[i].id);
2172 if (strEqual(filetype_id_lower, id_lower))
2173 filetype = filetype_id_list[i].filetype;
2177 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2181 free(filetype_id_lower);
2186 char *getLocalLevelTemplateFilename(void)
2188 return getDefaultLevelFilename(-1);
2191 char *getGlobalLevelTemplateFilename(void)
2193 // global variable "leveldir_current" must be modified in the loop below
2194 LevelDirTree *leveldir_current_last = leveldir_current;
2195 char *filename = NULL;
2197 // check for template level in path from current to topmost tree node
2199 while (leveldir_current != NULL)
2201 filename = getDefaultLevelFilename(-1);
2203 if (fileExists(filename))
2206 leveldir_current = leveldir_current->node_parent;
2209 // restore global variable "leveldir_current" modified in above loop
2210 leveldir_current = leveldir_current_last;
2215 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2219 // special case: level number is negative => check for level template file
2222 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2223 getSingleLevelBasename(-1));
2225 // replace local level template filename with global template filename
2226 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2228 // no fallback if template file not existing
2232 // special case: check for file name/pattern specified in "levelinfo.conf"
2233 if (leveldir_current->level_filename != NULL)
2235 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2237 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2238 leveldir_current->level_filename, nr);
2240 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2242 if (fileExists(lfi->filename))
2245 else if (leveldir_current->level_filetype != NULL)
2247 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2249 // check for specified native level file with standard file name
2250 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2251 "%03d.%s", nr, LEVELFILE_EXTENSION);
2252 if (fileExists(lfi->filename))
2256 // check for native Rocks'n'Diamonds level file
2257 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2258 "%03d.%s", nr, LEVELFILE_EXTENSION);
2259 if (fileExists(lfi->filename))
2262 // check for Emerald Mine level file (V1)
2263 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2264 'a' + (nr / 10) % 26, '0' + nr % 10);
2265 if (fileExists(lfi->filename))
2267 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2268 'A' + (nr / 10) % 26, '0' + nr % 10);
2269 if (fileExists(lfi->filename))
2272 // check for Emerald Mine level file (V2 to V5)
2273 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2274 if (fileExists(lfi->filename))
2277 // check for Emerald Mine level file (V6 / single mode)
2278 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2279 if (fileExists(lfi->filename))
2281 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2282 if (fileExists(lfi->filename))
2285 // check for Emerald Mine level file (V6 / teamwork mode)
2286 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2287 if (fileExists(lfi->filename))
2289 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2290 if (fileExists(lfi->filename))
2293 // check for various packed level file formats
2294 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2295 if (fileExists(lfi->filename))
2298 // no known level file found -- use default values (and fail later)
2299 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2300 "%03d.%s", nr, LEVELFILE_EXTENSION);
2303 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2305 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2306 lfi->type = getFileTypeFromBasename(lfi->basename);
2308 if (lfi->type == LEVEL_FILE_TYPE_RND)
2309 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2312 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2314 // always start with reliable default values
2315 setFileInfoToDefaults(level_file_info);
2317 level_file_info->nr = nr; // set requested level number
2319 determineLevelFileInfo_Filename(level_file_info);
2320 determineLevelFileInfo_Filetype(level_file_info);
2323 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2324 struct LevelFileInfo *lfi_to)
2326 lfi_to->nr = lfi_from->nr;
2327 lfi_to->type = lfi_from->type;
2328 lfi_to->packed = lfi_from->packed;
2330 setString(&lfi_to->basename, lfi_from->basename);
2331 setString(&lfi_to->filename, lfi_from->filename);
2334 // ----------------------------------------------------------------------------
2335 // functions for loading R'n'D level
2336 // ----------------------------------------------------------------------------
2338 int getMappedElement(int element)
2340 // remap some (historic, now obsolete) elements
2344 case EL_PLAYER_OBSOLETE:
2345 element = EL_PLAYER_1;
2348 case EL_KEY_OBSOLETE:
2352 case EL_EM_KEY_1_FILE_OBSOLETE:
2353 element = EL_EM_KEY_1;
2356 case EL_EM_KEY_2_FILE_OBSOLETE:
2357 element = EL_EM_KEY_2;
2360 case EL_EM_KEY_3_FILE_OBSOLETE:
2361 element = EL_EM_KEY_3;
2364 case EL_EM_KEY_4_FILE_OBSOLETE:
2365 element = EL_EM_KEY_4;
2368 case EL_ENVELOPE_OBSOLETE:
2369 element = EL_ENVELOPE_1;
2377 if (element >= NUM_FILE_ELEMENTS)
2379 Warn("invalid level element %d", element);
2381 element = EL_UNKNOWN;
2389 static int getMappedElementByVersion(int element, int game_version)
2391 // remap some elements due to certain game version
2393 if (game_version <= VERSION_IDENT(2,2,0,0))
2395 // map game font elements
2396 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2397 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2398 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2399 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2402 if (game_version < VERSION_IDENT(3,0,0,0))
2404 // map Supaplex gravity tube elements
2405 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2406 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2407 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2408 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2415 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2417 level->file_version = getFileVersion(file);
2418 level->game_version = getFileVersion(file);
2423 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2425 level->creation_date.year = getFile16BitBE(file);
2426 level->creation_date.month = getFile8Bit(file);
2427 level->creation_date.day = getFile8Bit(file);
2429 level->creation_date.src = DATE_SRC_LEVELFILE;
2434 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2436 int initial_player_stepsize;
2437 int initial_player_gravity;
2440 level->fieldx = getFile8Bit(file);
2441 level->fieldy = getFile8Bit(file);
2443 level->time = getFile16BitBE(file);
2444 level->gems_needed = getFile16BitBE(file);
2446 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2447 level->name[i] = getFile8Bit(file);
2448 level->name[MAX_LEVEL_NAME_LEN] = 0;
2450 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2451 level->score[i] = getFile8Bit(file);
2453 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2454 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2455 for (y = 0; y < 3; y++)
2456 for (x = 0; x < 3; x++)
2457 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2459 level->amoeba_speed = getFile8Bit(file);
2460 level->time_magic_wall = getFile8Bit(file);
2461 level->time_wheel = getFile8Bit(file);
2462 level->amoeba_content = getMappedElement(getFile8Bit(file));
2464 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2467 for (i = 0; i < MAX_PLAYERS; i++)
2468 level->initial_player_stepsize[i] = initial_player_stepsize;
2470 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2472 for (i = 0; i < MAX_PLAYERS; i++)
2473 level->initial_player_gravity[i] = initial_player_gravity;
2475 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2476 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2478 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2480 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2481 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2482 level->can_move_into_acid_bits = getFile32BitBE(file);
2483 level->dont_collide_with_bits = getFile8Bit(file);
2485 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2486 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2488 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2489 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2490 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2492 level->game_engine_type = getFile8Bit(file);
2494 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2499 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2503 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2504 level->name[i] = getFile8Bit(file);
2505 level->name[MAX_LEVEL_NAME_LEN] = 0;
2510 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2514 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2515 level->author[i] = getFile8Bit(file);
2516 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2521 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2524 int chunk_size_expected = level->fieldx * level->fieldy;
2526 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2527 stored with 16-bit encoding (and should be twice as big then).
2528 Even worse, playfield data was stored 16-bit when only yamyam content
2529 contained 16-bit elements and vice versa. */
2531 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2532 chunk_size_expected *= 2;
2534 if (chunk_size_expected != chunk_size)
2536 ReadUnusedBytesFromFile(file, chunk_size);
2537 return chunk_size_expected;
2540 for (y = 0; y < level->fieldy; y++)
2541 for (x = 0; x < level->fieldx; x++)
2542 level->field[x][y] =
2543 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2548 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2551 int header_size = 4;
2552 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2553 int chunk_size_expected = header_size + content_size;
2555 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2556 stored with 16-bit encoding (and should be twice as big then).
2557 Even worse, playfield data was stored 16-bit when only yamyam content
2558 contained 16-bit elements and vice versa. */
2560 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2561 chunk_size_expected += content_size;
2563 if (chunk_size_expected != chunk_size)
2565 ReadUnusedBytesFromFile(file, chunk_size);
2566 return chunk_size_expected;
2570 level->num_yamyam_contents = getFile8Bit(file);
2574 // correct invalid number of content fields -- should never happen
2575 if (level->num_yamyam_contents < 1 ||
2576 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2577 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2579 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2580 for (y = 0; y < 3; y++)
2581 for (x = 0; x < 3; x++)
2582 level->yamyam_content[i].e[x][y] =
2583 getMappedElement(level->encoding_16bit_field ?
2584 getFile16BitBE(file) : getFile8Bit(file));
2588 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2593 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2595 element = getMappedElement(getFile16BitBE(file));
2596 num_contents = getFile8Bit(file);
2598 getFile8Bit(file); // content x size (unused)
2599 getFile8Bit(file); // content y size (unused)
2601 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2603 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2604 for (y = 0; y < 3; y++)
2605 for (x = 0; x < 3; x++)
2606 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2608 // correct invalid number of content fields -- should never happen
2609 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2610 num_contents = STD_ELEMENT_CONTENTS;
2612 if (element == EL_YAMYAM)
2614 level->num_yamyam_contents = num_contents;
2616 for (i = 0; i < num_contents; i++)
2617 for (y = 0; y < 3; y++)
2618 for (x = 0; x < 3; x++)
2619 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2621 else if (element == EL_BD_AMOEBA)
2623 level->amoeba_content = content_array[0][0][0];
2627 Warn("cannot load content for element '%d'", element);
2633 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2639 int chunk_size_expected;
2641 element = getMappedElement(getFile16BitBE(file));
2642 if (!IS_ENVELOPE(element))
2643 element = EL_ENVELOPE_1;
2645 envelope_nr = element - EL_ENVELOPE_1;
2647 envelope_len = getFile16BitBE(file);
2649 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2650 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2652 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2654 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2655 if (chunk_size_expected != chunk_size)
2657 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2658 return chunk_size_expected;
2661 for (i = 0; i < envelope_len; i++)
2662 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2667 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2669 int num_changed_custom_elements = getFile16BitBE(file);
2670 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2673 if (chunk_size_expected != chunk_size)
2675 ReadUnusedBytesFromFile(file, chunk_size - 2);
2676 return chunk_size_expected;
2679 for (i = 0; i < num_changed_custom_elements; i++)
2681 int element = getMappedElement(getFile16BitBE(file));
2682 int properties = getFile32BitBE(file);
2684 if (IS_CUSTOM_ELEMENT(element))
2685 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2687 Warn("invalid custom element number %d", element);
2689 // older game versions that wrote level files with CUS1 chunks used
2690 // different default push delay values (not yet stored in level file)
2691 element_info[element].push_delay_fixed = 2;
2692 element_info[element].push_delay_random = 8;
2695 level->file_has_custom_elements = TRUE;
2700 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2702 int num_changed_custom_elements = getFile16BitBE(file);
2703 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2706 if (chunk_size_expected != chunk_size)
2708 ReadUnusedBytesFromFile(file, chunk_size - 2);
2709 return chunk_size_expected;
2712 for (i = 0; i < num_changed_custom_elements; i++)
2714 int element = getMappedElement(getFile16BitBE(file));
2715 int custom_target_element = getMappedElement(getFile16BitBE(file));
2717 if (IS_CUSTOM_ELEMENT(element))
2718 element_info[element].change->target_element = custom_target_element;
2720 Warn("invalid custom element number %d", element);
2723 level->file_has_custom_elements = TRUE;
2728 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2730 int num_changed_custom_elements = getFile16BitBE(file);
2731 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2734 if (chunk_size_expected != chunk_size)
2736 ReadUnusedBytesFromFile(file, chunk_size - 2);
2737 return chunk_size_expected;
2740 for (i = 0; i < num_changed_custom_elements; i++)
2742 int element = getMappedElement(getFile16BitBE(file));
2743 struct ElementInfo *ei = &element_info[element];
2744 unsigned int event_bits;
2746 if (!IS_CUSTOM_ELEMENT(element))
2748 Warn("invalid custom element number %d", element);
2750 element = EL_INTERNAL_DUMMY;
2753 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2754 ei->description[j] = getFile8Bit(file);
2755 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2757 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2759 // some free bytes for future properties and padding
2760 ReadUnusedBytesFromFile(file, 7);
2762 ei->use_gfx_element = getFile8Bit(file);
2763 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2765 ei->collect_score_initial = getFile8Bit(file);
2766 ei->collect_count_initial = getFile8Bit(file);
2768 ei->push_delay_fixed = getFile16BitBE(file);
2769 ei->push_delay_random = getFile16BitBE(file);
2770 ei->move_delay_fixed = getFile16BitBE(file);
2771 ei->move_delay_random = getFile16BitBE(file);
2773 ei->move_pattern = getFile16BitBE(file);
2774 ei->move_direction_initial = getFile8Bit(file);
2775 ei->move_stepsize = getFile8Bit(file);
2777 for (y = 0; y < 3; y++)
2778 for (x = 0; x < 3; x++)
2779 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2781 event_bits = getFile32BitBE(file);
2782 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2783 if (event_bits & (1 << j))
2784 ei->change->has_event[j] = TRUE;
2786 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2788 ei->change->delay_fixed = getFile16BitBE(file);
2789 ei->change->delay_random = getFile16BitBE(file);
2790 ei->change->delay_frames = getFile16BitBE(file);
2792 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2794 ei->change->explode = getFile8Bit(file);
2795 ei->change->use_target_content = getFile8Bit(file);
2796 ei->change->only_if_complete = getFile8Bit(file);
2797 ei->change->use_random_replace = getFile8Bit(file);
2799 ei->change->random_percentage = getFile8Bit(file);
2800 ei->change->replace_when = getFile8Bit(file);
2802 for (y = 0; y < 3; y++)
2803 for (x = 0; x < 3; x++)
2804 ei->change->target_content.e[x][y] =
2805 getMappedElement(getFile16BitBE(file));
2807 ei->slippery_type = getFile8Bit(file);
2809 // some free bytes for future properties and padding
2810 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2812 // mark that this custom element has been modified
2813 ei->modified_settings = TRUE;
2816 level->file_has_custom_elements = TRUE;
2821 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2823 struct ElementInfo *ei;
2824 int chunk_size_expected;
2828 // ---------- custom element base property values (96 bytes) ----------------
2830 element = getMappedElement(getFile16BitBE(file));
2832 if (!IS_CUSTOM_ELEMENT(element))
2834 Warn("invalid custom element number %d", element);
2836 ReadUnusedBytesFromFile(file, chunk_size - 2);
2841 ei = &element_info[element];
2843 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2844 ei->description[i] = getFile8Bit(file);
2845 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2847 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2849 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2851 ei->num_change_pages = getFile8Bit(file);
2853 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2854 if (chunk_size_expected != chunk_size)
2856 ReadUnusedBytesFromFile(file, chunk_size - 43);
2857 return chunk_size_expected;
2860 ei->ce_value_fixed_initial = getFile16BitBE(file);
2861 ei->ce_value_random_initial = getFile16BitBE(file);
2862 ei->use_last_ce_value = getFile8Bit(file);
2864 ei->use_gfx_element = getFile8Bit(file);
2865 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2867 ei->collect_score_initial = getFile8Bit(file);
2868 ei->collect_count_initial = getFile8Bit(file);
2870 ei->drop_delay_fixed = getFile8Bit(file);
2871 ei->push_delay_fixed = getFile8Bit(file);
2872 ei->drop_delay_random = getFile8Bit(file);
2873 ei->push_delay_random = getFile8Bit(file);
2874 ei->move_delay_fixed = getFile16BitBE(file);
2875 ei->move_delay_random = getFile16BitBE(file);
2877 // bits 0 - 15 of "move_pattern" ...
2878 ei->move_pattern = getFile16BitBE(file);
2879 ei->move_direction_initial = getFile8Bit(file);
2880 ei->move_stepsize = getFile8Bit(file);
2882 ei->slippery_type = getFile8Bit(file);
2884 for (y = 0; y < 3; y++)
2885 for (x = 0; x < 3; x++)
2886 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2888 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2889 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2890 ei->move_leave_type = getFile8Bit(file);
2892 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2893 ei->move_pattern |= (getFile16BitBE(file) << 16);
2895 ei->access_direction = getFile8Bit(file);
2897 ei->explosion_delay = getFile8Bit(file);
2898 ei->ignition_delay = getFile8Bit(file);
2899 ei->explosion_type = getFile8Bit(file);
2901 // some free bytes for future custom property values and padding
2902 ReadUnusedBytesFromFile(file, 1);
2904 // ---------- change page property values (48 bytes) ------------------------
2906 setElementChangePages(ei, ei->num_change_pages);
2908 for (i = 0; i < ei->num_change_pages; i++)
2910 struct ElementChangeInfo *change = &ei->change_page[i];
2911 unsigned int event_bits;
2913 // always start with reliable default values
2914 setElementChangeInfoToDefaults(change);
2916 // bits 0 - 31 of "has_event[]" ...
2917 event_bits = getFile32BitBE(file);
2918 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2919 if (event_bits & (1 << j))
2920 change->has_event[j] = TRUE;
2922 change->target_element = getMappedElement(getFile16BitBE(file));
2924 change->delay_fixed = getFile16BitBE(file);
2925 change->delay_random = getFile16BitBE(file);
2926 change->delay_frames = getFile16BitBE(file);
2928 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2930 change->explode = getFile8Bit(file);
2931 change->use_target_content = getFile8Bit(file);
2932 change->only_if_complete = getFile8Bit(file);
2933 change->use_random_replace = getFile8Bit(file);
2935 change->random_percentage = getFile8Bit(file);
2936 change->replace_when = getFile8Bit(file);
2938 for (y = 0; y < 3; y++)
2939 for (x = 0; x < 3; x++)
2940 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2942 change->can_change = getFile8Bit(file);
2944 change->trigger_side = getFile8Bit(file);
2946 change->trigger_player = getFile8Bit(file);
2947 change->trigger_page = getFile8Bit(file);
2949 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2950 CH_PAGE_ANY : (1 << change->trigger_page));
2952 change->has_action = getFile8Bit(file);
2953 change->action_type = getFile8Bit(file);
2954 change->action_mode = getFile8Bit(file);
2955 change->action_arg = getFile16BitBE(file);
2957 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
2958 event_bits = getFile8Bit(file);
2959 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2960 if (event_bits & (1 << (j - 32)))
2961 change->has_event[j] = TRUE;
2964 // mark this custom element as modified
2965 ei->modified_settings = TRUE;
2967 level->file_has_custom_elements = TRUE;
2972 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2974 struct ElementInfo *ei;
2975 struct ElementGroupInfo *group;
2979 element = getMappedElement(getFile16BitBE(file));
2981 if (!IS_GROUP_ELEMENT(element))
2983 Warn("invalid group element number %d", element);
2985 ReadUnusedBytesFromFile(file, chunk_size - 2);
2990 ei = &element_info[element];
2992 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2993 ei->description[i] = getFile8Bit(file);
2994 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2996 group = element_info[element].group;
2998 group->num_elements = getFile8Bit(file);
3000 ei->use_gfx_element = getFile8Bit(file);
3001 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3003 group->choice_mode = getFile8Bit(file);
3005 // some free bytes for future values and padding
3006 ReadUnusedBytesFromFile(file, 3);
3008 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3009 group->element[i] = getMappedElement(getFile16BitBE(file));
3011 // mark this group element as modified
3012 element_info[element].modified_settings = TRUE;
3014 level->file_has_custom_elements = TRUE;
3019 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3020 int element, int real_element)
3022 int micro_chunk_size = 0;
3023 int conf_type = getFile8Bit(file);
3024 int byte_mask = conf_type & CONF_MASK_BYTES;
3025 boolean element_found = FALSE;
3028 micro_chunk_size += 1;
3030 if (byte_mask == CONF_MASK_MULTI_BYTES)
3032 int num_bytes = getFile16BitBE(file);
3033 byte *buffer = checked_malloc(num_bytes);
3035 ReadBytesFromFile(file, buffer, num_bytes);
3037 for (i = 0; conf[i].data_type != -1; i++)
3039 if (conf[i].element == element &&
3040 conf[i].conf_type == conf_type)
3042 int data_type = conf[i].data_type;
3043 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3044 int max_num_entities = conf[i].max_num_entities;
3046 if (num_entities > max_num_entities)
3048 Warn("truncating number of entities for element %d from %d to %d",
3049 element, num_entities, max_num_entities);
3051 num_entities = max_num_entities;
3054 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3055 data_type == TYPE_CONTENT_LIST))
3057 // for element and content lists, zero entities are not allowed
3058 Warn("found empty list of entities for element %d", element);
3060 // do not set "num_entities" here to prevent reading behind buffer
3062 *(int *)(conf[i].num_entities) = 1; // at least one is required
3066 *(int *)(conf[i].num_entities) = num_entities;
3069 element_found = TRUE;
3071 if (data_type == TYPE_STRING)
3073 char *string = (char *)(conf[i].value);
3076 for (j = 0; j < max_num_entities; j++)
3077 string[j] = (j < num_entities ? buffer[j] : '\0');
3079 else if (data_type == TYPE_ELEMENT_LIST)
3081 int *element_array = (int *)(conf[i].value);
3084 for (j = 0; j < num_entities; j++)
3086 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3088 else if (data_type == TYPE_CONTENT_LIST)
3090 struct Content *content= (struct Content *)(conf[i].value);
3093 for (c = 0; c < num_entities; c++)
3094 for (y = 0; y < 3; y++)
3095 for (x = 0; x < 3; x++)
3096 content[c].e[x][y] =
3097 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3100 element_found = FALSE;
3106 checked_free(buffer);
3108 micro_chunk_size += 2 + num_bytes;
3110 else // constant size configuration data (1, 2 or 4 bytes)
3112 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3113 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3114 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3116 for (i = 0; conf[i].data_type != -1; i++)
3118 if (conf[i].element == element &&
3119 conf[i].conf_type == conf_type)
3121 int data_type = conf[i].data_type;
3123 if (data_type == TYPE_ELEMENT)
3124 value = getMappedElement(value);
3126 if (data_type == TYPE_BOOLEAN)
3127 *(boolean *)(conf[i].value) = value;
3129 *(int *) (conf[i].value) = value;
3131 element_found = TRUE;
3137 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3142 char *error_conf_chunk_bytes =
3143 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3144 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3145 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3146 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3147 int error_element = real_element;
3149 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3150 error_conf_chunk_bytes, error_conf_chunk_token,
3151 error_element, EL_NAME(error_element));
3154 return micro_chunk_size;
3157 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3159 int real_chunk_size = 0;
3161 li = *level; // copy level data into temporary buffer
3163 while (!checkEndOfFile(file))
3165 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3167 if (real_chunk_size >= chunk_size)
3171 *level = li; // copy temporary buffer back to level data
3173 return real_chunk_size;
3176 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3178 int real_chunk_size = 0;
3180 li = *level; // copy level data into temporary buffer
3182 while (!checkEndOfFile(file))
3184 int element = getMappedElement(getFile16BitBE(file));
3186 real_chunk_size += 2;
3187 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3189 if (real_chunk_size >= chunk_size)
3193 *level = li; // copy temporary buffer back to level data
3195 return real_chunk_size;
3198 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3200 int real_chunk_size = 0;
3202 li = *level; // copy level data into temporary buffer
3204 while (!checkEndOfFile(file))
3206 int element = getMappedElement(getFile16BitBE(file));
3208 real_chunk_size += 2;
3209 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3211 if (real_chunk_size >= chunk_size)
3215 *level = li; // copy temporary buffer back to level data
3217 return real_chunk_size;
3220 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3222 int element = getMappedElement(getFile16BitBE(file));
3223 int envelope_nr = element - EL_ENVELOPE_1;
3224 int real_chunk_size = 2;
3226 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3228 while (!checkEndOfFile(file))
3230 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3233 if (real_chunk_size >= chunk_size)
3237 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3239 return real_chunk_size;
3242 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3244 int element = getMappedElement(getFile16BitBE(file));
3245 int real_chunk_size = 2;
3246 struct ElementInfo *ei = &element_info[element];
3249 xx_ei = *ei; // copy element data into temporary buffer
3251 xx_ei.num_change_pages = -1;
3253 while (!checkEndOfFile(file))
3255 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3257 if (xx_ei.num_change_pages != -1)
3260 if (real_chunk_size >= chunk_size)
3266 if (ei->num_change_pages == -1)
3268 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3271 ei->num_change_pages = 1;
3273 setElementChangePages(ei, 1);
3274 setElementChangeInfoToDefaults(ei->change);
3276 return real_chunk_size;
3279 // initialize number of change pages stored for this custom element
3280 setElementChangePages(ei, ei->num_change_pages);
3281 for (i = 0; i < ei->num_change_pages; i++)
3282 setElementChangeInfoToDefaults(&ei->change_page[i]);
3284 // start with reading properties for the first change page
3285 xx_current_change_page = 0;
3287 while (!checkEndOfFile(file))
3289 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3291 xx_change = *change; // copy change data into temporary buffer
3293 resetEventBits(); // reset bits; change page might have changed
3295 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3298 *change = xx_change;
3300 setEventFlagsFromEventBits(change);
3302 if (real_chunk_size >= chunk_size)
3306 level->file_has_custom_elements = TRUE;
3308 return real_chunk_size;
3311 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3313 int element = getMappedElement(getFile16BitBE(file));
3314 int real_chunk_size = 2;
3315 struct ElementInfo *ei = &element_info[element];
3316 struct ElementGroupInfo *group = ei->group;
3318 xx_ei = *ei; // copy element data into temporary buffer
3319 xx_group = *group; // copy group data into temporary buffer
3321 while (!checkEndOfFile(file))
3323 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3326 if (real_chunk_size >= chunk_size)
3333 level->file_has_custom_elements = TRUE;
3335 return real_chunk_size;
3338 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3339 struct LevelFileInfo *level_file_info,
3340 boolean level_info_only)
3342 char *filename = level_file_info->filename;
3343 char cookie[MAX_LINE_LEN];
3344 char chunk_name[CHUNK_ID_LEN + 1];
3348 if (!(file = openFile(filename, MODE_READ)))
3350 level->no_valid_file = TRUE;
3351 level->no_level_file = TRUE;
3353 if (level_info_only)
3356 Warn("cannot read level '%s' -- using empty level", filename);
3358 if (!setup.editor.use_template_for_new_levels)
3361 // if level file not found, try to initialize level data from template
3362 filename = getGlobalLevelTemplateFilename();
3364 if (!(file = openFile(filename, MODE_READ)))
3367 // default: for empty levels, use level template for custom elements
3368 level->use_custom_template = TRUE;
3370 level->no_valid_file = FALSE;
3373 getFileChunkBE(file, chunk_name, NULL);
3374 if (strEqual(chunk_name, "RND1"))
3376 getFile32BitBE(file); // not used
3378 getFileChunkBE(file, chunk_name, NULL);
3379 if (!strEqual(chunk_name, "CAVE"))
3381 level->no_valid_file = TRUE;
3383 Warn("unknown format of level file '%s'", filename);
3390 else // check for pre-2.0 file format with cookie string
3392 strcpy(cookie, chunk_name);
3393 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3395 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3396 cookie[strlen(cookie) - 1] = '\0';
3398 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3400 level->no_valid_file = TRUE;
3402 Warn("unknown format of level file '%s'", filename);
3409 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3411 level->no_valid_file = TRUE;
3413 Warn("unsupported version of level file '%s'", filename);
3420 // pre-2.0 level files have no game version, so use file version here
3421 level->game_version = level->file_version;
3424 if (level->file_version < FILE_VERSION_1_2)
3426 // level files from versions before 1.2.0 without chunk structure
3427 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3428 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3436 int (*loader)(File *, int, struct LevelInfo *);
3440 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3441 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3442 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3443 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3444 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3445 { "INFO", -1, LoadLevel_INFO },
3446 { "BODY", -1, LoadLevel_BODY },
3447 { "CONT", -1, LoadLevel_CONT },
3448 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3449 { "CNT3", -1, LoadLevel_CNT3 },
3450 { "CUS1", -1, LoadLevel_CUS1 },
3451 { "CUS2", -1, LoadLevel_CUS2 },
3452 { "CUS3", -1, LoadLevel_CUS3 },
3453 { "CUS4", -1, LoadLevel_CUS4 },
3454 { "GRP1", -1, LoadLevel_GRP1 },
3455 { "CONF", -1, LoadLevel_CONF },
3456 { "ELEM", -1, LoadLevel_ELEM },
3457 { "NOTE", -1, LoadLevel_NOTE },
3458 { "CUSX", -1, LoadLevel_CUSX },
3459 { "GRPX", -1, LoadLevel_GRPX },
3464 while (getFileChunkBE(file, chunk_name, &chunk_size))
3468 while (chunk_info[i].name != NULL &&
3469 !strEqual(chunk_name, chunk_info[i].name))
3472 if (chunk_info[i].name == NULL)
3474 Warn("unknown chunk '%s' in level file '%s'",
3475 chunk_name, filename);
3477 ReadUnusedBytesFromFile(file, chunk_size);
3479 else if (chunk_info[i].size != -1 &&
3480 chunk_info[i].size != chunk_size)
3482 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3483 chunk_size, chunk_name, filename);
3485 ReadUnusedBytesFromFile(file, chunk_size);
3489 // call function to load this level chunk
3490 int chunk_size_expected =
3491 (chunk_info[i].loader)(file, chunk_size, level);
3493 // the size of some chunks cannot be checked before reading other
3494 // chunks first (like "HEAD" and "BODY") that contain some header
3495 // information, so check them here
3496 if (chunk_size_expected != chunk_size)
3498 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3499 chunk_size, chunk_name, filename);
3509 // ----------------------------------------------------------------------------
3510 // functions for loading EM level
3511 // ----------------------------------------------------------------------------
3513 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3515 static int ball_xy[8][2] =
3526 struct LevelInfo_EM *level_em = level->native_em_level;
3527 struct CAVE *cav = level_em->cav;
3530 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3531 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3533 cav->time_seconds = level->time;
3534 cav->gems_needed = level->gems_needed;
3536 cav->emerald_score = level->score[SC_EMERALD];
3537 cav->diamond_score = level->score[SC_DIAMOND];
3538 cav->alien_score = level->score[SC_ROBOT];
3539 cav->tank_score = level->score[SC_SPACESHIP];
3540 cav->bug_score = level->score[SC_BUG];
3541 cav->eater_score = level->score[SC_YAMYAM];
3542 cav->nut_score = level->score[SC_NUT];
3543 cav->dynamite_score = level->score[SC_DYNAMITE];
3544 cav->key_score = level->score[SC_KEY];
3545 cav->exit_score = level->score[SC_TIME_BONUS];
3547 cav->num_eater_arrays = level->num_yamyam_contents;
3549 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3550 for (y = 0; y < 3; y++)
3551 for (x = 0; x < 3; x++)
3552 cav->eater_array[i][y * 3 + x] =
3553 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3555 cav->amoeba_time = level->amoeba_speed;
3556 cav->wonderwall_time = level->time_magic_wall;
3557 cav->wheel_time = level->time_wheel;
3559 cav->android_move_time = level->android_move_time;
3560 cav->android_clone_time = level->android_clone_time;
3561 cav->ball_random = level->ball_random;
3562 cav->ball_active = level->ball_active_initial;
3563 cav->ball_time = level->ball_time;
3564 cav->num_ball_arrays = level->num_ball_contents;
3566 cav->lenses_score = level->lenses_score;
3567 cav->magnify_score = level->magnify_score;
3568 cav->slurp_score = level->slurp_score;
3570 cav->lenses_time = level->lenses_time;
3571 cav->magnify_time = level->magnify_time;
3573 cav->wind_direction =
3574 map_direction_RND_to_EM(level->wind_direction_initial);
3576 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3577 for (j = 0; j < 8; j++)
3578 cav->ball_array[i][j] =
3579 map_element_RND_to_EM_cave(level->ball_content[i].
3580 e[ball_xy[j][0]][ball_xy[j][1]]);
3582 map_android_clone_elements_RND_to_EM(level);
3584 // first fill the complete playfield with the empty space element
3585 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3586 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3587 cav->cave[x][y] = Cblank;
3589 // then copy the real level contents from level file into the playfield
3590 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3592 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3594 if (level->field[x][y] == EL_AMOEBA_DEAD)
3595 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3597 cav->cave[x][y] = new_element;
3600 for (i = 0; i < MAX_PLAYERS; i++)
3602 cav->player_x[i] = -1;
3603 cav->player_y[i] = -1;
3606 // initialize player positions and delete players from the playfield
3607 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3609 if (ELEM_IS_PLAYER(level->field[x][y]))
3611 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3613 cav->player_x[player_nr] = x;
3614 cav->player_y[player_nr] = y;
3616 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3621 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3623 static int ball_xy[8][2] =
3634 struct LevelInfo_EM *level_em = level->native_em_level;
3635 struct CAVE *cav = level_em->cav;
3638 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3639 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3641 level->time = cav->time_seconds;
3642 level->gems_needed = cav->gems_needed;
3644 sprintf(level->name, "Level %d", level->file_info.nr);
3646 level->score[SC_EMERALD] = cav->emerald_score;
3647 level->score[SC_DIAMOND] = cav->diamond_score;
3648 level->score[SC_ROBOT] = cav->alien_score;
3649 level->score[SC_SPACESHIP] = cav->tank_score;
3650 level->score[SC_BUG] = cav->bug_score;
3651 level->score[SC_YAMYAM] = cav->eater_score;
3652 level->score[SC_NUT] = cav->nut_score;
3653 level->score[SC_DYNAMITE] = cav->dynamite_score;
3654 level->score[SC_KEY] = cav->key_score;
3655 level->score[SC_TIME_BONUS] = cav->exit_score;
3657 level->num_yamyam_contents = cav->num_eater_arrays;
3659 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3660 for (y = 0; y < 3; y++)
3661 for (x = 0; x < 3; x++)
3662 level->yamyam_content[i].e[x][y] =
3663 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3665 level->amoeba_speed = cav->amoeba_time;
3666 level->time_magic_wall = cav->wonderwall_time;
3667 level->time_wheel = cav->wheel_time;
3669 level->android_move_time = cav->android_move_time;
3670 level->android_clone_time = cav->android_clone_time;
3671 level->ball_random = cav->ball_random;
3672 level->ball_active_initial = cav->ball_active;
3673 level->ball_time = cav->ball_time;
3674 level->num_ball_contents = cav->num_ball_arrays;
3676 level->lenses_score = cav->lenses_score;
3677 level->magnify_score = cav->magnify_score;
3678 level->slurp_score = cav->slurp_score;
3680 level->lenses_time = cav->lenses_time;
3681 level->magnify_time = cav->magnify_time;
3683 level->wind_direction_initial =
3684 map_direction_EM_to_RND(cav->wind_direction);
3686 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3687 for (j = 0; j < 8; j++)
3688 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3689 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3691 map_android_clone_elements_EM_to_RND(level);
3693 // convert the playfield (some elements need special treatment)
3694 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3696 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3698 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3699 new_element = EL_AMOEBA_DEAD;
3701 level->field[x][y] = new_element;
3704 for (i = 0; i < MAX_PLAYERS; i++)
3706 // in case of all players set to the same field, use the first player
3707 int nr = MAX_PLAYERS - i - 1;
3708 int jx = cav->player_x[nr];
3709 int jy = cav->player_y[nr];
3711 if (jx != -1 && jy != -1)
3712 level->field[jx][jy] = EL_PLAYER_1 + nr;
3717 // ----------------------------------------------------------------------------
3718 // functions for loading SP level
3719 // ----------------------------------------------------------------------------
3721 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3723 struct LevelInfo_SP *level_sp = level->native_sp_level;
3724 LevelInfoType *header = &level_sp->header;
3727 level_sp->width = level->fieldx;
3728 level_sp->height = level->fieldy;
3730 for (x = 0; x < level->fieldx; x++)
3731 for (y = 0; y < level->fieldy; y++)
3732 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3734 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3736 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3737 header->LevelTitle[i] = level->name[i];
3738 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3740 header->InfotronsNeeded = level->gems_needed;
3742 header->SpecialPortCount = 0;
3744 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3746 boolean gravity_port_found = FALSE;
3747 boolean gravity_port_valid = FALSE;
3748 int gravity_port_flag;
3749 int gravity_port_base_element;
3750 int element = level->field[x][y];
3752 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3753 element <= EL_SP_GRAVITY_ON_PORT_UP)
3755 gravity_port_found = TRUE;
3756 gravity_port_valid = TRUE;
3757 gravity_port_flag = 1;
3758 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3760 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3761 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3763 gravity_port_found = TRUE;
3764 gravity_port_valid = TRUE;
3765 gravity_port_flag = 0;
3766 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3768 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3769 element <= EL_SP_GRAVITY_PORT_UP)
3771 // change R'n'D style gravity inverting special port to normal port
3772 // (there are no gravity inverting ports in native Supaplex engine)
3774 gravity_port_found = TRUE;
3775 gravity_port_valid = FALSE;
3776 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3779 if (gravity_port_found)
3781 if (gravity_port_valid &&
3782 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3784 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3786 port->PortLocation = (y * level->fieldx + x) * 2;
3787 port->Gravity = gravity_port_flag;
3789 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3791 header->SpecialPortCount++;
3795 // change special gravity port to normal port
3797 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3800 level_sp->playfield[x][y] = element - EL_SP_START;
3805 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3807 struct LevelInfo_SP *level_sp = level->native_sp_level;
3808 LevelInfoType *header = &level_sp->header;
3809 boolean num_invalid_elements = 0;
3812 level->fieldx = level_sp->width;
3813 level->fieldy = level_sp->height;
3815 for (x = 0; x < level->fieldx; x++)
3817 for (y = 0; y < level->fieldy; y++)
3819 int element_old = level_sp->playfield[x][y];
3820 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3822 if (element_new == EL_UNKNOWN)
3824 num_invalid_elements++;
3826 Debug("level:native:SP", "invalid element %d at position %d, %d",
3830 level->field[x][y] = element_new;
3834 if (num_invalid_elements > 0)
3835 Warn("found %d invalid elements%s", num_invalid_elements,
3836 (!options.debug ? " (use '--debug' for more details)" : ""));
3838 for (i = 0; i < MAX_PLAYERS; i++)
3839 level->initial_player_gravity[i] =
3840 (header->InitialGravity == 1 ? TRUE : FALSE);
3842 // skip leading spaces
3843 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3844 if (header->LevelTitle[i] != ' ')
3848 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3849 level->name[j] = header->LevelTitle[i];
3850 level->name[j] = '\0';
3852 // cut trailing spaces
3854 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3855 level->name[j - 1] = '\0';
3857 level->gems_needed = header->InfotronsNeeded;
3859 for (i = 0; i < header->SpecialPortCount; i++)
3861 SpecialPortType *port = &header->SpecialPort[i];
3862 int port_location = port->PortLocation;
3863 int gravity = port->Gravity;
3864 int port_x, port_y, port_element;
3866 port_x = (port_location / 2) % level->fieldx;
3867 port_y = (port_location / 2) / level->fieldx;
3869 if (port_x < 0 || port_x >= level->fieldx ||
3870 port_y < 0 || port_y >= level->fieldy)
3872 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3877 port_element = level->field[port_x][port_y];
3879 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3880 port_element > EL_SP_GRAVITY_PORT_UP)
3882 Warn("no special port at position (%d, %d)", port_x, port_y);
3887 // change previous (wrong) gravity inverting special port to either
3888 // gravity enabling special port or gravity disabling special port
3889 level->field[port_x][port_y] +=
3890 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3891 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3894 // change special gravity ports without database entries to normal ports
3895 for (x = 0; x < level->fieldx; x++)
3896 for (y = 0; y < level->fieldy; y++)
3897 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3898 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3899 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3901 level->time = 0; // no time limit
3902 level->amoeba_speed = 0;
3903 level->time_magic_wall = 0;
3904 level->time_wheel = 0;
3905 level->amoeba_content = EL_EMPTY;
3908 // original Supaplex does not use score values -- use default values
3910 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3911 level->score[i] = 0;
3914 // there are no yamyams in supaplex levels
3915 for (i = 0; i < level->num_yamyam_contents; i++)
3916 for (x = 0; x < 3; x++)
3917 for (y = 0; y < 3; y++)
3918 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3921 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3923 struct LevelInfo_SP *level_sp = level->native_sp_level;
3924 struct DemoInfo_SP *demo = &level_sp->demo;
3927 // always start with reliable default values
3928 demo->is_available = FALSE;
3931 if (TAPE_IS_EMPTY(tape))
3934 demo->level_nr = tape.level_nr; // (currently not used)
3936 level_sp->header.DemoRandomSeed = tape.random_seed;
3940 for (i = 0; i < tape.length; i++)
3942 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3943 int demo_repeat = tape.pos[i].delay;
3944 int demo_entries = (demo_repeat + 15) / 16;
3946 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3948 Warn("tape truncated: size exceeds maximum SP demo size %d",
3954 for (j = 0; j < demo_repeat / 16; j++)
3955 demo->data[demo->length++] = 0xf0 | demo_action;
3957 if (demo_repeat % 16)
3958 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3961 demo->is_available = TRUE;
3964 static void setTapeInfoToDefaults(void);
3966 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3968 struct LevelInfo_SP *level_sp = level->native_sp_level;
3969 struct DemoInfo_SP *demo = &level_sp->demo;
3970 char *filename = level->file_info.filename;
3973 // always start with reliable default values
3974 setTapeInfoToDefaults();
3976 if (!demo->is_available)
3979 tape.level_nr = demo->level_nr; // (currently not used)
3980 tape.random_seed = level_sp->header.DemoRandomSeed;
3982 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3985 tape.pos[tape.counter].delay = 0;
3987 for (i = 0; i < demo->length; i++)
3989 int demo_action = demo->data[i] & 0x0f;
3990 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3991 int tape_action = map_key_SP_to_RND(demo_action);
3992 int tape_repeat = demo_repeat + 1;
3993 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3994 boolean success = 0;
3997 for (j = 0; j < tape_repeat; j++)
3998 success = TapeAddAction(action);
4002 Warn("SP demo truncated: size exceeds maximum tape size %d",
4009 TapeHaltRecording();
4013 // ----------------------------------------------------------------------------
4014 // functions for loading MM level
4015 // ----------------------------------------------------------------------------
4017 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4019 struct LevelInfo_MM *level_mm = level->native_mm_level;
4022 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4023 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4025 level_mm->time = level->time;
4026 level_mm->kettles_needed = level->gems_needed;
4027 level_mm->auto_count_kettles = level->auto_count_gems;
4029 level_mm->laser_red = level->mm_laser_red;
4030 level_mm->laser_green = level->mm_laser_green;
4031 level_mm->laser_blue = level->mm_laser_blue;
4033 strcpy(level_mm->name, level->name);
4034 strcpy(level_mm->author, level->author);
4036 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4037 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4038 level_mm->score[SC_KEY] = level->score[SC_KEY];
4039 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4040 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4042 level_mm->amoeba_speed = level->amoeba_speed;
4043 level_mm->time_fuse = level->mm_time_fuse;
4044 level_mm->time_bomb = level->mm_time_bomb;
4045 level_mm->time_ball = level->mm_time_ball;
4046 level_mm->time_block = level->mm_time_block;
4048 for (x = 0; x < level->fieldx; x++)
4049 for (y = 0; y < level->fieldy; y++)
4051 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4054 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4056 struct LevelInfo_MM *level_mm = level->native_mm_level;
4059 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4060 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4062 level->time = level_mm->time;
4063 level->gems_needed = level_mm->kettles_needed;
4064 level->auto_count_gems = level_mm->auto_count_kettles;
4066 level->mm_laser_red = level_mm->laser_red;
4067 level->mm_laser_green = level_mm->laser_green;
4068 level->mm_laser_blue = level_mm->laser_blue;
4070 strcpy(level->name, level_mm->name);
4072 // only overwrite author from 'levelinfo.conf' if author defined in level
4073 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4074 strcpy(level->author, level_mm->author);
4076 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4077 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4078 level->score[SC_KEY] = level_mm->score[SC_KEY];
4079 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4080 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4082 level->amoeba_speed = level_mm->amoeba_speed;
4083 level->mm_time_fuse = level_mm->time_fuse;
4084 level->mm_time_bomb = level_mm->time_bomb;
4085 level->mm_time_ball = level_mm->time_ball;
4086 level->mm_time_block = level_mm->time_block;
4088 for (x = 0; x < level->fieldx; x++)
4089 for (y = 0; y < level->fieldy; y++)
4090 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4094 // ----------------------------------------------------------------------------
4095 // functions for loading DC level
4096 // ----------------------------------------------------------------------------
4098 #define DC_LEVEL_HEADER_SIZE 344
4100 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4103 static int last_data_encoded;
4107 int diff_hi, diff_lo;
4108 int data_hi, data_lo;
4109 unsigned short data_decoded;
4113 last_data_encoded = 0;
4120 diff = data_encoded - last_data_encoded;
4121 diff_hi = diff & ~0xff;
4122 diff_lo = diff & 0xff;
4126 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4127 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4128 data_hi = data_hi & 0xff00;
4130 data_decoded = data_hi | data_lo;
4132 last_data_encoded = data_encoded;
4134 offset1 = (offset1 + 1) % 31;
4135 offset2 = offset2 & 0xff;
4137 return data_decoded;
4140 static int getMappedElement_DC(int element)
4148 // 0x0117 - 0x036e: (?)
4151 // 0x042d - 0x0684: (?)
4167 element = EL_CRYSTAL;
4170 case 0x0e77: // quicksand (boulder)
4171 element = EL_QUICKSAND_FAST_FULL;
4174 case 0x0e99: // slow quicksand (boulder)
4175 element = EL_QUICKSAND_FULL;
4179 element = EL_EM_EXIT_OPEN;
4183 element = EL_EM_EXIT_CLOSED;
4187 element = EL_EM_STEEL_EXIT_OPEN;
4191 element = EL_EM_STEEL_EXIT_CLOSED;
4194 case 0x0f4f: // dynamite (lit 1)
4195 element = EL_EM_DYNAMITE_ACTIVE;
4198 case 0x0f57: // dynamite (lit 2)
4199 element = EL_EM_DYNAMITE_ACTIVE;
4202 case 0x0f5f: // dynamite (lit 3)
4203 element = EL_EM_DYNAMITE_ACTIVE;
4206 case 0x0f67: // dynamite (lit 4)
4207 element = EL_EM_DYNAMITE_ACTIVE;
4214 element = EL_AMOEBA_WET;
4218 element = EL_AMOEBA_DROP;
4222 element = EL_DC_MAGIC_WALL;
4226 element = EL_SPACESHIP_UP;
4230 element = EL_SPACESHIP_DOWN;
4234 element = EL_SPACESHIP_LEFT;
4238 element = EL_SPACESHIP_RIGHT;
4242 element = EL_BUG_UP;
4246 element = EL_BUG_DOWN;
4250 element = EL_BUG_LEFT;
4254 element = EL_BUG_RIGHT;
4258 element = EL_MOLE_UP;
4262 element = EL_MOLE_DOWN;
4266 element = EL_MOLE_LEFT;
4270 element = EL_MOLE_RIGHT;
4278 element = EL_YAMYAM_UP;
4282 element = EL_SWITCHGATE_OPEN;
4286 element = EL_SWITCHGATE_CLOSED;
4290 element = EL_DC_SWITCHGATE_SWITCH_UP;
4294 element = EL_TIMEGATE_CLOSED;
4297 case 0x144c: // conveyor belt switch (green)
4298 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4301 case 0x144f: // conveyor belt switch (red)
4302 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4305 case 0x1452: // conveyor belt switch (blue)
4306 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4310 element = EL_CONVEYOR_BELT_3_MIDDLE;
4314 element = EL_CONVEYOR_BELT_3_LEFT;
4318 element = EL_CONVEYOR_BELT_3_RIGHT;
4322 element = EL_CONVEYOR_BELT_1_MIDDLE;
4326 element = EL_CONVEYOR_BELT_1_LEFT;
4330 element = EL_CONVEYOR_BELT_1_RIGHT;
4334 element = EL_CONVEYOR_BELT_4_MIDDLE;
4338 element = EL_CONVEYOR_BELT_4_LEFT;
4342 element = EL_CONVEYOR_BELT_4_RIGHT;
4346 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4350 element = EL_EXPANDABLE_WALL_VERTICAL;
4354 element = EL_EXPANDABLE_WALL_ANY;
4357 case 0x14ce: // growing steel wall (left/right)
4358 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4361 case 0x14df: // growing steel wall (up/down)
4362 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4365 case 0x14e8: // growing steel wall (up/down/left/right)
4366 element = EL_EXPANDABLE_STEELWALL_ANY;
4370 element = EL_SHIELD_DEADLY;
4374 element = EL_EXTRA_TIME;
4382 element = EL_EMPTY_SPACE;
4385 case 0x1578: // quicksand (empty)
4386 element = EL_QUICKSAND_FAST_EMPTY;
4389 case 0x1579: // slow quicksand (empty)
4390 element = EL_QUICKSAND_EMPTY;
4400 element = EL_EM_DYNAMITE;
4403 case 0x15a1: // key (red)
4404 element = EL_EM_KEY_1;
4407 case 0x15a2: // key (yellow)
4408 element = EL_EM_KEY_2;
4411 case 0x15a3: // key (blue)
4412 element = EL_EM_KEY_4;
4415 case 0x15a4: // key (green)
4416 element = EL_EM_KEY_3;
4419 case 0x15a5: // key (white)
4420 element = EL_DC_KEY_WHITE;
4424 element = EL_WALL_SLIPPERY;
4431 case 0x15a8: // wall (not round)
4435 case 0x15a9: // (blue)
4436 element = EL_CHAR_A;
4439 case 0x15aa: // (blue)
4440 element = EL_CHAR_B;
4443 case 0x15ab: // (blue)
4444 element = EL_CHAR_C;
4447 case 0x15ac: // (blue)
4448 element = EL_CHAR_D;
4451 case 0x15ad: // (blue)
4452 element = EL_CHAR_E;
4455 case 0x15ae: // (blue)
4456 element = EL_CHAR_F;
4459 case 0x15af: // (blue)
4460 element = EL_CHAR_G;
4463 case 0x15b0: // (blue)
4464 element = EL_CHAR_H;
4467 case 0x15b1: // (blue)
4468 element = EL_CHAR_I;
4471 case 0x15b2: // (blue)
4472 element = EL_CHAR_J;
4475 case 0x15b3: // (blue)
4476 element = EL_CHAR_K;
4479 case 0x15b4: // (blue)
4480 element = EL_CHAR_L;
4483 case 0x15b5: // (blue)
4484 element = EL_CHAR_M;
4487 case 0x15b6: // (blue)
4488 element = EL_CHAR_N;
4491 case 0x15b7: // (blue)
4492 element = EL_CHAR_O;
4495 case 0x15b8: // (blue)
4496 element = EL_CHAR_P;
4499 case 0x15b9: // (blue)
4500 element = EL_CHAR_Q;
4503 case 0x15ba: // (blue)
4504 element = EL_CHAR_R;
4507 case 0x15bb: // (blue)
4508 element = EL_CHAR_S;
4511 case 0x15bc: // (blue)
4512 element = EL_CHAR_T;
4515 case 0x15bd: // (blue)
4516 element = EL_CHAR_U;
4519 case 0x15be: // (blue)
4520 element = EL_CHAR_V;
4523 case 0x15bf: // (blue)
4524 element = EL_CHAR_W;
4527 case 0x15c0: // (blue)
4528 element = EL_CHAR_X;
4531 case 0x15c1: // (blue)
4532 element = EL_CHAR_Y;
4535 case 0x15c2: // (blue)
4536 element = EL_CHAR_Z;
4539 case 0x15c3: // (blue)
4540 element = EL_CHAR_AUMLAUT;
4543 case 0x15c4: // (blue)
4544 element = EL_CHAR_OUMLAUT;
4547 case 0x15c5: // (blue)
4548 element = EL_CHAR_UUMLAUT;
4551 case 0x15c6: // (blue)
4552 element = EL_CHAR_0;
4555 case 0x15c7: // (blue)
4556 element = EL_CHAR_1;
4559 case 0x15c8: // (blue)
4560 element = EL_CHAR_2;
4563 case 0x15c9: // (blue)
4564 element = EL_CHAR_3;
4567 case 0x15ca: // (blue)
4568 element = EL_CHAR_4;
4571 case 0x15cb: // (blue)
4572 element = EL_CHAR_5;
4575 case 0x15cc: // (blue)
4576 element = EL_CHAR_6;
4579 case 0x15cd: // (blue)
4580 element = EL_CHAR_7;
4583 case 0x15ce: // (blue)
4584 element = EL_CHAR_8;
4587 case 0x15cf: // (blue)
4588 element = EL_CHAR_9;
4591 case 0x15d0: // (blue)
4592 element = EL_CHAR_PERIOD;
4595 case 0x15d1: // (blue)
4596 element = EL_CHAR_EXCLAM;
4599 case 0x15d2: // (blue)
4600 element = EL_CHAR_COLON;
4603 case 0x15d3: // (blue)
4604 element = EL_CHAR_LESS;
4607 case 0x15d4: // (blue)
4608 element = EL_CHAR_GREATER;
4611 case 0x15d5: // (blue)
4612 element = EL_CHAR_QUESTION;
4615 case 0x15d6: // (blue)
4616 element = EL_CHAR_COPYRIGHT;
4619 case 0x15d7: // (blue)
4620 element = EL_CHAR_UP;
4623 case 0x15d8: // (blue)
4624 element = EL_CHAR_DOWN;
4627 case 0x15d9: // (blue)
4628 element = EL_CHAR_BUTTON;
4631 case 0x15da: // (blue)
4632 element = EL_CHAR_PLUS;
4635 case 0x15db: // (blue)
4636 element = EL_CHAR_MINUS;
4639 case 0x15dc: // (blue)
4640 element = EL_CHAR_APOSTROPHE;
4643 case 0x15dd: // (blue)
4644 element = EL_CHAR_PARENLEFT;
4647 case 0x15de: // (blue)
4648 element = EL_CHAR_PARENRIGHT;
4651 case 0x15df: // (green)
4652 element = EL_CHAR_A;
4655 case 0x15e0: // (green)
4656 element = EL_CHAR_B;
4659 case 0x15e1: // (green)
4660 element = EL_CHAR_C;
4663 case 0x15e2: // (green)
4664 element = EL_CHAR_D;
4667 case 0x15e3: // (green)
4668 element = EL_CHAR_E;
4671 case 0x15e4: // (green)
4672 element = EL_CHAR_F;
4675 case 0x15e5: // (green)
4676 element = EL_CHAR_G;
4679 case 0x15e6: // (green)
4680 element = EL_CHAR_H;
4683 case 0x15e7: // (green)
4684 element = EL_CHAR_I;
4687 case 0x15e8: // (green)
4688 element = EL_CHAR_J;
4691 case 0x15e9: // (green)
4692 element = EL_CHAR_K;
4695 case 0x15ea: // (green)
4696 element = EL_CHAR_L;
4699 case 0x15eb: // (green)
4700 element = EL_CHAR_M;
4703 case 0x15ec: // (green)
4704 element = EL_CHAR_N;
4707 case 0x15ed: // (green)
4708 element = EL_CHAR_O;
4711 case 0x15ee: // (green)
4712 element = EL_CHAR_P;
4715 case 0x15ef: // (green)
4716 element = EL_CHAR_Q;
4719 case 0x15f0: // (green)
4720 element = EL_CHAR_R;
4723 case 0x15f1: // (green)
4724 element = EL_CHAR_S;
4727 case 0x15f2: // (green)
4728 element = EL_CHAR_T;
4731 case 0x15f3: // (green)
4732 element = EL_CHAR_U;
4735 case 0x15f4: // (green)
4736 element = EL_CHAR_V;
4739 case 0x15f5: // (green)
4740 element = EL_CHAR_W;
4743 case 0x15f6: // (green)
4744 element = EL_CHAR_X;
4747 case 0x15f7: // (green)
4748 element = EL_CHAR_Y;
4751 case 0x15f8: // (green)
4752 element = EL_CHAR_Z;
4755 case 0x15f9: // (green)
4756 element = EL_CHAR_AUMLAUT;
4759 case 0x15fa: // (green)
4760 element = EL_CHAR_OUMLAUT;
4763 case 0x15fb: // (green)
4764 element = EL_CHAR_UUMLAUT;
4767 case 0x15fc: // (green)
4768 element = EL_CHAR_0;
4771 case 0x15fd: // (green)
4772 element = EL_CHAR_1;
4775 case 0x15fe: // (green)
4776 element = EL_CHAR_2;
4779 case 0x15ff: // (green)
4780 element = EL_CHAR_3;
4783 case 0x1600: // (green)
4784 element = EL_CHAR_4;
4787 case 0x1601: // (green)
4788 element = EL_CHAR_5;
4791 case 0x1602: // (green)
4792 element = EL_CHAR_6;
4795 case 0x1603: // (green)
4796 element = EL_CHAR_7;
4799 case 0x1604: // (green)
4800 element = EL_CHAR_8;
4803 case 0x1605: // (green)
4804 element = EL_CHAR_9;
4807 case 0x1606: // (green)
4808 element = EL_CHAR_PERIOD;
4811 case 0x1607: // (green)
4812 element = EL_CHAR_EXCLAM;
4815 case 0x1608: // (green)
4816 element = EL_CHAR_COLON;
4819 case 0x1609: // (green)
4820 element = EL_CHAR_LESS;
4823 case 0x160a: // (green)
4824 element = EL_CHAR_GREATER;
4827 case 0x160b: // (green)
4828 element = EL_CHAR_QUESTION;
4831 case 0x160c: // (green)
4832 element = EL_CHAR_COPYRIGHT;
4835 case 0x160d: // (green)
4836 element = EL_CHAR_UP;
4839 case 0x160e: // (green)
4840 element = EL_CHAR_DOWN;
4843 case 0x160f: // (green)
4844 element = EL_CHAR_BUTTON;
4847 case 0x1610: // (green)
4848 element = EL_CHAR_PLUS;
4851 case 0x1611: // (green)
4852 element = EL_CHAR_MINUS;
4855 case 0x1612: // (green)
4856 element = EL_CHAR_APOSTROPHE;
4859 case 0x1613: // (green)
4860 element = EL_CHAR_PARENLEFT;
4863 case 0x1614: // (green)
4864 element = EL_CHAR_PARENRIGHT;
4867 case 0x1615: // (blue steel)
4868 element = EL_STEEL_CHAR_A;
4871 case 0x1616: // (blue steel)
4872 element = EL_STEEL_CHAR_B;
4875 case 0x1617: // (blue steel)
4876 element = EL_STEEL_CHAR_C;
4879 case 0x1618: // (blue steel)
4880 element = EL_STEEL_CHAR_D;
4883 case 0x1619: // (blue steel)
4884 element = EL_STEEL_CHAR_E;
4887 case 0x161a: // (blue steel)
4888 element = EL_STEEL_CHAR_F;
4891 case 0x161b: // (blue steel)
4892 element = EL_STEEL_CHAR_G;
4895 case 0x161c: // (blue steel)
4896 element = EL_STEEL_CHAR_H;
4899 case 0x161d: // (blue steel)
4900 element = EL_STEEL_CHAR_I;
4903 case 0x161e: // (blue steel)
4904 element = EL_STEEL_CHAR_J;
4907 case 0x161f: // (blue steel)
4908 element = EL_STEEL_CHAR_K;
4911 case 0x1620: // (blue steel)
4912 element = EL_STEEL_CHAR_L;
4915 case 0x1621: // (blue steel)
4916 element = EL_STEEL_CHAR_M;
4919 case 0x1622: // (blue steel)
4920 element = EL_STEEL_CHAR_N;
4923 case 0x1623: // (blue steel)
4924 element = EL_STEEL_CHAR_O;
4927 case 0x1624: // (blue steel)
4928 element = EL_STEEL_CHAR_P;
4931 case 0x1625: // (blue steel)
4932 element = EL_STEEL_CHAR_Q;
4935 case 0x1626: // (blue steel)
4936 element = EL_STEEL_CHAR_R;
4939 case 0x1627: // (blue steel)
4940 element = EL_STEEL_CHAR_S;
4943 case 0x1628: // (blue steel)
4944 element = EL_STEEL_CHAR_T;
4947 case 0x1629: // (blue steel)
4948 element = EL_STEEL_CHAR_U;
4951 case 0x162a: // (blue steel)
4952 element = EL_STEEL_CHAR_V;
4955 case 0x162b: // (blue steel)
4956 element = EL_STEEL_CHAR_W;
4959 case 0x162c: // (blue steel)
4960 element = EL_STEEL_CHAR_X;
4963 case 0x162d: // (blue steel)
4964 element = EL_STEEL_CHAR_Y;
4967 case 0x162e: // (blue steel)
4968 element = EL_STEEL_CHAR_Z;
4971 case 0x162f: // (blue steel)
4972 element = EL_STEEL_CHAR_AUMLAUT;
4975 case 0x1630: // (blue steel)
4976 element = EL_STEEL_CHAR_OUMLAUT;
4979 case 0x1631: // (blue steel)
4980 element = EL_STEEL_CHAR_UUMLAUT;
4983 case 0x1632: // (blue steel)
4984 element = EL_STEEL_CHAR_0;
4987 case 0x1633: // (blue steel)
4988 element = EL_STEEL_CHAR_1;
4991 case 0x1634: // (blue steel)
4992 element = EL_STEEL_CHAR_2;
4995 case 0x1635: // (blue steel)
4996 element = EL_STEEL_CHAR_3;
4999 case 0x1636: // (blue steel)
5000 element = EL_STEEL_CHAR_4;
5003 case 0x1637: // (blue steel)
5004 element = EL_STEEL_CHAR_5;
5007 case 0x1638: // (blue steel)
5008 element = EL_STEEL_CHAR_6;
5011 case 0x1639: // (blue steel)
5012 element = EL_STEEL_CHAR_7;
5015 case 0x163a: // (blue steel)
5016 element = EL_STEEL_CHAR_8;
5019 case 0x163b: // (blue steel)
5020 element = EL_STEEL_CHAR_9;
5023 case 0x163c: // (blue steel)
5024 element = EL_STEEL_CHAR_PERIOD;
5027 case 0x163d: // (blue steel)
5028 element = EL_STEEL_CHAR_EXCLAM;
5031 case 0x163e: // (blue steel)
5032 element = EL_STEEL_CHAR_COLON;
5035 case 0x163f: // (blue steel)
5036 element = EL_STEEL_CHAR_LESS;
5039 case 0x1640: // (blue steel)
5040 element = EL_STEEL_CHAR_GREATER;
5043 case 0x1641: // (blue steel)
5044 element = EL_STEEL_CHAR_QUESTION;
5047 case 0x1642: // (blue steel)
5048 element = EL_STEEL_CHAR_COPYRIGHT;
5051 case 0x1643: // (blue steel)
5052 element = EL_STEEL_CHAR_UP;
5055 case 0x1644: // (blue steel)
5056 element = EL_STEEL_CHAR_DOWN;
5059 case 0x1645: // (blue steel)
5060 element = EL_STEEL_CHAR_BUTTON;
5063 case 0x1646: // (blue steel)
5064 element = EL_STEEL_CHAR_PLUS;
5067 case 0x1647: // (blue steel)
5068 element = EL_STEEL_CHAR_MINUS;
5071 case 0x1648: // (blue steel)
5072 element = EL_STEEL_CHAR_APOSTROPHE;
5075 case 0x1649: // (blue steel)
5076 element = EL_STEEL_CHAR_PARENLEFT;
5079 case 0x164a: // (blue steel)
5080 element = EL_STEEL_CHAR_PARENRIGHT;
5083 case 0x164b: // (green steel)
5084 element = EL_STEEL_CHAR_A;
5087 case 0x164c: // (green steel)
5088 element = EL_STEEL_CHAR_B;
5091 case 0x164d: // (green steel)
5092 element = EL_STEEL_CHAR_C;
5095 case 0x164e: // (green steel)
5096 element = EL_STEEL_CHAR_D;
5099 case 0x164f: // (green steel)
5100 element = EL_STEEL_CHAR_E;
5103 case 0x1650: // (green steel)
5104 element = EL_STEEL_CHAR_F;
5107 case 0x1651: // (green steel)
5108 element = EL_STEEL_CHAR_G;
5111 case 0x1652: // (green steel)
5112 element = EL_STEEL_CHAR_H;
5115 case 0x1653: // (green steel)
5116 element = EL_STEEL_CHAR_I;
5119 case 0x1654: // (green steel)
5120 element = EL_STEEL_CHAR_J;
5123 case 0x1655: // (green steel)
5124 element = EL_STEEL_CHAR_K;
5127 case 0x1656: // (green steel)
5128 element = EL_STEEL_CHAR_L;
5131 case 0x1657: // (green steel)
5132 element = EL_STEEL_CHAR_M;
5135 case 0x1658: // (green steel)
5136 element = EL_STEEL_CHAR_N;
5139 case 0x1659: // (green steel)
5140 element = EL_STEEL_CHAR_O;
5143 case 0x165a: // (green steel)
5144 element = EL_STEEL_CHAR_P;
5147 case 0x165b: // (green steel)
5148 element = EL_STEEL_CHAR_Q;
5151 case 0x165c: // (green steel)
5152 element = EL_STEEL_CHAR_R;
5155 case 0x165d: // (green steel)
5156 element = EL_STEEL_CHAR_S;
5159 case 0x165e: // (green steel)
5160 element = EL_STEEL_CHAR_T;
5163 case 0x165f: // (green steel)
5164 element = EL_STEEL_CHAR_U;
5167 case 0x1660: // (green steel)
5168 element = EL_STEEL_CHAR_V;
5171 case 0x1661: // (green steel)
5172 element = EL_STEEL_CHAR_W;
5175 case 0x1662: // (green steel)
5176 element = EL_STEEL_CHAR_X;
5179 case 0x1663: // (green steel)
5180 element = EL_STEEL_CHAR_Y;
5183 case 0x1664: // (green steel)
5184 element = EL_STEEL_CHAR_Z;
5187 case 0x1665: // (green steel)
5188 element = EL_STEEL_CHAR_AUMLAUT;
5191 case 0x1666: // (green steel)
5192 element = EL_STEEL_CHAR_OUMLAUT;
5195 case 0x1667: // (green steel)
5196 element = EL_STEEL_CHAR_UUMLAUT;
5199 case 0x1668: // (green steel)
5200 element = EL_STEEL_CHAR_0;
5203 case 0x1669: // (green steel)
5204 element = EL_STEEL_CHAR_1;
5207 case 0x166a: // (green steel)
5208 element = EL_STEEL_CHAR_2;
5211 case 0x166b: // (green steel)
5212 element = EL_STEEL_CHAR_3;
5215 case 0x166c: // (green steel)
5216 element = EL_STEEL_CHAR_4;
5219 case 0x166d: // (green steel)
5220 element = EL_STEEL_CHAR_5;
5223 case 0x166e: // (green steel)
5224 element = EL_STEEL_CHAR_6;
5227 case 0x166f: // (green steel)
5228 element = EL_STEEL_CHAR_7;
5231 case 0x1670: // (green steel)
5232 element = EL_STEEL_CHAR_8;
5235 case 0x1671: // (green steel)
5236 element = EL_STEEL_CHAR_9;
5239 case 0x1672: // (green steel)
5240 element = EL_STEEL_CHAR_PERIOD;
5243 case 0x1673: // (green steel)
5244 element = EL_STEEL_CHAR_EXCLAM;
5247 case 0x1674: // (green steel)
5248 element = EL_STEEL_CHAR_COLON;
5251 case 0x1675: // (green steel)
5252 element = EL_STEEL_CHAR_LESS;
5255 case 0x1676: // (green steel)
5256 element = EL_STEEL_CHAR_GREATER;
5259 case 0x1677: // (green steel)
5260 element = EL_STEEL_CHAR_QUESTION;
5263 case 0x1678: // (green steel)
5264 element = EL_STEEL_CHAR_COPYRIGHT;
5267 case 0x1679: // (green steel)
5268 element = EL_STEEL_CHAR_UP;
5271 case 0x167a: // (green steel)
5272 element = EL_STEEL_CHAR_DOWN;
5275 case 0x167b: // (green steel)
5276 element = EL_STEEL_CHAR_BUTTON;
5279 case 0x167c: // (green steel)
5280 element = EL_STEEL_CHAR_PLUS;
5283 case 0x167d: // (green steel)
5284 element = EL_STEEL_CHAR_MINUS;
5287 case 0x167e: // (green steel)
5288 element = EL_STEEL_CHAR_APOSTROPHE;
5291 case 0x167f: // (green steel)
5292 element = EL_STEEL_CHAR_PARENLEFT;
5295 case 0x1680: // (green steel)
5296 element = EL_STEEL_CHAR_PARENRIGHT;
5299 case 0x1681: // gate (red)
5300 element = EL_EM_GATE_1;
5303 case 0x1682: // secret gate (red)
5304 element = EL_EM_GATE_1_GRAY;
5307 case 0x1683: // gate (yellow)
5308 element = EL_EM_GATE_2;
5311 case 0x1684: // secret gate (yellow)
5312 element = EL_EM_GATE_2_GRAY;
5315 case 0x1685: // gate (blue)
5316 element = EL_EM_GATE_4;
5319 case 0x1686: // secret gate (blue)
5320 element = EL_EM_GATE_4_GRAY;
5323 case 0x1687: // gate (green)
5324 element = EL_EM_GATE_3;
5327 case 0x1688: // secret gate (green)
5328 element = EL_EM_GATE_3_GRAY;
5331 case 0x1689: // gate (white)
5332 element = EL_DC_GATE_WHITE;
5335 case 0x168a: // secret gate (white)
5336 element = EL_DC_GATE_WHITE_GRAY;
5339 case 0x168b: // secret gate (no key)
5340 element = EL_DC_GATE_FAKE_GRAY;
5344 element = EL_ROBOT_WHEEL;
5348 element = EL_DC_TIMEGATE_SWITCH;
5352 element = EL_ACID_POOL_BOTTOM;
5356 element = EL_ACID_POOL_TOPLEFT;
5360 element = EL_ACID_POOL_TOPRIGHT;
5364 element = EL_ACID_POOL_BOTTOMLEFT;
5368 element = EL_ACID_POOL_BOTTOMRIGHT;
5372 element = EL_STEELWALL;
5376 element = EL_STEELWALL_SLIPPERY;
5379 case 0x1695: // steel wall (not round)
5380 element = EL_STEELWALL;
5383 case 0x1696: // steel wall (left)
5384 element = EL_DC_STEELWALL_1_LEFT;
5387 case 0x1697: // steel wall (bottom)
5388 element = EL_DC_STEELWALL_1_BOTTOM;
5391 case 0x1698: // steel wall (right)
5392 element = EL_DC_STEELWALL_1_RIGHT;
5395 case 0x1699: // steel wall (top)
5396 element = EL_DC_STEELWALL_1_TOP;
5399 case 0x169a: // steel wall (left/bottom)
5400 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5403 case 0x169b: // steel wall (right/bottom)
5404 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5407 case 0x169c: // steel wall (right/top)
5408 element = EL_DC_STEELWALL_1_TOPRIGHT;
5411 case 0x169d: // steel wall (left/top)
5412 element = EL_DC_STEELWALL_1_TOPLEFT;
5415 case 0x169e: // steel wall (right/bottom small)
5416 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5419 case 0x169f: // steel wall (left/bottom small)
5420 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5423 case 0x16a0: // steel wall (right/top small)
5424 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5427 case 0x16a1: // steel wall (left/top small)
5428 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5431 case 0x16a2: // steel wall (left/right)
5432 element = EL_DC_STEELWALL_1_VERTICAL;
5435 case 0x16a3: // steel wall (top/bottom)
5436 element = EL_DC_STEELWALL_1_HORIZONTAL;
5439 case 0x16a4: // steel wall 2 (left end)
5440 element = EL_DC_STEELWALL_2_LEFT;
5443 case 0x16a5: // steel wall 2 (right end)
5444 element = EL_DC_STEELWALL_2_RIGHT;
5447 case 0x16a6: // steel wall 2 (top end)
5448 element = EL_DC_STEELWALL_2_TOP;
5451 case 0x16a7: // steel wall 2 (bottom end)
5452 element = EL_DC_STEELWALL_2_BOTTOM;
5455 case 0x16a8: // steel wall 2 (left/right)
5456 element = EL_DC_STEELWALL_2_HORIZONTAL;
5459 case 0x16a9: // steel wall 2 (up/down)
5460 element = EL_DC_STEELWALL_2_VERTICAL;
5463 case 0x16aa: // steel wall 2 (mid)
5464 element = EL_DC_STEELWALL_2_MIDDLE;
5468 element = EL_SIGN_EXCLAMATION;
5472 element = EL_SIGN_RADIOACTIVITY;
5476 element = EL_SIGN_STOP;
5480 element = EL_SIGN_WHEELCHAIR;
5484 element = EL_SIGN_PARKING;
5488 element = EL_SIGN_NO_ENTRY;
5492 element = EL_SIGN_HEART;
5496 element = EL_SIGN_GIVE_WAY;
5500 element = EL_SIGN_ENTRY_FORBIDDEN;
5504 element = EL_SIGN_EMERGENCY_EXIT;
5508 element = EL_SIGN_YIN_YANG;
5512 element = EL_WALL_EMERALD;
5516 element = EL_WALL_DIAMOND;
5520 element = EL_WALL_PEARL;
5524 element = EL_WALL_CRYSTAL;
5528 element = EL_INVISIBLE_WALL;
5532 element = EL_INVISIBLE_STEELWALL;
5536 // EL_INVISIBLE_SAND
5539 element = EL_LIGHT_SWITCH;
5543 element = EL_ENVELOPE_1;
5547 if (element >= 0x0117 && element <= 0x036e) // (?)
5548 element = EL_DIAMOND;
5549 else if (element >= 0x042d && element <= 0x0684) // (?)
5550 element = EL_EMERALD;
5551 else if (element >= 0x157c && element <= 0x158b)
5553 else if (element >= 0x1590 && element <= 0x159f)
5554 element = EL_DC_LANDMINE;
5555 else if (element >= 0x16bc && element <= 0x16cb)
5556 element = EL_INVISIBLE_SAND;
5559 Warn("unknown Diamond Caves element 0x%04x", element);
5561 element = EL_UNKNOWN;
5566 return getMappedElement(element);
5569 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5572 byte header[DC_LEVEL_HEADER_SIZE];
5574 int envelope_header_pos = 62;
5575 int envelope_content_pos = 94;
5576 int level_name_pos = 251;
5577 int level_author_pos = 292;
5578 int envelope_header_len;
5579 int envelope_content_len;
5581 int level_author_len;
5583 int num_yamyam_contents;
5586 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5588 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5590 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5592 header[i * 2 + 0] = header_word >> 8;
5593 header[i * 2 + 1] = header_word & 0xff;
5596 // read some values from level header to check level decoding integrity
5597 fieldx = header[6] | (header[7] << 8);
5598 fieldy = header[8] | (header[9] << 8);
5599 num_yamyam_contents = header[60] | (header[61] << 8);
5601 // do some simple sanity checks to ensure that level was correctly decoded
5602 if (fieldx < 1 || fieldx > 256 ||
5603 fieldy < 1 || fieldy > 256 ||
5604 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5606 level->no_valid_file = TRUE;
5608 Warn("cannot decode level from stream -- using empty level");
5613 // maximum envelope header size is 31 bytes
5614 envelope_header_len = header[envelope_header_pos];
5615 // maximum envelope content size is 110 (156?) bytes
5616 envelope_content_len = header[envelope_content_pos];
5618 // maximum level title size is 40 bytes
5619 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5620 // maximum level author size is 30 (51?) bytes
5621 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5625 for (i = 0; i < envelope_header_len; i++)
5626 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5627 level->envelope[0].text[envelope_size++] =
5628 header[envelope_header_pos + 1 + i];
5630 if (envelope_header_len > 0 && envelope_content_len > 0)
5632 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5633 level->envelope[0].text[envelope_size++] = '\n';
5634 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5635 level->envelope[0].text[envelope_size++] = '\n';
5638 for (i = 0; i < envelope_content_len; i++)
5639 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5640 level->envelope[0].text[envelope_size++] =
5641 header[envelope_content_pos + 1 + i];
5643 level->envelope[0].text[envelope_size] = '\0';
5645 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5646 level->envelope[0].ysize = 10;
5647 level->envelope[0].autowrap = TRUE;
5648 level->envelope[0].centered = TRUE;
5650 for (i = 0; i < level_name_len; i++)
5651 level->name[i] = header[level_name_pos + 1 + i];
5652 level->name[level_name_len] = '\0';
5654 for (i = 0; i < level_author_len; i++)
5655 level->author[i] = header[level_author_pos + 1 + i];
5656 level->author[level_author_len] = '\0';
5658 num_yamyam_contents = header[60] | (header[61] << 8);
5659 level->num_yamyam_contents =
5660 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5662 for (i = 0; i < num_yamyam_contents; i++)
5664 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5666 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5667 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5669 if (i < MAX_ELEMENT_CONTENTS)
5670 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5674 fieldx = header[6] | (header[7] << 8);
5675 fieldy = header[8] | (header[9] << 8);
5676 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5677 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5679 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5681 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5682 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5684 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5685 level->field[x][y] = getMappedElement_DC(element_dc);
5688 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5689 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5690 level->field[x][y] = EL_PLAYER_1;
5692 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5693 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5694 level->field[x][y] = EL_PLAYER_2;
5696 level->gems_needed = header[18] | (header[19] << 8);
5698 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5699 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5700 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5701 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5702 level->score[SC_NUT] = header[28] | (header[29] << 8);
5703 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5704 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5705 level->score[SC_BUG] = header[34] | (header[35] << 8);
5706 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5707 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5708 level->score[SC_KEY] = header[40] | (header[41] << 8);
5709 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5711 level->time = header[44] | (header[45] << 8);
5713 level->amoeba_speed = header[46] | (header[47] << 8);
5714 level->time_light = header[48] | (header[49] << 8);
5715 level->time_timegate = header[50] | (header[51] << 8);
5716 level->time_wheel = header[52] | (header[53] << 8);
5717 level->time_magic_wall = header[54] | (header[55] << 8);
5718 level->extra_time = header[56] | (header[57] << 8);
5719 level->shield_normal_time = header[58] | (header[59] << 8);
5721 // shield and extra time elements do not have a score
5722 level->score[SC_SHIELD] = 0;
5723 level->extra_time_score = 0;
5725 // set time for normal and deadly shields to the same value
5726 level->shield_deadly_time = level->shield_normal_time;
5728 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5729 // can slip down from flat walls, like normal walls and steel walls
5730 level->em_slippery_gems = TRUE;
5733 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5734 struct LevelFileInfo *level_file_info,
5735 boolean level_info_only)
5737 char *filename = level_file_info->filename;
5739 int num_magic_bytes = 8;
5740 char magic_bytes[num_magic_bytes + 1];
5741 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5743 if (!(file = openFile(filename, MODE_READ)))
5745 level->no_valid_file = TRUE;
5747 if (!level_info_only)
5748 Warn("cannot read level '%s' -- using empty level", filename);
5753 // fseek(file, 0x0000, SEEK_SET);
5755 if (level_file_info->packed)
5757 // read "magic bytes" from start of file
5758 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5759 magic_bytes[0] = '\0';
5761 // check "magic bytes" for correct file format
5762 if (!strPrefix(magic_bytes, "DC2"))
5764 level->no_valid_file = TRUE;
5766 Warn("unknown DC level file '%s' -- using empty level", filename);
5771 if (strPrefix(magic_bytes, "DC2Win95") ||
5772 strPrefix(magic_bytes, "DC2Win98"))
5774 int position_first_level = 0x00fa;
5775 int extra_bytes = 4;
5778 // advance file stream to first level inside the level package
5779 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5781 // each block of level data is followed by block of non-level data
5782 num_levels_to_skip *= 2;
5784 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5785 while (num_levels_to_skip >= 0)
5787 // advance file stream to next level inside the level package
5788 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5790 level->no_valid_file = TRUE;
5792 Warn("cannot fseek in file '%s' -- using empty level", filename);
5797 // skip apparently unused extra bytes following each level
5798 ReadUnusedBytesFromFile(file, extra_bytes);
5800 // read size of next level in level package
5801 skip_bytes = getFile32BitLE(file);
5803 num_levels_to_skip--;
5808 level->no_valid_file = TRUE;
5810 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5816 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5822 // ----------------------------------------------------------------------------
5823 // functions for loading SB level
5824 // ----------------------------------------------------------------------------
5826 int getMappedElement_SB(int element_ascii, boolean use_ces)
5834 sb_element_mapping[] =
5836 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5837 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5838 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5839 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5840 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5841 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5842 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5843 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5850 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5851 if (element_ascii == sb_element_mapping[i].ascii)
5852 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5854 return EL_UNDEFINED;
5857 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5858 struct LevelFileInfo *level_file_info,
5859 boolean level_info_only)
5861 char *filename = level_file_info->filename;
5862 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5863 char last_comment[MAX_LINE_LEN];
5864 char level_name[MAX_LINE_LEN];
5867 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5868 boolean read_continued_line = FALSE;
5869 boolean reading_playfield = FALSE;
5870 boolean got_valid_playfield_line = FALSE;
5871 boolean invalid_playfield_char = FALSE;
5872 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5873 int file_level_nr = 0;
5875 int x = 0, y = 0; // initialized to make compilers happy
5877 last_comment[0] = '\0';
5878 level_name[0] = '\0';
5880 if (!(file = openFile(filename, MODE_READ)))
5882 level->no_valid_file = TRUE;
5884 if (!level_info_only)
5885 Warn("cannot read level '%s' -- using empty level", filename);
5890 while (!checkEndOfFile(file))
5892 // level successfully read, but next level may follow here
5893 if (!got_valid_playfield_line && reading_playfield)
5895 // read playfield from single level file -- skip remaining file
5896 if (!level_file_info->packed)
5899 if (file_level_nr >= num_levels_to_skip)
5904 last_comment[0] = '\0';
5905 level_name[0] = '\0';
5907 reading_playfield = FALSE;
5910 got_valid_playfield_line = FALSE;
5912 // read next line of input file
5913 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5916 // check if line was completely read and is terminated by line break
5917 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5920 // cut trailing line break (this can be newline and/or carriage return)
5921 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5922 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5925 // copy raw input line for later use (mainly debugging output)
5926 strcpy(line_raw, line);
5928 if (read_continued_line)
5930 // append new line to existing line, if there is enough space
5931 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5932 strcat(previous_line, line_ptr);
5934 strcpy(line, previous_line); // copy storage buffer to line
5936 read_continued_line = FALSE;
5939 // if the last character is '\', continue at next line
5940 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5942 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
5943 strcpy(previous_line, line); // copy line to storage buffer
5945 read_continued_line = TRUE;
5951 if (line[0] == '\0')
5954 // extract comment text from comment line
5957 for (line_ptr = line; *line_ptr; line_ptr++)
5958 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5961 strcpy(last_comment, line_ptr);
5966 // extract level title text from line containing level title
5967 if (line[0] == '\'')
5969 strcpy(level_name, &line[1]);
5971 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5972 level_name[strlen(level_name) - 1] = '\0';
5977 // skip lines containing only spaces (or empty lines)
5978 for (line_ptr = line; *line_ptr; line_ptr++)
5979 if (*line_ptr != ' ')
5981 if (*line_ptr == '\0')
5984 // at this point, we have found a line containing part of a playfield
5986 got_valid_playfield_line = TRUE;
5988 if (!reading_playfield)
5990 reading_playfield = TRUE;
5991 invalid_playfield_char = FALSE;
5993 for (x = 0; x < MAX_LEV_FIELDX; x++)
5994 for (y = 0; y < MAX_LEV_FIELDY; y++)
5995 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6000 // start with topmost tile row
6004 // skip playfield line if larger row than allowed
6005 if (y >= MAX_LEV_FIELDY)
6008 // start with leftmost tile column
6011 // read playfield elements from line
6012 for (line_ptr = line; *line_ptr; line_ptr++)
6014 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6016 // stop parsing playfield line if larger column than allowed
6017 if (x >= MAX_LEV_FIELDX)
6020 if (mapped_sb_element == EL_UNDEFINED)
6022 invalid_playfield_char = TRUE;
6027 level->field[x][y] = mapped_sb_element;
6029 // continue with next tile column
6032 level->fieldx = MAX(x, level->fieldx);
6035 if (invalid_playfield_char)
6037 // if first playfield line, treat invalid lines as comment lines
6039 reading_playfield = FALSE;
6044 // continue with next tile row
6052 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6053 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6055 if (!reading_playfield)
6057 level->no_valid_file = TRUE;
6059 Warn("cannot read level '%s' -- using empty level", filename);
6064 if (*level_name != '\0')
6066 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6067 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6069 else if (*last_comment != '\0')
6071 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6072 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6076 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6079 // set all empty fields beyond the border walls to invisible steel wall
6080 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6082 if ((x == 0 || x == level->fieldx - 1 ||
6083 y == 0 || y == level->fieldy - 1) &&
6084 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6085 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6086 level->field, level->fieldx, level->fieldy);
6089 // set special level settings for Sokoban levels
6092 level->use_step_counter = TRUE;
6094 if (load_xsb_to_ces)
6096 // special global settings can now be set in level template
6098 level->use_custom_template = TRUE;
6103 // -------------------------------------------------------------------------
6104 // functions for handling native levels
6105 // -------------------------------------------------------------------------
6107 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6108 struct LevelFileInfo *level_file_info,
6109 boolean level_info_only)
6111 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6112 level->no_valid_file = TRUE;
6115 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6116 struct LevelFileInfo *level_file_info,
6117 boolean level_info_only)
6121 // determine position of requested level inside level package
6122 if (level_file_info->packed)
6123 pos = level_file_info->nr - leveldir_current->first_level;
6125 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6126 level->no_valid_file = TRUE;
6129 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6130 struct LevelFileInfo *level_file_info,
6131 boolean level_info_only)
6133 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6134 level->no_valid_file = TRUE;
6137 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6139 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6140 CopyNativeLevel_RND_to_EM(level);
6141 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6142 CopyNativeLevel_RND_to_SP(level);
6143 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6144 CopyNativeLevel_RND_to_MM(level);
6147 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6149 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6150 CopyNativeLevel_EM_to_RND(level);
6151 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6152 CopyNativeLevel_SP_to_RND(level);
6153 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6154 CopyNativeLevel_MM_to_RND(level);
6157 void SaveNativeLevel(struct LevelInfo *level)
6159 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6161 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6162 char *filename = getLevelFilenameFromBasename(basename);
6164 CopyNativeLevel_RND_to_SP(level);
6165 CopyNativeTape_RND_to_SP(level);
6167 SaveNativeLevel_SP(filename);
6172 // ----------------------------------------------------------------------------
6173 // functions for loading generic level
6174 // ----------------------------------------------------------------------------
6176 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6177 struct LevelFileInfo *level_file_info,
6178 boolean level_info_only)
6180 // always start with reliable default values
6181 setLevelInfoToDefaults(level, level_info_only, TRUE);
6183 switch (level_file_info->type)
6185 case LEVEL_FILE_TYPE_RND:
6186 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6189 case LEVEL_FILE_TYPE_EM:
6190 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6191 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6194 case LEVEL_FILE_TYPE_SP:
6195 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6196 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6199 case LEVEL_FILE_TYPE_MM:
6200 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6201 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6204 case LEVEL_FILE_TYPE_DC:
6205 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6208 case LEVEL_FILE_TYPE_SB:
6209 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6213 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6217 // if level file is invalid, restore level structure to default values
6218 if (level->no_valid_file)
6219 setLevelInfoToDefaults(level, level_info_only, FALSE);
6221 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6222 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6224 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6225 CopyNativeLevel_Native_to_RND(level);
6228 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6230 static struct LevelFileInfo level_file_info;
6232 // always start with reliable default values
6233 setFileInfoToDefaults(&level_file_info);
6235 level_file_info.nr = 0; // unknown level number
6236 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6238 setString(&level_file_info.filename, filename);
6240 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6243 static void LoadLevel_InitVersion(struct LevelInfo *level)
6247 if (leveldir_current == NULL) // only when dumping level
6250 // all engine modifications also valid for levels which use latest engine
6251 if (level->game_version < VERSION_IDENT(3,2,0,5))
6253 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6254 level->score[SC_TIME_BONUS] /= 10;
6257 if (leveldir_current->latest_engine)
6259 // ---------- use latest game engine --------------------------------------
6261 /* For all levels which are forced to use the latest game engine version
6262 (normally all but user contributed, private and undefined levels), set
6263 the game engine version to the actual version; this allows for actual
6264 corrections in the game engine to take effect for existing, converted
6265 levels (from "classic" or other existing games) to make the emulation
6266 of the corresponding game more accurate, while (hopefully) not breaking
6267 existing levels created from other players. */
6269 level->game_version = GAME_VERSION_ACTUAL;
6271 /* Set special EM style gems behaviour: EM style gems slip down from
6272 normal, steel and growing wall. As this is a more fundamental change,
6273 it seems better to set the default behaviour to "off" (as it is more
6274 natural) and make it configurable in the level editor (as a property
6275 of gem style elements). Already existing converted levels (neither
6276 private nor contributed levels) are changed to the new behaviour. */
6278 if (level->file_version < FILE_VERSION_2_0)
6279 level->em_slippery_gems = TRUE;
6284 // ---------- use game engine the level was created with --------------------
6286 /* For all levels which are not forced to use the latest game engine
6287 version (normally user contributed, private and undefined levels),
6288 use the version of the game engine the levels were created for.
6290 Since 2.0.1, the game engine version is now directly stored
6291 in the level file (chunk "VERS"), so there is no need anymore
6292 to set the game version from the file version (except for old,
6293 pre-2.0 levels, where the game version is still taken from the
6294 file format version used to store the level -- see above). */
6296 // player was faster than enemies in 1.0.0 and before
6297 if (level->file_version == FILE_VERSION_1_0)
6298 for (i = 0; i < MAX_PLAYERS; i++)
6299 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6301 // default behaviour for EM style gems was "slippery" only in 2.0.1
6302 if (level->game_version == VERSION_IDENT(2,0,1,0))
6303 level->em_slippery_gems = TRUE;
6305 // springs could be pushed over pits before (pre-release version) 2.2.0
6306 if (level->game_version < VERSION_IDENT(2,2,0,0))
6307 level->use_spring_bug = TRUE;
6309 if (level->game_version < VERSION_IDENT(3,2,0,5))
6311 // time orb caused limited time in endless time levels before 3.2.0-5
6312 level->use_time_orb_bug = TRUE;
6314 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6315 level->block_snap_field = FALSE;
6317 // extra time score was same value as time left score before 3.2.0-5
6318 level->extra_time_score = level->score[SC_TIME_BONUS];
6321 if (level->game_version < VERSION_IDENT(3,2,0,7))
6323 // default behaviour for snapping was "not continuous" before 3.2.0-7
6324 level->continuous_snapping = FALSE;
6327 // only few elements were able to actively move into acid before 3.1.0
6328 // trigger settings did not exist before 3.1.0; set to default "any"
6329 if (level->game_version < VERSION_IDENT(3,1,0,0))
6331 // correct "can move into acid" settings (all zero in old levels)
6333 level->can_move_into_acid_bits = 0; // nothing can move into acid
6334 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6336 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6337 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6338 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6339 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6341 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6342 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6344 // correct trigger settings (stored as zero == "none" in old levels)
6346 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6348 int element = EL_CUSTOM_START + i;
6349 struct ElementInfo *ei = &element_info[element];
6351 for (j = 0; j < ei->num_change_pages; j++)
6353 struct ElementChangeInfo *change = &ei->change_page[j];
6355 change->trigger_player = CH_PLAYER_ANY;
6356 change->trigger_page = CH_PAGE_ANY;
6361 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6363 int element = EL_CUSTOM_256;
6364 struct ElementInfo *ei = &element_info[element];
6365 struct ElementChangeInfo *change = &ei->change_page[0];
6367 /* This is needed to fix a problem that was caused by a bugfix in function
6368 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6369 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6370 not replace walkable elements, but instead just placed the player on it,
6371 without placing the Sokoban field under the player). Unfortunately, this
6372 breaks "Snake Bite" style levels when the snake is halfway through a door
6373 that just closes (the snake head is still alive and can be moved in this
6374 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6375 player (without Sokoban element) which then gets killed as designed). */
6377 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6378 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6379 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6380 change->target_element = EL_PLAYER_1;
6383 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6384 if (level->game_version < VERSION_IDENT(3,2,5,0))
6386 /* This is needed to fix a problem that was caused by a bugfix in function
6387 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6388 corrects the behaviour when a custom element changes to another custom
6389 element with a higher element number that has change actions defined.
6390 Normally, only one change per frame is allowed for custom elements.
6391 Therefore, it is checked if a custom element already changed in the
6392 current frame; if it did, subsequent changes are suppressed.
6393 Unfortunately, this is only checked for element changes, but not for
6394 change actions, which are still executed. As the function above loops
6395 through all custom elements from lower to higher, an element change
6396 resulting in a lower CE number won't be checked again, while a target
6397 element with a higher number will also be checked, and potential change
6398 actions will get executed for this CE, too (which is wrong), while
6399 further changes are ignored (which is correct). As this bugfix breaks
6400 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6401 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6402 behaviour for existing levels and tapes that make use of this bug */
6404 level->use_action_after_change_bug = TRUE;
6407 // not centering level after relocating player was default only in 3.2.3
6408 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6409 level->shifted_relocation = TRUE;
6411 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6412 if (level->game_version < VERSION_IDENT(3,2,6,0))
6413 level->em_explodes_by_fire = TRUE;
6415 // levels were solved by the first player entering an exit up to 4.1.0.0
6416 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6417 level->solved_by_one_player = TRUE;
6419 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6420 if (level->game_version < VERSION_IDENT(4,1,1,1))
6421 level->use_life_bugs = TRUE;
6423 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6424 if (level->game_version < VERSION_IDENT(4,1,1,1))
6425 level->sb_objects_needed = FALSE;
6428 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6432 // map elements that have changed in newer versions
6433 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6434 level->game_version);
6435 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6436 for (x = 0; x < 3; x++)
6437 for (y = 0; y < 3; y++)
6438 level->yamyam_content[i].e[x][y] =
6439 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6440 level->game_version);
6444 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6448 // map custom element change events that have changed in newer versions
6449 // (these following values were accidentally changed in version 3.0.1)
6450 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6451 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6453 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6455 int element = EL_CUSTOM_START + i;
6457 // order of checking and copying events to be mapped is important
6458 // (do not change the start and end value -- they are constant)
6459 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6461 if (HAS_CHANGE_EVENT(element, j - 2))
6463 SET_CHANGE_EVENT(element, j - 2, FALSE);
6464 SET_CHANGE_EVENT(element, j, TRUE);
6468 // order of checking and copying events to be mapped is important
6469 // (do not change the start and end value -- they are constant)
6470 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6472 if (HAS_CHANGE_EVENT(element, j - 1))
6474 SET_CHANGE_EVENT(element, j - 1, FALSE);
6475 SET_CHANGE_EVENT(element, j, TRUE);
6481 // initialize "can_change" field for old levels with only one change page
6482 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6484 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6486 int element = EL_CUSTOM_START + i;
6488 if (CAN_CHANGE(element))
6489 element_info[element].change->can_change = TRUE;
6493 // correct custom element values (for old levels without these options)
6494 if (level->game_version < VERSION_IDENT(3,1,1,0))
6496 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6498 int element = EL_CUSTOM_START + i;
6499 struct ElementInfo *ei = &element_info[element];
6501 if (ei->access_direction == MV_NO_DIRECTION)
6502 ei->access_direction = MV_ALL_DIRECTIONS;
6506 // correct custom element values (fix invalid values for all versions)
6509 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6511 int element = EL_CUSTOM_START + i;
6512 struct ElementInfo *ei = &element_info[element];
6514 for (j = 0; j < ei->num_change_pages; j++)
6516 struct ElementChangeInfo *change = &ei->change_page[j];
6518 if (change->trigger_player == CH_PLAYER_NONE)
6519 change->trigger_player = CH_PLAYER_ANY;
6521 if (change->trigger_side == CH_SIDE_NONE)
6522 change->trigger_side = CH_SIDE_ANY;
6527 // initialize "can_explode" field for old levels which did not store this
6528 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6529 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6531 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6533 int element = EL_CUSTOM_START + i;
6535 if (EXPLODES_1X1_OLD(element))
6536 element_info[element].explosion_type = EXPLODES_1X1;
6538 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6539 EXPLODES_SMASHED(element) ||
6540 EXPLODES_IMPACT(element)));
6544 // correct previously hard-coded move delay values for maze runner style
6545 if (level->game_version < VERSION_IDENT(3,1,1,0))
6547 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6549 int element = EL_CUSTOM_START + i;
6551 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6553 // previously hard-coded and therefore ignored
6554 element_info[element].move_delay_fixed = 9;
6555 element_info[element].move_delay_random = 0;
6560 // set some other uninitialized values of custom elements in older levels
6561 if (level->game_version < VERSION_IDENT(3,1,0,0))
6563 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6565 int element = EL_CUSTOM_START + i;
6567 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6569 element_info[element].explosion_delay = 17;
6570 element_info[element].ignition_delay = 8;
6575 static void LoadLevel_InitElements(struct LevelInfo *level)
6577 LoadLevel_InitStandardElements(level);
6579 if (level->file_has_custom_elements)
6580 LoadLevel_InitCustomElements(level);
6582 // initialize element properties for level editor etc.
6583 InitElementPropertiesEngine(level->game_version);
6584 InitElementPropertiesGfxElement();
6587 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6591 // map elements that have changed in newer versions
6592 for (y = 0; y < level->fieldy; y++)
6593 for (x = 0; x < level->fieldx; x++)
6594 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6595 level->game_version);
6597 // clear unused playfield data (nicer if level gets resized in editor)
6598 for (x = 0; x < MAX_LEV_FIELDX; x++)
6599 for (y = 0; y < MAX_LEV_FIELDY; y++)
6600 if (x >= level->fieldx || y >= level->fieldy)
6601 level->field[x][y] = EL_EMPTY;
6603 // copy elements to runtime playfield array
6604 for (x = 0; x < MAX_LEV_FIELDX; x++)
6605 for (y = 0; y < MAX_LEV_FIELDY; y++)
6606 Tile[x][y] = level->field[x][y];
6608 // initialize level size variables for faster access
6609 lev_fieldx = level->fieldx;
6610 lev_fieldy = level->fieldy;
6612 // determine border element for this level
6613 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6614 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6619 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6621 struct LevelFileInfo *level_file_info = &level->file_info;
6623 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6624 CopyNativeLevel_RND_to_Native(level);
6627 static void LoadLevelTemplate_LoadAndInit(void)
6629 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6631 LoadLevel_InitVersion(&level_template);
6632 LoadLevel_InitElements(&level_template);
6634 ActivateLevelTemplate();
6637 void LoadLevelTemplate(int nr)
6639 if (!fileExists(getGlobalLevelTemplateFilename()))
6641 Warn("no level template found for this level");
6646 setLevelFileInfo(&level_template.file_info, nr);
6648 LoadLevelTemplate_LoadAndInit();
6651 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6653 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6655 LoadLevelTemplate_LoadAndInit();
6658 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6660 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6662 if (level.use_custom_template)
6664 if (network_level != NULL)
6665 LoadNetworkLevelTemplate(network_level);
6667 LoadLevelTemplate(-1);
6670 LoadLevel_InitVersion(&level);
6671 LoadLevel_InitElements(&level);
6672 LoadLevel_InitPlayfield(&level);
6674 LoadLevel_InitNativeEngines(&level);
6677 void LoadLevel(int nr)
6679 SetLevelSetInfo(leveldir_current->identifier, nr);
6681 setLevelFileInfo(&level.file_info, nr);
6683 LoadLevel_LoadAndInit(NULL);
6686 void LoadLevelInfoOnly(int nr)
6688 setLevelFileInfo(&level.file_info, nr);
6690 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6693 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6695 SetLevelSetInfo(network_level->leveldir_identifier,
6696 network_level->file_info.nr);
6698 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6700 LoadLevel_LoadAndInit(network_level);
6703 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6707 chunk_size += putFileVersion(file, level->file_version);
6708 chunk_size += putFileVersion(file, level->game_version);
6713 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6717 chunk_size += putFile16BitBE(file, level->creation_date.year);
6718 chunk_size += putFile8Bit(file, level->creation_date.month);
6719 chunk_size += putFile8Bit(file, level->creation_date.day);
6724 #if ENABLE_HISTORIC_CHUNKS
6725 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6729 putFile8Bit(file, level->fieldx);
6730 putFile8Bit(file, level->fieldy);
6732 putFile16BitBE(file, level->time);
6733 putFile16BitBE(file, level->gems_needed);
6735 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6736 putFile8Bit(file, level->name[i]);
6738 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6739 putFile8Bit(file, level->score[i]);
6741 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6742 for (y = 0; y < 3; y++)
6743 for (x = 0; x < 3; x++)
6744 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6745 level->yamyam_content[i].e[x][y]));
6746 putFile8Bit(file, level->amoeba_speed);
6747 putFile8Bit(file, level->time_magic_wall);
6748 putFile8Bit(file, level->time_wheel);
6749 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6750 level->amoeba_content));
6751 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6752 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6753 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6754 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6756 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6758 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6759 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6760 putFile32BitBE(file, level->can_move_into_acid_bits);
6761 putFile8Bit(file, level->dont_collide_with_bits);
6763 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6764 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6766 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6767 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6768 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6770 putFile8Bit(file, level->game_engine_type);
6772 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6776 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6781 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6782 chunk_size += putFile8Bit(file, level->name[i]);
6787 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6792 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6793 chunk_size += putFile8Bit(file, level->author[i]);
6798 #if ENABLE_HISTORIC_CHUNKS
6799 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6804 for (y = 0; y < level->fieldy; y++)
6805 for (x = 0; x < level->fieldx; x++)
6806 if (level->encoding_16bit_field)
6807 chunk_size += putFile16BitBE(file, level->field[x][y]);
6809 chunk_size += putFile8Bit(file, level->field[x][y]);
6815 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6820 for (y = 0; y < level->fieldy; y++)
6821 for (x = 0; x < level->fieldx; x++)
6822 chunk_size += putFile16BitBE(file, level->field[x][y]);
6827 #if ENABLE_HISTORIC_CHUNKS
6828 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6832 putFile8Bit(file, EL_YAMYAM);
6833 putFile8Bit(file, level->num_yamyam_contents);
6834 putFile8Bit(file, 0);
6835 putFile8Bit(file, 0);
6837 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6838 for (y = 0; y < 3; y++)
6839 for (x = 0; x < 3; x++)
6840 if (level->encoding_16bit_field)
6841 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6843 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6847 #if ENABLE_HISTORIC_CHUNKS
6848 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6851 int num_contents, content_xsize, content_ysize;
6852 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6854 if (element == EL_YAMYAM)
6856 num_contents = level->num_yamyam_contents;
6860 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6861 for (y = 0; y < 3; y++)
6862 for (x = 0; x < 3; x++)
6863 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6865 else if (element == EL_BD_AMOEBA)
6871 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6872 for (y = 0; y < 3; y++)
6873 for (x = 0; x < 3; x++)
6874 content_array[i][x][y] = EL_EMPTY;
6875 content_array[0][0][0] = level->amoeba_content;
6879 // chunk header already written -- write empty chunk data
6880 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6882 Warn("cannot save content for element '%d'", element);
6887 putFile16BitBE(file, element);
6888 putFile8Bit(file, num_contents);
6889 putFile8Bit(file, content_xsize);
6890 putFile8Bit(file, content_ysize);
6892 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6894 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6895 for (y = 0; y < 3; y++)
6896 for (x = 0; x < 3; x++)
6897 putFile16BitBE(file, content_array[i][x][y]);
6901 #if ENABLE_HISTORIC_CHUNKS
6902 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6904 int envelope_nr = element - EL_ENVELOPE_1;
6905 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6909 chunk_size += putFile16BitBE(file, element);
6910 chunk_size += putFile16BitBE(file, envelope_len);
6911 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6912 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6914 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6915 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6917 for (i = 0; i < envelope_len; i++)
6918 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6924 #if ENABLE_HISTORIC_CHUNKS
6925 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6926 int num_changed_custom_elements)
6930 putFile16BitBE(file, num_changed_custom_elements);
6932 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6934 int element = EL_CUSTOM_START + i;
6936 struct ElementInfo *ei = &element_info[element];
6938 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6940 if (check < num_changed_custom_elements)
6942 putFile16BitBE(file, element);
6943 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6950 if (check != num_changed_custom_elements) // should not happen
6951 Warn("inconsistent number of custom element properties");
6955 #if ENABLE_HISTORIC_CHUNKS
6956 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6957 int num_changed_custom_elements)
6961 putFile16BitBE(file, num_changed_custom_elements);
6963 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6965 int element = EL_CUSTOM_START + i;
6967 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6969 if (check < num_changed_custom_elements)
6971 putFile16BitBE(file, element);
6972 putFile16BitBE(file, element_info[element].change->target_element);
6979 if (check != num_changed_custom_elements) // should not happen
6980 Warn("inconsistent number of custom target elements");
6984 #if ENABLE_HISTORIC_CHUNKS
6985 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6986 int num_changed_custom_elements)
6988 int i, j, x, y, check = 0;
6990 putFile16BitBE(file, num_changed_custom_elements);
6992 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6994 int element = EL_CUSTOM_START + i;
6995 struct ElementInfo *ei = &element_info[element];
6997 if (ei->modified_settings)
6999 if (check < num_changed_custom_elements)
7001 putFile16BitBE(file, element);
7003 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7004 putFile8Bit(file, ei->description[j]);
7006 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7008 // some free bytes for future properties and padding
7009 WriteUnusedBytesToFile(file, 7);
7011 putFile8Bit(file, ei->use_gfx_element);
7012 putFile16BitBE(file, ei->gfx_element_initial);
7014 putFile8Bit(file, ei->collect_score_initial);
7015 putFile8Bit(file, ei->collect_count_initial);
7017 putFile16BitBE(file, ei->push_delay_fixed);
7018 putFile16BitBE(file, ei->push_delay_random);
7019 putFile16BitBE(file, ei->move_delay_fixed);
7020 putFile16BitBE(file, ei->move_delay_random);
7022 putFile16BitBE(file, ei->move_pattern);
7023 putFile8Bit(file, ei->move_direction_initial);
7024 putFile8Bit(file, ei->move_stepsize);
7026 for (y = 0; y < 3; y++)
7027 for (x = 0; x < 3; x++)
7028 putFile16BitBE(file, ei->content.e[x][y]);
7030 putFile32BitBE(file, ei->change->events);
7032 putFile16BitBE(file, ei->change->target_element);
7034 putFile16BitBE(file, ei->change->delay_fixed);
7035 putFile16BitBE(file, ei->change->delay_random);
7036 putFile16BitBE(file, ei->change->delay_frames);
7038 putFile16BitBE(file, ei->change->initial_trigger_element);
7040 putFile8Bit(file, ei->change->explode);
7041 putFile8Bit(file, ei->change->use_target_content);
7042 putFile8Bit(file, ei->change->only_if_complete);
7043 putFile8Bit(file, ei->change->use_random_replace);
7045 putFile8Bit(file, ei->change->random_percentage);
7046 putFile8Bit(file, ei->change->replace_when);
7048 for (y = 0; y < 3; y++)
7049 for (x = 0; x < 3; x++)
7050 putFile16BitBE(file, ei->change->content.e[x][y]);
7052 putFile8Bit(file, ei->slippery_type);
7054 // some free bytes for future properties and padding
7055 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7062 if (check != num_changed_custom_elements) // should not happen
7063 Warn("inconsistent number of custom element properties");
7067 #if ENABLE_HISTORIC_CHUNKS
7068 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7070 struct ElementInfo *ei = &element_info[element];
7073 // ---------- custom element base property values (96 bytes) ----------------
7075 putFile16BitBE(file, element);
7077 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7078 putFile8Bit(file, ei->description[i]);
7080 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7082 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7084 putFile8Bit(file, ei->num_change_pages);
7086 putFile16BitBE(file, ei->ce_value_fixed_initial);
7087 putFile16BitBE(file, ei->ce_value_random_initial);
7088 putFile8Bit(file, ei->use_last_ce_value);
7090 putFile8Bit(file, ei->use_gfx_element);
7091 putFile16BitBE(file, ei->gfx_element_initial);
7093 putFile8Bit(file, ei->collect_score_initial);
7094 putFile8Bit(file, ei->collect_count_initial);
7096 putFile8Bit(file, ei->drop_delay_fixed);
7097 putFile8Bit(file, ei->push_delay_fixed);
7098 putFile8Bit(file, ei->drop_delay_random);
7099 putFile8Bit(file, ei->push_delay_random);
7100 putFile16BitBE(file, ei->move_delay_fixed);
7101 putFile16BitBE(file, ei->move_delay_random);
7103 // bits 0 - 15 of "move_pattern" ...
7104 putFile16BitBE(file, ei->move_pattern & 0xffff);
7105 putFile8Bit(file, ei->move_direction_initial);
7106 putFile8Bit(file, ei->move_stepsize);
7108 putFile8Bit(file, ei->slippery_type);
7110 for (y = 0; y < 3; y++)
7111 for (x = 0; x < 3; x++)
7112 putFile16BitBE(file, ei->content.e[x][y]);
7114 putFile16BitBE(file, ei->move_enter_element);
7115 putFile16BitBE(file, ei->move_leave_element);
7116 putFile8Bit(file, ei->move_leave_type);
7118 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7119 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7121 putFile8Bit(file, ei->access_direction);
7123 putFile8Bit(file, ei->explosion_delay);
7124 putFile8Bit(file, ei->ignition_delay);
7125 putFile8Bit(file, ei->explosion_type);
7127 // some free bytes for future custom property values and padding
7128 WriteUnusedBytesToFile(file, 1);
7130 // ---------- change page property values (48 bytes) ------------------------
7132 for (i = 0; i < ei->num_change_pages; i++)
7134 struct ElementChangeInfo *change = &ei->change_page[i];
7135 unsigned int event_bits;
7137 // bits 0 - 31 of "has_event[]" ...
7139 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7140 if (change->has_event[j])
7141 event_bits |= (1 << j);
7142 putFile32BitBE(file, event_bits);
7144 putFile16BitBE(file, change->target_element);
7146 putFile16BitBE(file, change->delay_fixed);
7147 putFile16BitBE(file, change->delay_random);
7148 putFile16BitBE(file, change->delay_frames);
7150 putFile16BitBE(file, change->initial_trigger_element);
7152 putFile8Bit(file, change->explode);
7153 putFile8Bit(file, change->use_target_content);
7154 putFile8Bit(file, change->only_if_complete);
7155 putFile8Bit(file, change->use_random_replace);
7157 putFile8Bit(file, change->random_percentage);
7158 putFile8Bit(file, change->replace_when);
7160 for (y = 0; y < 3; y++)
7161 for (x = 0; x < 3; x++)
7162 putFile16BitBE(file, change->target_content.e[x][y]);
7164 putFile8Bit(file, change->can_change);
7166 putFile8Bit(file, change->trigger_side);
7168 putFile8Bit(file, change->trigger_player);
7169 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7170 log_2(change->trigger_page)));
7172 putFile8Bit(file, change->has_action);
7173 putFile8Bit(file, change->action_type);
7174 putFile8Bit(file, change->action_mode);
7175 putFile16BitBE(file, change->action_arg);
7177 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7179 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7180 if (change->has_event[j])
7181 event_bits |= (1 << (j - 32));
7182 putFile8Bit(file, event_bits);
7187 #if ENABLE_HISTORIC_CHUNKS
7188 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7190 struct ElementInfo *ei = &element_info[element];
7191 struct ElementGroupInfo *group = ei->group;
7194 putFile16BitBE(file, element);
7196 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7197 putFile8Bit(file, ei->description[i]);
7199 putFile8Bit(file, group->num_elements);
7201 putFile8Bit(file, ei->use_gfx_element);
7202 putFile16BitBE(file, ei->gfx_element_initial);
7204 putFile8Bit(file, group->choice_mode);
7206 // some free bytes for future values and padding
7207 WriteUnusedBytesToFile(file, 3);
7209 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7210 putFile16BitBE(file, group->element[i]);
7214 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7215 boolean write_element)
7217 int save_type = entry->save_type;
7218 int data_type = entry->data_type;
7219 int conf_type = entry->conf_type;
7220 int byte_mask = conf_type & CONF_MASK_BYTES;
7221 int element = entry->element;
7222 int default_value = entry->default_value;
7224 boolean modified = FALSE;
7226 if (byte_mask != CONF_MASK_MULTI_BYTES)
7228 void *value_ptr = entry->value;
7229 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7232 // check if any settings have been modified before saving them
7233 if (value != default_value)
7236 // do not save if explicitly told or if unmodified default settings
7237 if ((save_type == SAVE_CONF_NEVER) ||
7238 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7242 num_bytes += putFile16BitBE(file, element);
7244 num_bytes += putFile8Bit(file, conf_type);
7245 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7246 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7247 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7250 else if (data_type == TYPE_STRING)
7252 char *default_string = entry->default_string;
7253 char *string = (char *)(entry->value);
7254 int string_length = strlen(string);
7257 // check if any settings have been modified before saving them
7258 if (!strEqual(string, default_string))
7261 // do not save if explicitly told or if unmodified default settings
7262 if ((save_type == SAVE_CONF_NEVER) ||
7263 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7267 num_bytes += putFile16BitBE(file, element);
7269 num_bytes += putFile8Bit(file, conf_type);
7270 num_bytes += putFile16BitBE(file, string_length);
7272 for (i = 0; i < string_length; i++)
7273 num_bytes += putFile8Bit(file, string[i]);
7275 else if (data_type == TYPE_ELEMENT_LIST)
7277 int *element_array = (int *)(entry->value);
7278 int num_elements = *(int *)(entry->num_entities);
7281 // check if any settings have been modified before saving them
7282 for (i = 0; i < num_elements; i++)
7283 if (element_array[i] != default_value)
7286 // do not save if explicitly told or if unmodified default settings
7287 if ((save_type == SAVE_CONF_NEVER) ||
7288 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7292 num_bytes += putFile16BitBE(file, element);
7294 num_bytes += putFile8Bit(file, conf_type);
7295 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7297 for (i = 0; i < num_elements; i++)
7298 num_bytes += putFile16BitBE(file, element_array[i]);
7300 else if (data_type == TYPE_CONTENT_LIST)
7302 struct Content *content = (struct Content *)(entry->value);
7303 int num_contents = *(int *)(entry->num_entities);
7306 // check if any settings have been modified before saving them
7307 for (i = 0; i < num_contents; i++)
7308 for (y = 0; y < 3; y++)
7309 for (x = 0; x < 3; x++)
7310 if (content[i].e[x][y] != default_value)
7313 // do not save if explicitly told or if unmodified default settings
7314 if ((save_type == SAVE_CONF_NEVER) ||
7315 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7319 num_bytes += putFile16BitBE(file, element);
7321 num_bytes += putFile8Bit(file, conf_type);
7322 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7324 for (i = 0; i < num_contents; i++)
7325 for (y = 0; y < 3; y++)
7326 for (x = 0; x < 3; x++)
7327 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7333 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7338 li = *level; // copy level data into temporary buffer
7340 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7341 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7346 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7351 li = *level; // copy level data into temporary buffer
7353 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7354 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7359 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7361 int envelope_nr = element - EL_ENVELOPE_1;
7365 chunk_size += putFile16BitBE(file, element);
7367 // copy envelope data into temporary buffer
7368 xx_envelope = level->envelope[envelope_nr];
7370 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7371 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7376 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7378 struct ElementInfo *ei = &element_info[element];
7382 chunk_size += putFile16BitBE(file, element);
7384 xx_ei = *ei; // copy element data into temporary buffer
7386 // set default description string for this specific element
7387 strcpy(xx_default_description, getDefaultElementDescription(ei));
7389 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7390 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7392 for (i = 0; i < ei->num_change_pages; i++)
7394 struct ElementChangeInfo *change = &ei->change_page[i];
7396 xx_current_change_page = i;
7398 xx_change = *change; // copy change data into temporary buffer
7401 setEventBitsFromEventFlags(change);
7403 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7404 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7411 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7413 struct ElementInfo *ei = &element_info[element];
7414 struct ElementGroupInfo *group = ei->group;
7418 chunk_size += putFile16BitBE(file, element);
7420 xx_ei = *ei; // copy element data into temporary buffer
7421 xx_group = *group; // copy group data into temporary buffer
7423 // set default description string for this specific element
7424 strcpy(xx_default_description, getDefaultElementDescription(ei));
7426 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7427 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7432 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7433 boolean save_as_template)
7439 if (!(file = fopen(filename, MODE_WRITE)))
7441 Warn("cannot save level file '%s'", filename);
7446 level->file_version = FILE_VERSION_ACTUAL;
7447 level->game_version = GAME_VERSION_ACTUAL;
7449 level->creation_date = getCurrentDate();
7451 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7452 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7454 chunk_size = SaveLevel_VERS(NULL, level);
7455 putFileChunkBE(file, "VERS", chunk_size);
7456 SaveLevel_VERS(file, level);
7458 chunk_size = SaveLevel_DATE(NULL, level);
7459 putFileChunkBE(file, "DATE", chunk_size);
7460 SaveLevel_DATE(file, level);
7462 chunk_size = SaveLevel_NAME(NULL, level);
7463 putFileChunkBE(file, "NAME", chunk_size);
7464 SaveLevel_NAME(file, level);
7466 chunk_size = SaveLevel_AUTH(NULL, level);
7467 putFileChunkBE(file, "AUTH", chunk_size);
7468 SaveLevel_AUTH(file, level);
7470 chunk_size = SaveLevel_INFO(NULL, level);
7471 putFileChunkBE(file, "INFO", chunk_size);
7472 SaveLevel_INFO(file, level);
7474 chunk_size = SaveLevel_BODY(NULL, level);
7475 putFileChunkBE(file, "BODY", chunk_size);
7476 SaveLevel_BODY(file, level);
7478 chunk_size = SaveLevel_ELEM(NULL, level);
7479 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7481 putFileChunkBE(file, "ELEM", chunk_size);
7482 SaveLevel_ELEM(file, level);
7485 for (i = 0; i < NUM_ENVELOPES; i++)
7487 int element = EL_ENVELOPE_1 + i;
7489 chunk_size = SaveLevel_NOTE(NULL, level, element);
7490 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7492 putFileChunkBE(file, "NOTE", chunk_size);
7493 SaveLevel_NOTE(file, level, element);
7497 // if not using template level, check for non-default custom/group elements
7498 if (!level->use_custom_template || save_as_template)
7500 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7502 int element = EL_CUSTOM_START + i;
7504 chunk_size = SaveLevel_CUSX(NULL, level, element);
7505 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7507 putFileChunkBE(file, "CUSX", chunk_size);
7508 SaveLevel_CUSX(file, level, element);
7512 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7514 int element = EL_GROUP_START + i;
7516 chunk_size = SaveLevel_GRPX(NULL, level, element);
7517 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7519 putFileChunkBE(file, "GRPX", chunk_size);
7520 SaveLevel_GRPX(file, level, element);
7527 SetFilePermissions(filename, PERMS_PRIVATE);
7530 void SaveLevel(int nr)
7532 char *filename = getDefaultLevelFilename(nr);
7534 SaveLevelFromFilename(&level, filename, FALSE);
7537 void SaveLevelTemplate(void)
7539 char *filename = getLocalLevelTemplateFilename();
7541 SaveLevelFromFilename(&level, filename, TRUE);
7544 boolean SaveLevelChecked(int nr)
7546 char *filename = getDefaultLevelFilename(nr);
7547 boolean new_level = !fileExists(filename);
7548 boolean level_saved = FALSE;
7550 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7555 Request("Level saved!", REQ_CONFIRM);
7563 void DumpLevel(struct LevelInfo *level)
7565 if (level->no_level_file || level->no_valid_file)
7567 Warn("cannot dump -- no valid level file found");
7573 Print("Level xxx (file version %08d, game version %08d)\n",
7574 level->file_version, level->game_version);
7577 Print("Level author: '%s'\n", level->author);
7578 Print("Level title: '%s'\n", level->name);
7580 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7582 Print("Level time: %d seconds\n", level->time);
7583 Print("Gems needed: %d\n", level->gems_needed);
7585 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7586 Print("Time for wheel: %d seconds\n", level->time_wheel);
7587 Print("Time for light: %d seconds\n", level->time_light);
7588 Print("Time for timegate: %d seconds\n", level->time_timegate);
7590 Print("Amoeba speed: %d\n", level->amoeba_speed);
7593 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7594 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7595 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7596 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7597 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7603 // ============================================================================
7604 // tape file functions
7605 // ============================================================================
7607 static void setTapeInfoToDefaults(void)
7611 // always start with reliable default values (empty tape)
7614 // default values (also for pre-1.2 tapes) with only the first player
7615 tape.player_participates[0] = TRUE;
7616 for (i = 1; i < MAX_PLAYERS; i++)
7617 tape.player_participates[i] = FALSE;
7619 // at least one (default: the first) player participates in every tape
7620 tape.num_participating_players = 1;
7622 tape.property_bits = TAPE_PROPERTY_NONE;
7624 tape.level_nr = level_nr;
7626 tape.changed = FALSE;
7628 tape.recording = FALSE;
7629 tape.playing = FALSE;
7630 tape.pausing = FALSE;
7632 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7633 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7635 tape.no_valid_file = FALSE;
7638 static int getTapePosSize(struct TapeInfo *tape)
7640 int tape_pos_size = 0;
7642 if (tape->use_key_actions)
7643 tape_pos_size += tape->num_participating_players;
7645 if (tape->use_mouse_actions)
7646 tape_pos_size += 3; // x and y position and mouse button mask
7648 tape_pos_size += 1; // tape action delay value
7650 return tape_pos_size;
7653 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7655 tape->use_key_actions = FALSE;
7656 tape->use_mouse_actions = FALSE;
7658 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7659 tape->use_key_actions = TRUE;
7661 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7662 tape->use_mouse_actions = TRUE;
7665 static int getTapeActionValue(struct TapeInfo *tape)
7667 return (tape->use_key_actions &&
7668 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7669 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7670 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7671 TAPE_ACTIONS_DEFAULT);
7674 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7676 tape->file_version = getFileVersion(file);
7677 tape->game_version = getFileVersion(file);
7682 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7686 tape->random_seed = getFile32BitBE(file);
7687 tape->date = getFile32BitBE(file);
7688 tape->length = getFile32BitBE(file);
7690 // read header fields that are new since version 1.2
7691 if (tape->file_version >= FILE_VERSION_1_2)
7693 byte store_participating_players = getFile8Bit(file);
7696 // since version 1.2, tapes store which players participate in the tape
7697 tape->num_participating_players = 0;
7698 for (i = 0; i < MAX_PLAYERS; i++)
7700 tape->player_participates[i] = FALSE;
7702 if (store_participating_players & (1 << i))
7704 tape->player_participates[i] = TRUE;
7705 tape->num_participating_players++;
7709 setTapeActionFlags(tape, getFile8Bit(file));
7711 tape->property_bits = getFile8Bit(file);
7713 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7715 engine_version = getFileVersion(file);
7716 if (engine_version > 0)
7717 tape->engine_version = engine_version;
7719 tape->engine_version = tape->game_version;
7725 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7727 tape->scr_fieldx = getFile8Bit(file);
7728 tape->scr_fieldy = getFile8Bit(file);
7733 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7735 int level_identifier_size;
7738 level_identifier_size = getFile16BitBE(file);
7740 tape->level_identifier =
7741 checked_realloc(tape->level_identifier, level_identifier_size);
7743 for (i = 0; i < level_identifier_size; i++)
7744 tape->level_identifier[i] = getFile8Bit(file);
7746 tape->level_nr = getFile16BitBE(file);
7748 chunk_size = 2 + level_identifier_size + 2;
7753 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7756 int tape_pos_size = getTapePosSize(tape);
7757 int chunk_size_expected = tape_pos_size * tape->length;
7759 if (chunk_size_expected != chunk_size)
7761 ReadUnusedBytesFromFile(file, chunk_size);
7762 return chunk_size_expected;
7765 for (i = 0; i < tape->length; i++)
7767 if (i >= MAX_TAPE_LEN)
7769 Warn("tape truncated -- size exceeds maximum tape size %d",
7772 // tape too large; read and ignore remaining tape data from this chunk
7773 for (;i < tape->length; i++)
7774 ReadUnusedBytesFromFile(file, tape_pos_size);
7779 if (tape->use_key_actions)
7781 for (j = 0; j < MAX_PLAYERS; j++)
7783 tape->pos[i].action[j] = MV_NONE;
7785 if (tape->player_participates[j])
7786 tape->pos[i].action[j] = getFile8Bit(file);
7790 if (tape->use_mouse_actions)
7792 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7793 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7794 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7797 tape->pos[i].delay = getFile8Bit(file);
7799 if (tape->file_version == FILE_VERSION_1_0)
7801 // eliminate possible diagonal moves in old tapes
7802 // this is only for backward compatibility
7804 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7805 byte action = tape->pos[i].action[0];
7806 int k, num_moves = 0;
7808 for (k = 0; k<4; k++)
7810 if (action & joy_dir[k])
7812 tape->pos[i + num_moves].action[0] = joy_dir[k];
7814 tape->pos[i + num_moves].delay = 0;
7823 tape->length += num_moves;
7826 else if (tape->file_version < FILE_VERSION_2_0)
7828 // convert pre-2.0 tapes to new tape format
7830 if (tape->pos[i].delay > 1)
7833 tape->pos[i + 1] = tape->pos[i];
7834 tape->pos[i + 1].delay = 1;
7837 for (j = 0; j < MAX_PLAYERS; j++)
7838 tape->pos[i].action[j] = MV_NONE;
7839 tape->pos[i].delay--;
7846 if (checkEndOfFile(file))
7850 if (i != tape->length)
7851 chunk_size = tape_pos_size * i;
7856 static void LoadTape_SokobanSolution(char *filename)
7859 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7861 if (!(file = openFile(filename, MODE_READ)))
7863 tape.no_valid_file = TRUE;
7868 while (!checkEndOfFile(file))
7870 unsigned char c = getByteFromFile(file);
7872 if (checkEndOfFile(file))
7879 tape.pos[tape.length].action[0] = MV_UP;
7880 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7886 tape.pos[tape.length].action[0] = MV_DOWN;
7887 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7893 tape.pos[tape.length].action[0] = MV_LEFT;
7894 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7900 tape.pos[tape.length].action[0] = MV_RIGHT;
7901 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7909 // ignore white-space characters
7913 tape.no_valid_file = TRUE;
7915 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
7923 if (tape.no_valid_file)
7926 tape.length_frames = GetTapeLengthFrames();
7927 tape.length_seconds = GetTapeLengthSeconds();
7930 void LoadTapeFromFilename(char *filename)
7932 char cookie[MAX_LINE_LEN];
7933 char chunk_name[CHUNK_ID_LEN + 1];
7937 // always start with reliable default values
7938 setTapeInfoToDefaults();
7940 if (strSuffix(filename, ".sln"))
7942 LoadTape_SokobanSolution(filename);
7947 if (!(file = openFile(filename, MODE_READ)))
7949 tape.no_valid_file = TRUE;
7954 getFileChunkBE(file, chunk_name, NULL);
7955 if (strEqual(chunk_name, "RND1"))
7957 getFile32BitBE(file); // not used
7959 getFileChunkBE(file, chunk_name, NULL);
7960 if (!strEqual(chunk_name, "TAPE"))
7962 tape.no_valid_file = TRUE;
7964 Warn("unknown format of tape file '%s'", filename);
7971 else // check for pre-2.0 file format with cookie string
7973 strcpy(cookie, chunk_name);
7974 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7976 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7977 cookie[strlen(cookie) - 1] = '\0';
7979 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7981 tape.no_valid_file = TRUE;
7983 Warn("unknown format of tape file '%s'", filename);
7990 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7992 tape.no_valid_file = TRUE;
7994 Warn("unsupported version of tape file '%s'", filename);
8001 // pre-2.0 tape files have no game version, so use file version here
8002 tape.game_version = tape.file_version;
8005 if (tape.file_version < FILE_VERSION_1_2)
8007 // tape files from versions before 1.2.0 without chunk structure
8008 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8009 LoadTape_BODY(file, 2 * tape.length, &tape);
8017 int (*loader)(File *, int, struct TapeInfo *);
8021 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8022 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8023 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8024 { "INFO", -1, LoadTape_INFO },
8025 { "BODY", -1, LoadTape_BODY },
8029 while (getFileChunkBE(file, chunk_name, &chunk_size))
8033 while (chunk_info[i].name != NULL &&
8034 !strEqual(chunk_name, chunk_info[i].name))
8037 if (chunk_info[i].name == NULL)
8039 Warn("unknown chunk '%s' in tape file '%s'",
8040 chunk_name, filename);
8042 ReadUnusedBytesFromFile(file, chunk_size);
8044 else if (chunk_info[i].size != -1 &&
8045 chunk_info[i].size != chunk_size)
8047 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8048 chunk_size, chunk_name, filename);
8050 ReadUnusedBytesFromFile(file, chunk_size);
8054 // call function to load this tape chunk
8055 int chunk_size_expected =
8056 (chunk_info[i].loader)(file, chunk_size, &tape);
8058 // the size of some chunks cannot be checked before reading other
8059 // chunks first (like "HEAD" and "BODY") that contain some header
8060 // information, so check them here
8061 if (chunk_size_expected != chunk_size)
8063 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8064 chunk_size, chunk_name, filename);
8072 tape.length_frames = GetTapeLengthFrames();
8073 tape.length_seconds = GetTapeLengthSeconds();
8076 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8078 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8080 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8081 tape.engine_version);
8085 void LoadTape(int nr)
8087 char *filename = getTapeFilename(nr);
8089 LoadTapeFromFilename(filename);
8092 void LoadSolutionTape(int nr)
8094 char *filename = getSolutionTapeFilename(nr);
8096 LoadTapeFromFilename(filename);
8098 if (TAPE_IS_EMPTY(tape) &&
8099 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8100 level.native_sp_level->demo.is_available)
8101 CopyNativeTape_SP_to_RND(&level);
8104 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8106 // chunk required for team mode tapes with non-default screen size
8107 return (tape->num_participating_players > 1 &&
8108 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8109 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8112 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8114 putFileVersion(file, tape->file_version);
8115 putFileVersion(file, tape->game_version);
8118 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8121 byte store_participating_players = 0;
8123 // set bits for participating players for compact storage
8124 for (i = 0; i < MAX_PLAYERS; i++)
8125 if (tape->player_participates[i])
8126 store_participating_players |= (1 << i);
8128 putFile32BitBE(file, tape->random_seed);
8129 putFile32BitBE(file, tape->date);
8130 putFile32BitBE(file, tape->length);
8132 putFile8Bit(file, store_participating_players);
8134 putFile8Bit(file, getTapeActionValue(tape));
8136 putFile8Bit(file, tape->property_bits);
8138 // unused bytes not at the end here for 4-byte alignment of engine_version
8139 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8141 putFileVersion(file, tape->engine_version);
8144 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8146 putFile8Bit(file, tape->scr_fieldx);
8147 putFile8Bit(file, tape->scr_fieldy);
8150 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8152 int level_identifier_size = strlen(tape->level_identifier) + 1;
8155 putFile16BitBE(file, level_identifier_size);
8157 for (i = 0; i < level_identifier_size; i++)
8158 putFile8Bit(file, tape->level_identifier[i]);
8160 putFile16BitBE(file, tape->level_nr);
8163 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8167 for (i = 0; i < tape->length; i++)
8169 if (tape->use_key_actions)
8171 for (j = 0; j < MAX_PLAYERS; j++)
8172 if (tape->player_participates[j])
8173 putFile8Bit(file, tape->pos[i].action[j]);
8176 if (tape->use_mouse_actions)
8178 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8179 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8180 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8183 putFile8Bit(file, tape->pos[i].delay);
8187 void SaveTapeToFilename(char *filename)
8191 int info_chunk_size;
8192 int body_chunk_size;
8194 if (!(file = fopen(filename, MODE_WRITE)))
8196 Warn("cannot save level recording file '%s'", filename);
8201 tape_pos_size = getTapePosSize(&tape);
8203 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8204 body_chunk_size = tape_pos_size * tape.length;
8206 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8207 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8209 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8210 SaveTape_VERS(file, &tape);
8212 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8213 SaveTape_HEAD(file, &tape);
8215 if (checkSaveTape_SCRN(&tape))
8217 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8218 SaveTape_SCRN(file, &tape);
8221 putFileChunkBE(file, "INFO", info_chunk_size);
8222 SaveTape_INFO(file, &tape);
8224 putFileChunkBE(file, "BODY", body_chunk_size);
8225 SaveTape_BODY(file, &tape);
8229 SetFilePermissions(filename, PERMS_PRIVATE);
8232 void SaveTape(int nr)
8234 char *filename = getTapeFilename(nr);
8237 InitTapeDirectory(leveldir_current->subdir);
8239 tape.file_version = FILE_VERSION_ACTUAL;
8240 tape.game_version = GAME_VERSION_ACTUAL;
8242 tape.num_participating_players = 0;
8244 // count number of participating players
8245 for (i = 0; i < MAX_PLAYERS; i++)
8246 if (tape.player_participates[i])
8247 tape.num_participating_players++;
8249 SaveTapeToFilename(filename);
8251 tape.changed = FALSE;
8254 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8255 unsigned int req_state_added)
8257 char *filename = getTapeFilename(nr);
8258 boolean new_tape = !fileExists(filename);
8259 boolean tape_saved = FALSE;
8261 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8266 Request(msg_saved, REQ_CONFIRM | req_state_added);
8274 boolean SaveTapeChecked(int nr)
8276 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8279 boolean SaveTapeChecked_LevelSolved(int nr)
8281 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8282 "Level solved! Tape saved!", REQ_STAY_OPEN);
8285 void DumpTape(struct TapeInfo *tape)
8287 int tape_frame_counter;
8290 if (tape->no_valid_file)
8292 Warn("cannot dump -- no valid tape file found");
8298 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8299 tape->level_nr, tape->file_version, tape->game_version);
8300 Print(" (effective engine version %08d)\n",
8301 tape->engine_version);
8302 Print("Level series identifier: '%s'\n", tape->level_identifier);
8305 tape_frame_counter = 0;
8307 for (i = 0; i < tape->length; i++)
8309 if (i >= MAX_TAPE_LEN)
8314 for (j = 0; j < MAX_PLAYERS; j++)
8316 if (tape->player_participates[j])
8318 int action = tape->pos[i].action[j];
8320 Print("%d:%02x ", j, action);
8321 Print("[%c%c%c%c|%c%c] - ",
8322 (action & JOY_LEFT ? '<' : ' '),
8323 (action & JOY_RIGHT ? '>' : ' '),
8324 (action & JOY_UP ? '^' : ' '),
8325 (action & JOY_DOWN ? 'v' : ' '),
8326 (action & JOY_BUTTON_1 ? '1' : ' '),
8327 (action & JOY_BUTTON_2 ? '2' : ' '));
8331 Print("(%03d) ", tape->pos[i].delay);
8332 Print("[%05d]\n", tape_frame_counter);
8334 tape_frame_counter += tape->pos[i].delay;
8341 // ============================================================================
8342 // score file functions
8343 // ============================================================================
8345 void LoadScore(int nr)
8348 char *filename = getScoreFilename(nr);
8349 char cookie[MAX_LINE_LEN];
8350 char line[MAX_LINE_LEN];
8354 // always start with reliable default values
8355 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8357 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8358 highscore[i].Score = 0;
8361 if (!(file = fopen(filename, MODE_READ)))
8364 // check file identifier
8365 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8367 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8368 cookie[strlen(cookie) - 1] = '\0';
8370 if (!checkCookieString(cookie, SCORE_COOKIE))
8372 Warn("unknown format of score file '%s'", filename);
8379 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8381 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8382 Warn("fscanf() failed; %s", strerror(errno));
8384 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8387 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8388 line[strlen(line) - 1] = '\0';
8390 for (line_ptr = line; *line_ptr; line_ptr++)
8392 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8394 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8395 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8404 void SaveScore(int nr)
8407 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8408 char *filename = getScoreFilename(nr);
8411 // used instead of "leveldir_current->subdir" (for network games)
8412 InitScoreDirectory(levelset.identifier);
8414 if (!(file = fopen(filename, MODE_WRITE)))
8416 Warn("cannot save score for level %d", nr);
8421 fprintf(file, "%s\n\n", SCORE_COOKIE);
8423 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8424 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8428 SetFilePermissions(filename, permissions);
8432 // ============================================================================
8433 // setup file functions
8434 // ============================================================================
8436 #define TOKEN_STR_PLAYER_PREFIX "player_"
8439 static struct TokenInfo global_setup_tokens[] =
8443 &setup.player_name, "player_name"
8447 &setup.multiple_users, "multiple_users"
8451 &setup.sound, "sound"
8455 &setup.sound_loops, "repeating_sound_loops"
8459 &setup.sound_music, "background_music"
8463 &setup.sound_simple, "simple_sound_effects"
8467 &setup.toons, "toons"
8471 &setup.scroll_delay, "scroll_delay"
8475 &setup.forced_scroll_delay, "forced_scroll_delay"
8479 &setup.scroll_delay_value, "scroll_delay_value"
8483 &setup.engine_snapshot_mode, "engine_snapshot_mode"
8487 &setup.engine_snapshot_memory, "engine_snapshot_memory"
8491 &setup.fade_screens, "fade_screens"
8495 &setup.autorecord, "automatic_tape_recording"
8499 &setup.show_titlescreen, "show_titlescreen"
8503 &setup.quick_doors, "quick_doors"
8507 &setup.team_mode, "team_mode"
8511 &setup.handicap, "handicap"
8515 &setup.skip_levels, "skip_levels"
8519 &setup.increment_levels, "increment_levels"
8523 &setup.auto_play_next_level, "auto_play_next_level"
8527 &setup.skip_scores_after_game, "skip_scores_after_game"
8531 &setup.time_limit, "time_limit"
8535 &setup.fullscreen, "fullscreen"
8539 &setup.window_scaling_percent, "window_scaling_percent"
8543 &setup.window_scaling_quality, "window_scaling_quality"
8547 &setup.screen_rendering_mode, "screen_rendering_mode"
8551 &setup.vsync_mode, "vsync_mode"
8555 &setup.ask_on_escape, "ask_on_escape"
8559 &setup.ask_on_escape_editor, "ask_on_escape_editor"
8563 &setup.ask_on_game_over, "ask_on_game_over"
8567 &setup.quick_switch, "quick_player_switch"
8571 &setup.input_on_focus, "input_on_focus"
8575 &setup.prefer_aga_graphics, "prefer_aga_graphics"
8579 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
8583 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
8587 &setup.game_speed_extended, "game_speed_extended"
8591 &setup.game_frame_delay, "game_frame_delay"
8595 &setup.sp_show_border_elements, "sp_show_border_elements"
8599 &setup.small_game_graphics, "small_game_graphics"
8603 &setup.show_snapshot_buttons, "show_snapshot_buttons"
8607 &setup.graphics_set, "graphics_set"
8611 &setup.sounds_set, "sounds_set"
8615 &setup.music_set, "music_set"
8619 &setup.override_level_graphics, "override_level_graphics"
8623 &setup.override_level_sounds, "override_level_sounds"
8627 &setup.override_level_music, "override_level_music"
8631 &setup.volume_simple, "volume_simple"
8635 &setup.volume_loops, "volume_loops"
8639 &setup.volume_music, "volume_music"
8643 &setup.network_mode, "network_mode"
8647 &setup.network_player_nr, "network_player"
8651 &setup.network_server_hostname, "network_server_hostname"
8655 &setup.touch.control_type, "touch.control_type"
8659 &setup.touch.move_distance, "touch.move_distance"
8663 &setup.touch.drop_distance, "touch.drop_distance"
8667 &setup.touch.transparency, "touch.transparency"
8671 &setup.touch.draw_outlined, "touch.draw_outlined"
8675 &setup.touch.draw_pressed, "touch.draw_pressed"
8679 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
8683 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
8687 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
8691 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
8695 static struct TokenInfo auto_setup_tokens[] =
8699 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
8703 static struct TokenInfo editor_setup_tokens[] =
8707 &setup.editor.el_classic, "editor.el_classic"
8711 &setup.editor.el_custom, "editor.el_custom"
8715 &setup.editor.el_user_defined, "editor.el_user_defined"
8719 &setup.editor.el_dynamic, "editor.el_dynamic"
8723 &setup.editor.el_headlines, "editor.el_headlines"
8727 &setup.editor.show_element_token, "editor.show_element_token"
8731 static struct TokenInfo editor_cascade_setup_tokens[] =
8735 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
8739 &setup.editor_cascade.el_em, "editor.cascade.el_em"
8743 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
8747 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
8751 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
8755 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
8759 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
8763 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
8767 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
8771 &setup.editor_cascade.el_df, "editor.cascade.el_df"
8775 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
8779 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
8783 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
8787 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
8791 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
8795 &setup.editor_cascade.el_user, "editor.cascade.el_user"
8799 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
8803 static struct TokenInfo shortcut_setup_tokens[] =
8807 &setup.shortcut.save_game, "shortcut.save_game"
8811 &setup.shortcut.load_game, "shortcut.load_game"
8815 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
8819 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
8823 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
8827 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
8831 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
8835 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
8839 &setup.shortcut.tape_eject, "shortcut.tape_eject"
8843 &setup.shortcut.tape_extra, "shortcut.tape_extra"
8847 &setup.shortcut.tape_stop, "shortcut.tape_stop"
8851 &setup.shortcut.tape_pause, "shortcut.tape_pause"
8855 &setup.shortcut.tape_record, "shortcut.tape_record"
8859 &setup.shortcut.tape_play, "shortcut.tape_play"
8863 &setup.shortcut.sound_simple, "shortcut.sound_simple"
8867 &setup.shortcut.sound_loops, "shortcut.sound_loops"
8871 &setup.shortcut.sound_music, "shortcut.sound_music"
8875 &setup.shortcut.snap_left, "shortcut.snap_left"
8879 &setup.shortcut.snap_right, "shortcut.snap_right"
8883 &setup.shortcut.snap_up, "shortcut.snap_up"
8887 &setup.shortcut.snap_down, "shortcut.snap_down"
8891 static struct SetupInputInfo setup_input;
8892 static struct TokenInfo player_setup_tokens[] =
8896 &setup_input.use_joystick, ".use_joystick"
8900 &setup_input.joy.device_name, ".joy.device_name"
8904 &setup_input.joy.xleft, ".joy.xleft"
8908 &setup_input.joy.xmiddle, ".joy.xmiddle"
8912 &setup_input.joy.xright, ".joy.xright"
8916 &setup_input.joy.yupper, ".joy.yupper"
8920 &setup_input.joy.ymiddle, ".joy.ymiddle"
8924 &setup_input.joy.ylower, ".joy.ylower"
8928 &setup_input.joy.snap, ".joy.snap_field"
8932 &setup_input.joy.drop, ".joy.place_bomb"
8936 &setup_input.key.left, ".key.move_left"
8940 &setup_input.key.right, ".key.move_right"
8944 &setup_input.key.up, ".key.move_up"
8948 &setup_input.key.down, ".key.move_down"
8952 &setup_input.key.snap, ".key.snap_field"
8956 &setup_input.key.drop, ".key.place_bomb"
8960 static struct TokenInfo system_setup_tokens[] =
8964 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
8968 &setup.system.sdl_videodriver, "system.sdl_videodriver"
8972 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
8976 &setup.system.audio_fragment_size, "system.audio_fragment_size"
8980 static struct TokenInfo internal_setup_tokens[] =
8984 &setup.internal.program_title, "program_title"
8988 &setup.internal.program_version, "program_version"
8992 &setup.internal.program_author, "program_author"
8996 &setup.internal.program_email, "program_email"
9000 &setup.internal.program_website, "program_website"
9004 &setup.internal.program_copyright, "program_copyright"
9008 &setup.internal.program_company, "program_company"
9012 &setup.internal.program_icon_file, "program_icon_file"
9016 &setup.internal.default_graphics_set, "default_graphics_set"
9020 &setup.internal.default_sounds_set, "default_sounds_set"
9024 &setup.internal.default_music_set, "default_music_set"
9028 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
9032 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
9036 &setup.internal.fallback_music_file, "fallback_music_file"
9040 &setup.internal.default_level_series, "default_level_series"
9044 &setup.internal.default_window_width, "default_window_width"
9048 &setup.internal.default_window_height, "default_window_height"
9052 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
9056 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
9060 &setup.internal.create_user_levelset, "create_user_levelset"
9064 &setup.internal.menu_game, "menu_game"
9068 &setup.internal.menu_editor, "menu_editor"
9072 &setup.internal.menu_graphics, "menu_graphics"
9076 &setup.internal.menu_sound, "menu_sound"
9080 &setup.internal.menu_artwork, "menu_artwork"
9084 &setup.internal.menu_input, "menu_input"
9088 &setup.internal.menu_touch, "menu_touch"
9092 &setup.internal.menu_shortcuts, "menu_shortcuts"
9096 &setup.internal.menu_exit, "menu_exit"
9100 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
9104 static struct TokenInfo debug_setup_tokens[] =
9108 &setup.debug.frame_delay[0], "debug.frame_delay_0"
9112 &setup.debug.frame_delay[1], "debug.frame_delay_1"
9116 &setup.debug.frame_delay[2], "debug.frame_delay_2"
9120 &setup.debug.frame_delay[3], "debug.frame_delay_3"
9124 &setup.debug.frame_delay[4], "debug.frame_delay_4"
9128 &setup.debug.frame_delay[5], "debug.frame_delay_5"
9132 &setup.debug.frame_delay[6], "debug.frame_delay_6"
9136 &setup.debug.frame_delay[7], "debug.frame_delay_7"
9140 &setup.debug.frame_delay[8], "debug.frame_delay_8"
9144 &setup.debug.frame_delay[9], "debug.frame_delay_9"
9148 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
9152 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
9156 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
9160 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
9164 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
9168 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
9172 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
9176 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
9180 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
9184 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
9188 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
9191 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
9195 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
9199 &setup.debug.xsn_mode, "debug.xsn_mode"
9203 &setup.debug.xsn_percent, "debug.xsn_percent"
9207 static struct TokenInfo options_setup_tokens[] =
9211 &setup.options.verbose, "options.verbose"
9215 static void setSetupInfoToDefaults(struct SetupInfo *si)
9219 si->player_name = getStringCopy(getDefaultUserName(user.nr));
9221 si->multiple_users = TRUE;
9224 si->sound_loops = TRUE;
9225 si->sound_music = TRUE;
9226 si->sound_simple = TRUE;
9228 si->scroll_delay = TRUE;
9229 si->forced_scroll_delay = FALSE;
9230 si->scroll_delay_value = STD_SCROLL_DELAY;
9231 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9232 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9233 si->fade_screens = TRUE;
9234 si->autorecord = TRUE;
9235 si->show_titlescreen = TRUE;
9236 si->quick_doors = FALSE;
9237 si->team_mode = FALSE;
9238 si->handicap = TRUE;
9239 si->skip_levels = TRUE;
9240 si->increment_levels = TRUE;
9241 si->auto_play_next_level = TRUE;
9242 si->skip_scores_after_game = FALSE;
9243 si->time_limit = TRUE;
9244 si->fullscreen = FALSE;
9245 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9246 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9247 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9248 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9249 si->ask_on_escape = TRUE;
9250 si->ask_on_escape_editor = TRUE;
9251 si->ask_on_game_over = TRUE;
9252 si->quick_switch = FALSE;
9253 si->input_on_focus = FALSE;
9254 si->prefer_aga_graphics = TRUE;
9255 si->prefer_lowpass_sounds = FALSE;
9256 si->prefer_extra_panel_items = TRUE;
9257 si->game_speed_extended = FALSE;
9258 si->game_frame_delay = GAME_FRAME_DELAY;
9259 si->sp_show_border_elements = FALSE;
9260 si->small_game_graphics = FALSE;
9261 si->show_snapshot_buttons = FALSE;
9263 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9264 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9265 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9267 si->override_level_graphics = FALSE;
9268 si->override_level_sounds = FALSE;
9269 si->override_level_music = FALSE;
9271 si->volume_simple = 100; // percent
9272 si->volume_loops = 100; // percent
9273 si->volume_music = 100; // percent
9275 si->network_mode = FALSE;
9276 si->network_player_nr = 0; // first player
9277 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9279 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9280 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
9281 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
9282 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
9283 si->touch.draw_outlined = TRUE;
9284 si->touch.draw_pressed = TRUE;
9286 for (i = 0; i < 2; i++)
9288 char *default_grid_button[6][2] =
9294 { "111222", " vv " },
9295 { "111222", " vv " }
9297 int grid_xsize = DEFAULT_GRID_XSIZE(i);
9298 int grid_ysize = DEFAULT_GRID_YSIZE(i);
9299 int min_xsize = MIN(6, grid_xsize);
9300 int min_ysize = MIN(6, grid_ysize);
9301 int startx = grid_xsize - min_xsize;
9302 int starty = grid_ysize - min_ysize;
9305 // virtual buttons grid can only be set to defaults if video is initialized
9306 // (this will be repeated if virtual buttons are not loaded from setup file)
9307 if (video.initialized)
9309 si->touch.grid_xsize[i] = grid_xsize;
9310 si->touch.grid_ysize[i] = grid_ysize;
9314 si->touch.grid_xsize[i] = -1;
9315 si->touch.grid_ysize[i] = -1;
9318 for (x = 0; x < MAX_GRID_XSIZE; x++)
9319 for (y = 0; y < MAX_GRID_YSIZE; y++)
9320 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9322 for (x = 0; x < min_xsize; x++)
9323 for (y = 0; y < min_ysize; y++)
9324 si->touch.grid_button[i][x][starty + y] =
9325 default_grid_button[y][0][x];
9327 for (x = 0; x < min_xsize; x++)
9328 for (y = 0; y < min_ysize; y++)
9329 si->touch.grid_button[i][startx + x][starty + y] =
9330 default_grid_button[y][1][x];
9333 si->touch.grid_initialized = video.initialized;
9335 si->editor.el_boulderdash = TRUE;
9336 si->editor.el_emerald_mine = TRUE;
9337 si->editor.el_emerald_mine_club = TRUE;
9338 si->editor.el_more = TRUE;
9339 si->editor.el_sokoban = TRUE;
9340 si->editor.el_supaplex = TRUE;
9341 si->editor.el_diamond_caves = TRUE;
9342 si->editor.el_dx_boulderdash = TRUE;
9344 si->editor.el_mirror_magic = TRUE;
9345 si->editor.el_deflektor = TRUE;
9347 si->editor.el_chars = TRUE;
9348 si->editor.el_steel_chars = TRUE;
9350 si->editor.el_classic = TRUE;
9351 si->editor.el_custom = TRUE;
9353 si->editor.el_user_defined = FALSE;
9354 si->editor.el_dynamic = TRUE;
9356 si->editor.el_headlines = TRUE;
9358 si->editor.show_element_token = FALSE;
9360 si->editor.use_template_for_new_levels = TRUE;
9362 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
9363 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
9364 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
9366 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
9367 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
9368 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
9369 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
9370 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9372 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
9373 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
9374 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
9375 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
9376 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
9377 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
9379 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
9380 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
9381 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
9383 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
9384 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
9385 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
9386 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
9388 for (i = 0; i < MAX_PLAYERS; i++)
9390 si->input[i].use_joystick = FALSE;
9391 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9392 si->input[i].joy.xleft = JOYSTICK_XLEFT;
9393 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9394 si->input[i].joy.xright = JOYSTICK_XRIGHT;
9395 si->input[i].joy.yupper = JOYSTICK_YUPPER;
9396 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9397 si->input[i].joy.ylower = JOYSTICK_YLOWER;
9398 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
9399 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
9400 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
9401 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9402 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
9403 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
9404 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
9405 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
9408 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
9409 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9410 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9411 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9413 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
9414 si->internal.program_version = getStringCopy(getProgramRealVersionString());
9415 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
9416 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
9417 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
9418 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9419 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
9421 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9423 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9424 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9425 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9427 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9428 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
9429 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
9431 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9432 si->internal.choose_from_top_leveldir = FALSE;
9433 si->internal.show_scaling_in_title = TRUE;
9434 si->internal.create_user_levelset = TRUE;
9436 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
9437 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9439 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9440 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9441 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9442 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9443 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9444 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9445 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9446 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9447 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9448 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9450 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9451 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9452 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9453 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9454 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9455 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9456 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9457 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9458 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9459 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9461 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9462 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9464 si->debug.show_frames_per_second = FALSE;
9466 si->debug.xsn_mode = AUTO;
9467 si->debug.xsn_percent = 0;
9469 si->options.verbose = FALSE;
9471 #if defined(PLATFORM_ANDROID)
9472 si->fullscreen = TRUE;
9475 setHideSetupEntry(&setup.debug.xsn_mode);
9478 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9480 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9483 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9485 si->editor_cascade.el_bd = TRUE;
9486 si->editor_cascade.el_em = TRUE;
9487 si->editor_cascade.el_emc = TRUE;
9488 si->editor_cascade.el_rnd = TRUE;
9489 si->editor_cascade.el_sb = TRUE;
9490 si->editor_cascade.el_sp = TRUE;
9491 si->editor_cascade.el_dc = TRUE;
9492 si->editor_cascade.el_dx = TRUE;
9494 si->editor_cascade.el_mm = TRUE;
9495 si->editor_cascade.el_df = TRUE;
9497 si->editor_cascade.el_chars = FALSE;
9498 si->editor_cascade.el_steel_chars = FALSE;
9499 si->editor_cascade.el_ce = FALSE;
9500 si->editor_cascade.el_ge = FALSE;
9501 si->editor_cascade.el_ref = FALSE;
9502 si->editor_cascade.el_user = FALSE;
9503 si->editor_cascade.el_dynamic = FALSE;
9506 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9508 static char *getHideSetupToken(void *setup_value)
9510 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9512 if (setup_value != NULL)
9513 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9515 return hide_setup_token;
9518 void setHideSetupEntry(void *setup_value)
9520 char *hide_setup_token = getHideSetupToken(setup_value);
9522 if (hide_setup_hash == NULL)
9523 hide_setup_hash = newSetupFileHash();
9525 if (setup_value != NULL)
9526 setHashEntry(hide_setup_hash, hide_setup_token, "");
9529 void removeHideSetupEntry(void *setup_value)
9531 char *hide_setup_token = getHideSetupToken(setup_value);
9533 if (setup_value != NULL)
9534 removeHashEntry(hide_setup_hash, hide_setup_token);
9537 boolean hideSetupEntry(void *setup_value)
9539 char *hide_setup_token = getHideSetupToken(setup_value);
9541 return (setup_value != NULL &&
9542 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9545 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9546 struct TokenInfo *token_info,
9547 int token_nr, char *token_text)
9549 char *token_hide_text = getStringCat2(token_text, ".hide");
9550 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9552 // set the value of this setup option in the setup option structure
9553 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9555 // check if this setup option should be hidden in the setup menu
9556 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9557 setHideSetupEntry(token_info[token_nr].value);
9559 free(token_hide_text);
9562 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9563 struct TokenInfo *token_info,
9566 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9567 token_info[token_nr].text);
9570 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9574 if (!setup_file_hash)
9577 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9578 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9580 setup.touch.grid_initialized = TRUE;
9581 for (i = 0; i < 2; i++)
9583 int grid_xsize = setup.touch.grid_xsize[i];
9584 int grid_ysize = setup.touch.grid_ysize[i];
9587 // if virtual buttons are not loaded from setup file, repeat initializing
9588 // virtual buttons grid with default values later when video is initialized
9589 if (grid_xsize == -1 ||
9592 setup.touch.grid_initialized = FALSE;
9597 for (y = 0; y < grid_ysize; y++)
9599 char token_string[MAX_LINE_LEN];
9601 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9603 char *value_string = getHashEntry(setup_file_hash, token_string);
9605 if (value_string == NULL)
9608 for (x = 0; x < grid_xsize; x++)
9610 char c = value_string[x];
9612 setup.touch.grid_button[i][x][y] =
9613 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9618 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9619 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9621 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9622 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9624 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9628 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9630 setup_input = setup.input[pnr];
9631 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9633 char full_token[100];
9635 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9636 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9639 setup.input[pnr] = setup_input;
9642 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9643 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9645 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
9646 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9648 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9649 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9651 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9652 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9654 setHideRelatedSetupEntries();
9657 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9661 if (!setup_file_hash)
9664 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9665 setSetupInfo(auto_setup_tokens, i,
9666 getHashEntry(setup_file_hash,
9667 auto_setup_tokens[i].text));
9670 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9674 if (!setup_file_hash)
9677 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9678 setSetupInfo(editor_cascade_setup_tokens, i,
9679 getHashEntry(setup_file_hash,
9680 editor_cascade_setup_tokens[i].text));
9683 void LoadUserNames(void)
9685 int last_user_nr = user.nr;
9688 if (global.user_names != NULL)
9690 for (i = 0; i < MAX_PLAYER_NAMES; i++)
9691 checked_free(global.user_names[i]);
9693 checked_free(global.user_names);
9696 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
9698 for (i = 0; i < MAX_PLAYER_NAMES; i++)
9702 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
9704 if (setup_file_hash)
9706 char *player_name = getHashEntry(setup_file_hash, "player_name");
9708 global.user_names[i] = getFixedUserName(player_name);
9710 freeSetupFileHash(setup_file_hash);
9713 if (global.user_names[i] == NULL)
9714 global.user_names[i] = getStringCopy(getDefaultUserName(i));
9717 user.nr = last_user_nr;
9720 void LoadSetupFromFilename(char *filename)
9722 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9724 if (setup_file_hash)
9726 decodeSetupFileHash(setup_file_hash);
9728 freeSetupFileHash(setup_file_hash);
9732 Debug("setup", "using default setup values");
9736 static void LoadSetup_SpecialPostProcessing(void)
9738 char *player_name_new;
9740 // needed to work around problems with fixed length strings
9741 player_name_new = getFixedUserName(setup.player_name);
9742 free(setup.player_name);
9743 setup.player_name = player_name_new;
9745 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
9746 if (setup.scroll_delay == FALSE)
9748 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9749 setup.scroll_delay = TRUE; // now always "on"
9752 // make sure that scroll delay value stays inside valid range
9753 setup.scroll_delay_value =
9754 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9757 void LoadSetup(void)
9761 // always start with reliable default values
9762 setSetupInfoToDefaults(&setup);
9764 // try to load setup values from default setup file
9765 filename = getDefaultSetupFilename();
9767 if (fileExists(filename))
9768 LoadSetupFromFilename(filename);
9770 // try to load setup values from user setup file
9771 filename = getSetupFilename();
9773 LoadSetupFromFilename(filename);
9775 LoadSetup_SpecialPostProcessing();
9778 void LoadSetup_AutoSetup(void)
9780 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9781 SetupFileHash *setup_file_hash = NULL;
9783 // always start with reliable default values
9784 setSetupInfoToDefaults_AutoSetup(&setup);
9786 setup_file_hash = loadSetupFileHash(filename);
9788 if (setup_file_hash)
9790 decodeSetupFileHash_AutoSetup(setup_file_hash);
9792 freeSetupFileHash(setup_file_hash);
9798 void LoadSetup_EditorCascade(void)
9800 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9801 SetupFileHash *setup_file_hash = NULL;
9803 // always start with reliable default values
9804 setSetupInfoToDefaults_EditorCascade(&setup);
9806 setup_file_hash = loadSetupFileHash(filename);
9808 if (setup_file_hash)
9810 decodeSetupFileHash_EditorCascade(setup_file_hash);
9812 freeSetupFileHash(setup_file_hash);
9818 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9821 char mapping_guid[MAX_LINE_LEN];
9822 char *mapping_start, *mapping_end;
9824 // get GUID from game controller mapping line: copy complete line
9825 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9826 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9828 // get GUID from game controller mapping line: cut after GUID part
9829 mapping_start = strchr(mapping_guid, ',');
9830 if (mapping_start != NULL)
9831 *mapping_start = '\0';
9833 // cut newline from game controller mapping line
9834 mapping_end = strchr(mapping_line, '\n');
9835 if (mapping_end != NULL)
9836 *mapping_end = '\0';
9838 // add mapping entry to game controller mappings hash
9839 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9842 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9847 if (!(file = fopen(filename, MODE_READ)))
9849 Warn("cannot read game controller mappings file '%s'", filename);
9856 char line[MAX_LINE_LEN];
9858 if (!fgets(line, MAX_LINE_LEN, file))
9861 addGameControllerMappingToHash(mappings_hash, line);
9867 void SaveSetup(void)
9869 char *filename = getSetupFilename();
9873 InitUserDataDirectory();
9875 if (!(file = fopen(filename, MODE_WRITE)))
9877 Warn("cannot write setup file '%s'", filename);
9882 fprintFileHeader(file, SETUP_FILENAME);
9884 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9886 // just to make things nicer :)
9887 if (global_setup_tokens[i].value == &setup.multiple_users ||
9888 global_setup_tokens[i].value == &setup.sound ||
9889 global_setup_tokens[i].value == &setup.graphics_set ||
9890 global_setup_tokens[i].value == &setup.volume_simple ||
9891 global_setup_tokens[i].value == &setup.network_mode ||
9892 global_setup_tokens[i].value == &setup.touch.control_type ||
9893 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
9894 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
9895 fprintf(file, "\n");
9897 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9900 for (i = 0; i < 2; i++)
9902 int grid_xsize = setup.touch.grid_xsize[i];
9903 int grid_ysize = setup.touch.grid_ysize[i];
9906 fprintf(file, "\n");
9908 for (y = 0; y < grid_ysize; y++)
9910 char token_string[MAX_LINE_LEN];
9911 char value_string[MAX_LINE_LEN];
9913 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9915 for (x = 0; x < grid_xsize; x++)
9917 char c = setup.touch.grid_button[i][x][y];
9919 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9922 value_string[grid_xsize] = '\0';
9924 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9928 fprintf(file, "\n");
9929 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9930 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9932 fprintf(file, "\n");
9933 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9934 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9936 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9940 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9941 fprintf(file, "\n");
9943 setup_input = setup.input[pnr];
9944 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9945 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9948 fprintf(file, "\n");
9949 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9950 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9952 // (internal setup values not saved to user setup file)
9954 fprintf(file, "\n");
9955 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9956 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
9957 setup.debug.xsn_mode != AUTO)
9958 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9960 fprintf(file, "\n");
9961 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9962 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9966 SetFilePermissions(filename, PERMS_PRIVATE);
9969 void SaveSetup_AutoSetup(void)
9971 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9975 InitUserDataDirectory();
9977 if (!(file = fopen(filename, MODE_WRITE)))
9979 Warn("cannot write auto setup file '%s'", filename);
9986 fprintFileHeader(file, AUTOSETUP_FILENAME);
9988 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9989 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9993 SetFilePermissions(filename, PERMS_PRIVATE);
9998 void SaveSetup_EditorCascade(void)
10000 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10004 InitUserDataDirectory();
10006 if (!(file = fopen(filename, MODE_WRITE)))
10008 Warn("cannot write editor cascade state file '%s'", filename);
10015 fprintFileHeader(file, EDITORCASCADE_FILENAME);
10017 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10018 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
10022 SetFilePermissions(filename, PERMS_PRIVATE);
10027 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
10032 if (!(file = fopen(filename, MODE_WRITE)))
10034 Warn("cannot write game controller mappings file '%s'", filename);
10039 BEGIN_HASH_ITERATION(mappings_hash, itr)
10041 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
10043 END_HASH_ITERATION(mappings_hash, itr)
10048 void SaveSetup_AddGameControllerMapping(char *mapping)
10050 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
10051 SetupFileHash *mappings_hash = newSetupFileHash();
10053 InitUserDataDirectory();
10055 // load existing personal game controller mappings
10056 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
10058 // add new mapping to personal game controller mappings
10059 addGameControllerMappingToHash(mappings_hash, mapping);
10061 // save updated personal game controller mappings
10062 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
10064 freeSetupFileHash(mappings_hash);
10068 void LoadCustomElementDescriptions(void)
10070 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10071 SetupFileHash *setup_file_hash;
10074 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10076 if (element_info[i].custom_description != NULL)
10078 free(element_info[i].custom_description);
10079 element_info[i].custom_description = NULL;
10083 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10086 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10088 char *token = getStringCat2(element_info[i].token_name, ".name");
10089 char *value = getHashEntry(setup_file_hash, token);
10092 element_info[i].custom_description = getStringCopy(value);
10097 freeSetupFileHash(setup_file_hash);
10100 static int getElementFromToken(char *token)
10102 char *value = getHashEntry(element_token_hash, token);
10105 return atoi(value);
10107 Warn("unknown element token '%s'", token);
10109 return EL_UNDEFINED;
10112 void FreeGlobalAnimEventInfo(void)
10114 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10116 if (gaei->event_list == NULL)
10121 for (i = 0; i < gaei->num_event_lists; i++)
10123 checked_free(gaei->event_list[i]->event_value);
10124 checked_free(gaei->event_list[i]);
10127 checked_free(gaei->event_list);
10129 gaei->event_list = NULL;
10130 gaei->num_event_lists = 0;
10133 static int AddGlobalAnimEventList(void)
10135 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10136 int list_pos = gaei->num_event_lists++;
10138 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
10139 sizeof(struct GlobalAnimEventListInfo *));
10141 gaei->event_list[list_pos] =
10142 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
10144 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10146 gaeli->event_value = NULL;
10147 gaeli->num_event_values = 0;
10152 static int AddGlobalAnimEventValue(int list_pos, int event_value)
10154 // do not add empty global animation events
10155 if (event_value == ANIM_EVENT_NONE)
10158 // if list position is undefined, create new list
10159 if (list_pos == ANIM_EVENT_UNDEFINED)
10160 list_pos = AddGlobalAnimEventList();
10162 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10163 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10164 int value_pos = gaeli->num_event_values++;
10166 gaeli->event_value = checked_realloc(gaeli->event_value,
10167 gaeli->num_event_values * sizeof(int *));
10169 gaeli->event_value[value_pos] = event_value;
10174 int GetGlobalAnimEventValue(int list_pos, int value_pos)
10176 if (list_pos == ANIM_EVENT_UNDEFINED)
10177 return ANIM_EVENT_NONE;
10179 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10180 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10182 return gaeli->event_value[value_pos];
10185 int GetGlobalAnimEventValueCount(int list_pos)
10187 if (list_pos == ANIM_EVENT_UNDEFINED)
10190 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10191 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10193 return gaeli->num_event_values;
10196 // This function checks if a string <s> of the format "string1, string2, ..."
10197 // exactly contains a string <s_contained>.
10199 static boolean string_has_parameter(char *s, char *s_contained)
10203 if (s == NULL || s_contained == NULL)
10206 if (strlen(s_contained) > strlen(s))
10209 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10211 char next_char = s[strlen(s_contained)];
10213 // check if next character is delimiter or whitespace
10214 return (next_char == ',' || next_char == '\0' ||
10215 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10218 // check if string contains another parameter string after a comma
10219 substring = strchr(s, ',');
10220 if (substring == NULL) // string does not contain a comma
10223 // advance string pointer to next character after the comma
10226 // skip potential whitespaces after the comma
10227 while (*substring == ' ' || *substring == '\t')
10230 return string_has_parameter(substring, s_contained);
10233 static int get_anim_parameter_value(char *s)
10235 int event_value[] =
10243 char *pattern_1[] =
10251 char *pattern_2 = ".part_";
10252 char *matching_char = NULL;
10254 int pattern_1_len = 0;
10255 int result = ANIM_EVENT_NONE;
10258 for (i = 0; i < ARRAY_SIZE(event_value); i++)
10260 matching_char = strstr(s_ptr, pattern_1[i]);
10261 pattern_1_len = strlen(pattern_1[i]);
10262 result = event_value[i];
10264 if (matching_char != NULL)
10268 if (matching_char == NULL)
10269 return ANIM_EVENT_NONE;
10271 s_ptr = matching_char + pattern_1_len;
10273 // check for main animation number ("anim_X" or "anim_XX")
10274 if (*s_ptr >= '0' && *s_ptr <= '9')
10276 int gic_anim_nr = (*s_ptr++ - '0');
10278 if (*s_ptr >= '0' && *s_ptr <= '9')
10279 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10281 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10282 return ANIM_EVENT_NONE;
10284 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10288 // invalid main animation number specified
10290 return ANIM_EVENT_NONE;
10293 // check for animation part number ("part_X" or "part_XX") (optional)
10294 if (strPrefix(s_ptr, pattern_2))
10296 s_ptr += strlen(pattern_2);
10298 if (*s_ptr >= '0' && *s_ptr <= '9')
10300 int gic_part_nr = (*s_ptr++ - '0');
10302 if (*s_ptr >= '0' && *s_ptr <= '9')
10303 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10305 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10306 return ANIM_EVENT_NONE;
10308 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10312 // invalid animation part number specified
10314 return ANIM_EVENT_NONE;
10318 // discard result if next character is neither delimiter nor whitespace
10319 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10320 *s_ptr == ' ' || *s_ptr == '\t'))
10321 return ANIM_EVENT_NONE;
10326 static int get_anim_parameter_values(char *s)
10328 int list_pos = ANIM_EVENT_UNDEFINED;
10329 int event_value = ANIM_EVENT_DEFAULT;
10331 if (string_has_parameter(s, "any"))
10332 event_value |= ANIM_EVENT_ANY;
10334 if (string_has_parameter(s, "click:self") ||
10335 string_has_parameter(s, "click") ||
10336 string_has_parameter(s, "self"))
10337 event_value |= ANIM_EVENT_SELF;
10339 if (string_has_parameter(s, "unclick:any"))
10340 event_value |= ANIM_EVENT_UNCLICK_ANY;
10342 // if animation event found, add it to global animation event list
10343 if (event_value != ANIM_EVENT_NONE)
10344 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10348 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10349 event_value = get_anim_parameter_value(s);
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);
10355 // continue with next part of the string, starting with next comma
10356 s = strchr(s + 1, ',');
10362 static int get_anim_action_parameter_value(char *token)
10364 // check most common default case first to massively speed things up
10365 if (strEqual(token, ARG_UNDEFINED))
10366 return ANIM_EVENT_ACTION_NONE;
10368 int result = getImageIDFromToken(token);
10372 char *gfx_token = getStringCat2("gfx.", token);
10374 result = getImageIDFromToken(gfx_token);
10376 checked_free(gfx_token);
10381 Key key = getKeyFromX11KeyName(token);
10383 if (key != KSYM_UNDEFINED)
10384 result = -(int)key;
10388 result = ANIM_EVENT_ACTION_NONE;
10393 int get_parameter_value(char *value_raw, char *suffix, int type)
10395 char *value = getStringToLower(value_raw);
10396 int result = 0; // probably a save default value
10398 if (strEqual(suffix, ".direction"))
10400 result = (strEqual(value, "left") ? MV_LEFT :
10401 strEqual(value, "right") ? MV_RIGHT :
10402 strEqual(value, "up") ? MV_UP :
10403 strEqual(value, "down") ? MV_DOWN : MV_NONE);
10405 else if (strEqual(suffix, ".position"))
10407 result = (strEqual(value, "left") ? POS_LEFT :
10408 strEqual(value, "right") ? POS_RIGHT :
10409 strEqual(value, "top") ? POS_TOP :
10410 strEqual(value, "upper") ? POS_UPPER :
10411 strEqual(value, "middle") ? POS_MIDDLE :
10412 strEqual(value, "lower") ? POS_LOWER :
10413 strEqual(value, "bottom") ? POS_BOTTOM :
10414 strEqual(value, "any") ? POS_ANY :
10415 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
10417 else if (strEqual(suffix, ".align"))
10419 result = (strEqual(value, "left") ? ALIGN_LEFT :
10420 strEqual(value, "right") ? ALIGN_RIGHT :
10421 strEqual(value, "center") ? ALIGN_CENTER :
10422 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10424 else if (strEqual(suffix, ".valign"))
10426 result = (strEqual(value, "top") ? VALIGN_TOP :
10427 strEqual(value, "bottom") ? VALIGN_BOTTOM :
10428 strEqual(value, "middle") ? VALIGN_MIDDLE :
10429 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10431 else if (strEqual(suffix, ".anim_mode"))
10433 result = (string_has_parameter(value, "none") ? ANIM_NONE :
10434 string_has_parameter(value, "loop") ? ANIM_LOOP :
10435 string_has_parameter(value, "linear") ? ANIM_LINEAR :
10436 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
10437 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
10438 string_has_parameter(value, "random") ? ANIM_RANDOM :
10439 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
10440 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
10441 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
10442 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10443 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
10444 string_has_parameter(value, "centered") ? ANIM_CENTERED :
10445 string_has_parameter(value, "all") ? ANIM_ALL :
10448 if (string_has_parameter(value, "once"))
10449 result |= ANIM_ONCE;
10451 if (string_has_parameter(value, "reverse"))
10452 result |= ANIM_REVERSE;
10454 if (string_has_parameter(value, "opaque_player"))
10455 result |= ANIM_OPAQUE_PLAYER;
10457 if (string_has_parameter(value, "static_panel"))
10458 result |= ANIM_STATIC_PANEL;
10460 else if (strEqual(suffix, ".init_event") ||
10461 strEqual(suffix, ".anim_event"))
10463 result = get_anim_parameter_values(value);
10465 else if (strEqual(suffix, ".init_delay_action") ||
10466 strEqual(suffix, ".anim_delay_action") ||
10467 strEqual(suffix, ".post_delay_action") ||
10468 strEqual(suffix, ".init_event_action") ||
10469 strEqual(suffix, ".anim_event_action"))
10471 result = get_anim_action_parameter_value(value_raw);
10473 else if (strEqual(suffix, ".class"))
10475 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10476 get_hash_from_key(value));
10478 else if (strEqual(suffix, ".style"))
10480 result = STYLE_DEFAULT;
10482 if (string_has_parameter(value, "accurate_borders"))
10483 result |= STYLE_ACCURATE_BORDERS;
10485 if (string_has_parameter(value, "inner_corners"))
10486 result |= STYLE_INNER_CORNERS;
10488 if (string_has_parameter(value, "reverse"))
10489 result |= STYLE_REVERSE;
10491 if (string_has_parameter(value, "leftmost_position"))
10492 result |= STYLE_LEFTMOST_POSITION;
10494 if (string_has_parameter(value, "block_clicks"))
10495 result |= STYLE_BLOCK;
10497 if (string_has_parameter(value, "passthrough_clicks"))
10498 result |= STYLE_PASSTHROUGH;
10500 if (string_has_parameter(value, "multiple_actions"))
10501 result |= STYLE_MULTIPLE_ACTIONS;
10503 else if (strEqual(suffix, ".fade_mode"))
10505 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
10506 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
10507 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
10508 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
10509 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
10510 FADE_MODE_DEFAULT);
10512 else if (strEqual(suffix, ".auto_delay_unit"))
10514 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
10515 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10516 AUTO_DELAY_UNIT_DEFAULT);
10518 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
10520 result = gfx.get_font_from_token_function(value);
10522 else // generic parameter of type integer or boolean
10524 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10525 type == TYPE_INTEGER ? get_integer_from_string(value) :
10526 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10527 ARG_UNDEFINED_VALUE);
10535 static int get_token_parameter_value(char *token, char *value_raw)
10539 if (token == NULL || value_raw == NULL)
10540 return ARG_UNDEFINED_VALUE;
10542 suffix = strrchr(token, '.');
10543 if (suffix == NULL)
10546 if (strEqual(suffix, ".element"))
10547 return getElementFromToken(value_raw);
10549 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10550 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10553 void InitMenuDesignSettings_Static(void)
10557 // always start with reliable default values from static default config
10558 for (i = 0; image_config_vars[i].token != NULL; i++)
10560 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10563 *image_config_vars[i].value =
10564 get_token_parameter_value(image_config_vars[i].token, value);
10568 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10572 // the following initializes hierarchical values from static configuration
10574 // special case: initialize "ARG_DEFAULT" values in static default config
10575 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10576 titlescreen_initial_first_default.fade_mode =
10577 title_initial_first_default.fade_mode;
10578 titlescreen_initial_first_default.fade_delay =
10579 title_initial_first_default.fade_delay;
10580 titlescreen_initial_first_default.post_delay =
10581 title_initial_first_default.post_delay;
10582 titlescreen_initial_first_default.auto_delay =
10583 title_initial_first_default.auto_delay;
10584 titlescreen_initial_first_default.auto_delay_unit =
10585 title_initial_first_default.auto_delay_unit;
10586 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
10587 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10588 titlescreen_first_default.post_delay = title_first_default.post_delay;
10589 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10590 titlescreen_first_default.auto_delay_unit =
10591 title_first_default.auto_delay_unit;
10592 titlemessage_initial_first_default.fade_mode =
10593 title_initial_first_default.fade_mode;
10594 titlemessage_initial_first_default.fade_delay =
10595 title_initial_first_default.fade_delay;
10596 titlemessage_initial_first_default.post_delay =
10597 title_initial_first_default.post_delay;
10598 titlemessage_initial_first_default.auto_delay =
10599 title_initial_first_default.auto_delay;
10600 titlemessage_initial_first_default.auto_delay_unit =
10601 title_initial_first_default.auto_delay_unit;
10602 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
10603 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10604 titlemessage_first_default.post_delay = title_first_default.post_delay;
10605 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10606 titlemessage_first_default.auto_delay_unit =
10607 title_first_default.auto_delay_unit;
10609 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
10610 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10611 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10612 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10613 titlescreen_initial_default.auto_delay_unit =
10614 title_initial_default.auto_delay_unit;
10615 titlescreen_default.fade_mode = title_default.fade_mode;
10616 titlescreen_default.fade_delay = title_default.fade_delay;
10617 titlescreen_default.post_delay = title_default.post_delay;
10618 titlescreen_default.auto_delay = title_default.auto_delay;
10619 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
10620 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
10621 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10622 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10623 titlemessage_initial_default.auto_delay_unit =
10624 title_initial_default.auto_delay_unit;
10625 titlemessage_default.fade_mode = title_default.fade_mode;
10626 titlemessage_default.fade_delay = title_default.fade_delay;
10627 titlemessage_default.post_delay = title_default.post_delay;
10628 titlemessage_default.auto_delay = title_default.auto_delay;
10629 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
10631 // special case: initialize "ARG_DEFAULT" values in static default config
10632 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
10633 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10635 titlescreen_initial_first[i] = titlescreen_initial_first_default;
10636 titlescreen_first[i] = titlescreen_first_default;
10637 titlemessage_initial_first[i] = titlemessage_initial_first_default;
10638 titlemessage_first[i] = titlemessage_first_default;
10640 titlescreen_initial[i] = titlescreen_initial_default;
10641 titlescreen[i] = titlescreen_default;
10642 titlemessage_initial[i] = titlemessage_initial_default;
10643 titlemessage[i] = titlemessage_default;
10646 // special case: initialize "ARG_DEFAULT" values in static default config
10647 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
10648 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10650 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
10653 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
10654 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
10655 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
10658 // special case: initialize "ARG_DEFAULT" values in static default config
10659 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
10660 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10662 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
10663 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
10664 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
10666 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
10669 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
10673 static void InitMenuDesignSettings_SpecialPostProcessing(void)
10677 struct XY *dst, *src;
10679 game_buttons_xy[] =
10681 { &game.button.save, &game.button.stop },
10682 { &game.button.pause2, &game.button.pause },
10683 { &game.button.load, &game.button.play },
10684 { &game.button.undo, &game.button.stop },
10685 { &game.button.redo, &game.button.play },
10691 // special case: initialize later added SETUP list size from LEVELS value
10692 if (menu.list_size[GAME_MODE_SETUP] == -1)
10693 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
10695 // set default position for snapshot buttons to stop/pause/play buttons
10696 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
10697 if ((*game_buttons_xy[i].dst).x == -1 &&
10698 (*game_buttons_xy[i].dst).y == -1)
10699 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
10701 // --------------------------------------------------------------------------
10702 // dynamic viewports (including playfield margins, borders and alignments)
10703 // --------------------------------------------------------------------------
10705 // dynamic viewports currently only supported for landscape mode
10706 int display_width = MAX(video.display_width, video.display_height);
10707 int display_height = MIN(video.display_width, video.display_height);
10709 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10711 struct RectWithBorder *vp_window = &viewport.window[i];
10712 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
10713 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
10714 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
10715 boolean dynamic_window_width = (vp_window->min_width != -1);
10716 boolean dynamic_window_height = (vp_window->min_height != -1);
10717 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
10718 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
10720 // adjust window size if min/max width/height is specified
10722 if (vp_window->min_width != -1)
10724 int window_width = display_width;
10726 // when using static window height, use aspect ratio of display
10727 if (vp_window->min_height == -1)
10728 window_width = vp_window->height * display_width / display_height;
10730 vp_window->width = MAX(vp_window->min_width, window_width);
10733 if (vp_window->min_height != -1)
10735 int window_height = display_height;
10737 // when using static window width, use aspect ratio of display
10738 if (vp_window->min_width == -1)
10739 window_height = vp_window->width * display_height / display_width;
10741 vp_window->height = MAX(vp_window->min_height, window_height);
10744 if (vp_window->max_width != -1)
10745 vp_window->width = MIN(vp_window->width, vp_window->max_width);
10747 if (vp_window->max_height != -1)
10748 vp_window->height = MIN(vp_window->height, vp_window->max_height);
10750 int playfield_width = vp_window->width;
10751 int playfield_height = vp_window->height;
10753 // adjust playfield size and position according to specified margins
10755 playfield_width -= vp_playfield->margin_left;
10756 playfield_width -= vp_playfield->margin_right;
10758 playfield_height -= vp_playfield->margin_top;
10759 playfield_height -= vp_playfield->margin_bottom;
10761 // adjust playfield size if min/max width/height is specified
10763 if (vp_playfield->min_width != -1)
10764 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
10766 if (vp_playfield->min_height != -1)
10767 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
10769 if (vp_playfield->max_width != -1)
10770 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
10772 if (vp_playfield->max_height != -1)
10773 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
10775 // adjust playfield position according to specified alignment
10777 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
10778 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
10779 else if (vp_playfield->align == ALIGN_CENTER)
10780 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
10781 else if (vp_playfield->align == ALIGN_RIGHT)
10782 vp_playfield->x += playfield_width - vp_playfield->width;
10784 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
10785 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
10786 else if (vp_playfield->valign == VALIGN_MIDDLE)
10787 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
10788 else if (vp_playfield->valign == VALIGN_BOTTOM)
10789 vp_playfield->y += playfield_height - vp_playfield->height;
10791 vp_playfield->x += vp_playfield->margin_left;
10792 vp_playfield->y += vp_playfield->margin_top;
10794 // adjust individual playfield borders if only default border is specified
10796 if (vp_playfield->border_left == -1)
10797 vp_playfield->border_left = vp_playfield->border_size;
10798 if (vp_playfield->border_right == -1)
10799 vp_playfield->border_right = vp_playfield->border_size;
10800 if (vp_playfield->border_top == -1)
10801 vp_playfield->border_top = vp_playfield->border_size;
10802 if (vp_playfield->border_bottom == -1)
10803 vp_playfield->border_bottom = vp_playfield->border_size;
10805 // set dynamic playfield borders if borders are specified as undefined
10806 // (but only if window size was dynamic and playfield size was static)
10808 if (dynamic_window_width && !dynamic_playfield_width)
10810 if (vp_playfield->border_left == -1)
10812 vp_playfield->border_left = (vp_playfield->x -
10813 vp_playfield->margin_left);
10814 vp_playfield->x -= vp_playfield->border_left;
10815 vp_playfield->width += vp_playfield->border_left;
10818 if (vp_playfield->border_right == -1)
10820 vp_playfield->border_right = (vp_window->width -
10822 vp_playfield->width -
10823 vp_playfield->margin_right);
10824 vp_playfield->width += vp_playfield->border_right;
10828 if (dynamic_window_height && !dynamic_playfield_height)
10830 if (vp_playfield->border_top == -1)
10832 vp_playfield->border_top = (vp_playfield->y -
10833 vp_playfield->margin_top);
10834 vp_playfield->y -= vp_playfield->border_top;
10835 vp_playfield->height += vp_playfield->border_top;
10838 if (vp_playfield->border_bottom == -1)
10840 vp_playfield->border_bottom = (vp_window->height -
10842 vp_playfield->height -
10843 vp_playfield->margin_bottom);
10844 vp_playfield->height += vp_playfield->border_bottom;
10848 // adjust playfield size to be a multiple of a defined alignment tile size
10850 int align_size = vp_playfield->align_size;
10851 int playfield_xtiles = vp_playfield->width / align_size;
10852 int playfield_ytiles = vp_playfield->height / align_size;
10853 int playfield_width_corrected = playfield_xtiles * align_size;
10854 int playfield_height_corrected = playfield_ytiles * align_size;
10855 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
10856 i == GFX_SPECIAL_ARG_EDITOR);
10858 if (is_playfield_mode &&
10859 dynamic_playfield_width &&
10860 vp_playfield->width != playfield_width_corrected)
10862 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
10864 vp_playfield->width = playfield_width_corrected;
10866 if (vp_playfield->align == ALIGN_LEFT)
10868 vp_playfield->border_left += playfield_xdiff;
10870 else if (vp_playfield->align == ALIGN_RIGHT)
10872 vp_playfield->border_right += playfield_xdiff;
10874 else if (vp_playfield->align == ALIGN_CENTER)
10876 int border_left_diff = playfield_xdiff / 2;
10877 int border_right_diff = playfield_xdiff - border_left_diff;
10879 vp_playfield->border_left += border_left_diff;
10880 vp_playfield->border_right += border_right_diff;
10884 if (is_playfield_mode &&
10885 dynamic_playfield_height &&
10886 vp_playfield->height != playfield_height_corrected)
10888 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
10890 vp_playfield->height = playfield_height_corrected;
10892 if (vp_playfield->valign == VALIGN_TOP)
10894 vp_playfield->border_top += playfield_ydiff;
10896 else if (vp_playfield->align == VALIGN_BOTTOM)
10898 vp_playfield->border_right += playfield_ydiff;
10900 else if (vp_playfield->align == VALIGN_MIDDLE)
10902 int border_top_diff = playfield_ydiff / 2;
10903 int border_bottom_diff = playfield_ydiff - border_top_diff;
10905 vp_playfield->border_top += border_top_diff;
10906 vp_playfield->border_bottom += border_bottom_diff;
10910 // adjust door positions according to specified alignment
10912 for (j = 0; j < 2; j++)
10914 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
10916 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
10917 vp_door->x = ALIGNED_VP_XPOS(vp_door);
10918 else if (vp_door->align == ALIGN_CENTER)
10919 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
10920 else if (vp_door->align == ALIGN_RIGHT)
10921 vp_door->x += vp_window->width - vp_door->width;
10923 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
10924 vp_door->y = ALIGNED_VP_YPOS(vp_door);
10925 else if (vp_door->valign == VALIGN_MIDDLE)
10926 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
10927 else if (vp_door->valign == VALIGN_BOTTOM)
10928 vp_door->y += vp_window->height - vp_door->height;
10933 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
10937 struct XYTileSize *dst, *src;
10940 editor_buttons_xy[] =
10943 &editor.button.element_left, &editor.palette.element_left,
10944 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
10947 &editor.button.element_middle, &editor.palette.element_middle,
10948 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
10951 &editor.button.element_right, &editor.palette.element_right,
10952 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
10959 // set default position for element buttons to element graphics
10960 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
10962 if ((*editor_buttons_xy[i].dst).x == -1 &&
10963 (*editor_buttons_xy[i].dst).y == -1)
10965 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
10967 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
10969 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
10973 // adjust editor palette rows and columns if specified to be dynamic
10975 if (editor.palette.cols == -1)
10977 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
10978 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
10979 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
10981 editor.palette.cols = (vp_width - sc_width) / bt_width;
10983 if (editor.palette.x == -1)
10985 int palette_width = editor.palette.cols * bt_width + sc_width;
10987 editor.palette.x = (vp_width - palette_width) / 2;
10991 if (editor.palette.rows == -1)
10993 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
10994 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
10995 int tx_height = getFontHeight(FONT_TEXT_2);
10997 editor.palette.rows = (vp_height - tx_height) / bt_height;
10999 if (editor.palette.y == -1)
11001 int palette_height = editor.palette.rows * bt_height + tx_height;
11003 editor.palette.y = (vp_height - palette_height) / 2;
11008 static void LoadMenuDesignSettingsFromFilename(char *filename)
11010 static struct TitleFadingInfo tfi;
11011 static struct TitleMessageInfo tmi;
11012 static struct TokenInfo title_tokens[] =
11014 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
11015 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
11016 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
11017 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
11018 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
11022 static struct TokenInfo titlemessage_tokens[] =
11024 { TYPE_INTEGER, &tmi.x, ".x" },
11025 { TYPE_INTEGER, &tmi.y, ".y" },
11026 { TYPE_INTEGER, &tmi.width, ".width" },
11027 { TYPE_INTEGER, &tmi.height, ".height" },
11028 { TYPE_INTEGER, &tmi.chars, ".chars" },
11029 { TYPE_INTEGER, &tmi.lines, ".lines" },
11030 { TYPE_INTEGER, &tmi.align, ".align" },
11031 { TYPE_INTEGER, &tmi.valign, ".valign" },
11032 { TYPE_INTEGER, &tmi.font, ".font" },
11033 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
11034 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
11035 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
11036 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
11037 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
11038 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
11039 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
11040 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
11041 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
11047 struct TitleFadingInfo *info;
11052 // initialize first titles from "enter screen" definitions, if defined
11053 { &title_initial_first_default, "menu.enter_screen.TITLE" },
11054 { &title_first_default, "menu.enter_screen.TITLE" },
11056 // initialize title screens from "next screen" definitions, if defined
11057 { &title_initial_default, "menu.next_screen.TITLE" },
11058 { &title_default, "menu.next_screen.TITLE" },
11064 struct TitleMessageInfo *array;
11067 titlemessage_arrays[] =
11069 // initialize first titles from "enter screen" definitions, if defined
11070 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
11071 { titlescreen_first, "menu.enter_screen.TITLE" },
11072 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
11073 { titlemessage_first, "menu.enter_screen.TITLE" },
11075 // initialize titles from "next screen" definitions, if defined
11076 { titlescreen_initial, "menu.next_screen.TITLE" },
11077 { titlescreen, "menu.next_screen.TITLE" },
11078 { titlemessage_initial, "menu.next_screen.TITLE" },
11079 { titlemessage, "menu.next_screen.TITLE" },
11081 // overwrite titles with title definitions, if defined
11082 { titlescreen_initial_first, "[title_initial]" },
11083 { titlescreen_first, "[title]" },
11084 { titlemessage_initial_first, "[title_initial]" },
11085 { titlemessage_first, "[title]" },
11087 { titlescreen_initial, "[title_initial]" },
11088 { titlescreen, "[title]" },
11089 { titlemessage_initial, "[title_initial]" },
11090 { titlemessage, "[title]" },
11092 // overwrite titles with title screen/message definitions, if defined
11093 { titlescreen_initial_first, "[titlescreen_initial]" },
11094 { titlescreen_first, "[titlescreen]" },
11095 { titlemessage_initial_first, "[titlemessage_initial]" },
11096 { titlemessage_first, "[titlemessage]" },
11098 { titlescreen_initial, "[titlescreen_initial]" },
11099 { titlescreen, "[titlescreen]" },
11100 { titlemessage_initial, "[titlemessage_initial]" },
11101 { titlemessage, "[titlemessage]" },
11105 SetupFileHash *setup_file_hash;
11108 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11111 // the following initializes hierarchical values from dynamic configuration
11113 // special case: initialize with default values that may be overwritten
11114 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
11115 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11117 struct TokenIntPtrInfo menu_config[] =
11119 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
11120 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
11121 { "menu.list_size", &menu.list_size[i] }
11124 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11126 char *token = menu_config[j].token;
11127 char *value = getHashEntry(setup_file_hash, token);
11130 *menu_config[j].value = get_integer_from_string(value);
11134 // special case: initialize with default values that may be overwritten
11135 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
11136 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11138 struct TokenIntPtrInfo menu_config[] =
11140 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
11141 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
11142 { "menu.list_size.INFO", &menu.list_size_info[i] }
11145 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11147 char *token = menu_config[j].token;
11148 char *value = getHashEntry(setup_file_hash, token);
11151 *menu_config[j].value = get_integer_from_string(value);
11155 // special case: initialize with default values that may be overwritten
11156 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
11157 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
11159 struct TokenIntPtrInfo menu_config[] =
11161 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
11162 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
11165 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11167 char *token = menu_config[j].token;
11168 char *value = getHashEntry(setup_file_hash, token);
11171 *menu_config[j].value = get_integer_from_string(value);
11175 // special case: initialize with default values that may be overwritten
11176 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
11177 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11179 struct TokenIntPtrInfo menu_config[] =
11181 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
11182 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
11183 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
11184 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
11185 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
11186 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
11187 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
11188 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
11189 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
11192 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11194 char *token = menu_config[j].token;
11195 char *value = getHashEntry(setup_file_hash, token);
11198 *menu_config[j].value = get_integer_from_string(value);
11202 // special case: initialize with default values that may be overwritten
11203 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11204 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11206 struct TokenIntPtrInfo menu_config[] =
11208 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
11209 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11210 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11211 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
11212 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11213 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11214 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
11215 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
11216 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
11219 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11221 char *token = menu_config[j].token;
11222 char *value = getHashEntry(setup_file_hash, token);
11225 *menu_config[j].value = get_token_parameter_value(token, value);
11229 // special case: initialize with default values that may be overwritten
11230 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11231 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11235 char *token_prefix;
11236 struct RectWithBorder *struct_ptr;
11240 { "viewport.window", &viewport.window[i] },
11241 { "viewport.playfield", &viewport.playfield[i] },
11242 { "viewport.door_1", &viewport.door_1[i] },
11243 { "viewport.door_2", &viewport.door_2[i] }
11246 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11248 struct TokenIntPtrInfo vp_config[] =
11250 { ".x", &vp_struct[j].struct_ptr->x },
11251 { ".y", &vp_struct[j].struct_ptr->y },
11252 { ".width", &vp_struct[j].struct_ptr->width },
11253 { ".height", &vp_struct[j].struct_ptr->height },
11254 { ".min_width", &vp_struct[j].struct_ptr->min_width },
11255 { ".min_height", &vp_struct[j].struct_ptr->min_height },
11256 { ".max_width", &vp_struct[j].struct_ptr->max_width },
11257 { ".max_height", &vp_struct[j].struct_ptr->max_height },
11258 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
11259 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
11260 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
11261 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
11262 { ".border_left", &vp_struct[j].struct_ptr->border_left },
11263 { ".border_right", &vp_struct[j].struct_ptr->border_right },
11264 { ".border_top", &vp_struct[j].struct_ptr->border_top },
11265 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
11266 { ".border_size", &vp_struct[j].struct_ptr->border_size },
11267 { ".align_size", &vp_struct[j].struct_ptr->align_size },
11268 { ".align", &vp_struct[j].struct_ptr->align },
11269 { ".valign", &vp_struct[j].struct_ptr->valign }
11272 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11274 char *token = getStringCat2(vp_struct[j].token_prefix,
11275 vp_config[k].token);
11276 char *value = getHashEntry(setup_file_hash, token);
11279 *vp_config[k].value = get_token_parameter_value(token, value);
11286 // special case: initialize with default values that may be overwritten
11287 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11288 for (i = 0; title_info[i].info != NULL; i++)
11290 struct TitleFadingInfo *info = title_info[i].info;
11291 char *base_token = title_info[i].text;
11293 for (j = 0; title_tokens[j].type != -1; j++)
11295 char *token = getStringCat2(base_token, title_tokens[j].text);
11296 char *value = getHashEntry(setup_file_hash, token);
11300 int parameter_value = get_token_parameter_value(token, value);
11304 *(int *)title_tokens[j].value = (int)parameter_value;
11313 // special case: initialize with default values that may be overwritten
11314 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11315 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11317 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11318 char *base_token = titlemessage_arrays[i].text;
11320 for (j = 0; titlemessage_tokens[j].type != -1; j++)
11322 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11323 char *value = getHashEntry(setup_file_hash, token);
11327 int parameter_value = get_token_parameter_value(token, value);
11329 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11333 if (titlemessage_tokens[j].type == TYPE_INTEGER)
11334 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
11336 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11346 // special case: check if network and preview player positions are redefined,
11347 // to compare this later against the main menu level preview being redefined
11348 struct TokenIntPtrInfo menu_config_players[] =
11350 { "main.network_players.x", &menu.main.network_players.redefined },
11351 { "main.network_players.y", &menu.main.network_players.redefined },
11352 { "main.preview_players.x", &menu.main.preview_players.redefined },
11353 { "main.preview_players.y", &menu.main.preview_players.redefined },
11354 { "preview.x", &preview.redefined },
11355 { "preview.y", &preview.redefined }
11358 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11359 *menu_config_players[i].value = FALSE;
11361 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11362 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11363 *menu_config_players[i].value = TRUE;
11365 // read (and overwrite with) values that may be specified in config file
11366 for (i = 0; image_config_vars[i].token != NULL; i++)
11368 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11370 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11371 if (value != NULL && !strEqual(value, ARG_DEFAULT))
11372 *image_config_vars[i].value =
11373 get_token_parameter_value(image_config_vars[i].token, value);
11376 freeSetupFileHash(setup_file_hash);
11379 void LoadMenuDesignSettings(void)
11381 char *filename_base = UNDEFINED_FILENAME, *filename_local;
11383 InitMenuDesignSettings_Static();
11384 InitMenuDesignSettings_SpecialPreProcessing();
11386 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11388 // first look for special settings configured in level series config
11389 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11391 if (fileExists(filename_base))
11392 LoadMenuDesignSettingsFromFilename(filename_base);
11395 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11397 if (filename_local != NULL && !strEqual(filename_base, filename_local))
11398 LoadMenuDesignSettingsFromFilename(filename_local);
11400 InitMenuDesignSettings_SpecialPostProcessing();
11403 void LoadMenuDesignSettings_AfterGraphics(void)
11405 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11408 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11410 char *filename = getEditorSetupFilename();
11411 SetupFileList *setup_file_list, *list;
11412 SetupFileHash *element_hash;
11413 int num_unknown_tokens = 0;
11416 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11419 element_hash = newSetupFileHash();
11421 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11422 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11424 // determined size may be larger than needed (due to unknown elements)
11426 for (list = setup_file_list; list != NULL; list = list->next)
11429 // add space for up to 3 more elements for padding that may be needed
11430 *num_elements += 3;
11432 // free memory for old list of elements, if needed
11433 checked_free(*elements);
11435 // allocate memory for new list of elements
11436 *elements = checked_malloc(*num_elements * sizeof(int));
11439 for (list = setup_file_list; list != NULL; list = list->next)
11441 char *value = getHashEntry(element_hash, list->token);
11443 if (value == NULL) // try to find obsolete token mapping
11445 char *mapped_token = get_mapped_token(list->token);
11447 if (mapped_token != NULL)
11449 value = getHashEntry(element_hash, mapped_token);
11451 free(mapped_token);
11457 (*elements)[(*num_elements)++] = atoi(value);
11461 if (num_unknown_tokens == 0)
11464 Warn("unknown token(s) found in config file:");
11465 Warn("- config file: '%s'", filename);
11467 num_unknown_tokens++;
11470 Warn("- token: '%s'", list->token);
11474 if (num_unknown_tokens > 0)
11477 while (*num_elements % 4) // pad with empty elements, if needed
11478 (*elements)[(*num_elements)++] = EL_EMPTY;
11480 freeSetupFileList(setup_file_list);
11481 freeSetupFileHash(element_hash);
11484 for (i = 0; i < *num_elements; i++)
11485 Debug("editor", "element '%s' [%d]\n",
11486 element_info[(*elements)[i]].token_name, (*elements)[i]);
11490 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11493 SetupFileHash *setup_file_hash = NULL;
11494 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11495 char *filename_music, *filename_prefix, *filename_info;
11501 token_to_value_ptr[] =
11503 { "title_header", &tmp_music_file_info.title_header },
11504 { "artist_header", &tmp_music_file_info.artist_header },
11505 { "album_header", &tmp_music_file_info.album_header },
11506 { "year_header", &tmp_music_file_info.year_header },
11508 { "title", &tmp_music_file_info.title },
11509 { "artist", &tmp_music_file_info.artist },
11510 { "album", &tmp_music_file_info.album },
11511 { "year", &tmp_music_file_info.year },
11517 filename_music = (is_sound ? getCustomSoundFilename(basename) :
11518 getCustomMusicFilename(basename));
11520 if (filename_music == NULL)
11523 // ---------- try to replace file extension ----------
11525 filename_prefix = getStringCopy(filename_music);
11526 if (strrchr(filename_prefix, '.') != NULL)
11527 *strrchr(filename_prefix, '.') = '\0';
11528 filename_info = getStringCat2(filename_prefix, ".txt");
11530 if (fileExists(filename_info))
11531 setup_file_hash = loadSetupFileHash(filename_info);
11533 free(filename_prefix);
11534 free(filename_info);
11536 if (setup_file_hash == NULL)
11538 // ---------- try to add file extension ----------
11540 filename_prefix = getStringCopy(filename_music);
11541 filename_info = getStringCat2(filename_prefix, ".txt");
11543 if (fileExists(filename_info))
11544 setup_file_hash = loadSetupFileHash(filename_info);
11546 free(filename_prefix);
11547 free(filename_info);
11550 if (setup_file_hash == NULL)
11553 // ---------- music file info found ----------
11555 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11557 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11559 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11561 *token_to_value_ptr[i].value_ptr =
11562 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11565 tmp_music_file_info.basename = getStringCopy(basename);
11566 tmp_music_file_info.music = music;
11567 tmp_music_file_info.is_sound = is_sound;
11569 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11570 *new_music_file_info = tmp_music_file_info;
11572 return new_music_file_info;
11575 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11577 return get_music_file_info_ext(basename, music, FALSE);
11580 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11582 return get_music_file_info_ext(basename, sound, TRUE);
11585 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11586 char *basename, boolean is_sound)
11588 for (; list != NULL; list = list->next)
11589 if (list->is_sound == is_sound && strEqual(list->basename, basename))
11595 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11597 return music_info_listed_ext(list, basename, FALSE);
11600 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11602 return music_info_listed_ext(list, basename, TRUE);
11605 void LoadMusicInfo(void)
11607 char *music_directory = getCustomMusicDirectory();
11608 int num_music = getMusicListSize();
11609 int num_music_noconf = 0;
11610 int num_sounds = getSoundListSize();
11612 DirectoryEntry *dir_entry;
11613 struct FileInfo *music, *sound;
11614 struct MusicFileInfo *next, **new;
11617 while (music_file_info != NULL)
11619 next = music_file_info->next;
11621 checked_free(music_file_info->basename);
11623 checked_free(music_file_info->title_header);
11624 checked_free(music_file_info->artist_header);
11625 checked_free(music_file_info->album_header);
11626 checked_free(music_file_info->year_header);
11628 checked_free(music_file_info->title);
11629 checked_free(music_file_info->artist);
11630 checked_free(music_file_info->album);
11631 checked_free(music_file_info->year);
11633 free(music_file_info);
11635 music_file_info = next;
11638 new = &music_file_info;
11640 for (i = 0; i < num_music; i++)
11642 music = getMusicListEntry(i);
11644 if (music->filename == NULL)
11647 if (strEqual(music->filename, UNDEFINED_FILENAME))
11650 // a configured file may be not recognized as music
11651 if (!FileIsMusic(music->filename))
11654 if (!music_info_listed(music_file_info, music->filename))
11656 *new = get_music_file_info(music->filename, i);
11659 new = &(*new)->next;
11663 if ((dir = openDirectory(music_directory)) == NULL)
11665 Warn("cannot read music directory '%s'", music_directory);
11670 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
11672 char *basename = dir_entry->basename;
11673 boolean music_already_used = FALSE;
11676 // skip all music files that are configured in music config file
11677 for (i = 0; i < num_music; i++)
11679 music = getMusicListEntry(i);
11681 if (music->filename == NULL)
11684 if (strEqual(basename, music->filename))
11686 music_already_used = TRUE;
11691 if (music_already_used)
11694 if (!FileIsMusic(dir_entry->filename))
11697 if (!music_info_listed(music_file_info, basename))
11699 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
11702 new = &(*new)->next;
11705 num_music_noconf++;
11708 closeDirectory(dir);
11710 for (i = 0; i < num_sounds; i++)
11712 sound = getSoundListEntry(i);
11714 if (sound->filename == NULL)
11717 if (strEqual(sound->filename, UNDEFINED_FILENAME))
11720 // a configured file may be not recognized as sound
11721 if (!FileIsSound(sound->filename))
11724 if (!sound_info_listed(music_file_info, sound->filename))
11726 *new = get_sound_file_info(sound->filename, i);
11728 new = &(*new)->next;
11733 static void add_helpanim_entry(int element, int action, int direction,
11734 int delay, int *num_list_entries)
11736 struct HelpAnimInfo *new_list_entry;
11737 (*num_list_entries)++;
11740 checked_realloc(helpanim_info,
11741 *num_list_entries * sizeof(struct HelpAnimInfo));
11742 new_list_entry = &helpanim_info[*num_list_entries - 1];
11744 new_list_entry->element = element;
11745 new_list_entry->action = action;
11746 new_list_entry->direction = direction;
11747 new_list_entry->delay = delay;
11750 static void print_unknown_token(char *filename, char *token, int token_nr)
11755 Warn("unknown token(s) found in config file:");
11756 Warn("- config file: '%s'", filename);
11759 Warn("- token: '%s'", token);
11762 static void print_unknown_token_end(int token_nr)
11768 void LoadHelpAnimInfo(void)
11770 char *filename = getHelpAnimFilename();
11771 SetupFileList *setup_file_list = NULL, *list;
11772 SetupFileHash *element_hash, *action_hash, *direction_hash;
11773 int num_list_entries = 0;
11774 int num_unknown_tokens = 0;
11777 if (fileExists(filename))
11778 setup_file_list = loadSetupFileList(filename);
11780 if (setup_file_list == NULL)
11782 // use reliable default values from static configuration
11783 SetupFileList *insert_ptr;
11785 insert_ptr = setup_file_list =
11786 newSetupFileList(helpanim_config[0].token,
11787 helpanim_config[0].value);
11789 for (i = 1; helpanim_config[i].token; i++)
11790 insert_ptr = addListEntry(insert_ptr,
11791 helpanim_config[i].token,
11792 helpanim_config[i].value);
11795 element_hash = newSetupFileHash();
11796 action_hash = newSetupFileHash();
11797 direction_hash = newSetupFileHash();
11799 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
11800 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11802 for (i = 0; i < NUM_ACTIONS; i++)
11803 setHashEntry(action_hash, element_action_info[i].suffix,
11804 i_to_a(element_action_info[i].value));
11806 // do not store direction index (bit) here, but direction value!
11807 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
11808 setHashEntry(direction_hash, element_direction_info[i].suffix,
11809 i_to_a(1 << element_direction_info[i].value));
11811 for (list = setup_file_list; list != NULL; list = list->next)
11813 char *element_token, *action_token, *direction_token;
11814 char *element_value, *action_value, *direction_value;
11815 int delay = atoi(list->value);
11817 if (strEqual(list->token, "end"))
11819 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11824 /* first try to break element into element/action/direction parts;
11825 if this does not work, also accept combined "element[.act][.dir]"
11826 elements (like "dynamite.active"), which are unique elements */
11828 if (strchr(list->token, '.') == NULL) // token contains no '.'
11830 element_value = getHashEntry(element_hash, list->token);
11831 if (element_value != NULL) // element found
11832 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11833 &num_list_entries);
11836 // no further suffixes found -- this is not an element
11837 print_unknown_token(filename, list->token, num_unknown_tokens++);
11843 // token has format "<prefix>.<something>"
11845 action_token = strchr(list->token, '.'); // suffix may be action ...
11846 direction_token = action_token; // ... or direction
11848 element_token = getStringCopy(list->token);
11849 *strchr(element_token, '.') = '\0';
11851 element_value = getHashEntry(element_hash, element_token);
11853 if (element_value == NULL) // this is no element
11855 element_value = getHashEntry(element_hash, list->token);
11856 if (element_value != NULL) // combined element found
11857 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11858 &num_list_entries);
11860 print_unknown_token(filename, list->token, num_unknown_tokens++);
11862 free(element_token);
11867 action_value = getHashEntry(action_hash, action_token);
11869 if (action_value != NULL) // action found
11871 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
11872 &num_list_entries);
11874 free(element_token);
11879 direction_value = getHashEntry(direction_hash, direction_token);
11881 if (direction_value != NULL) // direction found
11883 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
11884 &num_list_entries);
11886 free(element_token);
11891 if (strchr(action_token + 1, '.') == NULL)
11893 // no further suffixes found -- this is not an action nor direction
11895 element_value = getHashEntry(element_hash, list->token);
11896 if (element_value != NULL) // combined element found
11897 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11898 &num_list_entries);
11900 print_unknown_token(filename, list->token, num_unknown_tokens++);
11902 free(element_token);
11907 // token has format "<prefix>.<suffix>.<something>"
11909 direction_token = strchr(action_token + 1, '.');
11911 action_token = getStringCopy(action_token);
11912 *strchr(action_token + 1, '.') = '\0';
11914 action_value = getHashEntry(action_hash, action_token);
11916 if (action_value == NULL) // this is no action
11918 element_value = getHashEntry(element_hash, list->token);
11919 if (element_value != NULL) // combined element found
11920 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11921 &num_list_entries);
11923 print_unknown_token(filename, list->token, num_unknown_tokens++);
11925 free(element_token);
11926 free(action_token);
11931 direction_value = getHashEntry(direction_hash, direction_token);
11933 if (direction_value != NULL) // direction found
11935 add_helpanim_entry(atoi(element_value), atoi(action_value),
11936 atoi(direction_value), delay, &num_list_entries);
11938 free(element_token);
11939 free(action_token);
11944 // this is no direction
11946 element_value = getHashEntry(element_hash, list->token);
11947 if (element_value != NULL) // combined element found
11948 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11949 &num_list_entries);
11951 print_unknown_token(filename, list->token, num_unknown_tokens++);
11953 free(element_token);
11954 free(action_token);
11957 print_unknown_token_end(num_unknown_tokens);
11959 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11960 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
11962 freeSetupFileList(setup_file_list);
11963 freeSetupFileHash(element_hash);
11964 freeSetupFileHash(action_hash);
11965 freeSetupFileHash(direction_hash);
11968 for (i = 0; i < num_list_entries; i++)
11969 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
11970 EL_NAME(helpanim_info[i].element),
11971 helpanim_info[i].element,
11972 helpanim_info[i].action,
11973 helpanim_info[i].direction,
11974 helpanim_info[i].delay);
11978 void LoadHelpTextInfo(void)
11980 char *filename = getHelpTextFilename();
11983 if (helptext_info != NULL)
11985 freeSetupFileHash(helptext_info);
11986 helptext_info = NULL;
11989 if (fileExists(filename))
11990 helptext_info = loadSetupFileHash(filename);
11992 if (helptext_info == NULL)
11994 // use reliable default values from static configuration
11995 helptext_info = newSetupFileHash();
11997 for (i = 0; helptext_config[i].token; i++)
11998 setHashEntry(helptext_info,
11999 helptext_config[i].token,
12000 helptext_config[i].value);
12004 BEGIN_HASH_ITERATION(helptext_info, itr)
12006 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
12007 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
12009 END_HASH_ITERATION(hash, itr)
12014 // ----------------------------------------------------------------------------
12016 // ----------------------------------------------------------------------------
12018 #define MAX_NUM_CONVERT_LEVELS 1000
12020 void ConvertLevels(void)
12022 static LevelDirTree *convert_leveldir = NULL;
12023 static int convert_level_nr = -1;
12024 static int num_levels_handled = 0;
12025 static int num_levels_converted = 0;
12026 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
12029 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
12030 global.convert_leveldir);
12032 if (convert_leveldir == NULL)
12033 Fail("no such level identifier: '%s'", global.convert_leveldir);
12035 leveldir_current = convert_leveldir;
12037 if (global.convert_level_nr != -1)
12039 convert_leveldir->first_level = global.convert_level_nr;
12040 convert_leveldir->last_level = global.convert_level_nr;
12043 convert_level_nr = convert_leveldir->first_level;
12045 PrintLine("=", 79);
12046 Print("Converting levels\n");
12047 PrintLine("-", 79);
12048 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
12049 Print("Level series name: '%s'\n", convert_leveldir->name);
12050 Print("Level series author: '%s'\n", convert_leveldir->author);
12051 Print("Number of levels: %d\n", convert_leveldir->levels);
12052 PrintLine("=", 79);
12055 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12056 levels_failed[i] = FALSE;
12058 while (convert_level_nr <= convert_leveldir->last_level)
12060 char *level_filename;
12063 level_nr = convert_level_nr++;
12065 Print("Level %03d: ", level_nr);
12067 LoadLevel(level_nr);
12068 if (level.no_level_file || level.no_valid_file)
12070 Print("(no level)\n");
12074 Print("converting level ... ");
12076 level_filename = getDefaultLevelFilename(level_nr);
12077 new_level = !fileExists(level_filename);
12081 SaveLevel(level_nr);
12083 num_levels_converted++;
12085 Print("converted.\n");
12089 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
12090 levels_failed[level_nr] = TRUE;
12092 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
12095 num_levels_handled++;
12099 PrintLine("=", 79);
12100 Print("Number of levels handled: %d\n", num_levels_handled);
12101 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
12102 (num_levels_handled ?
12103 num_levels_converted * 100 / num_levels_handled : 0));
12104 PrintLine("-", 79);
12105 Print("Summary (for automatic parsing by scripts):\n");
12106 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
12107 convert_leveldir->identifier, num_levels_converted,
12108 num_levels_handled,
12109 (num_levels_handled ?
12110 num_levels_converted * 100 / num_levels_handled : 0));
12112 if (num_levels_handled != num_levels_converted)
12114 Print(", FAILED:");
12115 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12116 if (levels_failed[i])
12121 PrintLine("=", 79);
12123 CloseAllAndExit(0);
12127 // ----------------------------------------------------------------------------
12128 // create and save images for use in level sketches (raw BMP format)
12129 // ----------------------------------------------------------------------------
12131 void CreateLevelSketchImages(void)
12137 InitElementPropertiesGfxElement();
12139 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
12140 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
12142 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12144 int element = getMappedElement(i);
12145 char basename1[16];
12146 char basename2[16];
12150 sprintf(basename1, "%04d.bmp", i);
12151 sprintf(basename2, "%04ds.bmp", i);
12153 filename1 = getPath2(global.create_images_dir, basename1);
12154 filename2 = getPath2(global.create_images_dir, basename2);
12156 DrawSizedElement(0, 0, element, TILESIZE);
12157 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
12159 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
12160 Fail("cannot save level sketch image file '%s'", filename1);
12162 DrawSizedElement(0, 0, element, MINI_TILESIZE);
12163 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
12165 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
12166 Fail("cannot save level sketch image file '%s'", filename2);
12171 // create corresponding SQL statements (for normal and small images)
12174 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12175 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12178 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12179 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12181 // optional: create content for forum level sketch demonstration post
12183 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
12186 FreeBitmap(bitmap1);
12187 FreeBitmap(bitmap2);
12190 fprintf(stderr, "\n");
12192 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
12194 CloseAllAndExit(0);
12198 // ----------------------------------------------------------------------------
12199 // create and save images for custom and group elements (raw BMP format)
12200 // ----------------------------------------------------------------------------
12202 void CreateCustomElementImages(char *directory)
12204 char *src_basename = "RocksCE-template.ilbm";
12205 char *dst_basename = "RocksCE.bmp";
12206 char *src_filename = getPath2(directory, src_basename);
12207 char *dst_filename = getPath2(directory, dst_basename);
12208 Bitmap *src_bitmap;
12210 int yoffset_ce = 0;
12211 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12214 InitVideoDefaults();
12216 ReCreateBitmap(&backbuffer, video.width, video.height);
12218 src_bitmap = LoadImage(src_filename);
12220 bitmap = CreateBitmap(TILEX * 16 * 2,
12221 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12224 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12231 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12232 TILEX * x, TILEY * y + yoffset_ce);
12234 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12236 TILEX * x + TILEX * 16,
12237 TILEY * y + yoffset_ce);
12239 for (j = 2; j >= 0; j--)
12243 BlitBitmap(src_bitmap, bitmap,
12244 TILEX + c * 7, 0, 6, 10,
12245 TILEX * x + 6 + j * 7,
12246 TILEY * y + 11 + yoffset_ce);
12248 BlitBitmap(src_bitmap, bitmap,
12249 TILEX + c * 8, TILEY, 6, 10,
12250 TILEX * 16 + TILEX * x + 6 + j * 8,
12251 TILEY * y + 10 + yoffset_ce);
12257 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12264 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12265 TILEX * x, TILEY * y + yoffset_ge);
12267 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12269 TILEX * x + TILEX * 16,
12270 TILEY * y + yoffset_ge);
12272 for (j = 1; j >= 0; j--)
12276 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12277 TILEX * x + 6 + j * 10,
12278 TILEY * y + 11 + yoffset_ge);
12280 BlitBitmap(src_bitmap, bitmap,
12281 TILEX + c * 8, TILEY + 12, 6, 10,
12282 TILEX * 16 + TILEX * x + 10 + j * 8,
12283 TILEY * y + 10 + yoffset_ge);
12289 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12290 Fail("cannot save CE graphics file '%s'", dst_filename);
12292 FreeBitmap(bitmap);
12294 CloseAllAndExit(0);