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"
28 #define ENABLE_UNUSED_CODE 0 // currently unused functions
29 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
30 #define ENABLE_RESERVED_CODE 0 // reserved for later use
32 #define CHUNK_ID_LEN 4 // IFF style chunk id length
33 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
34 #define CHUNK_SIZE_NONE -1 // do not write chunk size
36 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
37 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
39 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
40 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
41 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
42 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
43 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
44 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
45 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
46 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
47 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
48 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
49 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
51 // (element number, number of change pages, change page number)
52 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
54 // (element number only)
55 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
56 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
57 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
59 // (nothing at all if unchanged)
60 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
62 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
63 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
64 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
66 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
68 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
69 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
70 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
72 // file identifier strings
73 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
74 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
75 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
77 // values for deciding when (not) to save configuration data
78 #define SAVE_CONF_NEVER 0
79 #define SAVE_CONF_ALWAYS 1
80 #define SAVE_CONF_WHEN_CHANGED -1
82 // values for chunks using micro chunks
83 #define CONF_MASK_1_BYTE 0x00
84 #define CONF_MASK_2_BYTE 0x40
85 #define CONF_MASK_4_BYTE 0x80
86 #define CONF_MASK_MULTI_BYTES 0xc0
88 #define CONF_MASK_BYTES 0xc0
89 #define CONF_MASK_TOKEN 0x3f
91 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
92 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
93 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
94 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
96 // these definitions are just for convenience of use and readability
97 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
98 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
99 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
100 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
102 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
103 (x) == CONF_MASK_2_BYTE ? 2 : \
104 (x) == CONF_MASK_4_BYTE ? 4 : 0)
106 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
107 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
108 #define CONF_ELEMENT_NUM_BYTES (2)
110 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
111 (t) == TYPE_ELEMENT_LIST ? \
112 CONF_ELEMENT_NUM_BYTES : \
113 (t) == TYPE_CONTENT || \
114 (t) == TYPE_CONTENT_LIST ? \
115 CONF_CONTENT_NUM_BYTES : 1)
117 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
118 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
119 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
121 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
123 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
124 CONF_ELEMENT_NUM_BYTES)
125 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
126 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
128 // temporary variables used to store pointers to structure members
129 static struct LevelInfo li;
130 static struct ElementInfo xx_ei, yy_ei;
131 static struct ElementChangeInfo xx_change;
132 static struct ElementGroupInfo xx_group;
133 static struct EnvelopeInfo xx_envelope;
134 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
135 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
136 static int xx_num_contents;
137 static int xx_current_change_page;
138 static char xx_default_string_empty[1] = "";
139 static int xx_string_length_unused;
141 struct LevelFileConfigInfo
143 int element; // element for which data is to be stored
144 int save_type; // save data always, never or when changed
145 int data_type; // data type (used internally, not stored)
146 int conf_type; // micro chunk identifier (stored in file)
149 void *value; // variable that holds the data to be stored
150 int default_value; // initial default value for this variable
153 void *value_copy; // variable that holds the data to be copied
154 void *num_entities; // number of entities for multi-byte data
155 int default_num_entities; // default number of entities for this data
156 int max_num_entities; // maximal number of entities for this data
157 char *default_string; // optional default string for string data
160 static struct LevelFileConfigInfo chunk_config_INFO[] =
162 // ---------- values not related to single elements -------------------------
165 -1, SAVE_CONF_ALWAYS,
166 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
167 &li.game_engine_type, GAME_ENGINE_TYPE_RND
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
172 &li.fieldx, STD_LEV_FIELDX
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
177 &li.fieldy, STD_LEV_FIELDY
180 -1, SAVE_CONF_ALWAYS,
181 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
185 -1, SAVE_CONF_ALWAYS,
186 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
191 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
196 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
197 &li.use_step_counter, FALSE
201 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
202 &li.wind_direction_initial, MV_NONE
206 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
207 &li.em_slippery_gems, FALSE
211 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
212 &li.use_custom_template, FALSE
216 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
217 &li.can_move_into_acid_bits, ~0 // default: everything can
221 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
222 &li.dont_collide_with_bits, ~0 // default: always deadly
226 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
227 &li.em_explodes_by_fire, FALSE
231 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
232 &li.score[SC_TIME_BONUS], 1
236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
237 &li.auto_exit_sokoban, FALSE
241 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
242 &li.auto_count_gems, FALSE
246 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
247 &li.solved_by_one_player, FALSE
251 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
252 &li.time_score_base, 1
256 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
257 &li.rate_time_over_score, FALSE
261 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
262 &li.bd_intermission, FALSE
266 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
267 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
271 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
272 &li.bd_pal_timing, FALSE
276 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
277 &li.bd_cycle_delay_ms, 160
281 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
282 &li.bd_cycle_delay_c64, 0
286 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
287 &li.bd_hatching_delay_cycles, 21
291 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
292 &li.bd_hatching_delay_seconds, 2
296 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
297 &li.bd_line_shifting_borders, FALSE
301 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
302 &li.bd_scan_first_and_last_row, TRUE
306 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
307 &li.bd_short_explosions, TRUE
311 TYPE_INTEGER, CONF_VALUE_8_BIT(23),
312 &li.bd_cave_random_seed_c64, 0
316 TYPE_INTEGER, CONF_VALUE_32_BIT(3),
317 &li.bd_color_b, GD_C64_COLOR(0)
321 TYPE_INTEGER, CONF_VALUE_32_BIT(4),
322 &li.bd_color_0, GD_C64_COLOR(0)
326 TYPE_INTEGER, CONF_VALUE_32_BIT(5),
327 &li.bd_color_1, GD_C64_COLOR(8)
331 TYPE_INTEGER, CONF_VALUE_32_BIT(6),
332 &li.bd_color_2, GD_C64_COLOR(11)
336 TYPE_INTEGER, CONF_VALUE_32_BIT(7),
337 &li.bd_color_3, GD_C64_COLOR(1)
341 TYPE_INTEGER, CONF_VALUE_32_BIT(8),
342 &li.bd_color_4, GD_C64_COLOR(5)
346 TYPE_INTEGER, CONF_VALUE_32_BIT(9),
347 &li.bd_color_5, GD_C64_COLOR(6)
357 static struct LevelFileConfigInfo chunk_config_ELEM[] =
359 // (these values are the same for each player)
362 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
363 &li.block_last_field, FALSE // default case for EM levels
367 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
368 &li.sp_block_last_field, TRUE // default case for SP levels
372 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
373 &li.instant_relocation, FALSE
377 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
378 &li.can_pass_to_walkable, FALSE
382 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
383 &li.block_snap_field, TRUE
387 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
388 &li.continuous_snapping, TRUE
392 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
393 &li.shifted_relocation, FALSE
397 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
398 &li.lazy_relocation, FALSE
402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
403 &li.finish_dig_collect, TRUE
407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
408 &li.keep_walkable_ce, FALSE
411 // (these values are different for each player)
414 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
415 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
419 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
420 &li.initial_player_gravity[0], FALSE
424 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
425 &li.use_start_element[0], FALSE
429 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
430 &li.start_element[0], EL_PLAYER_1
434 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
435 &li.use_artwork_element[0], FALSE
439 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
440 &li.artwork_element[0], EL_PLAYER_1
444 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
445 &li.use_explosion_element[0], FALSE
449 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
450 &li.explosion_element[0], EL_PLAYER_1
454 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
455 &li.use_initial_inventory[0], FALSE
459 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
460 &li.initial_inventory_size[0], 1
464 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
465 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
466 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
471 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
472 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
476 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
477 &li.initial_player_gravity[1], FALSE
481 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
482 &li.use_start_element[1], FALSE
486 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
487 &li.start_element[1], EL_PLAYER_2
491 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
492 &li.use_artwork_element[1], FALSE
496 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
497 &li.artwork_element[1], EL_PLAYER_2
501 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
502 &li.use_explosion_element[1], FALSE
506 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
507 &li.explosion_element[1], EL_PLAYER_2
511 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
512 &li.use_initial_inventory[1], FALSE
516 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
517 &li.initial_inventory_size[1], 1
521 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
522 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
523 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
528 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
529 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
533 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
534 &li.initial_player_gravity[2], FALSE
538 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
539 &li.use_start_element[2], FALSE
543 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
544 &li.start_element[2], EL_PLAYER_3
548 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
549 &li.use_artwork_element[2], FALSE
553 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
554 &li.artwork_element[2], EL_PLAYER_3
558 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
559 &li.use_explosion_element[2], FALSE
563 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
564 &li.explosion_element[2], EL_PLAYER_3
568 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
569 &li.use_initial_inventory[2], FALSE
573 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
574 &li.initial_inventory_size[2], 1
578 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
579 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
580 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
585 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
586 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
590 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
591 &li.initial_player_gravity[3], FALSE
595 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
596 &li.use_start_element[3], FALSE
600 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
601 &li.start_element[3], EL_PLAYER_4
605 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
606 &li.use_artwork_element[3], FALSE
610 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
611 &li.artwork_element[3], EL_PLAYER_4
615 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
616 &li.use_explosion_element[3], FALSE
620 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
621 &li.explosion_element[3], EL_PLAYER_4
625 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
626 &li.use_initial_inventory[3], FALSE
630 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
631 &li.initial_inventory_size[3], 1
635 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
636 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
637 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
640 // (these values are only valid for BD style levels)
641 // (some values for BD style amoeba following below)
644 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
645 &li.bd_diagonal_movements, FALSE
649 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
650 &li.bd_topmost_player_active, TRUE
654 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
655 &li.bd_pushing_prob, 25
659 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
660 &li.bd_pushing_prob_with_sweet, 100
664 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
665 &li.bd_push_mega_rock_with_sweet, FALSE
669 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
670 &li.bd_snap_element, EL_EMPTY
675 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
676 &li.bd_sand_looks_like, EL_BDX_SAND_1
681 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
682 &li.bd_rock_turns_to_on_falling, EL_BDX_ROCK_FALLING
686 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
687 &li.bd_rock_turns_to_on_impact, EL_BDX_ROCK
692 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
693 &li.score[SC_DIAMOND_EXTRA], 20
697 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
698 &li.bd_diamond_turns_to_on_falling, EL_BDX_DIAMOND_FALLING
702 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
703 &li.bd_diamond_turns_to_on_impact, EL_BDX_DIAMOND
707 EL_BDX_FIREFLY_1, -1,
708 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
709 &li.bd_firefly_1_explodes_to, EL_BDX_EXPLODING_1
713 EL_BDX_FIREFLY_2, -1,
714 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
715 &li.bd_firefly_2_explodes_to, EL_BDX_EXPLODING_1
719 EL_BDX_BUTTERFLY_1, -1,
720 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
721 &li.bd_butterfly_1_explodes_to, EL_BDX_DIAMOND_GROWING_1
725 EL_BDX_BUTTERFLY_2, -1,
726 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
727 &li.bd_butterfly_2_explodes_to, EL_BDX_DIAMOND_GROWING_1
732 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
733 &li.bd_stonefly_explodes_to, EL_BDX_ROCK_GROWING_1
737 EL_BDX_DRAGONFLY, -1,
738 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
739 &li.bd_dragonfly_explodes_to, EL_BDX_EXPLODING_1
743 EL_BDX_DIAMOND_GROWING_5, -1,
744 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
745 &li.bd_diamond_birth_turns_to, EL_BDX_DIAMOND
749 EL_BDX_BOMB_EXPLODING_4, -1,
750 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
751 &li.bd_bomb_explosion_turns_to, EL_BDX_WALL
755 EL_BDX_NITRO_PACK_EXPLODING_4, -1,
756 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
757 &li.bd_nitro_explosion_turns_to, EL_EMPTY
761 EL_BDX_EXPLODING_5, -1,
762 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
763 &li.bd_explosion_turns_to, EL_EMPTY
767 EL_BDX_MAGIC_WALL, -1,
768 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
769 &li.bd_magic_wall_wait_hatching, FALSE
772 EL_BDX_MAGIC_WALL, -1,
773 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
774 &li.bd_magic_wall_stops_amoeba, TRUE
777 EL_BDX_MAGIC_WALL, -1,
778 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
779 &li.bd_magic_wall_zero_infinite, TRUE
782 EL_BDX_MAGIC_WALL, -1,
783 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
784 &li.bd_magic_wall_break_scan, FALSE
787 EL_BDX_MAGIC_WALL, -1,
788 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
789 &li.bd_magic_wall_time, 999
792 EL_BDX_MAGIC_WALL, -1,
793 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
794 &li.bd_magic_wall_diamond_to, EL_BDX_ROCK_FALLING
797 EL_BDX_MAGIC_WALL, -1,
798 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
799 &li.bd_magic_wall_rock_to, EL_BDX_DIAMOND_FALLING
802 EL_BDX_MAGIC_WALL, -1,
803 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
804 &li.bd_magic_wall_mega_rock_to, EL_BDX_NITRO_PACK_FALLING
807 EL_BDX_MAGIC_WALL, -1,
808 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
809 &li.bd_magic_wall_nut_to, EL_BDX_NUT_FALLING
812 EL_BDX_MAGIC_WALL, -1,
813 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
814 &li.bd_magic_wall_nitro_pack_to, EL_BDX_MEGA_ROCK_FALLING
817 EL_BDX_MAGIC_WALL, -1,
818 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
819 &li.bd_magic_wall_flying_diamond_to, EL_BDX_FLYING_ROCK_FLYING
822 EL_BDX_MAGIC_WALL, -1,
823 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
824 &li.bd_magic_wall_flying_rock_to, EL_BDX_FLYING_DIAMOND_FLYING
829 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
830 &li.bd_clock_extra_time, 30
834 EL_BDX_VOODOO_DOLL, -1,
835 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
836 &li.bd_voodoo_collects_diamonds, FALSE
839 EL_BDX_VOODOO_DOLL, -1,
840 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
841 &li.bd_voodoo_hurt_kills_player, FALSE
844 EL_BDX_VOODOO_DOLL, -1,
845 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
846 &li.bd_voodoo_dies_by_rock, FALSE
849 EL_BDX_VOODOO_DOLL, -1,
850 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
851 &li.bd_voodoo_vanish_by_explosion, TRUE
854 EL_BDX_VOODOO_DOLL, -1,
855 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
856 &li.bd_voodoo_penalty_time, 30
861 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
862 &li.bd_slime_is_predictable, TRUE
866 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
867 &li.bd_slime_permeability_rate, 100
871 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
872 &li.bd_slime_permeability_bits_c64, 0
876 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
877 &li.bd_slime_random_seed_c64, -1
881 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
882 &li.bd_slime_eats_element_1, EL_BDX_DIAMOND
886 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
887 &li.bd_slime_converts_to_element_1, EL_BDX_DIAMOND_FALLING
891 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
892 &li.bd_slime_eats_element_2, EL_BDX_ROCK
896 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
897 &li.bd_slime_converts_to_element_2, EL_BDX_ROCK_FALLING
901 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
902 &li.bd_slime_eats_element_3, EL_BDX_NUT
906 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
907 &li.bd_slime_converts_to_element_3, EL_BDX_NUT_FALLING
912 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
913 &li.bd_acid_eats_element, EL_BDX_SAND_1
917 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
918 &li.bd_acid_spread_rate, 3
922 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
923 &li.bd_acid_turns_to_element, EL_BDX_EXPLODING_3
928 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
929 &li.bd_biter_move_delay, 0
933 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
934 &li.bd_biter_eats_element, EL_BDX_DIAMOND
939 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
940 &li.bd_bladder_converts_by_element, EL_BDX_VOODOO_DOLL
944 EL_BDX_EXPANDABLE_WALL_ANY, -1,
945 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
946 &li.bd_change_expanding_wall, FALSE
949 EL_BDX_EXPANDABLE_WALL_ANY, -1,
950 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
951 &li.bd_expanding_wall_looks_like, EL_BDX_WALL
955 EL_BDX_REPLICATOR, -1,
956 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
957 &li.bd_replicators_active, TRUE
960 EL_BDX_REPLICATOR, -1,
961 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
962 &li.bd_replicator_create_delay, 4
966 EL_BDX_CONVEYOR_LEFT, -1,
967 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
968 &li.bd_conveyor_belts_active, TRUE
971 EL_BDX_CONVEYOR_LEFT, -1,
972 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
973 &li.bd_conveyor_belts_changed, FALSE
978 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
979 &li.bd_water_cannot_flow_down, FALSE
984 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
985 &li.bd_nut_content, EL_BDX_NUT_BREAKING_1
989 EL_BDX_PNEUMATIC_HAMMER, -1,
990 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
991 &li.bd_hammer_walls_break_delay, 5
994 EL_BDX_PNEUMATIC_HAMMER, -1,
995 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
996 &li.bd_hammer_walls_reappear, FALSE
999 EL_BDX_PNEUMATIC_HAMMER, -1,
1000 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1001 &li.bd_hammer_walls_reappear_delay, 100
1005 EL_BDX_ROCKET_LAUNCHER, -1,
1006 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1007 &li.bd_infinite_rockets, FALSE
1011 EL_BDX_SKELETON, -1,
1012 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1013 &li.bd_num_skeletons_needed_for_pot, 5
1016 EL_BDX_SKELETON, -1,
1017 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1018 &li.bd_skeleton_worth_num_diamonds, 0
1022 EL_BDX_CREATURE_SWITCH, -1,
1023 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1024 &li.bd_creatures_start_backwards, FALSE
1027 EL_BDX_CREATURE_SWITCH, -1,
1028 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1029 &li.bd_creatures_turn_on_hatching, FALSE
1032 EL_BDX_CREATURE_SWITCH, -1,
1033 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1034 &li.bd_creatures_auto_turn_delay, 0
1038 EL_BDX_GRAVITY_SWITCH, -1,
1039 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1040 &li.bd_gravity_direction, GD_MV_DOWN
1043 EL_BDX_GRAVITY_SWITCH, -1,
1044 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1045 &li.bd_gravity_switch_active, FALSE
1048 EL_BDX_GRAVITY_SWITCH, -1,
1049 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1050 &li.bd_gravity_switch_delay, 10
1053 EL_BDX_GRAVITY_SWITCH, -1,
1054 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1055 &li.bd_gravity_affects_all, TRUE
1058 // (the following values are related to various game elements)
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1063 &li.score[SC_EMERALD], 10
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1069 &li.score[SC_DIAMOND], 10
1074 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1075 &li.score[SC_BUG], 10
1080 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1081 &li.score[SC_SPACESHIP], 10
1086 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1087 &li.score[SC_PACMAN], 10
1092 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1093 &li.score[SC_NUT], 10
1098 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1099 &li.score[SC_DYNAMITE], 10
1104 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1105 &li.score[SC_KEY], 10
1110 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1111 &li.score[SC_PEARL], 10
1116 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1117 &li.score[SC_CRYSTAL], 10
1122 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1123 &li.amoeba_content, EL_DIAMOND
1127 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1128 &li.amoeba_speed, 10
1132 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1133 &li.grow_into_diggable, TRUE
1137 EL_BDX_AMOEBA_1, -1,
1138 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1139 &li.bd_amoeba_1_threshold_too_big, 200
1142 EL_BDX_AMOEBA_1, -1,
1143 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1144 &li.bd_amoeba_1_slow_growth_time, 200
1147 EL_BDX_AMOEBA_1, -1,
1148 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1149 &li.bd_amoeba_1_content_too_big, EL_BDX_ROCK
1152 EL_BDX_AMOEBA_1, -1,
1153 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
1154 &li.bd_amoeba_1_content_enclosed, EL_BDX_DIAMOND
1157 EL_BDX_AMOEBA_1, -1,
1158 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1159 &li.bd_amoeba_1_slow_growth_rate, 3
1162 EL_BDX_AMOEBA_1, -1,
1163 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1164 &li.bd_amoeba_1_fast_growth_rate, 25
1167 EL_BDX_AMOEBA_1, -1,
1168 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1169 &li.bd_amoeba_wait_for_hatching, FALSE
1172 EL_BDX_AMOEBA_1, -1,
1173 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1174 &li.bd_amoeba_start_immediately, TRUE
1178 EL_BDX_AMOEBA_2, -1,
1179 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1180 &li.bd_amoeba_2_threshold_too_big, 200
1183 EL_BDX_AMOEBA_2, -1,
1184 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1185 &li.bd_amoeba_2_slow_growth_time, 200
1188 EL_BDX_AMOEBA_2, -1,
1189 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1190 &li.bd_amoeba_2_content_too_big, EL_BDX_ROCK
1193 EL_BDX_AMOEBA_2, -1,
1194 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
1195 &li.bd_amoeba_2_content_enclosed, EL_BDX_DIAMOND
1198 EL_BDX_AMOEBA_2, -1,
1199 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1200 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1203 EL_BDX_AMOEBA_2, -1,
1204 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1205 &li.bd_amoeba_2_content_looks_like, EL_BDX_AMOEBA_2
1208 EL_BDX_AMOEBA_2, -1,
1209 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1210 &li.bd_amoeba_2_slow_growth_rate, 3
1213 EL_BDX_AMOEBA_2, -1,
1214 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1215 &li.bd_amoeba_2_fast_growth_rate, 25
1218 EL_BDX_AMOEBA_2, -1,
1219 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1220 &li.bd_amoeba_2_explode_by_amoeba, TRUE
1225 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1226 &li.yamyam_content, EL_ROCK, NULL,
1227 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1231 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1232 &li.score[SC_YAMYAM], 10
1237 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1238 &li.score[SC_ROBOT], 10
1242 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1248 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1254 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1255 &li.time_magic_wall, 10
1259 EL_GAME_OF_LIFE, -1,
1260 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1261 &li.game_of_life[0], 2
1264 EL_GAME_OF_LIFE, -1,
1265 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1266 &li.game_of_life[1], 3
1269 EL_GAME_OF_LIFE, -1,
1270 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1271 &li.game_of_life[2], 3
1274 EL_GAME_OF_LIFE, -1,
1275 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1276 &li.game_of_life[3], 3
1279 EL_GAME_OF_LIFE, -1,
1280 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1281 &li.use_life_bugs, FALSE
1286 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1291 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1296 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1301 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1306 EL_TIMEGATE_SWITCH, -1,
1307 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1308 &li.time_timegate, 10
1312 EL_LIGHT_SWITCH_ACTIVE, -1,
1313 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1318 EL_SHIELD_NORMAL, -1,
1319 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1320 &li.shield_normal_time, 10
1323 EL_SHIELD_NORMAL, -1,
1324 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1325 &li.score[SC_SHIELD], 10
1329 EL_SHIELD_DEADLY, -1,
1330 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1331 &li.shield_deadly_time, 10
1334 EL_SHIELD_DEADLY, -1,
1335 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1336 &li.score[SC_SHIELD], 10
1341 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1346 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1347 &li.extra_time_score, 10
1351 EL_TIME_ORB_FULL, -1,
1352 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1353 &li.time_orb_time, 10
1356 EL_TIME_ORB_FULL, -1,
1357 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1358 &li.use_time_orb_bug, FALSE
1363 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1364 &li.use_spring_bug, FALSE
1369 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1370 &li.android_move_time, 10
1374 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1375 &li.android_clone_time, 10
1378 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1379 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1380 &li.android_clone_element[0], EL_EMPTY, NULL,
1381 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1385 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1386 &li.android_clone_element[0], EL_EMPTY, NULL,
1387 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1392 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1393 &li.lenses_score, 10
1397 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1402 EL_EMC_MAGNIFIER, -1,
1403 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1404 &li.magnify_score, 10
1407 EL_EMC_MAGNIFIER, -1,
1408 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1409 &li.magnify_time, 10
1413 EL_EMC_MAGIC_BALL, -1,
1414 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1418 EL_EMC_MAGIC_BALL, -1,
1419 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1420 &li.ball_random, FALSE
1423 EL_EMC_MAGIC_BALL, -1,
1424 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1425 &li.ball_active_initial, FALSE
1428 EL_EMC_MAGIC_BALL, -1,
1429 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1430 &li.ball_content, EL_EMPTY, NULL,
1431 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1435 EL_SOKOBAN_FIELD_EMPTY, -1,
1436 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1437 &li.sb_fields_needed, TRUE
1441 EL_SOKOBAN_OBJECT, -1,
1442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1443 &li.sb_objects_needed, TRUE
1448 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1449 &li.mm_laser_red, FALSE
1453 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1454 &li.mm_laser_green, FALSE
1458 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1459 &li.mm_laser_blue, TRUE
1464 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1465 &li.df_laser_red, TRUE
1469 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1470 &li.df_laser_green, TRUE
1474 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1475 &li.df_laser_blue, FALSE
1479 EL_MM_FUSE_ACTIVE, -1,
1480 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1481 &li.mm_time_fuse, 25
1485 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1486 &li.mm_time_bomb, 75
1490 EL_MM_GRAY_BALL, -1,
1491 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1492 &li.mm_time_ball, 75
1495 EL_MM_GRAY_BALL, -1,
1496 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1497 &li.mm_ball_choice_mode, ANIM_RANDOM
1500 EL_MM_GRAY_BALL, -1,
1501 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1502 &li.mm_ball_content, EL_EMPTY, NULL,
1503 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1506 EL_MM_GRAY_BALL, -1,
1507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1508 &li.rotate_mm_ball_content, TRUE
1511 EL_MM_GRAY_BALL, -1,
1512 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1513 &li.explode_mm_ball, FALSE
1517 EL_MM_STEEL_BLOCK, -1,
1518 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1519 &li.mm_time_block, 75
1522 EL_MM_LIGHTBALL, -1,
1523 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1524 &li.score[SC_ELEM_BONUS], 10
1534 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1538 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1539 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1543 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1544 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1549 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1550 &xx_envelope.autowrap, FALSE
1554 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1555 &xx_envelope.centered, FALSE
1560 TYPE_STRING, CONF_VALUE_BYTES(1),
1561 &xx_envelope.text, -1, NULL,
1562 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1563 &xx_default_string_empty[0]
1573 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1577 TYPE_STRING, CONF_VALUE_BYTES(1),
1578 &xx_ei.description[0], -1,
1579 &yy_ei.description[0],
1580 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1581 &xx_default_description[0]
1586 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1587 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1588 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1590 #if ENABLE_RESERVED_CODE
1591 // (reserved for later use)
1594 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1595 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1596 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1602 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1603 &xx_ei.use_gfx_element, FALSE,
1604 &yy_ei.use_gfx_element
1608 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1609 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1610 &yy_ei.gfx_element_initial
1615 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1616 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1617 &yy_ei.access_direction
1622 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1623 &xx_ei.collect_score_initial, 10,
1624 &yy_ei.collect_score_initial
1628 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1629 &xx_ei.collect_count_initial, 1,
1630 &yy_ei.collect_count_initial
1635 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1636 &xx_ei.ce_value_fixed_initial, 0,
1637 &yy_ei.ce_value_fixed_initial
1641 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1642 &xx_ei.ce_value_random_initial, 0,
1643 &yy_ei.ce_value_random_initial
1647 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1648 &xx_ei.use_last_ce_value, FALSE,
1649 &yy_ei.use_last_ce_value
1654 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1655 &xx_ei.push_delay_fixed, 8,
1656 &yy_ei.push_delay_fixed
1660 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1661 &xx_ei.push_delay_random, 8,
1662 &yy_ei.push_delay_random
1666 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1667 &xx_ei.drop_delay_fixed, 0,
1668 &yy_ei.drop_delay_fixed
1672 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1673 &xx_ei.drop_delay_random, 0,
1674 &yy_ei.drop_delay_random
1678 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1679 &xx_ei.move_delay_fixed, 0,
1680 &yy_ei.move_delay_fixed
1684 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1685 &xx_ei.move_delay_random, 0,
1686 &yy_ei.move_delay_random
1690 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1691 &xx_ei.step_delay_fixed, 0,
1692 &yy_ei.step_delay_fixed
1696 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1697 &xx_ei.step_delay_random, 0,
1698 &yy_ei.step_delay_random
1703 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1704 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1709 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1710 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1711 &yy_ei.move_direction_initial
1715 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1716 &xx_ei.move_stepsize, TILEX / 8,
1717 &yy_ei.move_stepsize
1722 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1723 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1724 &yy_ei.move_enter_element
1728 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1729 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1730 &yy_ei.move_leave_element
1734 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1735 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1736 &yy_ei.move_leave_type
1741 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1742 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1743 &yy_ei.slippery_type
1748 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1749 &xx_ei.explosion_type, EXPLODES_3X3,
1750 &yy_ei.explosion_type
1754 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1755 &xx_ei.explosion_delay, 16,
1756 &yy_ei.explosion_delay
1760 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1761 &xx_ei.ignition_delay, 8,
1762 &yy_ei.ignition_delay
1767 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1768 &xx_ei.content, EL_EMPTY_SPACE,
1770 &xx_num_contents, 1, 1
1773 // ---------- "num_change_pages" must be the last entry ---------------------
1776 -1, SAVE_CONF_ALWAYS,
1777 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1778 &xx_ei.num_change_pages, 1,
1779 &yy_ei.num_change_pages
1790 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1792 // ---------- "current_change_page" must be the first entry -----------------
1795 -1, SAVE_CONF_ALWAYS,
1796 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1797 &xx_current_change_page, -1
1800 // ---------- (the remaining entries can be in any order) -------------------
1804 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1805 &xx_change.can_change, FALSE
1810 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1811 &xx_event_bits[0], 0
1815 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1816 &xx_event_bits[1], 0
1821 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1822 &xx_change.trigger_player, CH_PLAYER_ANY
1826 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1827 &xx_change.trigger_side, CH_SIDE_ANY
1831 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1832 &xx_change.trigger_page, CH_PAGE_ANY
1837 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1838 &xx_change.target_element, EL_EMPTY_SPACE
1843 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1844 &xx_change.delay_fixed, 0
1848 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1849 &xx_change.delay_random, 0
1853 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1854 &xx_change.delay_frames, FRAMES_PER_SECOND
1859 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1860 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1865 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1866 &xx_change.explode, FALSE
1870 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1871 &xx_change.use_target_content, FALSE
1875 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1876 &xx_change.only_if_complete, FALSE
1880 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1881 &xx_change.use_random_replace, FALSE
1885 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1886 &xx_change.random_percentage, 100
1890 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1891 &xx_change.replace_when, CP_WHEN_EMPTY
1896 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1897 &xx_change.has_action, FALSE
1901 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1902 &xx_change.action_type, CA_NO_ACTION
1906 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1907 &xx_change.action_mode, CA_MODE_UNDEFINED
1911 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1912 &xx_change.action_arg, CA_ARG_UNDEFINED
1917 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1918 &xx_change.action_element, EL_EMPTY_SPACE
1923 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1924 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1925 &xx_num_contents, 1, 1
1935 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1939 TYPE_STRING, CONF_VALUE_BYTES(1),
1940 &xx_ei.description[0], -1, NULL,
1941 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1942 &xx_default_description[0]
1947 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1948 &xx_ei.use_gfx_element, FALSE
1952 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1953 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1958 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1959 &xx_group.choice_mode, ANIM_RANDOM
1964 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1965 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1966 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1976 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1980 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1981 &xx_ei.use_gfx_element, FALSE
1985 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1986 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1996 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
2000 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
2001 &li.block_snap_field, TRUE
2005 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
2006 &li.continuous_snapping, TRUE
2010 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
2011 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
2015 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
2016 &li.use_start_element[0], FALSE
2020 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
2021 &li.start_element[0], EL_PLAYER_1
2025 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
2026 &li.use_artwork_element[0], FALSE
2030 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
2031 &li.artwork_element[0], EL_PLAYER_1
2035 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
2036 &li.use_explosion_element[0], FALSE
2040 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
2041 &li.explosion_element[0], EL_PLAYER_1
2056 filetype_id_list[] =
2058 { LEVEL_FILE_TYPE_RND, "RND" },
2059 { LEVEL_FILE_TYPE_BD, "BD" },
2060 { LEVEL_FILE_TYPE_EM, "EM" },
2061 { LEVEL_FILE_TYPE_SP, "SP" },
2062 { LEVEL_FILE_TYPE_DX, "DX" },
2063 { LEVEL_FILE_TYPE_SB, "SB" },
2064 { LEVEL_FILE_TYPE_DC, "DC" },
2065 { LEVEL_FILE_TYPE_MM, "MM" },
2066 { LEVEL_FILE_TYPE_MM, "DF" },
2071 // ============================================================================
2072 // level file functions
2073 // ============================================================================
2075 static boolean check_special_flags(char *flag)
2077 if (strEqual(options.special_flags, flag) ||
2078 strEqual(leveldir_current->special_flags, flag))
2084 static struct DateInfo getCurrentDate(void)
2086 time_t epoch_seconds = time(NULL);
2087 struct tm *now = localtime(&epoch_seconds);
2088 struct DateInfo date;
2090 date.year = now->tm_year + 1900;
2091 date.month = now->tm_mon + 1;
2092 date.day = now->tm_mday;
2094 date.src = DATE_SRC_CLOCK;
2099 static void resetEventFlags(struct ElementChangeInfo *change)
2103 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2104 change->has_event[i] = FALSE;
2107 static void resetEventBits(void)
2111 for (i = 0; i < NUM_CE_BITFIELDS; i++)
2112 xx_event_bits[i] = 0;
2115 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2119 /* important: only change event flag if corresponding event bit is set
2120 (this is because all xx_event_bits[] values are loaded separately,
2121 and all xx_event_bits[] values are set back to zero before loading
2122 another value xx_event_bits[x] (each value representing 32 flags)) */
2124 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2125 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2126 change->has_event[i] = TRUE;
2129 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2133 /* in contrast to the above function setEventFlagsFromEventBits(), it
2134 would also be possible to set all bits in xx_event_bits[] to 0 or 1
2135 depending on the corresponding change->has_event[i] values here, as
2136 all xx_event_bits[] values are reset in resetEventBits() before */
2138 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2139 if (change->has_event[i])
2140 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2143 static char *getDefaultElementDescription(struct ElementInfo *ei)
2145 static char description[MAX_ELEMENT_NAME_LEN + 1];
2146 char *default_description = (ei->custom_description != NULL ?
2147 ei->custom_description :
2148 ei->editor_description);
2151 // always start with reliable default values
2152 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2153 description[i] = '\0';
2155 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2156 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2158 return &description[0];
2161 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2163 char *default_description = getDefaultElementDescription(ei);
2166 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2167 ei->description[i] = default_description[i];
2170 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2174 for (i = 0; conf[i].data_type != -1; i++)
2176 int default_value = conf[i].default_value;
2177 int data_type = conf[i].data_type;
2178 int conf_type = conf[i].conf_type;
2179 int byte_mask = conf_type & CONF_MASK_BYTES;
2181 if (byte_mask == CONF_MASK_MULTI_BYTES)
2183 int default_num_entities = conf[i].default_num_entities;
2184 int max_num_entities = conf[i].max_num_entities;
2186 *(int *)(conf[i].num_entities) = default_num_entities;
2188 if (data_type == TYPE_STRING)
2190 char *default_string = conf[i].default_string;
2191 char *string = (char *)(conf[i].value);
2193 strncpy(string, default_string, max_num_entities);
2195 else if (data_type == TYPE_ELEMENT_LIST)
2197 int *element_array = (int *)(conf[i].value);
2200 for (j = 0; j < max_num_entities; j++)
2201 element_array[j] = default_value;
2203 else if (data_type == TYPE_CONTENT_LIST)
2205 struct Content *content = (struct Content *)(conf[i].value);
2208 for (c = 0; c < max_num_entities; c++)
2209 for (y = 0; y < 3; y++)
2210 for (x = 0; x < 3; x++)
2211 content[c].e[x][y] = default_value;
2214 else // constant size configuration data (1, 2 or 4 bytes)
2216 if (data_type == TYPE_BOOLEAN)
2217 *(boolean *)(conf[i].value) = default_value;
2219 *(int *) (conf[i].value) = default_value;
2224 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2228 for (i = 0; conf[i].data_type != -1; i++)
2230 int data_type = conf[i].data_type;
2231 int conf_type = conf[i].conf_type;
2232 int byte_mask = conf_type & CONF_MASK_BYTES;
2234 if (byte_mask == CONF_MASK_MULTI_BYTES)
2236 int max_num_entities = conf[i].max_num_entities;
2238 if (data_type == TYPE_STRING)
2240 char *string = (char *)(conf[i].value);
2241 char *string_copy = (char *)(conf[i].value_copy);
2243 strncpy(string_copy, string, max_num_entities);
2245 else if (data_type == TYPE_ELEMENT_LIST)
2247 int *element_array = (int *)(conf[i].value);
2248 int *element_array_copy = (int *)(conf[i].value_copy);
2251 for (j = 0; j < max_num_entities; j++)
2252 element_array_copy[j] = element_array[j];
2254 else if (data_type == TYPE_CONTENT_LIST)
2256 struct Content *content = (struct Content *)(conf[i].value);
2257 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2260 for (c = 0; c < max_num_entities; c++)
2261 for (y = 0; y < 3; y++)
2262 for (x = 0; x < 3; x++)
2263 content_copy[c].e[x][y] = content[c].e[x][y];
2266 else // constant size configuration data (1, 2 or 4 bytes)
2268 if (data_type == TYPE_BOOLEAN)
2269 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2271 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2276 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2280 xx_ei = *ei_from; // copy element data into temporary buffer
2281 yy_ei = *ei_to; // copy element data into temporary buffer
2283 copyConfigFromConfigList(chunk_config_CUSX_base);
2288 // ---------- reinitialize and copy change pages ----------
2290 ei_to->num_change_pages = ei_from->num_change_pages;
2291 ei_to->current_change_page = ei_from->current_change_page;
2293 setElementChangePages(ei_to, ei_to->num_change_pages);
2295 for (i = 0; i < ei_to->num_change_pages; i++)
2296 ei_to->change_page[i] = ei_from->change_page[i];
2298 // ---------- copy group element info ----------
2299 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2300 *ei_to->group = *ei_from->group;
2302 // mark this custom element as modified
2303 ei_to->modified_settings = TRUE;
2306 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2308 int change_page_size = sizeof(struct ElementChangeInfo);
2310 ei->num_change_pages = MAX(1, change_pages);
2313 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2315 if (ei->current_change_page >= ei->num_change_pages)
2316 ei->current_change_page = ei->num_change_pages - 1;
2318 ei->change = &ei->change_page[ei->current_change_page];
2321 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2323 xx_change = *change; // copy change data into temporary buffer
2325 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2327 *change = xx_change;
2329 resetEventFlags(change);
2331 change->direct_action = 0;
2332 change->other_action = 0;
2334 change->pre_change_function = NULL;
2335 change->change_function = NULL;
2336 change->post_change_function = NULL;
2339 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2341 boolean add_border = FALSE;
2344 int x2 = STD_LEV_FIELDX - 1;
2345 int y2 = STD_LEV_FIELDY - 1;
2348 li = *level; // copy level data into temporary buffer
2349 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2350 *level = li; // copy temporary buffer back to level data
2352 setLevelInfoToDefaults_BD();
2353 setLevelInfoToDefaults_EM();
2354 setLevelInfoToDefaults_SP();
2355 setLevelInfoToDefaults_MM();
2357 level->native_bd_level = &native_bd_level;
2358 level->native_em_level = &native_em_level;
2359 level->native_sp_level = &native_sp_level;
2360 level->native_mm_level = &native_mm_level;
2362 level->file_version = FILE_VERSION_ACTUAL;
2363 level->game_version = GAME_VERSION_ACTUAL;
2365 level->creation_date = getCurrentDate();
2367 level->encoding_16bit_field = TRUE;
2368 level->encoding_16bit_yamyam = TRUE;
2369 level->encoding_16bit_amoeba = TRUE;
2371 // clear level name and level author string buffers
2372 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2373 level->name[i] = '\0';
2374 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2375 level->author[i] = '\0';
2377 // set level name and level author to default values
2378 strcpy(level->name, NAMELESS_LEVEL_NAME);
2379 strcpy(level->author, ANONYMOUS_NAME);
2381 // set default game engine type
2382 level->game_engine_type = setup.default_game_engine_type;
2384 // some game engines should have a default playfield with border elements
2385 if (level->game_engine_type == GAME_ENGINE_TYPE_BD ||
2386 level->game_engine_type == GAME_ENGINE_TYPE_EM ||
2387 level->game_engine_type == GAME_ENGINE_TYPE_SP)
2396 // set level playfield to playable default level with player and exit
2397 for (x = 0; x < MAX_LEV_FIELDX; x++)
2399 for (y = 0; y < MAX_LEV_FIELDY; y++)
2401 if (add_border && (x == 0 || x == STD_LEV_FIELDX - 1 ||
2402 y == 0 || y == STD_LEV_FIELDY - 1))
2403 level->field[x][y] = getEngineElement(EL_STEELWALL);
2405 level->field[x][y] = getEngineElement(EL_SAND);
2409 level->field[x1][y1] = getEngineElement(EL_PLAYER_1);
2410 level->field[x2][y2] = getEngineElement(EL_EXIT_CLOSED);
2412 BorderElement = getEngineElement(EL_STEELWALL);
2414 // detect custom elements when loading them
2415 level->file_has_custom_elements = FALSE;
2417 // set random colors for BD style levels according to preferred color type
2418 SetRandomLevelColors_BD(setup.bd_default_color_type);
2420 // set default color type and colors for BD style level colors
2421 SetDefaultLevelColorType_BD();
2422 SetDefaultLevelColors_BD();
2424 // set all bug compatibility flags to "false" => do not emulate this bug
2425 level->use_action_after_change_bug = FALSE;
2427 if (leveldir_current)
2429 // try to determine better author name than 'anonymous'
2430 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2432 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2433 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2437 switch (LEVELCLASS(leveldir_current))
2439 case LEVELCLASS_TUTORIAL:
2440 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2443 case LEVELCLASS_CONTRIB:
2444 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2445 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2448 case LEVELCLASS_PRIVATE:
2449 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2450 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2454 // keep default value
2461 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2463 static boolean clipboard_elements_initialized = FALSE;
2466 InitElementPropertiesStatic();
2468 li = *level; // copy level data into temporary buffer
2469 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2470 *level = li; // copy temporary buffer back to level data
2472 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2475 struct ElementInfo *ei = &element_info[element];
2477 if (element == EL_MM_GRAY_BALL)
2479 struct LevelInfo_MM *level_mm = level->native_mm_level;
2482 for (j = 0; j < level->num_mm_ball_contents; j++)
2483 level->mm_ball_content[j] =
2484 map_element_MM_to_RND(level_mm->ball_content[j]);
2487 // never initialize clipboard elements after the very first time
2488 // (to be able to use clipboard elements between several levels)
2489 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2492 if (IS_ENVELOPE(element))
2494 int envelope_nr = element - EL_ENVELOPE_1;
2496 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2498 level->envelope[envelope_nr] = xx_envelope;
2501 if (IS_CUSTOM_ELEMENT(element) ||
2502 IS_GROUP_ELEMENT(element) ||
2503 IS_INTERNAL_ELEMENT(element))
2505 xx_ei = *ei; // copy element data into temporary buffer
2507 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2512 setElementChangePages(ei, 1);
2513 setElementChangeInfoToDefaults(ei->change);
2515 if (IS_CUSTOM_ELEMENT(element) ||
2516 IS_GROUP_ELEMENT(element))
2518 setElementDescriptionToDefault(ei);
2520 ei->modified_settings = FALSE;
2523 if (IS_CUSTOM_ELEMENT(element) ||
2524 IS_INTERNAL_ELEMENT(element))
2526 // internal values used in level editor
2528 ei->access_type = 0;
2529 ei->access_layer = 0;
2530 ei->access_protected = 0;
2531 ei->walk_to_action = 0;
2532 ei->smash_targets = 0;
2535 ei->can_explode_by_fire = FALSE;
2536 ei->can_explode_smashed = FALSE;
2537 ei->can_explode_impact = FALSE;
2539 ei->current_change_page = 0;
2542 if (IS_GROUP_ELEMENT(element) ||
2543 IS_INTERNAL_ELEMENT(element))
2545 struct ElementGroupInfo *group;
2547 // initialize memory for list of elements in group
2548 if (ei->group == NULL)
2549 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2553 xx_group = *group; // copy group data into temporary buffer
2555 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2560 if (IS_EMPTY_ELEMENT(element) ||
2561 IS_INTERNAL_ELEMENT(element))
2563 xx_ei = *ei; // copy element data into temporary buffer
2565 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2571 clipboard_elements_initialized = TRUE;
2574 static void setLevelInfoToDefaults(struct LevelInfo *level,
2575 boolean level_info_only,
2576 boolean reset_file_status)
2578 setLevelInfoToDefaults_Level(level);
2580 if (!level_info_only)
2581 setLevelInfoToDefaults_Elements(level);
2583 if (reset_file_status)
2585 level->no_valid_file = FALSE;
2586 level->no_level_file = FALSE;
2589 level->changed = FALSE;
2592 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2594 level_file_info->nr = 0;
2595 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2596 level_file_info->packed = FALSE;
2598 setString(&level_file_info->basename, NULL);
2599 setString(&level_file_info->filename, NULL);
2602 int getMappedElement_SB(int, boolean);
2604 static void ActivateLevelTemplate(void)
2608 if (check_special_flags("load_xsb_to_ces"))
2610 // fill smaller playfields with padding "beyond border wall" elements
2611 if (level.fieldx < level_template.fieldx ||
2612 level.fieldy < level_template.fieldy)
2614 short field[level.fieldx][level.fieldy];
2615 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2616 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2617 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2618 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2620 // copy old playfield (which is smaller than the visible area)
2621 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2622 field[x][y] = level.field[x][y];
2624 // fill new, larger playfield with "beyond border wall" elements
2625 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2626 level.field[x][y] = getMappedElement_SB('_', TRUE);
2628 // copy the old playfield to the middle of the new playfield
2629 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2630 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2632 level.fieldx = new_fieldx;
2633 level.fieldy = new_fieldy;
2637 // Currently there is no special action needed to activate the template
2638 // data, because 'element_info' property settings overwrite the original
2639 // level data, while all other variables do not change.
2641 // Exception: 'from_level_template' elements in the original level playfield
2642 // are overwritten with the corresponding elements at the same position in
2643 // playfield from the level template.
2645 for (x = 0; x < level.fieldx; x++)
2646 for (y = 0; y < level.fieldy; y++)
2647 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2648 level.field[x][y] = level_template.field[x][y];
2650 if (check_special_flags("load_xsb_to_ces"))
2652 struct LevelInfo level_backup = level;
2654 // overwrite all individual level settings from template level settings
2655 level = level_template;
2657 // restore level file info
2658 level.file_info = level_backup.file_info;
2660 // restore playfield size
2661 level.fieldx = level_backup.fieldx;
2662 level.fieldy = level_backup.fieldy;
2664 // restore playfield content
2665 for (x = 0; x < level.fieldx; x++)
2666 for (y = 0; y < level.fieldy; y++)
2667 level.field[x][y] = level_backup.field[x][y];
2669 // restore name and author from individual level
2670 strcpy(level.name, level_backup.name);
2671 strcpy(level.author, level_backup.author);
2673 // restore flag "use_custom_template"
2674 level.use_custom_template = level_backup.use_custom_template;
2678 boolean isLevelsetFilename_BD(char *filename)
2680 return (strSuffixLower(filename, ".bd") ||
2681 strSuffixLower(filename, ".bdr") ||
2682 strSuffixLower(filename, ".brc") ||
2683 strSuffixLower(filename, ".gds"));
2686 static boolean checkForPackageFromBasename_BD(char *basename)
2688 // check for native BD level file extensions
2689 if (!isLevelsetFilename_BD(basename))
2692 // check for standard single-level BD files (like "001.bd")
2693 if (strSuffixLower(basename, ".bd") &&
2694 strlen(basename) == 6 &&
2695 basename[0] >= '0' && basename[0] <= '9' &&
2696 basename[1] >= '0' && basename[1] <= '9' &&
2697 basename[2] >= '0' && basename[2] <= '9')
2700 // this is a level package in native BD file format
2704 static char *getLevelFilenameFromBasename(char *basename)
2706 static char *filename = NULL;
2708 checked_free(filename);
2710 filename = getPath2(getCurrentLevelDir(), basename);
2715 static int getFileTypeFromBasename(char *basename)
2717 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2719 static char *filename = NULL;
2720 struct stat file_status;
2722 // ---------- try to determine file type from filename ----------
2724 // check for typical filename of a Supaplex level package file
2725 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2726 return LEVEL_FILE_TYPE_SP;
2728 // check for typical filename of a Diamond Caves II level package file
2729 if (strSuffixLower(basename, ".dc") ||
2730 strSuffixLower(basename, ".dc2"))
2731 return LEVEL_FILE_TYPE_DC;
2733 // check for typical filename of a Sokoban level package file
2734 if (strSuffixLower(basename, ".xsb") &&
2735 strchr(basename, '%') == NULL)
2736 return LEVEL_FILE_TYPE_SB;
2738 // check for typical filename of a Boulder Dash (GDash) level package file
2739 if (checkForPackageFromBasename_BD(basename))
2740 return LEVEL_FILE_TYPE_BD;
2742 // ---------- try to determine file type from filesize ----------
2744 checked_free(filename);
2745 filename = getPath2(getCurrentLevelDir(), basename);
2747 if (stat(filename, &file_status) == 0)
2749 // check for typical filesize of a Supaplex level package file
2750 if (file_status.st_size == 170496)
2751 return LEVEL_FILE_TYPE_SP;
2754 return LEVEL_FILE_TYPE_UNKNOWN;
2757 static int getFileTypeFromMagicBytes(char *filename, int type)
2761 if ((file = openFile(filename, MODE_READ)))
2763 char chunk_name[CHUNK_ID_LEN + 1];
2765 getFileChunkBE(file, chunk_name, NULL);
2767 if (strEqual(chunk_name, "MMII") ||
2768 strEqual(chunk_name, "MIRR"))
2769 type = LEVEL_FILE_TYPE_MM;
2777 static boolean checkForPackageFromBasename(char *basename)
2779 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2780 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2782 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2785 static char *getSingleLevelBasenameExt(int nr, char *extension)
2787 static char basename[MAX_FILENAME_LEN];
2790 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2792 sprintf(basename, "%03d.%s", nr, extension);
2797 static char *getSingleLevelBasename(int nr)
2799 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2802 static char *getPackedLevelBasename(int type)
2804 static char basename[MAX_FILENAME_LEN];
2805 char *directory = getCurrentLevelDir();
2807 DirectoryEntry *dir_entry;
2809 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2811 if ((dir = openDirectory(directory)) == NULL)
2813 Warn("cannot read current level directory '%s'", directory);
2818 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2820 char *entry_basename = dir_entry->basename;
2821 int entry_type = getFileTypeFromBasename(entry_basename);
2823 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2825 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2828 strcpy(basename, entry_basename);
2835 closeDirectory(dir);
2840 static char *getSingleLevelFilename(int nr)
2842 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2845 #if ENABLE_UNUSED_CODE
2846 static char *getPackedLevelFilename(int type)
2848 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2852 char *getDefaultLevelFilename(int nr)
2854 return getSingleLevelFilename(nr);
2857 #if ENABLE_UNUSED_CODE
2858 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2862 lfi->packed = FALSE;
2864 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2865 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2869 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2870 int type, char *format, ...)
2872 static char basename[MAX_FILENAME_LEN];
2875 va_start(ap, format);
2876 vsprintf(basename, format, ap);
2880 lfi->packed = FALSE;
2882 setString(&lfi->basename, basename);
2883 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2886 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2892 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2893 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2896 static int getFiletypeFromID(char *filetype_id)
2898 char *filetype_id_lower;
2899 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2902 if (filetype_id == NULL)
2903 return LEVEL_FILE_TYPE_UNKNOWN;
2905 filetype_id_lower = getStringToLower(filetype_id);
2907 for (i = 0; filetype_id_list[i].id != NULL; i++)
2909 char *id_lower = getStringToLower(filetype_id_list[i].id);
2911 if (strEqual(filetype_id_lower, id_lower))
2912 filetype = filetype_id_list[i].filetype;
2916 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2920 free(filetype_id_lower);
2925 char *getLocalLevelTemplateFilename(void)
2927 return getDefaultLevelFilename(-1);
2930 char *getGlobalLevelTemplateFilename(void)
2932 return getFilenameFromCurrentLevelDirUpward(LEVELTEMPLATE_FILENAME);
2935 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2939 // special case: level number is negative => check for level template file
2942 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2943 getSingleLevelBasename(-1));
2945 // replace local level template filename with global template filename
2946 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2948 // no fallback if template file not existing
2952 // special case: check for file name/pattern specified in "levelinfo.conf"
2953 if (leveldir_current->level_filename != NULL)
2955 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2957 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2958 leveldir_current->level_filename, nr);
2960 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2962 if (fileExists(lfi->filename))
2965 else if (leveldir_current->level_filetype != NULL)
2967 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2969 // check for specified native level file with standard file name
2970 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2971 "%03d.%s", nr, LEVELFILE_EXTENSION);
2972 if (fileExists(lfi->filename))
2976 // check for native Rocks'n'Diamonds level file
2977 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2978 "%03d.%s", nr, LEVELFILE_EXTENSION);
2979 if (fileExists(lfi->filename))
2982 // check for native Boulder Dash level file
2983 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2984 if (fileExists(lfi->filename))
2987 // check for Emerald Mine level file (V1)
2988 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2989 'a' + (nr / 10) % 26, '0' + nr % 10);
2990 if (fileExists(lfi->filename))
2992 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2993 'A' + (nr / 10) % 26, '0' + nr % 10);
2994 if (fileExists(lfi->filename))
2997 // check for Emerald Mine level file (V2 to V5)
2998 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2999 if (fileExists(lfi->filename))
3002 // check for Emerald Mine level file (V6 / single mode)
3003 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
3004 if (fileExists(lfi->filename))
3006 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
3007 if (fileExists(lfi->filename))
3010 // check for Emerald Mine level file (V6 / teamwork mode)
3011 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
3012 if (fileExists(lfi->filename))
3014 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
3015 if (fileExists(lfi->filename))
3018 // check for various packed level file formats
3019 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
3020 if (fileExists(lfi->filename))
3023 // no known level file found -- use default values (and fail later)
3024 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3025 "%03d.%s", nr, LEVELFILE_EXTENSION);
3028 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3030 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3031 lfi->type = getFileTypeFromBasename(lfi->basename);
3033 if (lfi->type == LEVEL_FILE_TYPE_RND)
3034 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3037 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3039 // always start with reliable default values
3040 setFileInfoToDefaults(level_file_info);
3042 level_file_info->nr = nr; // set requested level number
3044 determineLevelFileInfo_Filename(level_file_info);
3045 determineLevelFileInfo_Filetype(level_file_info);
3048 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3049 struct LevelFileInfo *lfi_to)
3051 lfi_to->nr = lfi_from->nr;
3052 lfi_to->type = lfi_from->type;
3053 lfi_to->packed = lfi_from->packed;
3055 setString(&lfi_to->basename, lfi_from->basename);
3056 setString(&lfi_to->filename, lfi_from->filename);
3059 // ----------------------------------------------------------------------------
3060 // functions for loading R'n'D level
3061 // ----------------------------------------------------------------------------
3063 int getMappedElement(int element)
3065 // remap some (historic, now obsolete) elements
3069 case EL_PLAYER_OBSOLETE:
3070 element = EL_PLAYER_1;
3073 case EL_KEY_OBSOLETE:
3077 case EL_EM_KEY_1_FILE_OBSOLETE:
3078 element = EL_EM_KEY_1;
3081 case EL_EM_KEY_2_FILE_OBSOLETE:
3082 element = EL_EM_KEY_2;
3085 case EL_EM_KEY_3_FILE_OBSOLETE:
3086 element = EL_EM_KEY_3;
3089 case EL_EM_KEY_4_FILE_OBSOLETE:
3090 element = EL_EM_KEY_4;
3093 case EL_ENVELOPE_OBSOLETE:
3094 element = EL_ENVELOPE_1;
3102 if (element >= NUM_FILE_ELEMENTS)
3104 Warn("invalid level element %d", element);
3106 element = EL_UNKNOWN;
3114 static int getMappedElementByVersion(int element, int game_version)
3116 // remap some elements due to certain game version
3118 if (game_version <= VERSION_IDENT(2,2,0,0))
3120 // map game font elements
3121 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
3122 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3123 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
3124 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
3127 if (game_version < VERSION_IDENT(3,0,0,0))
3129 // map Supaplex gravity tube elements
3130 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
3131 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3132 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
3133 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
3140 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3142 level->file_version = getFileVersion(file);
3143 level->game_version = getFileVersion(file);
3148 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3150 level->creation_date.year = getFile16BitBE(file);
3151 level->creation_date.month = getFile8Bit(file);
3152 level->creation_date.day = getFile8Bit(file);
3154 level->creation_date.src = DATE_SRC_LEVELFILE;
3159 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3161 int initial_player_stepsize;
3162 int initial_player_gravity;
3165 level->fieldx = getFile8Bit(file);
3166 level->fieldy = getFile8Bit(file);
3168 level->time = getFile16BitBE(file);
3169 level->gems_needed = getFile16BitBE(file);
3171 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3172 level->name[i] = getFile8Bit(file);
3173 level->name[MAX_LEVEL_NAME_LEN] = 0;
3175 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3176 level->score[i] = getFile8Bit(file);
3178 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3179 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3180 for (y = 0; y < 3; y++)
3181 for (x = 0; x < 3; x++)
3182 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3184 level->amoeba_speed = getFile8Bit(file);
3185 level->time_magic_wall = getFile8Bit(file);
3186 level->time_wheel = getFile8Bit(file);
3187 level->amoeba_content = getMappedElement(getFile8Bit(file));
3189 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3192 for (i = 0; i < MAX_PLAYERS; i++)
3193 level->initial_player_stepsize[i] = initial_player_stepsize;
3195 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3197 for (i = 0; i < MAX_PLAYERS; i++)
3198 level->initial_player_gravity[i] = initial_player_gravity;
3200 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3201 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3203 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3205 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3206 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3207 level->can_move_into_acid_bits = getFile32BitBE(file);
3208 level->dont_collide_with_bits = getFile8Bit(file);
3210 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3211 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3213 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3214 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3215 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3217 level->game_engine_type = getFile8Bit(file);
3219 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3224 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3228 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3229 level->name[i] = getFile8Bit(file);
3230 level->name[MAX_LEVEL_NAME_LEN] = 0;
3235 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3239 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3240 level->author[i] = getFile8Bit(file);
3241 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3246 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3249 int chunk_size_expected = level->fieldx * level->fieldy;
3251 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3252 stored with 16-bit encoding (and should be twice as big then).
3253 Even worse, playfield data was stored 16-bit when only yamyam content
3254 contained 16-bit elements and vice versa. */
3256 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3257 chunk_size_expected *= 2;
3259 if (chunk_size_expected != chunk_size)
3261 ReadUnusedBytesFromFile(file, chunk_size);
3262 return chunk_size_expected;
3265 for (y = 0; y < level->fieldy; y++)
3266 for (x = 0; x < level->fieldx; x++)
3267 level->field[x][y] =
3268 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3273 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3276 int header_size = 4;
3277 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3278 int chunk_size_expected = header_size + content_size;
3280 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3281 stored with 16-bit encoding (and should be twice as big then).
3282 Even worse, playfield data was stored 16-bit when only yamyam content
3283 contained 16-bit elements and vice versa. */
3285 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3286 chunk_size_expected += content_size;
3288 if (chunk_size_expected != chunk_size)
3290 ReadUnusedBytesFromFile(file, chunk_size);
3291 return chunk_size_expected;
3295 level->num_yamyam_contents = getFile8Bit(file);
3299 // correct invalid number of content fields -- should never happen
3300 if (level->num_yamyam_contents < 1 ||
3301 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3302 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3304 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3305 for (y = 0; y < 3; y++)
3306 for (x = 0; x < 3; x++)
3307 level->yamyam_content[i].e[x][y] =
3308 getMappedElement(level->encoding_16bit_field ?
3309 getFile16BitBE(file) : getFile8Bit(file));
3313 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3318 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3320 element = getMappedElement(getFile16BitBE(file));
3321 num_contents = getFile8Bit(file);
3323 getFile8Bit(file); // content x size (unused)
3324 getFile8Bit(file); // content y size (unused)
3326 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3328 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3329 for (y = 0; y < 3; y++)
3330 for (x = 0; x < 3; x++)
3331 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3333 // correct invalid number of content fields -- should never happen
3334 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3335 num_contents = STD_ELEMENT_CONTENTS;
3337 if (element == EL_YAMYAM)
3339 level->num_yamyam_contents = num_contents;
3341 for (i = 0; i < num_contents; i++)
3342 for (y = 0; y < 3; y++)
3343 for (x = 0; x < 3; x++)
3344 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3346 else if (element == EL_BD_AMOEBA)
3348 level->amoeba_content = content_array[0][0][0];
3352 Warn("cannot load content for element '%d'", element);
3358 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3364 int chunk_size_expected;
3366 element = getMappedElement(getFile16BitBE(file));
3367 if (!IS_ENVELOPE(element))
3368 element = EL_ENVELOPE_1;
3370 envelope_nr = element - EL_ENVELOPE_1;
3372 envelope_len = getFile16BitBE(file);
3374 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3375 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3377 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3379 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3380 if (chunk_size_expected != chunk_size)
3382 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3383 return chunk_size_expected;
3386 for (i = 0; i < envelope_len; i++)
3387 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3392 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3394 int num_changed_custom_elements = getFile16BitBE(file);
3395 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3398 if (chunk_size_expected != chunk_size)
3400 ReadUnusedBytesFromFile(file, chunk_size - 2);
3401 return chunk_size_expected;
3404 for (i = 0; i < num_changed_custom_elements; i++)
3406 int element = getMappedElement(getFile16BitBE(file));
3407 int properties = getFile32BitBE(file);
3409 if (IS_CUSTOM_ELEMENT(element))
3410 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3412 Warn("invalid custom element number %d", element);
3414 // older game versions that wrote level files with CUS1 chunks used
3415 // different default push delay values (not yet stored in level file)
3416 element_info[element].push_delay_fixed = 2;
3417 element_info[element].push_delay_random = 8;
3420 level->file_has_custom_elements = TRUE;
3425 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3427 int num_changed_custom_elements = getFile16BitBE(file);
3428 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3431 if (chunk_size_expected != chunk_size)
3433 ReadUnusedBytesFromFile(file, chunk_size - 2);
3434 return chunk_size_expected;
3437 for (i = 0; i < num_changed_custom_elements; i++)
3439 int element = getMappedElement(getFile16BitBE(file));
3440 int custom_target_element = getMappedElement(getFile16BitBE(file));
3442 if (IS_CUSTOM_ELEMENT(element))
3443 element_info[element].change->target_element = custom_target_element;
3445 Warn("invalid custom element number %d", element);
3448 level->file_has_custom_elements = TRUE;
3453 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3455 int num_changed_custom_elements = getFile16BitBE(file);
3456 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3459 if (chunk_size_expected != chunk_size)
3461 ReadUnusedBytesFromFile(file, chunk_size - 2);
3462 return chunk_size_expected;
3465 for (i = 0; i < num_changed_custom_elements; i++)
3467 int element = getMappedElement(getFile16BitBE(file));
3468 struct ElementInfo *ei = &element_info[element];
3469 unsigned int event_bits;
3471 if (!IS_CUSTOM_ELEMENT(element))
3473 Warn("invalid custom element number %d", element);
3475 element = EL_INTERNAL_DUMMY;
3478 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3479 ei->description[j] = getFile8Bit(file);
3480 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3482 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3484 // some free bytes for future properties and padding
3485 ReadUnusedBytesFromFile(file, 7);
3487 ei->use_gfx_element = getFile8Bit(file);
3488 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3490 ei->collect_score_initial = getFile8Bit(file);
3491 ei->collect_count_initial = getFile8Bit(file);
3493 ei->push_delay_fixed = getFile16BitBE(file);
3494 ei->push_delay_random = getFile16BitBE(file);
3495 ei->move_delay_fixed = getFile16BitBE(file);
3496 ei->move_delay_random = getFile16BitBE(file);
3498 ei->move_pattern = getFile16BitBE(file);
3499 ei->move_direction_initial = getFile8Bit(file);
3500 ei->move_stepsize = getFile8Bit(file);
3502 for (y = 0; y < 3; y++)
3503 for (x = 0; x < 3; x++)
3504 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3506 // bits 0 - 31 of "has_event[]"
3507 event_bits = getFile32BitBE(file);
3508 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3509 if (event_bits & (1u << j))
3510 ei->change->has_event[j] = TRUE;
3512 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3514 ei->change->delay_fixed = getFile16BitBE(file);
3515 ei->change->delay_random = getFile16BitBE(file);
3516 ei->change->delay_frames = getFile16BitBE(file);
3518 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3520 ei->change->explode = getFile8Bit(file);
3521 ei->change->use_target_content = getFile8Bit(file);
3522 ei->change->only_if_complete = getFile8Bit(file);
3523 ei->change->use_random_replace = getFile8Bit(file);
3525 ei->change->random_percentage = getFile8Bit(file);
3526 ei->change->replace_when = getFile8Bit(file);
3528 for (y = 0; y < 3; y++)
3529 for (x = 0; x < 3; x++)
3530 ei->change->target_content.e[x][y] =
3531 getMappedElement(getFile16BitBE(file));
3533 ei->slippery_type = getFile8Bit(file);
3535 // some free bytes for future properties and padding
3536 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3538 // mark that this custom element has been modified
3539 ei->modified_settings = TRUE;
3542 level->file_has_custom_elements = TRUE;
3547 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3549 struct ElementInfo *ei;
3550 int chunk_size_expected;
3554 // ---------- custom element base property values (96 bytes) ----------------
3556 element = getMappedElement(getFile16BitBE(file));
3558 if (!IS_CUSTOM_ELEMENT(element))
3560 Warn("invalid custom element number %d", element);
3562 ReadUnusedBytesFromFile(file, chunk_size - 2);
3567 ei = &element_info[element];
3569 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3570 ei->description[i] = getFile8Bit(file);
3571 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3573 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3575 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3577 ei->num_change_pages = getFile8Bit(file);
3579 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3580 if (chunk_size_expected != chunk_size)
3582 ReadUnusedBytesFromFile(file, chunk_size - 43);
3583 return chunk_size_expected;
3586 ei->ce_value_fixed_initial = getFile16BitBE(file);
3587 ei->ce_value_random_initial = getFile16BitBE(file);
3588 ei->use_last_ce_value = getFile8Bit(file);
3590 ei->use_gfx_element = getFile8Bit(file);
3591 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3593 ei->collect_score_initial = getFile8Bit(file);
3594 ei->collect_count_initial = getFile8Bit(file);
3596 ei->drop_delay_fixed = getFile8Bit(file);
3597 ei->push_delay_fixed = getFile8Bit(file);
3598 ei->drop_delay_random = getFile8Bit(file);
3599 ei->push_delay_random = getFile8Bit(file);
3600 ei->move_delay_fixed = getFile16BitBE(file);
3601 ei->move_delay_random = getFile16BitBE(file);
3603 // bits 0 - 15 of "move_pattern" ...
3604 ei->move_pattern = getFile16BitBE(file);
3605 ei->move_direction_initial = getFile8Bit(file);
3606 ei->move_stepsize = getFile8Bit(file);
3608 ei->slippery_type = getFile8Bit(file);
3610 for (y = 0; y < 3; y++)
3611 for (x = 0; x < 3; x++)
3612 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3614 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3615 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3616 ei->move_leave_type = getFile8Bit(file);
3618 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3619 ei->move_pattern |= (getFile16BitBE(file) << 16);
3621 ei->access_direction = getFile8Bit(file);
3623 ei->explosion_delay = getFile8Bit(file);
3624 ei->ignition_delay = getFile8Bit(file);
3625 ei->explosion_type = getFile8Bit(file);
3627 // some free bytes for future custom property values and padding
3628 ReadUnusedBytesFromFile(file, 1);
3630 // ---------- change page property values (48 bytes) ------------------------
3632 setElementChangePages(ei, ei->num_change_pages);
3634 for (i = 0; i < ei->num_change_pages; i++)
3636 struct ElementChangeInfo *change = &ei->change_page[i];
3637 unsigned int event_bits;
3639 // always start with reliable default values
3640 setElementChangeInfoToDefaults(change);
3642 // bits 0 - 31 of "has_event[]" ...
3643 event_bits = getFile32BitBE(file);
3644 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3645 if (event_bits & (1u << j))
3646 change->has_event[j] = TRUE;
3648 change->target_element = getMappedElement(getFile16BitBE(file));
3650 change->delay_fixed = getFile16BitBE(file);
3651 change->delay_random = getFile16BitBE(file);
3652 change->delay_frames = getFile16BitBE(file);
3654 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3656 change->explode = getFile8Bit(file);
3657 change->use_target_content = getFile8Bit(file);
3658 change->only_if_complete = getFile8Bit(file);
3659 change->use_random_replace = getFile8Bit(file);
3661 change->random_percentage = getFile8Bit(file);
3662 change->replace_when = getFile8Bit(file);
3664 for (y = 0; y < 3; y++)
3665 for (x = 0; x < 3; x++)
3666 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3668 change->can_change = getFile8Bit(file);
3670 change->trigger_side = getFile8Bit(file);
3672 change->trigger_player = getFile8Bit(file);
3673 change->trigger_page = getFile8Bit(file);
3675 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3676 CH_PAGE_ANY : (1 << change->trigger_page));
3678 change->has_action = getFile8Bit(file);
3679 change->action_type = getFile8Bit(file);
3680 change->action_mode = getFile8Bit(file);
3681 change->action_arg = getFile16BitBE(file);
3683 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3684 event_bits = getFile8Bit(file);
3685 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3686 if (event_bits & (1u << (j - 32)))
3687 change->has_event[j] = TRUE;
3690 // mark this custom element as modified
3691 ei->modified_settings = TRUE;
3693 level->file_has_custom_elements = TRUE;
3698 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3700 struct ElementInfo *ei;
3701 struct ElementGroupInfo *group;
3705 element = getMappedElement(getFile16BitBE(file));
3707 if (!IS_GROUP_ELEMENT(element))
3709 Warn("invalid group element number %d", element);
3711 ReadUnusedBytesFromFile(file, chunk_size - 2);
3716 ei = &element_info[element];
3718 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3719 ei->description[i] = getFile8Bit(file);
3720 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3722 group = element_info[element].group;
3724 group->num_elements = getFile8Bit(file);
3726 ei->use_gfx_element = getFile8Bit(file);
3727 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3729 group->choice_mode = getFile8Bit(file);
3731 // some free bytes for future values and padding
3732 ReadUnusedBytesFromFile(file, 3);
3734 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3735 group->element[i] = getMappedElement(getFile16BitBE(file));
3737 // mark this group element as modified
3738 element_info[element].modified_settings = TRUE;
3740 level->file_has_custom_elements = TRUE;
3745 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3746 int element, int real_element)
3748 int micro_chunk_size = 0;
3749 int conf_type = getFile8Bit(file);
3750 int byte_mask = conf_type & CONF_MASK_BYTES;
3751 boolean element_found = FALSE;
3754 micro_chunk_size += 1;
3756 if (byte_mask == CONF_MASK_MULTI_BYTES)
3758 int num_bytes = getFile16BitBE(file);
3759 byte *buffer = checked_malloc(num_bytes);
3761 ReadBytesFromFile(file, buffer, num_bytes);
3763 for (i = 0; conf[i].data_type != -1; i++)
3765 if (conf[i].element == element &&
3766 conf[i].conf_type == conf_type)
3768 int data_type = conf[i].data_type;
3769 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3770 int max_num_entities = conf[i].max_num_entities;
3772 if (num_entities > max_num_entities)
3774 Warn("truncating number of entities for element %d from %d to %d",
3775 element, num_entities, max_num_entities);
3777 num_entities = max_num_entities;
3780 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3781 data_type == TYPE_CONTENT_LIST))
3783 // for element and content lists, zero entities are not allowed
3784 Warn("found empty list of entities for element %d", element);
3786 // do not set "num_entities" here to prevent reading behind buffer
3788 *(int *)(conf[i].num_entities) = 1; // at least one is required
3792 *(int *)(conf[i].num_entities) = num_entities;
3795 element_found = TRUE;
3797 if (data_type == TYPE_STRING)
3799 char *string = (char *)(conf[i].value);
3802 for (j = 0; j < max_num_entities; j++)
3803 string[j] = (j < num_entities ? buffer[j] : '\0');
3805 else if (data_type == TYPE_ELEMENT_LIST)
3807 int *element_array = (int *)(conf[i].value);
3810 for (j = 0; j < num_entities; j++)
3812 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3814 else if (data_type == TYPE_CONTENT_LIST)
3816 struct Content *content= (struct Content *)(conf[i].value);
3819 for (c = 0; c < num_entities; c++)
3820 for (y = 0; y < 3; y++)
3821 for (x = 0; x < 3; x++)
3822 content[c].e[x][y] =
3823 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3826 element_found = FALSE;
3832 checked_free(buffer);
3834 micro_chunk_size += 2 + num_bytes;
3836 else // constant size configuration data (1, 2 or 4 bytes)
3838 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3839 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3840 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3842 for (i = 0; conf[i].data_type != -1; i++)
3844 if (conf[i].element == element &&
3845 conf[i].conf_type == conf_type)
3847 int data_type = conf[i].data_type;
3849 if (data_type == TYPE_ELEMENT)
3850 value = getMappedElement(value);
3852 if (data_type == TYPE_BOOLEAN)
3853 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3855 *(int *) (conf[i].value) = value;
3857 element_found = TRUE;
3863 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3868 char *error_conf_chunk_bytes =
3869 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3870 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3871 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3872 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3873 int error_element = real_element;
3875 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3876 error_conf_chunk_bytes, error_conf_chunk_token,
3877 error_element, EL_NAME(error_element));
3880 return micro_chunk_size;
3883 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3885 int real_chunk_size = 0;
3887 li = *level; // copy level data into temporary buffer
3889 while (!checkEndOfFile(file))
3891 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3893 if (real_chunk_size >= chunk_size)
3897 *level = li; // copy temporary buffer back to level data
3899 return real_chunk_size;
3902 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3904 int real_chunk_size = 0;
3906 li = *level; // copy level data into temporary buffer
3908 while (!checkEndOfFile(file))
3910 int element = getMappedElement(getFile16BitBE(file));
3912 real_chunk_size += 2;
3913 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3915 if (real_chunk_size >= chunk_size)
3919 *level = li; // copy temporary buffer back to level data
3921 return real_chunk_size;
3924 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3926 int real_chunk_size = 0;
3928 li = *level; // copy level data into temporary buffer
3930 while (!checkEndOfFile(file))
3932 int element = getMappedElement(getFile16BitBE(file));
3934 real_chunk_size += 2;
3935 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3937 if (real_chunk_size >= chunk_size)
3941 *level = li; // copy temporary buffer back to level data
3943 return real_chunk_size;
3946 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3948 int element = getMappedElement(getFile16BitBE(file));
3949 int envelope_nr = element - EL_ENVELOPE_1;
3950 int real_chunk_size = 2;
3952 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3954 while (!checkEndOfFile(file))
3956 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3959 if (real_chunk_size >= chunk_size)
3963 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3965 return real_chunk_size;
3968 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3970 int element = getMappedElement(getFile16BitBE(file));
3971 int real_chunk_size = 2;
3972 struct ElementInfo *ei = &element_info[element];
3975 xx_ei = *ei; // copy element data into temporary buffer
3977 xx_ei.num_change_pages = -1;
3979 while (!checkEndOfFile(file))
3981 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3983 if (xx_ei.num_change_pages != -1)
3986 if (real_chunk_size >= chunk_size)
3992 if (ei->num_change_pages == -1)
3994 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3997 ei->num_change_pages = 1;
3999 setElementChangePages(ei, 1);
4000 setElementChangeInfoToDefaults(ei->change);
4002 return real_chunk_size;
4005 // initialize number of change pages stored for this custom element
4006 setElementChangePages(ei, ei->num_change_pages);
4007 for (i = 0; i < ei->num_change_pages; i++)
4008 setElementChangeInfoToDefaults(&ei->change_page[i]);
4010 // start with reading properties for the first change page
4011 xx_current_change_page = 0;
4013 while (!checkEndOfFile(file))
4015 // level file might contain invalid change page number
4016 if (xx_current_change_page >= ei->num_change_pages)
4019 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4021 xx_change = *change; // copy change data into temporary buffer
4023 resetEventBits(); // reset bits; change page might have changed
4025 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4028 *change = xx_change;
4030 setEventFlagsFromEventBits(change);
4032 if (real_chunk_size >= chunk_size)
4036 level->file_has_custom_elements = TRUE;
4038 return real_chunk_size;
4041 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4043 int element = getMappedElement(getFile16BitBE(file));
4044 int real_chunk_size = 2;
4045 struct ElementInfo *ei = &element_info[element];
4046 struct ElementGroupInfo *group = ei->group;
4051 xx_ei = *ei; // copy element data into temporary buffer
4052 xx_group = *group; // copy group data into temporary buffer
4054 while (!checkEndOfFile(file))
4056 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4059 if (real_chunk_size >= chunk_size)
4066 level->file_has_custom_elements = TRUE;
4068 return real_chunk_size;
4071 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4073 int element = getMappedElement(getFile16BitBE(file));
4074 int real_chunk_size = 2;
4075 struct ElementInfo *ei = &element_info[element];
4077 xx_ei = *ei; // copy element data into temporary buffer
4079 while (!checkEndOfFile(file))
4081 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4084 if (real_chunk_size >= chunk_size)
4090 level->file_has_custom_elements = TRUE;
4092 return real_chunk_size;
4095 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4096 struct LevelFileInfo *level_file_info,
4097 boolean level_info_only)
4099 char *filename = level_file_info->filename;
4100 char cookie[MAX_LINE_LEN];
4101 char chunk_name[CHUNK_ID_LEN + 1];
4105 if (!(file = openFile(filename, MODE_READ)))
4107 level->no_valid_file = TRUE;
4108 level->no_level_file = TRUE;
4110 if (level_info_only)
4113 Warn("cannot read level '%s' -- using empty level", filename);
4115 if (!setup.editor.use_template_for_new_levels)
4118 // if level file not found, try to initialize level data from template
4119 filename = getGlobalLevelTemplateFilename();
4121 if (!(file = openFile(filename, MODE_READ)))
4124 // default: for empty levels, use level template for custom elements
4125 level->use_custom_template = TRUE;
4127 level->no_valid_file = FALSE;
4130 getFileChunkBE(file, chunk_name, NULL);
4131 if (strEqual(chunk_name, "RND1"))
4133 getFile32BitBE(file); // not used
4135 getFileChunkBE(file, chunk_name, NULL);
4136 if (!strEqual(chunk_name, "CAVE"))
4138 level->no_valid_file = TRUE;
4140 Warn("unknown format of level file '%s'", filename);
4147 else // check for pre-2.0 file format with cookie string
4149 strcpy(cookie, chunk_name);
4150 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4152 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4153 cookie[strlen(cookie) - 1] = '\0';
4155 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4157 level->no_valid_file = TRUE;
4159 Warn("unknown format of level file '%s'", filename);
4166 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4168 level->no_valid_file = TRUE;
4170 Warn("unsupported version of level file '%s'", filename);
4177 // pre-2.0 level files have no game version, so use file version here
4178 level->game_version = level->file_version;
4181 if (level->file_version < FILE_VERSION_1_2)
4183 // level files from versions before 1.2.0 without chunk structure
4184 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4185 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4193 int (*loader)(File *, int, struct LevelInfo *);
4197 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4198 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4199 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4200 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4201 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4202 { "INFO", -1, LoadLevel_INFO },
4203 { "BODY", -1, LoadLevel_BODY },
4204 { "CONT", -1, LoadLevel_CONT },
4205 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4206 { "CNT3", -1, LoadLevel_CNT3 },
4207 { "CUS1", -1, LoadLevel_CUS1 },
4208 { "CUS2", -1, LoadLevel_CUS2 },
4209 { "CUS3", -1, LoadLevel_CUS3 },
4210 { "CUS4", -1, LoadLevel_CUS4 },
4211 { "GRP1", -1, LoadLevel_GRP1 },
4212 { "CONF", -1, LoadLevel_CONF },
4213 { "ELEM", -1, LoadLevel_ELEM },
4214 { "NOTE", -1, LoadLevel_NOTE },
4215 { "CUSX", -1, LoadLevel_CUSX },
4216 { "GRPX", -1, LoadLevel_GRPX },
4217 { "EMPX", -1, LoadLevel_EMPX },
4222 while (getFileChunkBE(file, chunk_name, &chunk_size))
4226 while (chunk_info[i].name != NULL &&
4227 !strEqual(chunk_name, chunk_info[i].name))
4230 if (chunk_info[i].name == NULL)
4232 Warn("unknown chunk '%s' in level file '%s'",
4233 chunk_name, filename);
4235 ReadUnusedBytesFromFile(file, chunk_size);
4237 else if (chunk_info[i].size != -1 &&
4238 chunk_info[i].size != chunk_size)
4240 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4241 chunk_size, chunk_name, filename);
4243 ReadUnusedBytesFromFile(file, chunk_size);
4247 // call function to load this level chunk
4248 int chunk_size_expected =
4249 (chunk_info[i].loader)(file, chunk_size, level);
4251 if (chunk_size_expected < 0)
4253 Warn("error reading chunk '%s' in level file '%s'",
4254 chunk_name, filename);
4259 // the size of some chunks cannot be checked before reading other
4260 // chunks first (like "HEAD" and "BODY") that contain some header
4261 // information, so check them here
4262 if (chunk_size_expected != chunk_size)
4264 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4265 chunk_size, chunk_name, filename);
4277 // ----------------------------------------------------------------------------
4278 // functions for loading BD level
4279 // ----------------------------------------------------------------------------
4281 #define LEVEL_TO_CAVE(e) (map_element_RND_to_BD_cave(e))
4282 #define CAVE_TO_LEVEL(e) (map_element_BD_to_RND_cave(e))
4284 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4286 struct LevelInfo_BD *level_bd = level->native_bd_level;
4287 GdCave *cave = NULL; // will be changed below
4288 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4289 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4292 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4294 // cave and map newly allocated when set to defaults above
4295 cave = level_bd->cave;
4298 cave->intermission = level->bd_intermission;
4301 cave->level_time[0] = level->time;
4302 cave->level_diamonds[0] = level->gems_needed;
4305 cave->scheduling = level->bd_scheduling_type;
4306 cave->pal_timing = level->bd_pal_timing;
4307 cave->level_speed[0] = level->bd_cycle_delay_ms;
4308 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4309 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4310 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4313 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4314 cave->diamond_value = level->score[SC_EMERALD];
4315 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4317 // compatibility settings
4318 cave->lineshift = level->bd_line_shifting_borders;
4319 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4320 cave->short_explosions = level->bd_short_explosions;
4322 // player properties
4323 cave->diagonal_movements = level->bd_diagonal_movements;
4324 cave->active_is_first_found = level->bd_topmost_player_active;
4325 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4326 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4327 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4328 cave->snap_element = LEVEL_TO_CAVE(level->bd_snap_element);
4330 // element properties
4331 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4332 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4333 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4334 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4335 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4336 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4337 cave->level_magic_wall_time[0] = level->bd_magic_wall_time;
4338 cave->magic_timer_zero_is_infinite = level->bd_magic_wall_zero_infinite;
4339 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4340 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4341 cave->magic_wall_breakscan = level->bd_magic_wall_break_scan;
4343 cave->magic_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4344 cave->magic_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4345 cave->magic_mega_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4346 cave->magic_nut_to = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4347 cave->magic_nitro_pack_to = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4348 cave->magic_flying_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4349 cave->magic_flying_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4351 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4352 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4353 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4354 cave->level_amoeba_threshold[0] = level->bd_amoeba_1_threshold_too_big;
4355 cave->level_amoeba_time[0] = level->bd_amoeba_1_slow_growth_time;
4356 cave->amoeba_growth_prob = level->bd_amoeba_1_slow_growth_rate * 10000;
4357 cave->amoeba_fast_growth_prob = level->bd_amoeba_1_fast_growth_rate * 10000;
4358 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4359 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4360 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4361 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4363 cave->amoeba_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_1_content_too_big);
4364 cave->amoeba_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_1_content_enclosed);
4365 cave->amoeba_2_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4366 cave->amoeba_2_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4367 cave->amoeba_2_explosion_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4368 cave->amoeba_2_looks_like = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4370 cave->slime_predictable = level->bd_slime_is_predictable;
4371 cave->slime_correct_random = level->bd_slime_correct_random;
4372 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4373 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4374 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4375 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4376 cave->slime_eats_1 = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4377 cave->slime_converts_1 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4378 cave->slime_eats_2 = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4379 cave->slime_converts_2 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4380 cave->slime_eats_3 = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4381 cave->slime_converts_3 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4383 cave->acid_eats_this = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4384 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4385 cave->acid_turns_to = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4387 cave->biter_delay_frame = level->bd_biter_move_delay;
4388 cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4390 cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4392 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4394 cave->replicators_active = level->bd_replicators_active;
4395 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4397 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4398 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4400 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4402 cave->nut_turns_to_when_crushed = LEVEL_TO_CAVE(level->bd_nut_content);
4404 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4405 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4406 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4408 cave->infinite_rockets = level->bd_infinite_rockets;
4410 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4411 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4413 cave->expanding_wall_looks_like = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4414 cave->dirt_looks_like = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4416 cave->creatures_backwards = level->bd_creatures_start_backwards;
4417 cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4418 cave->creatures_direction_auto_change_time = level->bd_creatures_auto_turn_delay;
4420 cave->gravity = level->bd_gravity_direction;
4421 cave->gravity_switch_active = level->bd_gravity_switch_active;
4422 cave->gravity_change_time = level->bd_gravity_switch_delay;
4423 cave->gravity_affects_all = level->bd_gravity_affects_all;
4425 cave->stone_falling_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4426 cave->stone_bouncing_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4427 cave->diamond_falling_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4428 cave->diamond_bouncing_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4430 cave->firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_1_explodes_to);
4431 cave->alt_firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4432 cave->butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_1_explodes_to);
4433 cave->alt_butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4434 cave->stonefly_explode_to = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4435 cave->dragonfly_explode_to = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4437 cave->diamond_birth_effect = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4438 cave->bomb_explosion_effect = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4439 cave->nitro_explosion_effect = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4440 cave->explosion_effect = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4442 cave->colorb = level->bd_color_b;
4443 cave->color0 = level->bd_color_0;
4444 cave->color1 = level->bd_color_1;
4445 cave->color2 = level->bd_color_2;
4446 cave->color3 = level->bd_color_3;
4447 cave->color4 = level->bd_color_4;
4448 cave->color5 = level->bd_color_5;
4451 strncpy(cave->name, level->name, sizeof(GdString));
4452 cave->name[sizeof(GdString) - 1] = '\0';
4454 // playfield elements
4455 for (x = 0; x < cave->w; x++)
4456 for (y = 0; y < cave->h; y++)
4457 cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4460 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4462 struct LevelInfo_BD *level_bd = level->native_bd_level;
4463 GdCave *cave = level_bd->cave;
4464 int bd_level_nr = level_bd->level_nr;
4467 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4468 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4471 level->bd_intermission = cave->intermission;
4474 level->time = cave->level_time[bd_level_nr];
4475 level->gems_needed = cave->level_diamonds[bd_level_nr];
4478 level->bd_scheduling_type = cave->scheduling;
4479 level->bd_pal_timing = cave->pal_timing;
4480 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4481 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4482 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4483 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4486 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4487 level->score[SC_EMERALD] = cave->diamond_value;
4488 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4490 // compatibility settings
4491 level->bd_line_shifting_borders = cave->lineshift;
4492 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4493 level->bd_short_explosions = cave->short_explosions;
4495 // player properties
4496 level->bd_diagonal_movements = cave->diagonal_movements;
4497 level->bd_topmost_player_active = cave->active_is_first_found;
4498 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4499 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4500 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4501 level->bd_snap_element = CAVE_TO_LEVEL(cave->snap_element);
4503 // element properties
4504 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4505 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4506 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4507 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4508 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4509 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4510 level->bd_magic_wall_time = cave->level_magic_wall_time[bd_level_nr];
4511 level->bd_magic_wall_zero_infinite = cave->magic_timer_zero_is_infinite;
4512 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4513 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4514 level->bd_magic_wall_break_scan = cave->magic_wall_breakscan;
4516 level->bd_magic_wall_diamond_to = CAVE_TO_LEVEL(cave->magic_diamond_to);
4517 level->bd_magic_wall_rock_to = CAVE_TO_LEVEL(cave->magic_stone_to);
4518 level->bd_magic_wall_mega_rock_to = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4519 level->bd_magic_wall_nut_to = CAVE_TO_LEVEL(cave->magic_nut_to);
4520 level->bd_magic_wall_nitro_pack_to = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4521 level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4522 level->bd_magic_wall_flying_rock_to = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4524 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4525 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4526 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4527 level->bd_amoeba_1_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4528 level->bd_amoeba_1_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4529 level->bd_amoeba_1_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4530 level->bd_amoeba_1_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4531 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4532 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4533 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4534 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4536 level->bd_amoeba_1_content_too_big = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4537 level->bd_amoeba_1_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4538 level->bd_amoeba_2_content_too_big = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4539 level->bd_amoeba_2_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4540 level->bd_amoeba_2_content_exploding = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4541 level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4543 level->bd_slime_is_predictable = cave->slime_predictable;
4544 level->bd_slime_correct_random = cave->slime_correct_random;
4545 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4546 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4547 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4548 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4549 level->bd_slime_eats_element_1 = CAVE_TO_LEVEL(cave->slime_eats_1);
4550 level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4551 level->bd_slime_eats_element_2 = CAVE_TO_LEVEL(cave->slime_eats_2);
4552 level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4553 level->bd_slime_eats_element_3 = CAVE_TO_LEVEL(cave->slime_eats_3);
4554 level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4556 level->bd_acid_eats_element = CAVE_TO_LEVEL(cave->acid_eats_this);
4557 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4558 level->bd_acid_turns_to_element = CAVE_TO_LEVEL(cave->acid_turns_to);
4560 level->bd_biter_move_delay = cave->biter_delay_frame;
4561 level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat);
4563 level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4565 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4567 level->bd_replicators_active = cave->replicators_active;
4568 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4570 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4571 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4573 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4575 level->bd_nut_content = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4577 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4578 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4579 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4581 level->bd_infinite_rockets = cave->infinite_rockets;
4583 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4584 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4586 level->bd_expanding_wall_looks_like = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4587 level->bd_sand_looks_like = CAVE_TO_LEVEL(cave->dirt_looks_like);
4589 level->bd_creatures_start_backwards = cave->creatures_backwards;
4590 level->bd_creatures_turn_on_hatching = cave->creatures_direction_auto_change_on_start;
4591 level->bd_creatures_auto_turn_delay = cave->creatures_direction_auto_change_time;
4593 level->bd_gravity_direction = cave->gravity;
4594 level->bd_gravity_switch_active = cave->gravity_switch_active;
4595 level->bd_gravity_switch_delay = cave->gravity_change_time;
4596 level->bd_gravity_affects_all = cave->gravity_affects_all;
4598 level->bd_rock_turns_to_on_falling = CAVE_TO_LEVEL(cave->stone_falling_effect);
4599 level->bd_rock_turns_to_on_impact = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4600 level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4601 level->bd_diamond_turns_to_on_impact = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4603 level->bd_firefly_1_explodes_to = CAVE_TO_LEVEL(cave->firefly_explode_to);
4604 level->bd_firefly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4605 level->bd_butterfly_1_explodes_to = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4606 level->bd_butterfly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4607 level->bd_stonefly_explodes_to = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4608 level->bd_dragonfly_explodes_to = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4610 level->bd_diamond_birth_turns_to = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4611 level->bd_bomb_explosion_turns_to = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4612 level->bd_nitro_explosion_turns_to = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4613 level->bd_explosion_turns_to = CAVE_TO_LEVEL(cave->explosion_effect);
4615 level->bd_color_b = cave->colorb;
4616 level->bd_color_0 = cave->color0;
4617 level->bd_color_1 = cave->color1;
4618 level->bd_color_2 = cave->color2;
4619 level->bd_color_3 = cave->color3;
4620 level->bd_color_4 = cave->color4;
4621 level->bd_color_5 = cave->color5;
4623 // set default color type and colors for BD style level colors
4624 SetDefaultLevelColorType_BD();
4625 SetDefaultLevelColors_BD();
4628 char *cave_name_latin1 = getLatin1FromUTF8(cave->name);
4629 char *cave_name_final = (gd_caveset_has_levels() ?
4630 getStringPrint("%s / %d", cave_name_latin1, bd_level_nr + 1) :
4631 getStringCopy(cave_name_latin1));
4633 strncpy(level->name, cave_name_final, MAX_LEVEL_NAME_LEN);
4634 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4636 // playfield elements
4637 for (x = 0; x < level->fieldx; x++)
4638 for (y = 0; y < level->fieldy; y++)
4639 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4641 checked_free(cave_name_latin1);
4642 checked_free(cave_name_final);
4645 static void setTapeInfoToDefaults(void);
4647 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4649 struct LevelInfo_BD *level_bd = level->native_bd_level;
4650 GdCave *cave = level_bd->cave;
4651 GdReplay *replay = level_bd->replay;
4657 // always start with reliable default values
4658 setTapeInfoToDefaults();
4660 tape.level_nr = level_nr; // (currently not used)
4661 tape.random_seed = replay->seed;
4663 TapeSetDateFromIsoDateString(replay->date);
4666 tape.pos[tape.counter].delay = 0;
4668 tape.bd_replay = TRUE;
4670 // all time calculations only used to display approximate tape time
4671 int cave_speed = cave->speed;
4672 int milliseconds_game = 0;
4673 int milliseconds_elapsed = 20;
4675 for (i = 0; i < replay->movements->len; i++)
4677 int replay_action = replay->movements->data[i];
4678 int tape_action = map_action_BD_to_RND(replay_action);
4679 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4680 boolean success = 0;
4684 success = TapeAddAction(action);
4686 milliseconds_game += milliseconds_elapsed;
4688 if (milliseconds_game >= cave_speed)
4690 milliseconds_game -= cave_speed;
4697 tape.pos[tape.counter].delay = 0;
4698 tape.pos[tape.counter].action[0] = 0;
4702 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4708 TapeHaltRecording();
4710 if (!replay->success)
4711 Warn("BD replay is marked as not successful");
4715 // ----------------------------------------------------------------------------
4716 // functions for loading EM level
4717 // ----------------------------------------------------------------------------
4719 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4721 static int ball_xy[8][2] =
4732 struct LevelInfo_EM *level_em = level->native_em_level;
4733 struct CAVE *cav = level_em->cav;
4736 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4737 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4739 cav->time_seconds = level->time;
4740 cav->gems_needed = level->gems_needed;
4742 cav->emerald_score = level->score[SC_EMERALD];
4743 cav->diamond_score = level->score[SC_DIAMOND];
4744 cav->alien_score = level->score[SC_ROBOT];
4745 cav->tank_score = level->score[SC_SPACESHIP];
4746 cav->bug_score = level->score[SC_BUG];
4747 cav->eater_score = level->score[SC_YAMYAM];
4748 cav->nut_score = level->score[SC_NUT];
4749 cav->dynamite_score = level->score[SC_DYNAMITE];
4750 cav->key_score = level->score[SC_KEY];
4751 cav->exit_score = level->score[SC_TIME_BONUS];
4753 cav->num_eater_arrays = level->num_yamyam_contents;
4755 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4756 for (y = 0; y < 3; y++)
4757 for (x = 0; x < 3; x++)
4758 cav->eater_array[i][y * 3 + x] =
4759 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4761 cav->amoeba_time = level->amoeba_speed;
4762 cav->wonderwall_time = level->time_magic_wall;
4763 cav->wheel_time = level->time_wheel;
4765 cav->android_move_time = level->android_move_time;
4766 cav->android_clone_time = level->android_clone_time;
4767 cav->ball_random = level->ball_random;
4768 cav->ball_active = level->ball_active_initial;
4769 cav->ball_time = level->ball_time;
4770 cav->num_ball_arrays = level->num_ball_contents;
4772 cav->lenses_score = level->lenses_score;
4773 cav->magnify_score = level->magnify_score;
4774 cav->slurp_score = level->slurp_score;
4776 cav->lenses_time = level->lenses_time;
4777 cav->magnify_time = level->magnify_time;
4779 cav->wind_time = 9999;
4780 cav->wind_direction =
4781 map_direction_RND_to_EM(level->wind_direction_initial);
4783 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4784 for (j = 0; j < 8; j++)
4785 cav->ball_array[i][j] =
4786 map_element_RND_to_EM_cave(level->ball_content[i].
4787 e[ball_xy[j][0]][ball_xy[j][1]]);
4789 map_android_clone_elements_RND_to_EM(level);
4791 // first fill the complete playfield with the empty space element
4792 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4793 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4794 cav->cave[x][y] = Cblank;
4796 // then copy the real level contents from level file into the playfield
4797 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4799 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4801 if (level->field[x][y] == EL_AMOEBA_DEAD)
4802 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4804 cav->cave[x][y] = new_element;
4807 for (i = 0; i < MAX_PLAYERS; i++)
4809 cav->player_x[i] = -1;
4810 cav->player_y[i] = -1;
4813 // initialize player positions and delete players from the playfield
4814 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4816 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4818 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4820 cav->player_x[player_nr] = x;
4821 cav->player_y[player_nr] = y;
4823 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4828 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4830 static int ball_xy[8][2] =
4841 struct LevelInfo_EM *level_em = level->native_em_level;
4842 struct CAVE *cav = level_em->cav;
4845 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4846 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4848 level->time = cav->time_seconds;
4849 level->gems_needed = cav->gems_needed;
4851 sprintf(level->name, "Level %d", level->file_info.nr);
4853 level->score[SC_EMERALD] = cav->emerald_score;
4854 level->score[SC_DIAMOND] = cav->diamond_score;
4855 level->score[SC_ROBOT] = cav->alien_score;
4856 level->score[SC_SPACESHIP] = cav->tank_score;
4857 level->score[SC_BUG] = cav->bug_score;
4858 level->score[SC_YAMYAM] = cav->eater_score;
4859 level->score[SC_NUT] = cav->nut_score;
4860 level->score[SC_DYNAMITE] = cav->dynamite_score;
4861 level->score[SC_KEY] = cav->key_score;
4862 level->score[SC_TIME_BONUS] = cav->exit_score;
4864 level->num_yamyam_contents = cav->num_eater_arrays;
4866 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4867 for (y = 0; y < 3; y++)
4868 for (x = 0; x < 3; x++)
4869 level->yamyam_content[i].e[x][y] =
4870 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4872 level->amoeba_speed = cav->amoeba_time;
4873 level->time_magic_wall = cav->wonderwall_time;
4874 level->time_wheel = cav->wheel_time;
4876 level->android_move_time = cav->android_move_time;
4877 level->android_clone_time = cav->android_clone_time;
4878 level->ball_random = cav->ball_random;
4879 level->ball_active_initial = cav->ball_active;
4880 level->ball_time = cav->ball_time;
4881 level->num_ball_contents = cav->num_ball_arrays;
4883 level->lenses_score = cav->lenses_score;
4884 level->magnify_score = cav->magnify_score;
4885 level->slurp_score = cav->slurp_score;
4887 level->lenses_time = cav->lenses_time;
4888 level->magnify_time = cav->magnify_time;
4890 level->wind_direction_initial =
4891 map_direction_EM_to_RND(cav->wind_direction);
4893 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4894 for (j = 0; j < 8; j++)
4895 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4896 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4898 map_android_clone_elements_EM_to_RND(level);
4900 // convert the playfield (some elements need special treatment)
4901 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4903 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4905 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4906 new_element = EL_AMOEBA_DEAD;
4908 level->field[x][y] = new_element;
4911 for (i = 0; i < MAX_PLAYERS; i++)
4913 // in case of all players set to the same field, use the first player
4914 int nr = MAX_PLAYERS - i - 1;
4915 int jx = cav->player_x[nr];
4916 int jy = cav->player_y[nr];
4918 if (jx != -1 && jy != -1)
4919 level->field[jx][jy] = EL_PLAYER_1 + nr;
4922 // time score is counted for each 10 seconds left in Emerald Mine levels
4923 level->time_score_base = 10;
4927 // ----------------------------------------------------------------------------
4928 // functions for loading SP level
4929 // ----------------------------------------------------------------------------
4931 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4933 struct LevelInfo_SP *level_sp = level->native_sp_level;
4934 LevelInfoType *header = &level_sp->header;
4937 level_sp->width = level->fieldx;
4938 level_sp->height = level->fieldy;
4940 for (x = 0; x < level->fieldx; x++)
4941 for (y = 0; y < level->fieldy; y++)
4942 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4944 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4946 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4947 header->LevelTitle[i] = level->name[i];
4948 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4950 header->InfotronsNeeded = level->gems_needed;
4952 header->SpecialPortCount = 0;
4954 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4956 boolean gravity_port_found = FALSE;
4957 boolean gravity_port_valid = FALSE;
4958 int gravity_port_flag;
4959 int gravity_port_base_element;
4960 int element = level->field[x][y];
4962 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4963 element <= EL_SP_GRAVITY_ON_PORT_UP)
4965 gravity_port_found = TRUE;
4966 gravity_port_valid = TRUE;
4967 gravity_port_flag = 1;
4968 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4970 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4971 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4973 gravity_port_found = TRUE;
4974 gravity_port_valid = TRUE;
4975 gravity_port_flag = 0;
4976 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4978 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4979 element <= EL_SP_GRAVITY_PORT_UP)
4981 // change R'n'D style gravity inverting special port to normal port
4982 // (there are no gravity inverting ports in native Supaplex engine)
4984 gravity_port_found = TRUE;
4985 gravity_port_valid = FALSE;
4986 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4989 if (gravity_port_found)
4991 if (gravity_port_valid &&
4992 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4994 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4996 port->PortLocation = (y * level->fieldx + x) * 2;
4997 port->Gravity = gravity_port_flag;
4999 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
5001 header->SpecialPortCount++;
5005 // change special gravity port to normal port
5007 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
5010 level_sp->playfield[x][y] = element - EL_SP_START;
5015 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
5017 struct LevelInfo_SP *level_sp = level->native_sp_level;
5018 LevelInfoType *header = &level_sp->header;
5019 boolean num_invalid_elements = 0;
5022 level->fieldx = level_sp->width;
5023 level->fieldy = level_sp->height;
5025 for (x = 0; x < level->fieldx; x++)
5027 for (y = 0; y < level->fieldy; y++)
5029 int element_old = level_sp->playfield[x][y];
5030 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5032 if (element_new == EL_UNKNOWN)
5034 num_invalid_elements++;
5036 Debug("level:native:SP", "invalid element %d at position %d, %d",
5040 level->field[x][y] = element_new;
5044 if (num_invalid_elements > 0)
5045 Warn("found %d invalid elements%s", num_invalid_elements,
5046 (!options.debug ? " (use '--debug' for more details)" : ""));
5048 for (i = 0; i < MAX_PLAYERS; i++)
5049 level->initial_player_gravity[i] =
5050 (header->InitialGravity == 1 ? TRUE : FALSE);
5052 // skip leading spaces
5053 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5054 if (header->LevelTitle[i] != ' ')
5058 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5059 level->name[j] = header->LevelTitle[i];
5060 level->name[j] = '\0';
5062 // cut trailing spaces
5064 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5065 level->name[j - 1] = '\0';
5067 level->gems_needed = header->InfotronsNeeded;
5069 for (i = 0; i < header->SpecialPortCount; i++)
5071 SpecialPortType *port = &header->SpecialPort[i];
5072 int port_location = port->PortLocation;
5073 int gravity = port->Gravity;
5074 int port_x, port_y, port_element;
5076 port_x = (port_location / 2) % level->fieldx;
5077 port_y = (port_location / 2) / level->fieldx;
5079 if (port_x < 0 || port_x >= level->fieldx ||
5080 port_y < 0 || port_y >= level->fieldy)
5082 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5087 port_element = level->field[port_x][port_y];
5089 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5090 port_element > EL_SP_GRAVITY_PORT_UP)
5092 Warn("no special port at position (%d, %d)", port_x, port_y);
5097 // change previous (wrong) gravity inverting special port to either
5098 // gravity enabling special port or gravity disabling special port
5099 level->field[port_x][port_y] +=
5100 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5101 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5104 // change special gravity ports without database entries to normal ports
5105 for (x = 0; x < level->fieldx; x++)
5106 for (y = 0; y < level->fieldy; y++)
5107 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5108 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5109 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5111 level->time = 0; // no time limit
5112 level->amoeba_speed = 0;
5113 level->time_magic_wall = 0;
5114 level->time_wheel = 0;
5115 level->amoeba_content = EL_EMPTY;
5117 // original Supaplex does not use score values -- rate by playing time
5118 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5119 level->score[i] = 0;
5121 level->rate_time_over_score = TRUE;
5123 // there are no yamyams in supaplex levels
5124 for (i = 0; i < level->num_yamyam_contents; i++)
5125 for (x = 0; x < 3; x++)
5126 for (y = 0; y < 3; y++)
5127 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5130 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5132 struct LevelInfo_SP *level_sp = level->native_sp_level;
5133 struct DemoInfo_SP *demo = &level_sp->demo;
5136 // always start with reliable default values
5137 demo->is_available = FALSE;
5140 if (TAPE_IS_EMPTY(tape))
5143 demo->level_nr = tape.level_nr; // (currently not used)
5145 level_sp->header.DemoRandomSeed = tape.random_seed;
5149 for (i = 0; i < tape.length; i++)
5151 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5152 int demo_repeat = tape.pos[i].delay;
5153 int demo_entries = (demo_repeat + 15) / 16;
5155 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5157 Warn("tape truncated: size exceeds maximum SP demo size %d",
5163 for (j = 0; j < demo_repeat / 16; j++)
5164 demo->data[demo->length++] = 0xf0 | demo_action;
5166 if (demo_repeat % 16)
5167 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5170 demo->is_available = TRUE;
5173 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5175 struct LevelInfo_SP *level_sp = level->native_sp_level;
5176 struct DemoInfo_SP *demo = &level_sp->demo;
5177 char *filename = level->file_info.filename;
5180 // always start with reliable default values
5181 setTapeInfoToDefaults();
5183 if (!demo->is_available)
5186 tape.level_nr = demo->level_nr; // (currently not used)
5187 tape.random_seed = level_sp->header.DemoRandomSeed;
5189 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5192 tape.pos[tape.counter].delay = 0;
5194 for (i = 0; i < demo->length; i++)
5196 int demo_action = demo->data[i] & 0x0f;
5197 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5198 int tape_action = map_key_SP_to_RND(demo_action);
5199 int tape_repeat = demo_repeat + 1;
5200 byte action[MAX_TAPE_ACTIONS] = { tape_action };
5201 boolean success = 0;
5204 for (j = 0; j < tape_repeat; j++)
5205 success = TapeAddAction(action);
5209 Warn("SP demo truncated: size exceeds maximum tape size %d",
5216 TapeHaltRecording();
5220 // ----------------------------------------------------------------------------
5221 // functions for loading MM level
5222 // ----------------------------------------------------------------------------
5224 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5226 struct LevelInfo_MM *level_mm = level->native_mm_level;
5229 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5230 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5232 level_mm->time = level->time;
5233 level_mm->kettles_needed = level->gems_needed;
5234 level_mm->auto_count_kettles = level->auto_count_gems;
5236 level_mm->mm_laser_red = level->mm_laser_red;
5237 level_mm->mm_laser_green = level->mm_laser_green;
5238 level_mm->mm_laser_blue = level->mm_laser_blue;
5240 level_mm->df_laser_red = level->df_laser_red;
5241 level_mm->df_laser_green = level->df_laser_green;
5242 level_mm->df_laser_blue = level->df_laser_blue;
5244 strcpy(level_mm->name, level->name);
5245 strcpy(level_mm->author, level->author);
5247 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
5248 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
5249 level_mm->score[SC_KEY] = level->score[SC_KEY];
5250 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5251 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5253 level_mm->amoeba_speed = level->amoeba_speed;
5254 level_mm->time_fuse = level->mm_time_fuse;
5255 level_mm->time_bomb = level->mm_time_bomb;
5256 level_mm->time_ball = level->mm_time_ball;
5257 level_mm->time_block = level->mm_time_block;
5259 level_mm->num_ball_contents = level->num_mm_ball_contents;
5260 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5261 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5262 level_mm->explode_ball = level->explode_mm_ball;
5264 for (i = 0; i < level->num_mm_ball_contents; i++)
5265 level_mm->ball_content[i] =
5266 map_element_RND_to_MM(level->mm_ball_content[i]);
5268 for (x = 0; x < level->fieldx; x++)
5269 for (y = 0; y < level->fieldy; y++)
5271 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5274 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5276 struct LevelInfo_MM *level_mm = level->native_mm_level;
5279 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5280 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5282 level->time = level_mm->time;
5283 level->gems_needed = level_mm->kettles_needed;
5284 level->auto_count_gems = level_mm->auto_count_kettles;
5286 level->mm_laser_red = level_mm->mm_laser_red;
5287 level->mm_laser_green = level_mm->mm_laser_green;
5288 level->mm_laser_blue = level_mm->mm_laser_blue;
5290 level->df_laser_red = level_mm->df_laser_red;
5291 level->df_laser_green = level_mm->df_laser_green;
5292 level->df_laser_blue = level_mm->df_laser_blue;
5294 strcpy(level->name, level_mm->name);
5296 // only overwrite author from 'levelinfo.conf' if author defined in level
5297 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5298 strcpy(level->author, level_mm->author);
5300 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5301 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5302 level->score[SC_KEY] = level_mm->score[SC_KEY];
5303 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5304 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5306 level->amoeba_speed = level_mm->amoeba_speed;
5307 level->mm_time_fuse = level_mm->time_fuse;
5308 level->mm_time_bomb = level_mm->time_bomb;
5309 level->mm_time_ball = level_mm->time_ball;
5310 level->mm_time_block = level_mm->time_block;
5312 level->num_mm_ball_contents = level_mm->num_ball_contents;
5313 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5314 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5315 level->explode_mm_ball = level_mm->explode_ball;
5317 for (i = 0; i < level->num_mm_ball_contents; i++)
5318 level->mm_ball_content[i] =
5319 map_element_MM_to_RND(level_mm->ball_content[i]);
5321 for (x = 0; x < level->fieldx; x++)
5322 for (y = 0; y < level->fieldy; y++)
5323 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5327 // ----------------------------------------------------------------------------
5328 // functions for loading DC level
5329 // ----------------------------------------------------------------------------
5331 #define DC_LEVEL_HEADER_SIZE 344
5333 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5336 static int last_data_encoded;
5340 int diff_hi, diff_lo;
5341 int data_hi, data_lo;
5342 unsigned short data_decoded;
5346 last_data_encoded = 0;
5353 diff = data_encoded - last_data_encoded;
5354 diff_hi = diff & ~0xff;
5355 diff_lo = diff & 0xff;
5359 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5360 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5361 data_hi = data_hi & 0xff00;
5363 data_decoded = data_hi | data_lo;
5365 last_data_encoded = data_encoded;
5367 offset1 = (offset1 + 1) % 31;
5368 offset2 = offset2 & 0xff;
5370 return data_decoded;
5373 static int getMappedElement_DC(int element)
5381 // 0x0117 - 0x036e: (?)
5384 // 0x042d - 0x0684: (?)
5400 element = EL_CRYSTAL;
5403 case 0x0e77: // quicksand (boulder)
5404 element = EL_QUICKSAND_FAST_FULL;
5407 case 0x0e99: // slow quicksand (boulder)
5408 element = EL_QUICKSAND_FULL;
5412 element = EL_EM_EXIT_OPEN;
5416 element = EL_EM_EXIT_CLOSED;
5420 element = EL_EM_STEEL_EXIT_OPEN;
5424 element = EL_EM_STEEL_EXIT_CLOSED;
5427 case 0x0f4f: // dynamite (lit 1)
5428 element = EL_EM_DYNAMITE_ACTIVE;
5431 case 0x0f57: // dynamite (lit 2)
5432 element = EL_EM_DYNAMITE_ACTIVE;
5435 case 0x0f5f: // dynamite (lit 3)
5436 element = EL_EM_DYNAMITE_ACTIVE;
5439 case 0x0f67: // dynamite (lit 4)
5440 element = EL_EM_DYNAMITE_ACTIVE;
5447 element = EL_AMOEBA_WET;
5451 element = EL_AMOEBA_DROP;
5455 element = EL_DC_MAGIC_WALL;
5459 element = EL_SPACESHIP_UP;
5463 element = EL_SPACESHIP_DOWN;
5467 element = EL_SPACESHIP_LEFT;
5471 element = EL_SPACESHIP_RIGHT;
5475 element = EL_BUG_UP;
5479 element = EL_BUG_DOWN;
5483 element = EL_BUG_LEFT;
5487 element = EL_BUG_RIGHT;
5491 element = EL_MOLE_UP;
5495 element = EL_MOLE_DOWN;
5499 element = EL_MOLE_LEFT;
5503 element = EL_MOLE_RIGHT;
5511 element = EL_YAMYAM_UP;
5515 element = EL_SWITCHGATE_OPEN;
5519 element = EL_SWITCHGATE_CLOSED;
5523 element = EL_DC_SWITCHGATE_SWITCH_UP;
5527 element = EL_TIMEGATE_CLOSED;
5530 case 0x144c: // conveyor belt switch (green)
5531 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5534 case 0x144f: // conveyor belt switch (red)
5535 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5538 case 0x1452: // conveyor belt switch (blue)
5539 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5543 element = EL_CONVEYOR_BELT_3_MIDDLE;
5547 element = EL_CONVEYOR_BELT_3_LEFT;
5551 element = EL_CONVEYOR_BELT_3_RIGHT;
5555 element = EL_CONVEYOR_BELT_1_MIDDLE;
5559 element = EL_CONVEYOR_BELT_1_LEFT;
5563 element = EL_CONVEYOR_BELT_1_RIGHT;
5567 element = EL_CONVEYOR_BELT_4_MIDDLE;
5571 element = EL_CONVEYOR_BELT_4_LEFT;
5575 element = EL_CONVEYOR_BELT_4_RIGHT;
5579 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5583 element = EL_EXPANDABLE_WALL_VERTICAL;
5587 element = EL_EXPANDABLE_WALL_ANY;
5590 case 0x14ce: // growing steel wall (left/right)
5591 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5594 case 0x14df: // growing steel wall (up/down)
5595 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5598 case 0x14e8: // growing steel wall (up/down/left/right)
5599 element = EL_EXPANDABLE_STEELWALL_ANY;
5603 element = EL_SHIELD_DEADLY;
5607 element = EL_EXTRA_TIME;
5615 element = EL_EMPTY_SPACE;
5618 case 0x1578: // quicksand (empty)
5619 element = EL_QUICKSAND_FAST_EMPTY;
5622 case 0x1579: // slow quicksand (empty)
5623 element = EL_QUICKSAND_EMPTY;
5633 element = EL_EM_DYNAMITE;
5636 case 0x15a1: // key (red)
5637 element = EL_EM_KEY_1;
5640 case 0x15a2: // key (yellow)
5641 element = EL_EM_KEY_2;
5644 case 0x15a3: // key (blue)
5645 element = EL_EM_KEY_4;
5648 case 0x15a4: // key (green)
5649 element = EL_EM_KEY_3;
5652 case 0x15a5: // key (white)
5653 element = EL_DC_KEY_WHITE;
5657 element = EL_WALL_SLIPPERY;
5664 case 0x15a8: // wall (not round)
5668 case 0x15a9: // (blue)
5669 element = EL_CHAR_A;
5672 case 0x15aa: // (blue)
5673 element = EL_CHAR_B;
5676 case 0x15ab: // (blue)
5677 element = EL_CHAR_C;
5680 case 0x15ac: // (blue)
5681 element = EL_CHAR_D;
5684 case 0x15ad: // (blue)
5685 element = EL_CHAR_E;
5688 case 0x15ae: // (blue)
5689 element = EL_CHAR_F;
5692 case 0x15af: // (blue)
5693 element = EL_CHAR_G;
5696 case 0x15b0: // (blue)
5697 element = EL_CHAR_H;
5700 case 0x15b1: // (blue)
5701 element = EL_CHAR_I;
5704 case 0x15b2: // (blue)
5705 element = EL_CHAR_J;
5708 case 0x15b3: // (blue)
5709 element = EL_CHAR_K;
5712 case 0x15b4: // (blue)
5713 element = EL_CHAR_L;
5716 case 0x15b5: // (blue)
5717 element = EL_CHAR_M;
5720 case 0x15b6: // (blue)
5721 element = EL_CHAR_N;
5724 case 0x15b7: // (blue)
5725 element = EL_CHAR_O;
5728 case 0x15b8: // (blue)
5729 element = EL_CHAR_P;
5732 case 0x15b9: // (blue)
5733 element = EL_CHAR_Q;
5736 case 0x15ba: // (blue)
5737 element = EL_CHAR_R;
5740 case 0x15bb: // (blue)
5741 element = EL_CHAR_S;
5744 case 0x15bc: // (blue)
5745 element = EL_CHAR_T;
5748 case 0x15bd: // (blue)
5749 element = EL_CHAR_U;
5752 case 0x15be: // (blue)
5753 element = EL_CHAR_V;
5756 case 0x15bf: // (blue)
5757 element = EL_CHAR_W;
5760 case 0x15c0: // (blue)
5761 element = EL_CHAR_X;
5764 case 0x15c1: // (blue)
5765 element = EL_CHAR_Y;
5768 case 0x15c2: // (blue)
5769 element = EL_CHAR_Z;
5772 case 0x15c3: // (blue)
5773 element = EL_CHAR_AUMLAUT;
5776 case 0x15c4: // (blue)
5777 element = EL_CHAR_OUMLAUT;
5780 case 0x15c5: // (blue)
5781 element = EL_CHAR_UUMLAUT;
5784 case 0x15c6: // (blue)
5785 element = EL_CHAR_0;
5788 case 0x15c7: // (blue)
5789 element = EL_CHAR_1;
5792 case 0x15c8: // (blue)
5793 element = EL_CHAR_2;
5796 case 0x15c9: // (blue)
5797 element = EL_CHAR_3;
5800 case 0x15ca: // (blue)
5801 element = EL_CHAR_4;
5804 case 0x15cb: // (blue)
5805 element = EL_CHAR_5;
5808 case 0x15cc: // (blue)
5809 element = EL_CHAR_6;
5812 case 0x15cd: // (blue)
5813 element = EL_CHAR_7;
5816 case 0x15ce: // (blue)
5817 element = EL_CHAR_8;
5820 case 0x15cf: // (blue)
5821 element = EL_CHAR_9;
5824 case 0x15d0: // (blue)
5825 element = EL_CHAR_PERIOD;
5828 case 0x15d1: // (blue)
5829 element = EL_CHAR_EXCLAM;
5832 case 0x15d2: // (blue)
5833 element = EL_CHAR_COLON;
5836 case 0x15d3: // (blue)
5837 element = EL_CHAR_LESS;
5840 case 0x15d4: // (blue)
5841 element = EL_CHAR_GREATER;
5844 case 0x15d5: // (blue)
5845 element = EL_CHAR_QUESTION;
5848 case 0x15d6: // (blue)
5849 element = EL_CHAR_COPYRIGHT;
5852 case 0x15d7: // (blue)
5853 element = EL_CHAR_UP;
5856 case 0x15d8: // (blue)
5857 element = EL_CHAR_DOWN;
5860 case 0x15d9: // (blue)
5861 element = EL_CHAR_BUTTON;
5864 case 0x15da: // (blue)
5865 element = EL_CHAR_PLUS;
5868 case 0x15db: // (blue)
5869 element = EL_CHAR_MINUS;
5872 case 0x15dc: // (blue)
5873 element = EL_CHAR_APOSTROPHE;
5876 case 0x15dd: // (blue)
5877 element = EL_CHAR_PARENLEFT;
5880 case 0x15de: // (blue)
5881 element = EL_CHAR_PARENRIGHT;
5884 case 0x15df: // (green)
5885 element = EL_CHAR_A;
5888 case 0x15e0: // (green)
5889 element = EL_CHAR_B;
5892 case 0x15e1: // (green)
5893 element = EL_CHAR_C;
5896 case 0x15e2: // (green)
5897 element = EL_CHAR_D;
5900 case 0x15e3: // (green)
5901 element = EL_CHAR_E;
5904 case 0x15e4: // (green)
5905 element = EL_CHAR_F;
5908 case 0x15e5: // (green)
5909 element = EL_CHAR_G;
5912 case 0x15e6: // (green)
5913 element = EL_CHAR_H;
5916 case 0x15e7: // (green)
5917 element = EL_CHAR_I;
5920 case 0x15e8: // (green)
5921 element = EL_CHAR_J;
5924 case 0x15e9: // (green)
5925 element = EL_CHAR_K;
5928 case 0x15ea: // (green)
5929 element = EL_CHAR_L;
5932 case 0x15eb: // (green)
5933 element = EL_CHAR_M;
5936 case 0x15ec: // (green)
5937 element = EL_CHAR_N;
5940 case 0x15ed: // (green)
5941 element = EL_CHAR_O;
5944 case 0x15ee: // (green)
5945 element = EL_CHAR_P;
5948 case 0x15ef: // (green)
5949 element = EL_CHAR_Q;
5952 case 0x15f0: // (green)
5953 element = EL_CHAR_R;
5956 case 0x15f1: // (green)
5957 element = EL_CHAR_S;
5960 case 0x15f2: // (green)
5961 element = EL_CHAR_T;
5964 case 0x15f3: // (green)
5965 element = EL_CHAR_U;
5968 case 0x15f4: // (green)
5969 element = EL_CHAR_V;
5972 case 0x15f5: // (green)
5973 element = EL_CHAR_W;
5976 case 0x15f6: // (green)
5977 element = EL_CHAR_X;
5980 case 0x15f7: // (green)
5981 element = EL_CHAR_Y;
5984 case 0x15f8: // (green)
5985 element = EL_CHAR_Z;
5988 case 0x15f9: // (green)
5989 element = EL_CHAR_AUMLAUT;
5992 case 0x15fa: // (green)
5993 element = EL_CHAR_OUMLAUT;
5996 case 0x15fb: // (green)
5997 element = EL_CHAR_UUMLAUT;
6000 case 0x15fc: // (green)
6001 element = EL_CHAR_0;
6004 case 0x15fd: // (green)
6005 element = EL_CHAR_1;
6008 case 0x15fe: // (green)
6009 element = EL_CHAR_2;
6012 case 0x15ff: // (green)
6013 element = EL_CHAR_3;
6016 case 0x1600: // (green)
6017 element = EL_CHAR_4;
6020 case 0x1601: // (green)
6021 element = EL_CHAR_5;
6024 case 0x1602: // (green)
6025 element = EL_CHAR_6;
6028 case 0x1603: // (green)
6029 element = EL_CHAR_7;
6032 case 0x1604: // (green)
6033 element = EL_CHAR_8;
6036 case 0x1605: // (green)
6037 element = EL_CHAR_9;
6040 case 0x1606: // (green)
6041 element = EL_CHAR_PERIOD;
6044 case 0x1607: // (green)
6045 element = EL_CHAR_EXCLAM;
6048 case 0x1608: // (green)
6049 element = EL_CHAR_COLON;
6052 case 0x1609: // (green)
6053 element = EL_CHAR_LESS;
6056 case 0x160a: // (green)
6057 element = EL_CHAR_GREATER;
6060 case 0x160b: // (green)
6061 element = EL_CHAR_QUESTION;
6064 case 0x160c: // (green)
6065 element = EL_CHAR_COPYRIGHT;
6068 case 0x160d: // (green)
6069 element = EL_CHAR_UP;
6072 case 0x160e: // (green)
6073 element = EL_CHAR_DOWN;
6076 case 0x160f: // (green)
6077 element = EL_CHAR_BUTTON;
6080 case 0x1610: // (green)
6081 element = EL_CHAR_PLUS;
6084 case 0x1611: // (green)
6085 element = EL_CHAR_MINUS;
6088 case 0x1612: // (green)
6089 element = EL_CHAR_APOSTROPHE;
6092 case 0x1613: // (green)
6093 element = EL_CHAR_PARENLEFT;
6096 case 0x1614: // (green)
6097 element = EL_CHAR_PARENRIGHT;
6100 case 0x1615: // (blue steel)
6101 element = EL_STEEL_CHAR_A;
6104 case 0x1616: // (blue steel)
6105 element = EL_STEEL_CHAR_B;
6108 case 0x1617: // (blue steel)
6109 element = EL_STEEL_CHAR_C;
6112 case 0x1618: // (blue steel)
6113 element = EL_STEEL_CHAR_D;
6116 case 0x1619: // (blue steel)
6117 element = EL_STEEL_CHAR_E;
6120 case 0x161a: // (blue steel)
6121 element = EL_STEEL_CHAR_F;
6124 case 0x161b: // (blue steel)
6125 element = EL_STEEL_CHAR_G;
6128 case 0x161c: // (blue steel)
6129 element = EL_STEEL_CHAR_H;
6132 case 0x161d: // (blue steel)
6133 element = EL_STEEL_CHAR_I;
6136 case 0x161e: // (blue steel)
6137 element = EL_STEEL_CHAR_J;
6140 case 0x161f: // (blue steel)
6141 element = EL_STEEL_CHAR_K;
6144 case 0x1620: // (blue steel)
6145 element = EL_STEEL_CHAR_L;
6148 case 0x1621: // (blue steel)
6149 element = EL_STEEL_CHAR_M;
6152 case 0x1622: // (blue steel)
6153 element = EL_STEEL_CHAR_N;
6156 case 0x1623: // (blue steel)
6157 element = EL_STEEL_CHAR_O;
6160 case 0x1624: // (blue steel)
6161 element = EL_STEEL_CHAR_P;
6164 case 0x1625: // (blue steel)
6165 element = EL_STEEL_CHAR_Q;
6168 case 0x1626: // (blue steel)
6169 element = EL_STEEL_CHAR_R;
6172 case 0x1627: // (blue steel)
6173 element = EL_STEEL_CHAR_S;
6176 case 0x1628: // (blue steel)
6177 element = EL_STEEL_CHAR_T;
6180 case 0x1629: // (blue steel)
6181 element = EL_STEEL_CHAR_U;
6184 case 0x162a: // (blue steel)
6185 element = EL_STEEL_CHAR_V;
6188 case 0x162b: // (blue steel)
6189 element = EL_STEEL_CHAR_W;
6192 case 0x162c: // (blue steel)
6193 element = EL_STEEL_CHAR_X;
6196 case 0x162d: // (blue steel)
6197 element = EL_STEEL_CHAR_Y;
6200 case 0x162e: // (blue steel)
6201 element = EL_STEEL_CHAR_Z;
6204 case 0x162f: // (blue steel)
6205 element = EL_STEEL_CHAR_AUMLAUT;
6208 case 0x1630: // (blue steel)
6209 element = EL_STEEL_CHAR_OUMLAUT;
6212 case 0x1631: // (blue steel)
6213 element = EL_STEEL_CHAR_UUMLAUT;
6216 case 0x1632: // (blue steel)
6217 element = EL_STEEL_CHAR_0;
6220 case 0x1633: // (blue steel)
6221 element = EL_STEEL_CHAR_1;
6224 case 0x1634: // (blue steel)
6225 element = EL_STEEL_CHAR_2;
6228 case 0x1635: // (blue steel)
6229 element = EL_STEEL_CHAR_3;
6232 case 0x1636: // (blue steel)
6233 element = EL_STEEL_CHAR_4;
6236 case 0x1637: // (blue steel)
6237 element = EL_STEEL_CHAR_5;
6240 case 0x1638: // (blue steel)
6241 element = EL_STEEL_CHAR_6;
6244 case 0x1639: // (blue steel)
6245 element = EL_STEEL_CHAR_7;
6248 case 0x163a: // (blue steel)
6249 element = EL_STEEL_CHAR_8;
6252 case 0x163b: // (blue steel)
6253 element = EL_STEEL_CHAR_9;
6256 case 0x163c: // (blue steel)
6257 element = EL_STEEL_CHAR_PERIOD;
6260 case 0x163d: // (blue steel)
6261 element = EL_STEEL_CHAR_EXCLAM;
6264 case 0x163e: // (blue steel)
6265 element = EL_STEEL_CHAR_COLON;
6268 case 0x163f: // (blue steel)
6269 element = EL_STEEL_CHAR_LESS;
6272 case 0x1640: // (blue steel)
6273 element = EL_STEEL_CHAR_GREATER;
6276 case 0x1641: // (blue steel)
6277 element = EL_STEEL_CHAR_QUESTION;
6280 case 0x1642: // (blue steel)
6281 element = EL_STEEL_CHAR_COPYRIGHT;
6284 case 0x1643: // (blue steel)
6285 element = EL_STEEL_CHAR_UP;
6288 case 0x1644: // (blue steel)
6289 element = EL_STEEL_CHAR_DOWN;
6292 case 0x1645: // (blue steel)
6293 element = EL_STEEL_CHAR_BUTTON;
6296 case 0x1646: // (blue steel)
6297 element = EL_STEEL_CHAR_PLUS;
6300 case 0x1647: // (blue steel)
6301 element = EL_STEEL_CHAR_MINUS;
6304 case 0x1648: // (blue steel)
6305 element = EL_STEEL_CHAR_APOSTROPHE;
6308 case 0x1649: // (blue steel)
6309 element = EL_STEEL_CHAR_PARENLEFT;
6312 case 0x164a: // (blue steel)
6313 element = EL_STEEL_CHAR_PARENRIGHT;
6316 case 0x164b: // (green steel)
6317 element = EL_STEEL_CHAR_A;
6320 case 0x164c: // (green steel)
6321 element = EL_STEEL_CHAR_B;
6324 case 0x164d: // (green steel)
6325 element = EL_STEEL_CHAR_C;
6328 case 0x164e: // (green steel)
6329 element = EL_STEEL_CHAR_D;
6332 case 0x164f: // (green steel)
6333 element = EL_STEEL_CHAR_E;
6336 case 0x1650: // (green steel)
6337 element = EL_STEEL_CHAR_F;
6340 case 0x1651: // (green steel)
6341 element = EL_STEEL_CHAR_G;
6344 case 0x1652: // (green steel)
6345 element = EL_STEEL_CHAR_H;
6348 case 0x1653: // (green steel)
6349 element = EL_STEEL_CHAR_I;
6352 case 0x1654: // (green steel)
6353 element = EL_STEEL_CHAR_J;
6356 case 0x1655: // (green steel)
6357 element = EL_STEEL_CHAR_K;
6360 case 0x1656: // (green steel)
6361 element = EL_STEEL_CHAR_L;
6364 case 0x1657: // (green steel)
6365 element = EL_STEEL_CHAR_M;
6368 case 0x1658: // (green steel)
6369 element = EL_STEEL_CHAR_N;
6372 case 0x1659: // (green steel)
6373 element = EL_STEEL_CHAR_O;
6376 case 0x165a: // (green steel)
6377 element = EL_STEEL_CHAR_P;
6380 case 0x165b: // (green steel)
6381 element = EL_STEEL_CHAR_Q;
6384 case 0x165c: // (green steel)
6385 element = EL_STEEL_CHAR_R;
6388 case 0x165d: // (green steel)
6389 element = EL_STEEL_CHAR_S;
6392 case 0x165e: // (green steel)
6393 element = EL_STEEL_CHAR_T;
6396 case 0x165f: // (green steel)
6397 element = EL_STEEL_CHAR_U;
6400 case 0x1660: // (green steel)
6401 element = EL_STEEL_CHAR_V;
6404 case 0x1661: // (green steel)
6405 element = EL_STEEL_CHAR_W;
6408 case 0x1662: // (green steel)
6409 element = EL_STEEL_CHAR_X;
6412 case 0x1663: // (green steel)
6413 element = EL_STEEL_CHAR_Y;
6416 case 0x1664: // (green steel)
6417 element = EL_STEEL_CHAR_Z;
6420 case 0x1665: // (green steel)
6421 element = EL_STEEL_CHAR_AUMLAUT;
6424 case 0x1666: // (green steel)
6425 element = EL_STEEL_CHAR_OUMLAUT;
6428 case 0x1667: // (green steel)
6429 element = EL_STEEL_CHAR_UUMLAUT;
6432 case 0x1668: // (green steel)
6433 element = EL_STEEL_CHAR_0;
6436 case 0x1669: // (green steel)
6437 element = EL_STEEL_CHAR_1;
6440 case 0x166a: // (green steel)
6441 element = EL_STEEL_CHAR_2;
6444 case 0x166b: // (green steel)
6445 element = EL_STEEL_CHAR_3;
6448 case 0x166c: // (green steel)
6449 element = EL_STEEL_CHAR_4;
6452 case 0x166d: // (green steel)
6453 element = EL_STEEL_CHAR_5;
6456 case 0x166e: // (green steel)
6457 element = EL_STEEL_CHAR_6;
6460 case 0x166f: // (green steel)
6461 element = EL_STEEL_CHAR_7;
6464 case 0x1670: // (green steel)
6465 element = EL_STEEL_CHAR_8;
6468 case 0x1671: // (green steel)
6469 element = EL_STEEL_CHAR_9;
6472 case 0x1672: // (green steel)
6473 element = EL_STEEL_CHAR_PERIOD;
6476 case 0x1673: // (green steel)
6477 element = EL_STEEL_CHAR_EXCLAM;
6480 case 0x1674: // (green steel)
6481 element = EL_STEEL_CHAR_COLON;
6484 case 0x1675: // (green steel)
6485 element = EL_STEEL_CHAR_LESS;
6488 case 0x1676: // (green steel)
6489 element = EL_STEEL_CHAR_GREATER;
6492 case 0x1677: // (green steel)
6493 element = EL_STEEL_CHAR_QUESTION;
6496 case 0x1678: // (green steel)
6497 element = EL_STEEL_CHAR_COPYRIGHT;
6500 case 0x1679: // (green steel)
6501 element = EL_STEEL_CHAR_UP;
6504 case 0x167a: // (green steel)
6505 element = EL_STEEL_CHAR_DOWN;
6508 case 0x167b: // (green steel)
6509 element = EL_STEEL_CHAR_BUTTON;
6512 case 0x167c: // (green steel)
6513 element = EL_STEEL_CHAR_PLUS;
6516 case 0x167d: // (green steel)
6517 element = EL_STEEL_CHAR_MINUS;
6520 case 0x167e: // (green steel)
6521 element = EL_STEEL_CHAR_APOSTROPHE;
6524 case 0x167f: // (green steel)
6525 element = EL_STEEL_CHAR_PARENLEFT;
6528 case 0x1680: // (green steel)
6529 element = EL_STEEL_CHAR_PARENRIGHT;
6532 case 0x1681: // gate (red)
6533 element = EL_EM_GATE_1;
6536 case 0x1682: // secret gate (red)
6537 element = EL_EM_GATE_1_GRAY;
6540 case 0x1683: // gate (yellow)
6541 element = EL_EM_GATE_2;
6544 case 0x1684: // secret gate (yellow)
6545 element = EL_EM_GATE_2_GRAY;
6548 case 0x1685: // gate (blue)
6549 element = EL_EM_GATE_4;
6552 case 0x1686: // secret gate (blue)
6553 element = EL_EM_GATE_4_GRAY;
6556 case 0x1687: // gate (green)
6557 element = EL_EM_GATE_3;
6560 case 0x1688: // secret gate (green)
6561 element = EL_EM_GATE_3_GRAY;
6564 case 0x1689: // gate (white)
6565 element = EL_DC_GATE_WHITE;
6568 case 0x168a: // secret gate (white)
6569 element = EL_DC_GATE_WHITE_GRAY;
6572 case 0x168b: // secret gate (no key)
6573 element = EL_DC_GATE_FAKE_GRAY;
6577 element = EL_ROBOT_WHEEL;
6581 element = EL_DC_TIMEGATE_SWITCH;
6585 element = EL_ACID_POOL_BOTTOM;
6589 element = EL_ACID_POOL_TOPLEFT;
6593 element = EL_ACID_POOL_TOPRIGHT;
6597 element = EL_ACID_POOL_BOTTOMLEFT;
6601 element = EL_ACID_POOL_BOTTOMRIGHT;
6605 element = EL_STEELWALL;
6609 element = EL_STEELWALL_SLIPPERY;
6612 case 0x1695: // steel wall (not round)
6613 element = EL_STEELWALL;
6616 case 0x1696: // steel wall (left)
6617 element = EL_DC_STEELWALL_1_LEFT;
6620 case 0x1697: // steel wall (bottom)
6621 element = EL_DC_STEELWALL_1_BOTTOM;
6624 case 0x1698: // steel wall (right)
6625 element = EL_DC_STEELWALL_1_RIGHT;
6628 case 0x1699: // steel wall (top)
6629 element = EL_DC_STEELWALL_1_TOP;
6632 case 0x169a: // steel wall (left/bottom)
6633 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6636 case 0x169b: // steel wall (right/bottom)
6637 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6640 case 0x169c: // steel wall (right/top)
6641 element = EL_DC_STEELWALL_1_TOPRIGHT;
6644 case 0x169d: // steel wall (left/top)
6645 element = EL_DC_STEELWALL_1_TOPLEFT;
6648 case 0x169e: // steel wall (right/bottom small)
6649 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6652 case 0x169f: // steel wall (left/bottom small)
6653 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6656 case 0x16a0: // steel wall (right/top small)
6657 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6660 case 0x16a1: // steel wall (left/top small)
6661 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6664 case 0x16a2: // steel wall (left/right)
6665 element = EL_DC_STEELWALL_1_VERTICAL;
6668 case 0x16a3: // steel wall (top/bottom)
6669 element = EL_DC_STEELWALL_1_HORIZONTAL;
6672 case 0x16a4: // steel wall 2 (left end)
6673 element = EL_DC_STEELWALL_2_LEFT;
6676 case 0x16a5: // steel wall 2 (right end)
6677 element = EL_DC_STEELWALL_2_RIGHT;
6680 case 0x16a6: // steel wall 2 (top end)
6681 element = EL_DC_STEELWALL_2_TOP;
6684 case 0x16a7: // steel wall 2 (bottom end)
6685 element = EL_DC_STEELWALL_2_BOTTOM;
6688 case 0x16a8: // steel wall 2 (left/right)
6689 element = EL_DC_STEELWALL_2_HORIZONTAL;
6692 case 0x16a9: // steel wall 2 (up/down)
6693 element = EL_DC_STEELWALL_2_VERTICAL;
6696 case 0x16aa: // steel wall 2 (mid)
6697 element = EL_DC_STEELWALL_2_MIDDLE;
6701 element = EL_SIGN_EXCLAMATION;
6705 element = EL_SIGN_RADIOACTIVITY;
6709 element = EL_SIGN_STOP;
6713 element = EL_SIGN_WHEELCHAIR;
6717 element = EL_SIGN_PARKING;
6721 element = EL_SIGN_NO_ENTRY;
6725 element = EL_SIGN_HEART;
6729 element = EL_SIGN_GIVE_WAY;
6733 element = EL_SIGN_ENTRY_FORBIDDEN;
6737 element = EL_SIGN_EMERGENCY_EXIT;
6741 element = EL_SIGN_YIN_YANG;
6745 element = EL_WALL_EMERALD;
6749 element = EL_WALL_DIAMOND;
6753 element = EL_WALL_PEARL;
6757 element = EL_WALL_CRYSTAL;
6761 element = EL_INVISIBLE_WALL;
6765 element = EL_INVISIBLE_STEELWALL;
6769 // EL_INVISIBLE_SAND
6772 element = EL_LIGHT_SWITCH;
6776 element = EL_ENVELOPE_1;
6780 if (element >= 0x0117 && element <= 0x036e) // (?)
6781 element = EL_DIAMOND;
6782 else if (element >= 0x042d && element <= 0x0684) // (?)
6783 element = EL_EMERALD;
6784 else if (element >= 0x157c && element <= 0x158b)
6786 else if (element >= 0x1590 && element <= 0x159f)
6787 element = EL_DC_LANDMINE;
6788 else if (element >= 0x16bc && element <= 0x16cb)
6789 element = EL_INVISIBLE_SAND;
6792 Warn("unknown Diamond Caves element 0x%04x", element);
6794 element = EL_UNKNOWN;
6799 return getMappedElement(element);
6802 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6804 byte header[DC_LEVEL_HEADER_SIZE];
6806 int envelope_header_pos = 62;
6807 int envelope_content_pos = 94;
6808 int level_name_pos = 251;
6809 int level_author_pos = 292;
6810 int envelope_header_len;
6811 int envelope_content_len;
6813 int level_author_len;
6815 int num_yamyam_contents;
6818 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6820 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6822 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6824 header[i * 2 + 0] = header_word >> 8;
6825 header[i * 2 + 1] = header_word & 0xff;
6828 // read some values from level header to check level decoding integrity
6829 fieldx = header[6] | (header[7] << 8);
6830 fieldy = header[8] | (header[9] << 8);
6831 num_yamyam_contents = header[60] | (header[61] << 8);
6833 // do some simple sanity checks to ensure that level was correctly decoded
6834 if (fieldx < 1 || fieldx > 256 ||
6835 fieldy < 1 || fieldy > 256 ||
6836 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6838 level->no_valid_file = TRUE;
6840 Warn("cannot decode level from stream -- using empty level");
6845 // maximum envelope header size is 31 bytes
6846 envelope_header_len = header[envelope_header_pos];
6847 // maximum envelope content size is 110 (156?) bytes
6848 envelope_content_len = header[envelope_content_pos];
6850 // maximum level title size is 40 bytes
6851 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6852 // maximum level author size is 30 (51?) bytes
6853 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6857 for (i = 0; i < envelope_header_len; i++)
6858 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6859 level->envelope[0].text[envelope_size++] =
6860 header[envelope_header_pos + 1 + i];
6862 if (envelope_header_len > 0 && envelope_content_len > 0)
6864 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6865 level->envelope[0].text[envelope_size++] = '\n';
6866 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6867 level->envelope[0].text[envelope_size++] = '\n';
6870 for (i = 0; i < envelope_content_len; i++)
6871 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6872 level->envelope[0].text[envelope_size++] =
6873 header[envelope_content_pos + 1 + i];
6875 level->envelope[0].text[envelope_size] = '\0';
6877 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6878 level->envelope[0].ysize = 10;
6879 level->envelope[0].autowrap = TRUE;
6880 level->envelope[0].centered = TRUE;
6882 for (i = 0; i < level_name_len; i++)
6883 level->name[i] = header[level_name_pos + 1 + i];
6884 level->name[level_name_len] = '\0';
6886 for (i = 0; i < level_author_len; i++)
6887 level->author[i] = header[level_author_pos + 1 + i];
6888 level->author[level_author_len] = '\0';
6890 num_yamyam_contents = header[60] | (header[61] << 8);
6891 level->num_yamyam_contents =
6892 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6894 for (i = 0; i < num_yamyam_contents; i++)
6896 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6898 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6899 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6901 if (i < MAX_ELEMENT_CONTENTS)
6902 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6906 fieldx = header[6] | (header[7] << 8);
6907 fieldy = header[8] | (header[9] << 8);
6908 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6909 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6911 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6913 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6914 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6916 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6917 level->field[x][y] = getMappedElement_DC(element_dc);
6920 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6921 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6922 level->field[x][y] = EL_PLAYER_1;
6924 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6925 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6926 level->field[x][y] = EL_PLAYER_2;
6928 level->gems_needed = header[18] | (header[19] << 8);
6930 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6931 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6932 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6933 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6934 level->score[SC_NUT] = header[28] | (header[29] << 8);
6935 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6936 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6937 level->score[SC_BUG] = header[34] | (header[35] << 8);
6938 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6939 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6940 level->score[SC_KEY] = header[40] | (header[41] << 8);
6941 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6943 level->time = header[44] | (header[45] << 8);
6945 level->amoeba_speed = header[46] | (header[47] << 8);
6946 level->time_light = header[48] | (header[49] << 8);
6947 level->time_timegate = header[50] | (header[51] << 8);
6948 level->time_wheel = header[52] | (header[53] << 8);
6949 level->time_magic_wall = header[54] | (header[55] << 8);
6950 level->extra_time = header[56] | (header[57] << 8);
6951 level->shield_normal_time = header[58] | (header[59] << 8);
6953 // shield and extra time elements do not have a score
6954 level->score[SC_SHIELD] = 0;
6955 level->extra_time_score = 0;
6957 // set time for normal and deadly shields to the same value
6958 level->shield_deadly_time = level->shield_normal_time;
6960 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6961 // can slip down from flat walls, like normal walls and steel walls
6962 level->em_slippery_gems = TRUE;
6964 // time score is counted for each 10 seconds left in Diamond Caves levels
6965 level->time_score_base = 10;
6968 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6969 struct LevelFileInfo *level_file_info,
6970 boolean level_info_only)
6972 char *filename = level_file_info->filename;
6974 int num_magic_bytes = 8;
6975 char magic_bytes[num_magic_bytes + 1];
6976 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6978 if (!(file = openFile(filename, MODE_READ)))
6980 level->no_valid_file = TRUE;
6982 if (!level_info_only)
6983 Warn("cannot read level '%s' -- using empty level", filename);
6988 // fseek(file, 0x0000, SEEK_SET);
6990 if (level_file_info->packed)
6992 // read "magic bytes" from start of file
6993 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6994 magic_bytes[0] = '\0';
6996 // check "magic bytes" for correct file format
6997 if (!strPrefix(magic_bytes, "DC2"))
6999 level->no_valid_file = TRUE;
7001 Warn("unknown DC level file '%s' -- using empty level", filename);
7006 if (strPrefix(magic_bytes, "DC2Win95") ||
7007 strPrefix(magic_bytes, "DC2Win98"))
7009 int position_first_level = 0x00fa;
7010 int extra_bytes = 4;
7013 // advance file stream to first level inside the level package
7014 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7016 // each block of level data is followed by block of non-level data
7017 num_levels_to_skip *= 2;
7019 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
7020 while (num_levels_to_skip >= 0)
7022 // advance file stream to next level inside the level package
7023 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7025 level->no_valid_file = TRUE;
7027 Warn("cannot fseek in file '%s' -- using empty level", filename);
7032 // skip apparently unused extra bytes following each level
7033 ReadUnusedBytesFromFile(file, extra_bytes);
7035 // read size of next level in level package
7036 skip_bytes = getFile32BitLE(file);
7038 num_levels_to_skip--;
7043 level->no_valid_file = TRUE;
7045 Warn("unknown DC2 level file '%s' -- using empty level", filename);
7051 LoadLevelFromFileStream_DC(file, level);
7057 // ----------------------------------------------------------------------------
7058 // functions for loading SB level
7059 // ----------------------------------------------------------------------------
7061 int getMappedElement_SB(int element_ascii, boolean use_ces)
7069 sb_element_mapping[] =
7071 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
7072 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
7073 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
7074 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
7075 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
7076 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
7077 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
7078 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
7085 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7086 if (element_ascii == sb_element_mapping[i].ascii)
7087 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7089 return EL_UNDEFINED;
7092 static void SetLevelSettings_SB(struct LevelInfo *level)
7096 level->use_step_counter = TRUE;
7099 level->score[SC_TIME_BONUS] = 0;
7100 level->time_score_base = 1;
7101 level->rate_time_over_score = TRUE;
7104 level->auto_exit_sokoban = TRUE;
7107 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7108 struct LevelFileInfo *level_file_info,
7109 boolean level_info_only)
7111 char *filename = level_file_info->filename;
7112 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7113 char last_comment[MAX_LINE_LEN];
7114 char level_name[MAX_LINE_LEN];
7117 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7118 boolean read_continued_line = FALSE;
7119 boolean reading_playfield = FALSE;
7120 boolean got_valid_playfield_line = FALSE;
7121 boolean invalid_playfield_char = FALSE;
7122 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7123 int file_level_nr = 0;
7124 int x = 0, y = 0; // initialized to make compilers happy
7126 last_comment[0] = '\0';
7127 level_name[0] = '\0';
7129 if (!(file = openFile(filename, MODE_READ)))
7131 level->no_valid_file = TRUE;
7133 if (!level_info_only)
7134 Warn("cannot read level '%s' -- using empty level", filename);
7139 while (!checkEndOfFile(file))
7141 // level successfully read, but next level may follow here
7142 if (!got_valid_playfield_line && reading_playfield)
7144 // read playfield from single level file -- skip remaining file
7145 if (!level_file_info->packed)
7148 if (file_level_nr >= num_levels_to_skip)
7153 last_comment[0] = '\0';
7154 level_name[0] = '\0';
7156 reading_playfield = FALSE;
7159 got_valid_playfield_line = FALSE;
7161 // read next line of input file
7162 if (!getStringFromFile(file, line, MAX_LINE_LEN))
7165 // cut trailing line break (this can be newline and/or carriage return)
7166 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7167 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7170 // copy raw input line for later use (mainly debugging output)
7171 strcpy(line_raw, line);
7173 if (read_continued_line)
7175 // append new line to existing line, if there is enough space
7176 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7177 strcat(previous_line, line_ptr);
7179 strcpy(line, previous_line); // copy storage buffer to line
7181 read_continued_line = FALSE;
7184 // if the last character is '\', continue at next line
7185 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7187 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
7188 strcpy(previous_line, line); // copy line to storage buffer
7190 read_continued_line = TRUE;
7196 if (line[0] == '\0')
7199 // extract comment text from comment line
7202 for (line_ptr = line; *line_ptr; line_ptr++)
7203 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7206 strcpy(last_comment, line_ptr);
7211 // extract level title text from line containing level title
7212 if (line[0] == '\'')
7214 strcpy(level_name, &line[1]);
7216 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7217 level_name[strlen(level_name) - 1] = '\0';
7222 // skip lines containing only spaces (or empty lines)
7223 for (line_ptr = line; *line_ptr; line_ptr++)
7224 if (*line_ptr != ' ')
7226 if (*line_ptr == '\0')
7229 // at this point, we have found a line containing part of a playfield
7231 got_valid_playfield_line = TRUE;
7233 if (!reading_playfield)
7235 reading_playfield = TRUE;
7236 invalid_playfield_char = FALSE;
7238 for (x = 0; x < MAX_LEV_FIELDX; x++)
7239 for (y = 0; y < MAX_LEV_FIELDY; y++)
7240 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7245 // start with topmost tile row
7249 // skip playfield line if larger row than allowed
7250 if (y >= MAX_LEV_FIELDY)
7253 // start with leftmost tile column
7256 // read playfield elements from line
7257 for (line_ptr = line; *line_ptr; line_ptr++)
7259 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7261 // stop parsing playfield line if larger column than allowed
7262 if (x >= MAX_LEV_FIELDX)
7265 if (mapped_sb_element == EL_UNDEFINED)
7267 invalid_playfield_char = TRUE;
7272 level->field[x][y] = mapped_sb_element;
7274 // continue with next tile column
7277 level->fieldx = MAX(x, level->fieldx);
7280 if (invalid_playfield_char)
7282 // if first playfield line, treat invalid lines as comment lines
7284 reading_playfield = FALSE;
7289 // continue with next tile row
7297 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7298 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7300 if (!reading_playfield)
7302 level->no_valid_file = TRUE;
7304 Warn("cannot read level '%s' -- using empty level", filename);
7309 if (*level_name != '\0')
7311 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7312 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7314 else if (*last_comment != '\0')
7316 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7317 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7321 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7324 // set all empty fields beyond the border walls to invisible steel wall
7325 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7327 if ((x == 0 || x == level->fieldx - 1 ||
7328 y == 0 || y == level->fieldy - 1) &&
7329 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7330 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7331 level->field, level->fieldx, level->fieldy);
7334 // set special level settings for Sokoban levels
7335 SetLevelSettings_SB(level);
7337 if (load_xsb_to_ces)
7339 // special global settings can now be set in level template
7340 level->use_custom_template = TRUE;
7345 // -------------------------------------------------------------------------
7346 // functions for handling native levels
7347 // -------------------------------------------------------------------------
7349 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7350 struct LevelFileInfo *level_file_info,
7351 boolean level_info_only)
7355 // determine position of requested level inside level package
7356 if (level_file_info->packed)
7357 pos = level_file_info->nr - leveldir_current->first_level;
7359 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7360 level->no_valid_file = TRUE;
7363 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7364 struct LevelFileInfo *level_file_info,
7365 boolean level_info_only)
7367 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7368 level->no_valid_file = TRUE;
7371 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7372 struct LevelFileInfo *level_file_info,
7373 boolean level_info_only)
7377 // determine position of requested level inside level package
7378 if (level_file_info->packed)
7379 pos = level_file_info->nr - leveldir_current->first_level;
7381 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7382 level->no_valid_file = TRUE;
7385 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7386 struct LevelFileInfo *level_file_info,
7387 boolean level_info_only)
7389 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7390 level->no_valid_file = TRUE;
7393 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7395 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7396 CopyNativeLevel_RND_to_BD(level);
7397 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7398 CopyNativeLevel_RND_to_EM(level);
7399 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7400 CopyNativeLevel_RND_to_SP(level);
7401 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7402 CopyNativeLevel_RND_to_MM(level);
7405 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7407 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7408 CopyNativeLevel_BD_to_RND(level);
7409 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7410 CopyNativeLevel_EM_to_RND(level);
7411 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7412 CopyNativeLevel_SP_to_RND(level);
7413 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7414 CopyNativeLevel_MM_to_RND(level);
7417 void SaveNativeLevel(struct LevelInfo *level)
7419 // saving native level files only supported for some game engines
7420 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7421 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7424 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7425 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7426 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7427 char *filename = getLevelFilenameFromBasename(basename);
7429 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7432 boolean success = FALSE;
7434 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7436 CopyNativeLevel_RND_to_BD(level);
7437 // CopyNativeTape_RND_to_BD(level);
7439 success = SaveNativeLevel_BD(filename);
7441 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7443 CopyNativeLevel_RND_to_SP(level);
7444 CopyNativeTape_RND_to_SP(level);
7446 success = SaveNativeLevel_SP(filename);
7450 Request("Native level file saved!", REQ_CONFIRM);
7452 Request("Failed to save native level file!", REQ_CONFIRM);
7456 // ----------------------------------------------------------------------------
7457 // functions for loading generic level
7458 // ----------------------------------------------------------------------------
7460 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7461 struct LevelFileInfo *level_file_info,
7462 boolean level_info_only)
7464 // always start with reliable default values
7465 setLevelInfoToDefaults(level, level_info_only, TRUE);
7467 switch (level_file_info->type)
7469 case LEVEL_FILE_TYPE_RND:
7470 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7473 case LEVEL_FILE_TYPE_BD:
7474 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7475 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7478 case LEVEL_FILE_TYPE_EM:
7479 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7480 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7483 case LEVEL_FILE_TYPE_SP:
7484 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7485 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7488 case LEVEL_FILE_TYPE_MM:
7489 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7490 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7493 case LEVEL_FILE_TYPE_DC:
7494 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7497 case LEVEL_FILE_TYPE_SB:
7498 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7502 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7506 // if level file is invalid, restore level structure to default values
7507 if (level->no_valid_file)
7508 setLevelInfoToDefaults(level, level_info_only, FALSE);
7510 if (check_special_flags("use_native_bd_game_engine"))
7511 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7513 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7514 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7516 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7517 CopyNativeLevel_Native_to_RND(level);
7520 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7522 static struct LevelFileInfo level_file_info;
7524 // always start with reliable default values
7525 setFileInfoToDefaults(&level_file_info);
7527 level_file_info.nr = 0; // unknown level number
7528 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7530 setString(&level_file_info.filename, filename);
7532 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7535 static void LoadLevel_FixEnvelopes(struct LevelInfo *level, boolean skip_single_lines)
7537 // This function removes newlines in envelopes after lines of text ending in the last column
7538 // of the envelope. In earlier versions, these newlines were removed when displaying envelopes,
7539 // but caused trouble in the level editor. In version 4.3.2.3, this problem was partially
7540 // fixed in the level editor (but only for single full-width text lines followed by a newline,
7541 // not for multiple lines ending in the last column, followed by a newline), but now produced
7542 // unwanted newlines in the game for envelopes stored by previous game versions, which was not
7543 // intended by the level author (and sometimes caused text lines not being displayed anymore at
7544 // the bottom of the envelope).
7546 // This function should solve these problems by removing such newline characters from envelopes
7547 // stored by older game versions.
7551 for (envelope_nr = 0; envelope_nr < NUM_ENVELOPES; envelope_nr++)
7553 char *envelope_ptr = level->envelope[envelope_nr].text;
7554 int envelope_xsize = level->envelope[envelope_nr].xsize;
7555 int envelope_size = strlen(envelope_ptr);
7559 for (i = 0; i < envelope_size; i++)
7561 // check for newlines in envelope
7562 if (envelope_ptr[i] == '\n')
7564 int line_length = i - start;
7566 // check for (non-empty) lines that are a multiple of the envelope width,
7567 // causing a line break inside the envelope (text area in editor and in game)
7568 if (line_length > 0 && line_length % envelope_xsize == 0)
7570 // special case: skip fixing single lines for newer versions
7571 boolean skip_fixing_line = (line_length == 1 && skip_single_lines);
7573 if (!skip_fixing_line)
7577 // remove newline character from string
7578 for (j = i; j < envelope_size; j++)
7579 envelope_ptr[j] = envelope_ptr[j + 1];
7582 // continue with next line (that was copied over the newline)
7587 // continue with next character after newline
7595 static void LoadLevel_InitVersion(struct LevelInfo *level)
7599 if (leveldir_current == NULL) // only when dumping level
7602 // all engine modifications also valid for levels which use latest engine
7603 if (level->game_version < VERSION_IDENT(3,2,0,5))
7605 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7606 level->time_score_base = 10;
7609 if (leveldir_current->latest_engine)
7611 // ---------- use latest game engine --------------------------------------
7613 /* For all levels which are forced to use the latest game engine version
7614 (normally all but user contributed, private and undefined levels), set
7615 the game engine version to the actual version; this allows for actual
7616 corrections in the game engine to take effect for existing, converted
7617 levels (from "classic" or other existing games) to make the emulation
7618 of the corresponding game more accurate, while (hopefully) not breaking
7619 existing levels created from other players. */
7621 level->game_version = GAME_VERSION_ACTUAL;
7623 /* Set special EM style gems behaviour: EM style gems slip down from
7624 normal, steel and growing wall. As this is a more fundamental change,
7625 it seems better to set the default behaviour to "off" (as it is more
7626 natural) and make it configurable in the level editor (as a property
7627 of gem style elements). Already existing converted levels (neither
7628 private nor contributed levels) are changed to the new behaviour. */
7630 if (level->file_version < FILE_VERSION_2_0)
7631 level->em_slippery_gems = TRUE;
7636 // ---------- use game engine the level was created with --------------------
7638 /* For all levels which are not forced to use the latest game engine
7639 version (normally user contributed, private and undefined levels),
7640 use the version of the game engine the levels were created for.
7642 Since 2.0.1, the game engine version is now directly stored
7643 in the level file (chunk "VERS"), so there is no need anymore
7644 to set the game version from the file version (except for old,
7645 pre-2.0 levels, where the game version is still taken from the
7646 file format version used to store the level -- see above). */
7648 // player was faster than enemies in 1.0.0 and before
7649 if (level->file_version == FILE_VERSION_1_0)
7650 for (i = 0; i < MAX_PLAYERS; i++)
7651 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7653 // default behaviour for EM style gems was "slippery" only in 2.0.1
7654 if (level->game_version == VERSION_IDENT(2,0,1,0))
7655 level->em_slippery_gems = TRUE;
7657 // springs could be pushed over pits before (pre-release version) 2.2.0
7658 if (level->game_version < VERSION_IDENT(2,2,0,0))
7659 level->use_spring_bug = TRUE;
7661 if (level->game_version < VERSION_IDENT(3,2,0,5))
7663 // time orb caused limited time in endless time levels before 3.2.0-5
7664 level->use_time_orb_bug = TRUE;
7666 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7667 level->block_snap_field = FALSE;
7669 // extra time score was same value as time left score before 3.2.0-5
7670 level->extra_time_score = level->score[SC_TIME_BONUS];
7673 if (level->game_version < VERSION_IDENT(3,2,0,7))
7675 // default behaviour for snapping was "not continuous" before 3.2.0-7
7676 level->continuous_snapping = FALSE;
7679 // only few elements were able to actively move into acid before 3.1.0
7680 // trigger settings did not exist before 3.1.0; set to default "any"
7681 if (level->game_version < VERSION_IDENT(3,1,0,0))
7683 // correct "can move into acid" settings (all zero in old levels)
7685 level->can_move_into_acid_bits = 0; // nothing can move into acid
7686 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7688 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7689 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7690 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7691 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7693 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7694 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7696 // correct trigger settings (stored as zero == "none" in old levels)
7698 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7700 int element = EL_CUSTOM_START + i;
7701 struct ElementInfo *ei = &element_info[element];
7703 for (j = 0; j < ei->num_change_pages; j++)
7705 struct ElementChangeInfo *change = &ei->change_page[j];
7707 change->trigger_player = CH_PLAYER_ANY;
7708 change->trigger_page = CH_PAGE_ANY;
7713 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7715 int element = EL_CUSTOM_256;
7716 struct ElementInfo *ei = &element_info[element];
7717 struct ElementChangeInfo *change = &ei->change_page[0];
7719 /* This is needed to fix a problem that was caused by a bugfix in function
7720 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7721 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7722 not replace walkable elements, but instead just placed the player on it,
7723 without placing the Sokoban field under the player). Unfortunately, this
7724 breaks "Snake Bite" style levels when the snake is halfway through a door
7725 that just closes (the snake head is still alive and can be moved in this
7726 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7727 player (without Sokoban element) which then gets killed as designed). */
7729 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7730 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7731 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7732 change->target_element = EL_PLAYER_1;
7735 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7736 if (level->game_version < VERSION_IDENT(3,2,5,0))
7738 /* This is needed to fix a problem that was caused by a bugfix in function
7739 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7740 corrects the behaviour when a custom element changes to another custom
7741 element with a higher element number that has change actions defined.
7742 Normally, only one change per frame is allowed for custom elements.
7743 Therefore, it is checked if a custom element already changed in the
7744 current frame; if it did, subsequent changes are suppressed.
7745 Unfortunately, this is only checked for element changes, but not for
7746 change actions, which are still executed. As the function above loops
7747 through all custom elements from lower to higher, an element change
7748 resulting in a lower CE number won't be checked again, while a target
7749 element with a higher number will also be checked, and potential change
7750 actions will get executed for this CE, too (which is wrong), while
7751 further changes are ignored (which is correct). As this bugfix breaks
7752 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7753 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7754 behaviour for existing levels and tapes that make use of this bug */
7756 level->use_action_after_change_bug = TRUE;
7759 // not centering level after relocating player was default only in 3.2.3
7760 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7761 level->shifted_relocation = TRUE;
7763 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7764 if (level->game_version < VERSION_IDENT(3,2,6,0))
7765 level->em_explodes_by_fire = TRUE;
7767 // levels were solved by the first player entering an exit up to 4.1.0.0
7768 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7769 level->solved_by_one_player = TRUE;
7771 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7772 if (level->game_version < VERSION_IDENT(4,1,1,1))
7773 level->use_life_bugs = TRUE;
7775 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7776 if (level->game_version < VERSION_IDENT(4,1,1,1))
7777 level->sb_objects_needed = FALSE;
7779 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7780 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7781 level->finish_dig_collect = FALSE;
7783 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7784 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7785 level->keep_walkable_ce = TRUE;
7787 // envelopes may contain broken or too many line breaks before 4.4.0.0
7788 if (level->game_version < VERSION_IDENT(4,4,0,0))
7789 LoadLevel_FixEnvelopes(level, (level->game_version >= VERSION_IDENT(4,3,2,3)));
7792 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7794 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7797 // check if this level is (not) a Sokoban level
7798 for (y = 0; y < level->fieldy; y++)
7799 for (x = 0; x < level->fieldx; x++)
7800 if (!IS_SB_ELEMENT(Tile[x][y]))
7801 is_sokoban_level = FALSE;
7803 if (is_sokoban_level)
7805 // set special level settings for Sokoban levels
7806 SetLevelSettings_SB(level);
7810 static void LoadLevel_InitSettings(struct LevelInfo *level)
7812 // adjust level settings for (non-native) Sokoban-style levels
7813 LoadLevel_InitSettings_SB(level);
7815 // rename levels with title "nameless level" or if renaming is forced
7816 if (leveldir_current->empty_level_name != NULL &&
7817 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7818 leveldir_current->force_level_name))
7819 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7820 leveldir_current->empty_level_name, level_nr);
7823 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7827 // map elements that have changed in newer versions
7828 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7829 level->game_version);
7830 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7831 for (x = 0; x < 3; x++)
7832 for (y = 0; y < 3; y++)
7833 level->yamyam_content[i].e[x][y] =
7834 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7835 level->game_version);
7839 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7843 // map custom element change events that have changed in newer versions
7844 // (these following values were accidentally changed in version 3.0.1)
7845 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7846 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7848 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7850 int element = EL_CUSTOM_START + i;
7852 // order of checking and copying events to be mapped is important
7853 // (do not change the start and end value -- they are constant)
7854 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7856 if (HAS_CHANGE_EVENT(element, j - 2))
7858 SET_CHANGE_EVENT(element, j - 2, FALSE);
7859 SET_CHANGE_EVENT(element, j, TRUE);
7863 // order of checking and copying events to be mapped is important
7864 // (do not change the start and end value -- they are constant)
7865 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7867 if (HAS_CHANGE_EVENT(element, j - 1))
7869 SET_CHANGE_EVENT(element, j - 1, FALSE);
7870 SET_CHANGE_EVENT(element, j, TRUE);
7876 // initialize "can_change" field for old levels with only one change page
7877 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7879 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7881 int element = EL_CUSTOM_START + i;
7883 if (CAN_CHANGE(element))
7884 element_info[element].change->can_change = TRUE;
7888 // correct custom element values (for old levels without these options)
7889 if (level->game_version < VERSION_IDENT(3,1,1,0))
7891 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7893 int element = EL_CUSTOM_START + i;
7894 struct ElementInfo *ei = &element_info[element];
7896 if (ei->access_direction == MV_NO_DIRECTION)
7897 ei->access_direction = MV_ALL_DIRECTIONS;
7901 // correct custom element values (fix invalid values for all versions)
7904 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7906 int element = EL_CUSTOM_START + i;
7907 struct ElementInfo *ei = &element_info[element];
7909 for (j = 0; j < ei->num_change_pages; j++)
7911 struct ElementChangeInfo *change = &ei->change_page[j];
7913 if (change->trigger_player == CH_PLAYER_NONE)
7914 change->trigger_player = CH_PLAYER_ANY;
7916 if (change->trigger_side == CH_SIDE_NONE)
7917 change->trigger_side = CH_SIDE_ANY;
7922 // initialize "can_explode" field for old levels which did not store this
7923 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7924 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7926 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7928 int element = EL_CUSTOM_START + i;
7930 if (EXPLODES_1X1_OLD(element))
7931 element_info[element].explosion_type = EXPLODES_1X1;
7933 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7934 EXPLODES_SMASHED(element) ||
7935 EXPLODES_IMPACT(element)));
7939 // correct previously hard-coded move delay values for maze runner style
7940 if (level->game_version < VERSION_IDENT(3,1,1,0))
7942 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7944 int element = EL_CUSTOM_START + i;
7946 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7948 // previously hard-coded and therefore ignored
7949 element_info[element].move_delay_fixed = 9;
7950 element_info[element].move_delay_random = 0;
7955 // set some other uninitialized values of custom elements in older levels
7956 if (level->game_version < VERSION_IDENT(3,1,0,0))
7958 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7960 int element = EL_CUSTOM_START + i;
7962 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7964 element_info[element].explosion_delay = 17;
7965 element_info[element].ignition_delay = 8;
7969 // set mouse click change events to work for left/middle/right mouse button
7970 if (level->game_version < VERSION_IDENT(4,2,3,0))
7972 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7974 int element = EL_CUSTOM_START + i;
7975 struct ElementInfo *ei = &element_info[element];
7977 for (j = 0; j < ei->num_change_pages; j++)
7979 struct ElementChangeInfo *change = &ei->change_page[j];
7981 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7982 change->has_event[CE_PRESSED_BY_MOUSE] ||
7983 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7984 change->has_event[CE_MOUSE_PRESSED_ON_X])
7985 change->trigger_side = CH_SIDE_ANY;
7991 static void LoadLevel_InitElements(struct LevelInfo *level)
7993 LoadLevel_InitStandardElements(level);
7995 if (level->file_has_custom_elements)
7996 LoadLevel_InitCustomElements(level);
7998 // initialize element properties for level editor etc.
7999 InitElementPropertiesEngine(level->game_version);
8000 InitElementPropertiesGfxElement();
8003 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
8007 // map elements that have changed in newer versions
8008 for (y = 0; y < level->fieldy; y++)
8009 for (x = 0; x < level->fieldx; x++)
8010 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
8011 level->game_version);
8013 // clear unused playfield data (nicer if level gets resized in editor)
8014 for (x = 0; x < MAX_LEV_FIELDX; x++)
8015 for (y = 0; y < MAX_LEV_FIELDY; y++)
8016 if (x >= level->fieldx || y >= level->fieldy)
8017 level->field[x][y] = EL_EMPTY;
8019 // copy elements to runtime playfield array
8020 for (x = 0; x < MAX_LEV_FIELDX; x++)
8021 for (y = 0; y < MAX_LEV_FIELDY; y++)
8022 Tile[x][y] = level->field[x][y];
8024 // initialize level size variables for faster access
8025 lev_fieldx = level->fieldx;
8026 lev_fieldy = level->fieldy;
8028 // determine border element for this level
8029 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
8030 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
8035 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
8037 struct LevelFileInfo *level_file_info = &level->file_info;
8039 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
8040 CopyNativeLevel_RND_to_Native(level);
8043 static void LoadLevelTemplate_LoadAndInit(void)
8045 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
8047 LoadLevel_InitVersion(&level_template);
8048 LoadLevel_InitElements(&level_template);
8049 LoadLevel_InitSettings(&level_template);
8051 ActivateLevelTemplate();
8054 void LoadLevelTemplate(int nr)
8056 if (!fileExists(getGlobalLevelTemplateFilename()))
8058 Warn("no level template found for this level");
8063 setLevelFileInfo(&level_template.file_info, nr);
8065 LoadLevelTemplate_LoadAndInit();
8068 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
8070 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
8072 LoadLevelTemplate_LoadAndInit();
8075 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
8077 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
8079 if (level.use_custom_template)
8081 if (network_level != NULL)
8082 LoadNetworkLevelTemplate(network_level);
8084 LoadLevelTemplate(-1);
8087 LoadLevel_InitVersion(&level);
8088 LoadLevel_InitElements(&level);
8089 LoadLevel_InitPlayfield(&level);
8090 LoadLevel_InitSettings(&level);
8092 LoadLevel_InitNativeEngines(&level);
8095 void LoadLevel(int nr)
8097 SetLevelSetInfo(leveldir_current->identifier, nr);
8099 setLevelFileInfo(&level.file_info, nr);
8101 LoadLevel_LoadAndInit(NULL);
8104 void LoadLevelInfoOnly(int nr)
8106 setLevelFileInfo(&level.file_info, nr);
8108 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8111 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8113 SetLevelSetInfo(network_level->leveldir_identifier,
8114 network_level->file_info.nr);
8116 copyLevelFileInfo(&network_level->file_info, &level.file_info);
8118 LoadLevel_LoadAndInit(network_level);
8121 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8125 chunk_size += putFileVersion(file, level->file_version);
8126 chunk_size += putFileVersion(file, level->game_version);
8131 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8135 chunk_size += putFile16BitBE(file, level->creation_date.year);
8136 chunk_size += putFile8Bit(file, level->creation_date.month);
8137 chunk_size += putFile8Bit(file, level->creation_date.day);
8142 #if ENABLE_HISTORIC_CHUNKS
8143 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8147 putFile8Bit(file, level->fieldx);
8148 putFile8Bit(file, level->fieldy);
8150 putFile16BitBE(file, level->time);
8151 putFile16BitBE(file, level->gems_needed);
8153 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8154 putFile8Bit(file, level->name[i]);
8156 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8157 putFile8Bit(file, level->score[i]);
8159 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8160 for (y = 0; y < 3; y++)
8161 for (x = 0; x < 3; x++)
8162 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8163 level->yamyam_content[i].e[x][y]));
8164 putFile8Bit(file, level->amoeba_speed);
8165 putFile8Bit(file, level->time_magic_wall);
8166 putFile8Bit(file, level->time_wheel);
8167 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8168 level->amoeba_content));
8169 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8170 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8171 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8172 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8174 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8176 putFile8Bit(file, (level->block_last_field ? 1 : 0));
8177 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8178 putFile32BitBE(file, level->can_move_into_acid_bits);
8179 putFile8Bit(file, level->dont_collide_with_bits);
8181 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8182 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8184 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8185 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8186 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8188 putFile8Bit(file, level->game_engine_type);
8190 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8194 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8199 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8200 chunk_size += putFile8Bit(file, level->name[i]);
8205 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8210 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8211 chunk_size += putFile8Bit(file, level->author[i]);
8216 #if ENABLE_HISTORIC_CHUNKS
8217 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8222 for (y = 0; y < level->fieldy; y++)
8223 for (x = 0; x < level->fieldx; x++)
8224 if (level->encoding_16bit_field)
8225 chunk_size += putFile16BitBE(file, level->field[x][y]);
8227 chunk_size += putFile8Bit(file, level->field[x][y]);
8233 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8238 for (y = 0; y < level->fieldy; y++)
8239 for (x = 0; x < level->fieldx; x++)
8240 chunk_size += putFile16BitBE(file, level->field[x][y]);
8245 #if ENABLE_HISTORIC_CHUNKS
8246 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8250 putFile8Bit(file, EL_YAMYAM);
8251 putFile8Bit(file, level->num_yamyam_contents);
8252 putFile8Bit(file, 0);
8253 putFile8Bit(file, 0);
8255 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8256 for (y = 0; y < 3; y++)
8257 for (x = 0; x < 3; x++)
8258 if (level->encoding_16bit_field)
8259 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8261 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8265 #if ENABLE_HISTORIC_CHUNKS
8266 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8269 int num_contents, content_xsize, content_ysize;
8270 int content_array[MAX_ELEMENT_CONTENTS][3][3];
8272 if (element == EL_YAMYAM)
8274 num_contents = level->num_yamyam_contents;
8278 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8279 for (y = 0; y < 3; y++)
8280 for (x = 0; x < 3; x++)
8281 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8283 else if (element == EL_BD_AMOEBA)
8289 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8290 for (y = 0; y < 3; y++)
8291 for (x = 0; x < 3; x++)
8292 content_array[i][x][y] = EL_EMPTY;
8293 content_array[0][0][0] = level->amoeba_content;
8297 // chunk header already written -- write empty chunk data
8298 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8300 Warn("cannot save content for element '%d'", element);
8305 putFile16BitBE(file, element);
8306 putFile8Bit(file, num_contents);
8307 putFile8Bit(file, content_xsize);
8308 putFile8Bit(file, content_ysize);
8310 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8312 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8313 for (y = 0; y < 3; y++)
8314 for (x = 0; x < 3; x++)
8315 putFile16BitBE(file, content_array[i][x][y]);
8319 #if ENABLE_HISTORIC_CHUNKS
8320 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8322 int envelope_nr = element - EL_ENVELOPE_1;
8323 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8327 chunk_size += putFile16BitBE(file, element);
8328 chunk_size += putFile16BitBE(file, envelope_len);
8329 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8330 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8332 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8333 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8335 for (i = 0; i < envelope_len; i++)
8336 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8342 #if ENABLE_HISTORIC_CHUNKS
8343 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8344 int num_changed_custom_elements)
8348 putFile16BitBE(file, num_changed_custom_elements);
8350 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8352 int element = EL_CUSTOM_START + i;
8354 struct ElementInfo *ei = &element_info[element];
8356 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8358 if (check < num_changed_custom_elements)
8360 putFile16BitBE(file, element);
8361 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8368 if (check != num_changed_custom_elements) // should not happen
8369 Warn("inconsistent number of custom element properties");
8373 #if ENABLE_HISTORIC_CHUNKS
8374 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8375 int num_changed_custom_elements)
8379 putFile16BitBE(file, num_changed_custom_elements);
8381 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8383 int element = EL_CUSTOM_START + i;
8385 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8387 if (check < num_changed_custom_elements)
8389 putFile16BitBE(file, element);
8390 putFile16BitBE(file, element_info[element].change->target_element);
8397 if (check != num_changed_custom_elements) // should not happen
8398 Warn("inconsistent number of custom target elements");
8402 #if ENABLE_HISTORIC_CHUNKS
8403 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8404 int num_changed_custom_elements)
8406 int i, j, x, y, check = 0;
8408 putFile16BitBE(file, num_changed_custom_elements);
8410 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8412 int element = EL_CUSTOM_START + i;
8413 struct ElementInfo *ei = &element_info[element];
8415 if (ei->modified_settings)
8417 if (check < num_changed_custom_elements)
8419 putFile16BitBE(file, element);
8421 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8422 putFile8Bit(file, ei->description[j]);
8424 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8426 // some free bytes for future properties and padding
8427 WriteUnusedBytesToFile(file, 7);
8429 putFile8Bit(file, ei->use_gfx_element);
8430 putFile16BitBE(file, ei->gfx_element_initial);
8432 putFile8Bit(file, ei->collect_score_initial);
8433 putFile8Bit(file, ei->collect_count_initial);
8435 putFile16BitBE(file, ei->push_delay_fixed);
8436 putFile16BitBE(file, ei->push_delay_random);
8437 putFile16BitBE(file, ei->move_delay_fixed);
8438 putFile16BitBE(file, ei->move_delay_random);
8440 putFile16BitBE(file, ei->move_pattern);
8441 putFile8Bit(file, ei->move_direction_initial);
8442 putFile8Bit(file, ei->move_stepsize);
8444 for (y = 0; y < 3; y++)
8445 for (x = 0; x < 3; x++)
8446 putFile16BitBE(file, ei->content.e[x][y]);
8448 putFile32BitBE(file, ei->change->events);
8450 putFile16BitBE(file, ei->change->target_element);
8452 putFile16BitBE(file, ei->change->delay_fixed);
8453 putFile16BitBE(file, ei->change->delay_random);
8454 putFile16BitBE(file, ei->change->delay_frames);
8456 putFile16BitBE(file, ei->change->initial_trigger_element);
8458 putFile8Bit(file, ei->change->explode);
8459 putFile8Bit(file, ei->change->use_target_content);
8460 putFile8Bit(file, ei->change->only_if_complete);
8461 putFile8Bit(file, ei->change->use_random_replace);
8463 putFile8Bit(file, ei->change->random_percentage);
8464 putFile8Bit(file, ei->change->replace_when);
8466 for (y = 0; y < 3; y++)
8467 for (x = 0; x < 3; x++)
8468 putFile16BitBE(file, ei->change->content.e[x][y]);
8470 putFile8Bit(file, ei->slippery_type);
8472 // some free bytes for future properties and padding
8473 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8480 if (check != num_changed_custom_elements) // should not happen
8481 Warn("inconsistent number of custom element properties");
8485 #if ENABLE_HISTORIC_CHUNKS
8486 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8488 struct ElementInfo *ei = &element_info[element];
8491 // ---------- custom element base property values (96 bytes) ----------------
8493 putFile16BitBE(file, element);
8495 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8496 putFile8Bit(file, ei->description[i]);
8498 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8500 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8502 putFile8Bit(file, ei->num_change_pages);
8504 putFile16BitBE(file, ei->ce_value_fixed_initial);
8505 putFile16BitBE(file, ei->ce_value_random_initial);
8506 putFile8Bit(file, ei->use_last_ce_value);
8508 putFile8Bit(file, ei->use_gfx_element);
8509 putFile16BitBE(file, ei->gfx_element_initial);
8511 putFile8Bit(file, ei->collect_score_initial);
8512 putFile8Bit(file, ei->collect_count_initial);
8514 putFile8Bit(file, ei->drop_delay_fixed);
8515 putFile8Bit(file, ei->push_delay_fixed);
8516 putFile8Bit(file, ei->drop_delay_random);
8517 putFile8Bit(file, ei->push_delay_random);
8518 putFile16BitBE(file, ei->move_delay_fixed);
8519 putFile16BitBE(file, ei->move_delay_random);
8521 // bits 0 - 15 of "move_pattern" ...
8522 putFile16BitBE(file, ei->move_pattern & 0xffff);
8523 putFile8Bit(file, ei->move_direction_initial);
8524 putFile8Bit(file, ei->move_stepsize);
8526 putFile8Bit(file, ei->slippery_type);
8528 for (y = 0; y < 3; y++)
8529 for (x = 0; x < 3; x++)
8530 putFile16BitBE(file, ei->content.e[x][y]);
8532 putFile16BitBE(file, ei->move_enter_element);
8533 putFile16BitBE(file, ei->move_leave_element);
8534 putFile8Bit(file, ei->move_leave_type);
8536 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8537 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8539 putFile8Bit(file, ei->access_direction);
8541 putFile8Bit(file, ei->explosion_delay);
8542 putFile8Bit(file, ei->ignition_delay);
8543 putFile8Bit(file, ei->explosion_type);
8545 // some free bytes for future custom property values and padding
8546 WriteUnusedBytesToFile(file, 1);
8548 // ---------- change page property values (48 bytes) ------------------------
8550 for (i = 0; i < ei->num_change_pages; i++)
8552 struct ElementChangeInfo *change = &ei->change_page[i];
8553 unsigned int event_bits;
8555 // bits 0 - 31 of "has_event[]" ...
8557 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8558 if (change->has_event[j])
8559 event_bits |= (1u << j);
8560 putFile32BitBE(file, event_bits);
8562 putFile16BitBE(file, change->target_element);
8564 putFile16BitBE(file, change->delay_fixed);
8565 putFile16BitBE(file, change->delay_random);
8566 putFile16BitBE(file, change->delay_frames);
8568 putFile16BitBE(file, change->initial_trigger_element);
8570 putFile8Bit(file, change->explode);
8571 putFile8Bit(file, change->use_target_content);
8572 putFile8Bit(file, change->only_if_complete);
8573 putFile8Bit(file, change->use_random_replace);
8575 putFile8Bit(file, change->random_percentage);
8576 putFile8Bit(file, change->replace_when);
8578 for (y = 0; y < 3; y++)
8579 for (x = 0; x < 3; x++)
8580 putFile16BitBE(file, change->target_content.e[x][y]);
8582 putFile8Bit(file, change->can_change);
8584 putFile8Bit(file, change->trigger_side);
8586 putFile8Bit(file, change->trigger_player);
8587 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8588 log_2(change->trigger_page)));
8590 putFile8Bit(file, change->has_action);
8591 putFile8Bit(file, change->action_type);
8592 putFile8Bit(file, change->action_mode);
8593 putFile16BitBE(file, change->action_arg);
8595 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8597 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8598 if (change->has_event[j])
8599 event_bits |= (1u << (j - 32));
8600 putFile8Bit(file, event_bits);
8605 #if ENABLE_HISTORIC_CHUNKS
8606 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8608 struct ElementInfo *ei = &element_info[element];
8609 struct ElementGroupInfo *group = ei->group;
8612 putFile16BitBE(file, element);
8614 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8615 putFile8Bit(file, ei->description[i]);
8617 putFile8Bit(file, group->num_elements);
8619 putFile8Bit(file, ei->use_gfx_element);
8620 putFile16BitBE(file, ei->gfx_element_initial);
8622 putFile8Bit(file, group->choice_mode);
8624 // some free bytes for future values and padding
8625 WriteUnusedBytesToFile(file, 3);
8627 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8628 putFile16BitBE(file, group->element[i]);
8632 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8633 boolean write_element)
8635 int save_type = entry->save_type;
8636 int data_type = entry->data_type;
8637 int conf_type = entry->conf_type;
8638 int byte_mask = conf_type & CONF_MASK_BYTES;
8639 int element = entry->element;
8640 int default_value = entry->default_value;
8642 boolean modified = FALSE;
8644 if (byte_mask != CONF_MASK_MULTI_BYTES)
8646 void *value_ptr = entry->value;
8647 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8650 // check if any settings have been modified before saving them
8651 if (value != default_value)
8654 // do not save if explicitly told or if unmodified default settings
8655 if ((save_type == SAVE_CONF_NEVER) ||
8656 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8660 num_bytes += putFile16BitBE(file, element);
8662 num_bytes += putFile8Bit(file, conf_type);
8663 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8664 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8665 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8668 else if (data_type == TYPE_STRING)
8670 char *default_string = entry->default_string;
8671 char *string = (char *)(entry->value);
8672 int string_length = strlen(string);
8675 // check if any settings have been modified before saving them
8676 if (!strEqual(string, default_string))
8679 // do not save if explicitly told or if unmodified default settings
8680 if ((save_type == SAVE_CONF_NEVER) ||
8681 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8685 num_bytes += putFile16BitBE(file, element);
8687 num_bytes += putFile8Bit(file, conf_type);
8688 num_bytes += putFile16BitBE(file, string_length);
8690 for (i = 0; i < string_length; i++)
8691 num_bytes += putFile8Bit(file, string[i]);
8693 else if (data_type == TYPE_ELEMENT_LIST)
8695 int *element_array = (int *)(entry->value);
8696 int num_elements = *(int *)(entry->num_entities);
8699 // check if any settings have been modified before saving them
8700 for (i = 0; i < num_elements; i++)
8701 if (element_array[i] != default_value)
8704 // do not save if explicitly told or if unmodified default settings
8705 if ((save_type == SAVE_CONF_NEVER) ||
8706 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8710 num_bytes += putFile16BitBE(file, element);
8712 num_bytes += putFile8Bit(file, conf_type);
8713 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8715 for (i = 0; i < num_elements; i++)
8716 num_bytes += putFile16BitBE(file, element_array[i]);
8718 else if (data_type == TYPE_CONTENT_LIST)
8720 struct Content *content = (struct Content *)(entry->value);
8721 int num_contents = *(int *)(entry->num_entities);
8724 // check if any settings have been modified before saving them
8725 for (i = 0; i < num_contents; i++)
8726 for (y = 0; y < 3; y++)
8727 for (x = 0; x < 3; x++)
8728 if (content[i].e[x][y] != default_value)
8731 // do not save if explicitly told or if unmodified default settings
8732 if ((save_type == SAVE_CONF_NEVER) ||
8733 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8737 num_bytes += putFile16BitBE(file, element);
8739 num_bytes += putFile8Bit(file, conf_type);
8740 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8742 for (i = 0; i < num_contents; i++)
8743 for (y = 0; y < 3; y++)
8744 for (x = 0; x < 3; x++)
8745 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8751 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8756 li = *level; // copy level data into temporary buffer
8758 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8759 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8764 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8769 li = *level; // copy level data into temporary buffer
8771 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8772 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8777 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8779 int envelope_nr = element - EL_ENVELOPE_1;
8783 chunk_size += putFile16BitBE(file, element);
8785 // copy envelope data into temporary buffer
8786 xx_envelope = level->envelope[envelope_nr];
8788 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8789 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8794 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8796 struct ElementInfo *ei = &element_info[element];
8800 chunk_size += putFile16BitBE(file, element);
8802 xx_ei = *ei; // copy element data into temporary buffer
8804 // set default description string for this specific element
8805 strcpy(xx_default_description, getDefaultElementDescription(ei));
8807 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8808 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8810 for (i = 0; i < ei->num_change_pages; i++)
8812 struct ElementChangeInfo *change = &ei->change_page[i];
8814 xx_current_change_page = i;
8816 xx_change = *change; // copy change data into temporary buffer
8819 setEventBitsFromEventFlags(change);
8821 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8822 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8829 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8831 struct ElementInfo *ei = &element_info[element];
8832 struct ElementGroupInfo *group = ei->group;
8836 chunk_size += putFile16BitBE(file, element);
8838 xx_ei = *ei; // copy element data into temporary buffer
8839 xx_group = *group; // copy group data into temporary buffer
8841 // set default description string for this specific element
8842 strcpy(xx_default_description, getDefaultElementDescription(ei));
8844 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8845 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8850 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8852 struct ElementInfo *ei = &element_info[element];
8856 chunk_size += putFile16BitBE(file, element);
8858 xx_ei = *ei; // copy element data into temporary buffer
8860 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8861 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8866 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8867 boolean save_as_template)
8873 if (!(file = fopen(filename, MODE_WRITE)))
8875 Warn("cannot save level file '%s'", filename);
8880 level->file_version = FILE_VERSION_ACTUAL;
8881 level->game_version = GAME_VERSION_ACTUAL;
8883 level->creation_date = getCurrentDate();
8885 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8886 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8888 chunk_size = SaveLevel_VERS(NULL, level);
8889 putFileChunkBE(file, "VERS", chunk_size);
8890 SaveLevel_VERS(file, level);
8892 chunk_size = SaveLevel_DATE(NULL, level);
8893 putFileChunkBE(file, "DATE", chunk_size);
8894 SaveLevel_DATE(file, level);
8896 chunk_size = SaveLevel_NAME(NULL, level);
8897 putFileChunkBE(file, "NAME", chunk_size);
8898 SaveLevel_NAME(file, level);
8900 chunk_size = SaveLevel_AUTH(NULL, level);
8901 putFileChunkBE(file, "AUTH", chunk_size);
8902 SaveLevel_AUTH(file, level);
8904 chunk_size = SaveLevel_INFO(NULL, level);
8905 putFileChunkBE(file, "INFO", chunk_size);
8906 SaveLevel_INFO(file, level);
8908 chunk_size = SaveLevel_BODY(NULL, level);
8909 putFileChunkBE(file, "BODY", chunk_size);
8910 SaveLevel_BODY(file, level);
8912 chunk_size = SaveLevel_ELEM(NULL, level);
8913 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8915 putFileChunkBE(file, "ELEM", chunk_size);
8916 SaveLevel_ELEM(file, level);
8919 for (i = 0; i < NUM_ENVELOPES; i++)
8921 int element = EL_ENVELOPE_1 + i;
8923 chunk_size = SaveLevel_NOTE(NULL, level, element);
8924 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8926 putFileChunkBE(file, "NOTE", chunk_size);
8927 SaveLevel_NOTE(file, level, element);
8931 // if not using template level, check for non-default custom/group elements
8932 if (!level->use_custom_template || save_as_template)
8934 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8936 int element = EL_CUSTOM_START + i;
8938 chunk_size = SaveLevel_CUSX(NULL, level, element);
8939 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8941 putFileChunkBE(file, "CUSX", chunk_size);
8942 SaveLevel_CUSX(file, level, element);
8946 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8948 int element = EL_GROUP_START + i;
8950 chunk_size = SaveLevel_GRPX(NULL, level, element);
8951 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8953 putFileChunkBE(file, "GRPX", chunk_size);
8954 SaveLevel_GRPX(file, level, element);
8958 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8960 int element = GET_EMPTY_ELEMENT(i);
8962 chunk_size = SaveLevel_EMPX(NULL, level, element);
8963 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8965 putFileChunkBE(file, "EMPX", chunk_size);
8966 SaveLevel_EMPX(file, level, element);
8973 SetFilePermissions(filename, PERMS_PRIVATE);
8976 void SaveLevel(int nr)
8978 char *filename = getDefaultLevelFilename(nr);
8980 SaveLevelFromFilename(&level, filename, FALSE);
8983 void SaveLevelTemplate(void)
8985 char *filename = getLocalLevelTemplateFilename();
8987 SaveLevelFromFilename(&level, filename, TRUE);
8990 boolean SaveLevelChecked(int nr)
8992 char *filename = getDefaultLevelFilename(nr);
8993 boolean new_level = !fileExists(filename);
8994 boolean level_saved = FALSE;
8996 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
9001 Request("Level saved!", REQ_CONFIRM);
9009 void DumpLevel(struct LevelInfo *level)
9011 if (level->no_level_file || level->no_valid_file)
9013 Warn("cannot dump -- no valid level file found");
9019 Print("Level xxx (file version %08d, game version %08d)\n",
9020 level->file_version, level->game_version);
9023 Print("Level author: '%s'\n", level->author);
9024 Print("Level title: '%s'\n", level->name);
9026 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
9028 Print("Level time: %d seconds\n", level->time);
9029 Print("Gems needed: %d\n", level->gems_needed);
9031 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
9032 Print("Time for wheel: %d seconds\n", level->time_wheel);
9033 Print("Time for light: %d seconds\n", level->time_light);
9034 Print("Time for timegate: %d seconds\n", level->time_timegate);
9036 Print("Amoeba speed: %d\n", level->amoeba_speed);
9039 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
9040 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
9041 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
9042 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
9043 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
9044 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
9050 for (i = 0; i < NUM_ENVELOPES; i++)
9052 char *text = level->envelope[i].text;
9053 int text_len = strlen(text);
9054 boolean has_text = FALSE;
9056 for (j = 0; j < text_len; j++)
9057 if (text[j] != ' ' && text[j] != '\n')
9063 Print("Envelope %d:\n'%s'\n", i + 1, text);
9071 void DumpLevels(void)
9073 static LevelDirTree *dumplevel_leveldir = NULL;
9075 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9076 global.dumplevel_leveldir);
9078 if (dumplevel_leveldir == NULL)
9079 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
9081 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
9082 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
9083 Fail("no such level number: %d", global.dumplevel_level_nr);
9085 leveldir_current = dumplevel_leveldir;
9087 LoadLevel(global.dumplevel_level_nr);
9093 void DumpLevelsetFromFilename_BD(char *filename)
9095 if (leveldir_current == NULL) // no levelsets loaded yet
9098 if (!LoadNativeLevel_BD(filename, 0, FALSE))
9099 CloseAllAndExit(0); // function has already printed warning
9102 Print("Levelset '%s'\n", filename);
9112 void DumpLevelset(void)
9114 static LevelDirTree *dumplevelset_leveldir = NULL;
9116 dumplevelset_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9117 global.dumplevelset_leveldir);
9118 if (dumplevelset_leveldir == NULL)
9119 Fail("no such level identifier: '%s'", global.dumplevelset_leveldir);
9122 Print("Levelset '%s'\n", dumplevelset_leveldir->identifier);
9125 Print("Number of levels: %d\n", dumplevelset_leveldir->levels);
9126 Print("First level number: %d\n", dumplevelset_leveldir->first_level);
9134 // ============================================================================
9135 // tape file functions
9136 // ============================================================================
9138 static void setTapeInfoToDefaults(void)
9142 // always start with reliable default values (empty tape)
9145 // default values (also for pre-1.2 tapes) with only the first player
9146 tape.player_participates[0] = TRUE;
9147 for (i = 1; i < MAX_PLAYERS; i++)
9148 tape.player_participates[i] = FALSE;
9150 // at least one (default: the first) player participates in every tape
9151 tape.num_participating_players = 1;
9153 tape.property_bits = TAPE_PROPERTY_NONE;
9155 tape.level_nr = level_nr;
9157 tape.changed = FALSE;
9158 tape.solved = FALSE;
9160 tape.recording = FALSE;
9161 tape.playing = FALSE;
9162 tape.pausing = FALSE;
9164 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9165 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9167 tape.no_info_chunk = TRUE;
9168 tape.no_valid_file = FALSE;
9171 static int getTapePosSize(struct TapeInfo *tape)
9173 int tape_pos_size = 0;
9175 if (tape->use_key_actions)
9176 tape_pos_size += tape->num_participating_players;
9178 if (tape->use_mouse_actions)
9179 tape_pos_size += 3; // x and y position and mouse button mask
9181 tape_pos_size += 1; // tape action delay value
9183 return tape_pos_size;
9186 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9188 tape->use_key_actions = FALSE;
9189 tape->use_mouse_actions = FALSE;
9191 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9192 tape->use_key_actions = TRUE;
9194 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9195 tape->use_mouse_actions = TRUE;
9198 static int getTapeActionValue(struct TapeInfo *tape)
9200 return (tape->use_key_actions &&
9201 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9202 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
9203 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9204 TAPE_ACTIONS_DEFAULT);
9207 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9209 tape->file_version = getFileVersion(file);
9210 tape->game_version = getFileVersion(file);
9215 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9219 tape->random_seed = getFile32BitBE(file);
9220 tape->date = getFile32BitBE(file);
9221 tape->length = getFile32BitBE(file);
9223 // read header fields that are new since version 1.2
9224 if (tape->file_version >= FILE_VERSION_1_2)
9226 byte store_participating_players = getFile8Bit(file);
9229 // since version 1.2, tapes store which players participate in the tape
9230 tape->num_participating_players = 0;
9231 for (i = 0; i < MAX_PLAYERS; i++)
9233 tape->player_participates[i] = FALSE;
9235 if (store_participating_players & (1 << i))
9237 tape->player_participates[i] = TRUE;
9238 tape->num_participating_players++;
9242 setTapeActionFlags(tape, getFile8Bit(file));
9244 tape->property_bits = getFile8Bit(file);
9245 tape->solved = getFile8Bit(file);
9247 engine_version = getFileVersion(file);
9248 if (engine_version > 0)
9249 tape->engine_version = engine_version;
9251 tape->engine_version = tape->game_version;
9257 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9259 tape->scr_fieldx = getFile8Bit(file);
9260 tape->scr_fieldy = getFile8Bit(file);
9265 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9267 char *level_identifier = NULL;
9268 int level_identifier_size;
9271 tape->no_info_chunk = FALSE;
9273 level_identifier_size = getFile16BitBE(file);
9275 level_identifier = checked_malloc(level_identifier_size);
9277 for (i = 0; i < level_identifier_size; i++)
9278 level_identifier[i] = getFile8Bit(file);
9280 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9281 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9283 checked_free(level_identifier);
9285 tape->level_nr = getFile16BitBE(file);
9287 chunk_size = 2 + level_identifier_size + 2;
9292 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9295 int tape_pos_size = getTapePosSize(tape);
9296 int chunk_size_expected = tape_pos_size * tape->length;
9298 if (chunk_size_expected != chunk_size)
9300 ReadUnusedBytesFromFile(file, chunk_size);
9301 return chunk_size_expected;
9304 for (i = 0; i < tape->length; i++)
9306 if (i >= MAX_TAPE_LEN)
9308 Warn("tape truncated -- size exceeds maximum tape size %d",
9311 // tape too large; read and ignore remaining tape data from this chunk
9312 for (;i < tape->length; i++)
9313 ReadUnusedBytesFromFile(file, tape_pos_size);
9318 if (tape->use_key_actions)
9320 for (j = 0; j < MAX_PLAYERS; j++)
9322 tape->pos[i].action[j] = MV_NONE;
9324 if (tape->player_participates[j])
9325 tape->pos[i].action[j] = getFile8Bit(file);
9329 if (tape->use_mouse_actions)
9331 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
9332 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
9333 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9336 tape->pos[i].delay = getFile8Bit(file);
9338 if (tape->file_version == FILE_VERSION_1_0)
9340 // eliminate possible diagonal moves in old tapes
9341 // this is only for backward compatibility
9343 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9344 byte action = tape->pos[i].action[0];
9345 int k, num_moves = 0;
9347 for (k = 0; k < 4; k++)
9349 if (action & joy_dir[k])
9351 tape->pos[i + num_moves].action[0] = joy_dir[k];
9353 tape->pos[i + num_moves].delay = 0;
9362 tape->length += num_moves;
9365 else if (tape->file_version < FILE_VERSION_2_0)
9367 // convert pre-2.0 tapes to new tape format
9369 if (tape->pos[i].delay > 1)
9372 tape->pos[i + 1] = tape->pos[i];
9373 tape->pos[i + 1].delay = 1;
9376 for (j = 0; j < MAX_PLAYERS; j++)
9377 tape->pos[i].action[j] = MV_NONE;
9378 tape->pos[i].delay--;
9385 if (checkEndOfFile(file))
9389 if (i != tape->length)
9390 chunk_size = tape_pos_size * i;
9395 static void LoadTape_SokobanSolution(char *filename)
9398 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9400 if (!(file = openFile(filename, MODE_READ)))
9402 tape.no_valid_file = TRUE;
9407 while (!checkEndOfFile(file))
9409 unsigned char c = getByteFromFile(file);
9411 if (checkEndOfFile(file))
9418 tape.pos[tape.length].action[0] = MV_UP;
9419 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9425 tape.pos[tape.length].action[0] = MV_DOWN;
9426 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9432 tape.pos[tape.length].action[0] = MV_LEFT;
9433 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9439 tape.pos[tape.length].action[0] = MV_RIGHT;
9440 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9448 // ignore white-space characters
9452 tape.no_valid_file = TRUE;
9454 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9462 if (tape.no_valid_file)
9465 tape.length_frames = GetTapeLengthFrames();
9466 tape.length_seconds = GetTapeLengthSeconds();
9469 void LoadTapeFromFilename(char *filename)
9471 char cookie[MAX_LINE_LEN];
9472 char chunk_name[CHUNK_ID_LEN + 1];
9476 // always start with reliable default values
9477 setTapeInfoToDefaults();
9479 if (strSuffix(filename, ".sln"))
9481 LoadTape_SokobanSolution(filename);
9486 if (!(file = openFile(filename, MODE_READ)))
9488 tape.no_valid_file = TRUE;
9493 getFileChunkBE(file, chunk_name, NULL);
9494 if (strEqual(chunk_name, "RND1"))
9496 getFile32BitBE(file); // not used
9498 getFileChunkBE(file, chunk_name, NULL);
9499 if (!strEqual(chunk_name, "TAPE"))
9501 tape.no_valid_file = TRUE;
9503 Warn("unknown format of tape file '%s'", filename);
9510 else // check for pre-2.0 file format with cookie string
9512 strcpy(cookie, chunk_name);
9513 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9515 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9516 cookie[strlen(cookie) - 1] = '\0';
9518 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9520 tape.no_valid_file = TRUE;
9522 Warn("unknown format of tape file '%s'", filename);
9529 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9531 tape.no_valid_file = TRUE;
9533 Warn("unsupported version of tape file '%s'", filename);
9540 // pre-2.0 tape files have no game version, so use file version here
9541 tape.game_version = tape.file_version;
9544 if (tape.file_version < FILE_VERSION_1_2)
9546 // tape files from versions before 1.2.0 without chunk structure
9547 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9548 LoadTape_BODY(file, 2 * tape.length, &tape);
9556 int (*loader)(File *, int, struct TapeInfo *);
9560 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9561 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9562 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9563 { "INFO", -1, LoadTape_INFO },
9564 { "BODY", -1, LoadTape_BODY },
9568 while (getFileChunkBE(file, chunk_name, &chunk_size))
9572 while (chunk_info[i].name != NULL &&
9573 !strEqual(chunk_name, chunk_info[i].name))
9576 if (chunk_info[i].name == NULL)
9578 Warn("unknown chunk '%s' in tape file '%s'",
9579 chunk_name, filename);
9581 ReadUnusedBytesFromFile(file, chunk_size);
9583 else if (chunk_info[i].size != -1 &&
9584 chunk_info[i].size != chunk_size)
9586 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9587 chunk_size, chunk_name, filename);
9589 ReadUnusedBytesFromFile(file, chunk_size);
9593 // call function to load this tape chunk
9594 int chunk_size_expected =
9595 (chunk_info[i].loader)(file, chunk_size, &tape);
9597 // the size of some chunks cannot be checked before reading other
9598 // chunks first (like "HEAD" and "BODY") that contain some header
9599 // information, so check them here
9600 if (chunk_size_expected != chunk_size)
9602 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9603 chunk_size, chunk_name, filename);
9611 tape.length_frames = GetTapeLengthFrames();
9612 tape.length_seconds = GetTapeLengthSeconds();
9615 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9617 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9619 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9620 tape.engine_version);
9624 void LoadTape(int nr)
9626 char *filename = getTapeFilename(nr);
9628 LoadTapeFromFilename(filename);
9631 void LoadSolutionTape(int nr)
9633 char *filename = getSolutionTapeFilename(nr);
9635 LoadTapeFromFilename(filename);
9637 if (TAPE_IS_EMPTY(tape))
9639 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9640 level.native_bd_level->replay != NULL)
9641 CopyNativeTape_BD_to_RND(&level);
9642 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9643 level.native_sp_level->demo.is_available)
9644 CopyNativeTape_SP_to_RND(&level);
9648 void LoadScoreTape(char *score_tape_basename, int nr)
9650 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9652 LoadTapeFromFilename(filename);
9655 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9657 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9659 LoadTapeFromFilename(filename);
9662 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9664 // chunk required for team mode tapes with non-default screen size
9665 return (tape->num_participating_players > 1 &&
9666 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9667 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9670 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9672 putFileVersion(file, tape->file_version);
9673 putFileVersion(file, tape->game_version);
9676 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9679 byte store_participating_players = 0;
9681 // set bits for participating players for compact storage
9682 for (i = 0; i < MAX_PLAYERS; i++)
9683 if (tape->player_participates[i])
9684 store_participating_players |= (1 << i);
9686 putFile32BitBE(file, tape->random_seed);
9687 putFile32BitBE(file, tape->date);
9688 putFile32BitBE(file, tape->length);
9690 putFile8Bit(file, store_participating_players);
9692 putFile8Bit(file, getTapeActionValue(tape));
9694 putFile8Bit(file, tape->property_bits);
9695 putFile8Bit(file, tape->solved);
9697 putFileVersion(file, tape->engine_version);
9700 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9702 putFile8Bit(file, tape->scr_fieldx);
9703 putFile8Bit(file, tape->scr_fieldy);
9706 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9708 int level_identifier_size = strlen(tape->level_identifier) + 1;
9711 putFile16BitBE(file, level_identifier_size);
9713 for (i = 0; i < level_identifier_size; i++)
9714 putFile8Bit(file, tape->level_identifier[i]);
9716 putFile16BitBE(file, tape->level_nr);
9719 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9723 for (i = 0; i < tape->length; i++)
9725 if (tape->use_key_actions)
9727 for (j = 0; j < MAX_PLAYERS; j++)
9728 if (tape->player_participates[j])
9729 putFile8Bit(file, tape->pos[i].action[j]);
9732 if (tape->use_mouse_actions)
9734 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9735 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9736 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9739 putFile8Bit(file, tape->pos[i].delay);
9743 void SaveTapeToFilename(char *filename)
9747 int info_chunk_size;
9748 int body_chunk_size;
9750 if (!(file = fopen(filename, MODE_WRITE)))
9752 Warn("cannot save level recording file '%s'", filename);
9757 tape_pos_size = getTapePosSize(&tape);
9759 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9760 body_chunk_size = tape_pos_size * tape.length;
9762 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9763 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9765 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9766 SaveTape_VERS(file, &tape);
9768 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9769 SaveTape_HEAD(file, &tape);
9771 if (checkSaveTape_SCRN(&tape))
9773 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9774 SaveTape_SCRN(file, &tape);
9777 putFileChunkBE(file, "INFO", info_chunk_size);
9778 SaveTape_INFO(file, &tape);
9780 putFileChunkBE(file, "BODY", body_chunk_size);
9781 SaveTape_BODY(file, &tape);
9785 SetFilePermissions(filename, PERMS_PRIVATE);
9788 static void SaveTapeExt(char *filename)
9792 tape.file_version = FILE_VERSION_ACTUAL;
9793 tape.game_version = GAME_VERSION_ACTUAL;
9795 tape.num_participating_players = 0;
9797 // count number of participating players
9798 for (i = 0; i < MAX_PLAYERS; i++)
9799 if (tape.player_participates[i])
9800 tape.num_participating_players++;
9802 SaveTapeToFilename(filename);
9804 tape.changed = FALSE;
9807 void SaveTape(int nr)
9809 char *filename = getTapeFilename(nr);
9811 InitTapeDirectory(leveldir_current->subdir);
9813 SaveTapeExt(filename);
9816 void SaveScoreTape(int nr)
9818 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9820 // used instead of "leveldir_current->subdir" (for network games)
9821 InitScoreTapeDirectory(levelset.identifier, nr);
9823 SaveTapeExt(filename);
9826 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9827 unsigned int req_state_added)
9829 char *filename = getTapeFilename(nr);
9830 boolean new_tape = !fileExists(filename);
9831 boolean tape_saved = FALSE;
9833 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9838 Request(msg_saved, REQ_CONFIRM | req_state_added);
9846 boolean SaveTapeChecked(int nr)
9848 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9851 boolean SaveTapeChecked_LevelSolved(int nr)
9853 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9854 "Level solved! Tape saved!", REQ_STAY_OPEN);
9857 void DumpTape(struct TapeInfo *tape)
9859 int tape_frame_counter;
9862 if (tape->no_valid_file)
9864 Warn("cannot dump -- no valid tape file found");
9871 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9872 tape->level_nr, tape->file_version, tape->game_version);
9873 Print(" (effective engine version %08d)\n",
9874 tape->engine_version);
9875 Print("Level series identifier: '%s'\n", tape->level_identifier);
9877 Print("Solution tape: %s\n",
9878 tape->solved ? "yes" :
9879 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9881 Print("Special tape properties: ");
9882 if (tape->property_bits == TAPE_PROPERTY_NONE)
9884 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9885 Print("[em_random_bug]");
9886 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9887 Print("[game_speed]");
9888 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9890 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9891 Print("[single_step]");
9892 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9893 Print("[snapshot]");
9894 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9895 Print("[replayed]");
9896 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9897 Print("[tas_keys]");
9898 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9899 Print("[small_graphics]");
9902 int year2 = tape->date / 10000;
9903 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9904 int month_index_raw = (tape->date / 100) % 100;
9905 int month_index = month_index_raw % 12; // prevent invalid index
9906 int month = month_index + 1;
9907 int day = tape->date % 100;
9909 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9913 tape_frame_counter = 0;
9915 for (i = 0; i < tape->length; i++)
9917 if (i >= MAX_TAPE_LEN)
9922 for (j = 0; j < MAX_PLAYERS; j++)
9924 if (tape->player_participates[j])
9926 int action = tape->pos[i].action[j];
9928 Print("%d:%02x ", j, action);
9929 Print("[%c%c%c%c|%c%c] - ",
9930 (action & JOY_LEFT ? '<' : ' '),
9931 (action & JOY_RIGHT ? '>' : ' '),
9932 (action & JOY_UP ? '^' : ' '),
9933 (action & JOY_DOWN ? 'v' : ' '),
9934 (action & JOY_BUTTON_1 ? '1' : ' '),
9935 (action & JOY_BUTTON_2 ? '2' : ' '));
9939 Print("(%03d) ", tape->pos[i].delay);
9940 Print("[%05d]\n", tape_frame_counter);
9942 tape_frame_counter += tape->pos[i].delay;
9948 void DumpTapes(void)
9950 static LevelDirTree *dumptape_leveldir = NULL;
9952 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9953 global.dumptape_leveldir);
9955 if (dumptape_leveldir == NULL)
9956 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9958 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9959 global.dumptape_level_nr > dumptape_leveldir->last_level)
9960 Fail("no such level number: %d", global.dumptape_level_nr);
9962 leveldir_current = dumptape_leveldir;
9964 if (options.mytapes)
9965 LoadTape(global.dumptape_level_nr);
9967 LoadSolutionTape(global.dumptape_level_nr);
9975 // ============================================================================
9976 // score file functions
9977 // ============================================================================
9979 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9983 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9985 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9986 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9987 scores->entry[i].score = 0;
9988 scores->entry[i].time = 0;
9990 scores->entry[i].id = -1;
9991 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9992 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9993 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9994 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9995 strcpy(scores->entry[i].country_code, "??");
9998 scores->num_entries = 0;
9999 scores->last_added = -1;
10000 scores->last_added_local = -1;
10002 scores->updated = FALSE;
10003 scores->uploaded = FALSE;
10004 scores->tape_downloaded = FALSE;
10005 scores->force_last_added = FALSE;
10007 // The following values are intentionally not reset here:
10011 // - continue_playing
10012 // - continue_on_return
10015 static void setScoreInfoToDefaults(void)
10017 setScoreInfoToDefaultsExt(&scores);
10020 static void setServerScoreInfoToDefaults(void)
10022 setScoreInfoToDefaultsExt(&server_scores);
10025 static void LoadScore_OLD(int nr)
10028 char *filename = getScoreFilename(nr);
10029 char cookie[MAX_LINE_LEN];
10030 char line[MAX_LINE_LEN];
10034 if (!(file = fopen(filename, MODE_READ)))
10037 // check file identifier
10038 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
10040 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10041 cookie[strlen(cookie) - 1] = '\0';
10043 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10045 Warn("unknown format of score file '%s'", filename);
10052 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10054 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
10055 Warn("fscanf() failed; %s", strerror(errno));
10057 if (fgets(line, MAX_LINE_LEN, file) == NULL)
10060 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
10061 line[strlen(line) - 1] = '\0';
10063 for (line_ptr = line; *line_ptr; line_ptr++)
10065 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
10067 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
10068 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10077 static void ConvertScore_OLD(void)
10079 // only convert score to time for levels that rate playing time over score
10080 if (!level.rate_time_over_score)
10083 // convert old score to playing time for score-less levels (like Supaplex)
10084 int time_final_max = 999;
10087 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10089 int score = scores.entry[i].score;
10091 if (score > 0 && score < time_final_max)
10092 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
10096 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
10098 scores->file_version = getFileVersion(file);
10099 scores->game_version = getFileVersion(file);
10104 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
10106 char *level_identifier = NULL;
10107 int level_identifier_size;
10110 level_identifier_size = getFile16BitBE(file);
10112 level_identifier = checked_malloc(level_identifier_size);
10114 for (i = 0; i < level_identifier_size; i++)
10115 level_identifier[i] = getFile8Bit(file);
10117 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
10118 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
10120 checked_free(level_identifier);
10122 scores->level_nr = getFile16BitBE(file);
10123 scores->num_entries = getFile16BitBE(file);
10125 chunk_size = 2 + level_identifier_size + 2 + 2;
10130 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10134 for (i = 0; i < scores->num_entries; i++)
10136 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10137 scores->entry[i].name[j] = getFile8Bit(file);
10139 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10142 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10147 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10151 for (i = 0; i < scores->num_entries; i++)
10152 scores->entry[i].score = getFile16BitBE(file);
10154 chunk_size = scores->num_entries * 2;
10159 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10163 for (i = 0; i < scores->num_entries; i++)
10164 scores->entry[i].score = getFile32BitBE(file);
10166 chunk_size = scores->num_entries * 4;
10171 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10175 for (i = 0; i < scores->num_entries; i++)
10176 scores->entry[i].time = getFile32BitBE(file);
10178 chunk_size = scores->num_entries * 4;
10183 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10187 for (i = 0; i < scores->num_entries; i++)
10189 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10190 scores->entry[i].tape_basename[j] = getFile8Bit(file);
10192 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10195 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10200 void LoadScore(int nr)
10202 char *filename = getScoreFilename(nr);
10203 char cookie[MAX_LINE_LEN];
10204 char chunk_name[CHUNK_ID_LEN + 1];
10206 boolean old_score_file_format = FALSE;
10209 // always start with reliable default values
10210 setScoreInfoToDefaults();
10212 if (!(file = openFile(filename, MODE_READ)))
10215 getFileChunkBE(file, chunk_name, NULL);
10216 if (strEqual(chunk_name, "RND1"))
10218 getFile32BitBE(file); // not used
10220 getFileChunkBE(file, chunk_name, NULL);
10221 if (!strEqual(chunk_name, "SCOR"))
10223 Warn("unknown format of score file '%s'", filename);
10230 else // check for old file format with cookie string
10232 strcpy(cookie, chunk_name);
10233 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10235 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10236 cookie[strlen(cookie) - 1] = '\0';
10238 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10240 Warn("unknown format of score file '%s'", filename);
10247 old_score_file_format = TRUE;
10250 if (old_score_file_format)
10252 // score files from versions before 4.2.4.0 without chunk structure
10255 // convert score to time, if possible (mainly for Supaplex levels)
10256 ConvertScore_OLD();
10264 int (*loader)(File *, int, struct ScoreInfo *);
10268 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
10269 { "INFO", -1, LoadScore_INFO },
10270 { "NAME", -1, LoadScore_NAME },
10271 { "SCOR", -1, LoadScore_SCOR },
10272 { "SC4R", -1, LoadScore_SC4R },
10273 { "TIME", -1, LoadScore_TIME },
10274 { "TAPE", -1, LoadScore_TAPE },
10279 while (getFileChunkBE(file, chunk_name, &chunk_size))
10283 while (chunk_info[i].name != NULL &&
10284 !strEqual(chunk_name, chunk_info[i].name))
10287 if (chunk_info[i].name == NULL)
10289 Warn("unknown chunk '%s' in score file '%s'",
10290 chunk_name, filename);
10292 ReadUnusedBytesFromFile(file, chunk_size);
10294 else if (chunk_info[i].size != -1 &&
10295 chunk_info[i].size != chunk_size)
10297 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10298 chunk_size, chunk_name, filename);
10300 ReadUnusedBytesFromFile(file, chunk_size);
10304 // call function to load this score chunk
10305 int chunk_size_expected =
10306 (chunk_info[i].loader)(file, chunk_size, &scores);
10308 // the size of some chunks cannot be checked before reading other
10309 // chunks first (like "HEAD" and "BODY") that contain some header
10310 // information, so check them here
10311 if (chunk_size_expected != chunk_size)
10313 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10314 chunk_size, chunk_name, filename);
10323 #if ENABLE_HISTORIC_CHUNKS
10324 void SaveScore_OLD(int nr)
10327 char *filename = getScoreFilename(nr);
10330 // used instead of "leveldir_current->subdir" (for network games)
10331 InitScoreDirectory(levelset.identifier);
10333 if (!(file = fopen(filename, MODE_WRITE)))
10335 Warn("cannot save score for level %d", nr);
10340 fprintf(file, "%s\n\n", SCORE_COOKIE);
10342 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10343 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10347 SetFilePermissions(filename, PERMS_PRIVATE);
10351 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10353 putFileVersion(file, scores->file_version);
10354 putFileVersion(file, scores->game_version);
10357 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10359 int level_identifier_size = strlen(scores->level_identifier) + 1;
10362 putFile16BitBE(file, level_identifier_size);
10364 for (i = 0; i < level_identifier_size; i++)
10365 putFile8Bit(file, scores->level_identifier[i]);
10367 putFile16BitBE(file, scores->level_nr);
10368 putFile16BitBE(file, scores->num_entries);
10371 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10375 for (i = 0; i < scores->num_entries; i++)
10377 int name_size = strlen(scores->entry[i].name);
10379 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10380 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10384 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10388 for (i = 0; i < scores->num_entries; i++)
10389 putFile16BitBE(file, scores->entry[i].score);
10392 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10396 for (i = 0; i < scores->num_entries; i++)
10397 putFile32BitBE(file, scores->entry[i].score);
10400 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10404 for (i = 0; i < scores->num_entries; i++)
10405 putFile32BitBE(file, scores->entry[i].time);
10408 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10412 for (i = 0; i < scores->num_entries; i++)
10414 int size = strlen(scores->entry[i].tape_basename);
10416 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10417 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10421 static void SaveScoreToFilename(char *filename)
10424 int info_chunk_size;
10425 int name_chunk_size;
10426 int scor_chunk_size;
10427 int sc4r_chunk_size;
10428 int time_chunk_size;
10429 int tape_chunk_size;
10430 boolean has_large_score_values;
10433 if (!(file = fopen(filename, MODE_WRITE)))
10435 Warn("cannot save score file '%s'", filename);
10440 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10441 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10442 scor_chunk_size = scores.num_entries * 2;
10443 sc4r_chunk_size = scores.num_entries * 4;
10444 time_chunk_size = scores.num_entries * 4;
10445 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10447 has_large_score_values = FALSE;
10448 for (i = 0; i < scores.num_entries; i++)
10449 if (scores.entry[i].score > 0xffff)
10450 has_large_score_values = TRUE;
10452 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10453 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10455 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10456 SaveScore_VERS(file, &scores);
10458 putFileChunkBE(file, "INFO", info_chunk_size);
10459 SaveScore_INFO(file, &scores);
10461 putFileChunkBE(file, "NAME", name_chunk_size);
10462 SaveScore_NAME(file, &scores);
10464 if (has_large_score_values)
10466 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10467 SaveScore_SC4R(file, &scores);
10471 putFileChunkBE(file, "SCOR", scor_chunk_size);
10472 SaveScore_SCOR(file, &scores);
10475 putFileChunkBE(file, "TIME", time_chunk_size);
10476 SaveScore_TIME(file, &scores);
10478 putFileChunkBE(file, "TAPE", tape_chunk_size);
10479 SaveScore_TAPE(file, &scores);
10483 SetFilePermissions(filename, PERMS_PRIVATE);
10486 void SaveScore(int nr)
10488 char *filename = getScoreFilename(nr);
10491 // used instead of "leveldir_current->subdir" (for network games)
10492 InitScoreDirectory(levelset.identifier);
10494 scores.file_version = FILE_VERSION_ACTUAL;
10495 scores.game_version = GAME_VERSION_ACTUAL;
10497 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10498 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10499 scores.level_nr = level_nr;
10501 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10502 if (scores.entry[i].score == 0 &&
10503 scores.entry[i].time == 0 &&
10504 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10507 scores.num_entries = i;
10509 if (scores.num_entries == 0)
10512 SaveScoreToFilename(filename);
10515 static void LoadServerScoreFromCache(int nr)
10517 struct ScoreEntry score_entry;
10526 { &score_entry.score, FALSE, 0 },
10527 { &score_entry.time, FALSE, 0 },
10528 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10529 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10530 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10531 { &score_entry.id, FALSE, 0 },
10532 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10533 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10534 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10535 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10539 char *filename = getScoreCacheFilename(nr);
10540 SetupFileHash *score_hash = loadSetupFileHash(filename);
10543 server_scores.num_entries = 0;
10545 if (score_hash == NULL)
10548 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10550 score_entry = server_scores.entry[i];
10552 for (j = 0; score_mapping[j].value != NULL; j++)
10556 sprintf(token, "%02d.%d", i, j);
10558 char *value = getHashEntry(score_hash, token);
10563 if (score_mapping[j].is_string)
10565 char *score_value = (char *)score_mapping[j].value;
10566 int value_size = score_mapping[j].string_size;
10568 strncpy(score_value, value, value_size);
10569 score_value[value_size] = '\0';
10573 int *score_value = (int *)score_mapping[j].value;
10575 *score_value = atoi(value);
10578 server_scores.num_entries = i + 1;
10581 server_scores.entry[i] = score_entry;
10584 freeSetupFileHash(score_hash);
10587 void LoadServerScore(int nr, boolean download_score)
10589 if (!setup.use_api_server)
10592 // always start with reliable default values
10593 setServerScoreInfoToDefaults();
10595 // 1st step: load server scores from cache file (which may not exist)
10596 // (this should prevent reading it while the thread is writing to it)
10597 LoadServerScoreFromCache(nr);
10599 if (download_score && runtime.use_api_server)
10601 // 2nd step: download server scores from score server to cache file
10602 // (as thread, as it might time out if the server is not reachable)
10603 ApiGetScoreAsThread(nr);
10607 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10609 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10611 // if score tape not uploaded, ask for uploading missing tapes later
10612 if (!setup.has_remaining_tapes)
10613 setup.ask_for_remaining_tapes = TRUE;
10615 setup.provide_uploading_tapes = TRUE;
10616 setup.has_remaining_tapes = TRUE;
10618 SaveSetup_ServerSetup();
10621 void SaveServerScore(int nr, boolean tape_saved)
10623 if (!runtime.use_api_server)
10625 PrepareScoreTapesForUpload(leveldir_current->subdir);
10630 ApiAddScoreAsThread(nr, tape_saved, NULL);
10633 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10634 char *score_tape_filename)
10636 if (!runtime.use_api_server)
10639 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10642 void LoadLocalAndServerScore(int nr, boolean download_score)
10644 int last_added_local = scores.last_added_local;
10645 boolean force_last_added = scores.force_last_added;
10647 // needed if only showing server scores
10648 setScoreInfoToDefaults();
10650 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10653 // restore last added local score entry (before merging server scores)
10654 scores.last_added = scores.last_added_local = last_added_local;
10656 if (setup.use_api_server &&
10657 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10659 // load server scores from cache file and trigger update from server
10660 LoadServerScore(nr, download_score);
10662 // merge local scores with scores from server
10663 MergeServerScore();
10666 if (force_last_added)
10667 scores.force_last_added = force_last_added;
10671 // ============================================================================
10672 // setup file functions
10673 // ============================================================================
10675 #define TOKEN_STR_PLAYER_PREFIX "player_"
10678 static struct TokenInfo global_setup_tokens[] =
10682 &setup.player_name, "player_name"
10686 &setup.multiple_users, "multiple_users"
10690 &setup.sound, "sound"
10694 &setup.sound_loops, "repeating_sound_loops"
10698 &setup.sound_music, "background_music"
10702 &setup.sound_simple, "simple_sound_effects"
10706 &setup.toons, "toons"
10710 &setup.global_animations, "global_animations"
10714 &setup.scroll_delay, "scroll_delay"
10718 &setup.forced_scroll_delay, "forced_scroll_delay"
10722 &setup.scroll_delay_value, "scroll_delay_value"
10726 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10730 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10734 &setup.fade_screens, "fade_screens"
10738 &setup.autorecord, "automatic_tape_recording"
10742 &setup.autorecord_after_replay, "autorecord_after_replay"
10746 &setup.auto_pause_on_start, "auto_pause_on_start"
10750 &setup.show_titlescreen, "show_titlescreen"
10754 &setup.quick_doors, "quick_doors"
10758 &setup.team_mode, "team_mode"
10762 &setup.handicap, "handicap"
10766 &setup.skip_levels, "skip_levels"
10769 TYPE_SWITCH_3_STATES,
10770 &setup.allow_skipping_levels, "allow_skipping_levels"
10774 &setup.increment_levels, "increment_levels"
10778 &setup.auto_play_next_level, "auto_play_next_level"
10782 &setup.count_score_after_game, "count_score_after_game"
10786 &setup.show_scores_after_game, "show_scores_after_game"
10790 &setup.time_limit, "time_limit"
10794 &setup.fullscreen, "fullscreen"
10798 &setup.window_scaling_percent, "window_scaling_percent"
10802 &setup.window_scaling_quality, "window_scaling_quality"
10806 &setup.screen_rendering_mode, "screen_rendering_mode"
10810 &setup.vsync_mode, "vsync_mode"
10814 &setup.ask_on_escape, "ask_on_escape"
10818 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10822 &setup.ask_on_game_over, "ask_on_game_over"
10826 &setup.ask_on_quit_game, "ask_on_quit_game"
10830 &setup.ask_on_quit_program, "ask_on_quit_program"
10834 &setup.quick_switch, "quick_player_switch"
10838 &setup.input_on_focus, "input_on_focus"
10842 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10846 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10850 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10854 &setup.game_speed_extended, "game_speed_extended"
10858 &setup.game_frame_delay, "game_frame_delay"
10862 &setup.default_game_engine_type, "default_game_engine_type"
10866 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10870 &setup.bd_skip_hatching, "bd_skip_hatching"
10874 &setup.bd_scroll_delay, "bd_scroll_delay"
10878 &setup.bd_show_invisible_outbox, "bd_show_invisible_outbox"
10881 TYPE_SWITCH_3_STATES,
10882 &setup.bd_smooth_movements, "bd_smooth_movements"
10885 TYPE_SWITCH_3_STATES,
10886 &setup.bd_pushing_graphics, "bd_pushing_graphics"
10889 TYPE_SWITCH_3_STATES,
10890 &setup.bd_up_down_graphics, "bd_up_down_graphics"
10893 TYPE_SWITCH_3_STATES,
10894 &setup.bd_falling_sounds, "bd_falling_sounds"
10898 &setup.bd_palette_c64, "bd_palette_c64"
10902 &setup.bd_palette_c64dtv, "bd_palette_c64dtv"
10906 &setup.bd_palette_atari, "bd_palette_atari"
10910 &setup.bd_default_color_type, "bd_default_color_type"
10914 &setup.bd_random_colors, "bd_random_colors"
10918 &setup.sp_show_border_elements, "sp_show_border_elements"
10922 &setup.small_game_graphics, "small_game_graphics"
10926 &setup.show_load_save_buttons, "show_load_save_buttons"
10930 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10934 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10938 &setup.graphics_set, "graphics_set"
10942 &setup.sounds_set, "sounds_set"
10946 &setup.music_set, "music_set"
10949 TYPE_SWITCH_3_STATES,
10950 &setup.override_level_graphics, "override_level_graphics"
10953 TYPE_SWITCH_3_STATES,
10954 &setup.override_level_sounds, "override_level_sounds"
10957 TYPE_SWITCH_3_STATES,
10958 &setup.override_level_music, "override_level_music"
10962 &setup.volume_simple, "volume_simple"
10966 &setup.volume_loops, "volume_loops"
10970 &setup.volume_music, "volume_music"
10974 &setup.audio_sample_rate_44100, "audio_sample_rate_44100"
10978 &setup.network_mode, "network_mode"
10982 &setup.network_player_nr, "network_player"
10986 &setup.network_server_hostname, "network_server_hostname"
10990 &setup.touch.control_type, "touch.control_type"
10994 &setup.touch.move_distance, "touch.move_distance"
10998 &setup.touch.drop_distance, "touch.drop_distance"
11002 &setup.touch.transparency, "touch.transparency"
11006 &setup.touch.draw_outlined, "touch.draw_outlined"
11010 &setup.touch.draw_pressed, "touch.draw_pressed"
11014 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
11018 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
11022 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
11026 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
11030 &setup.touch.overlay_buttons, "touch.overlay_buttons"
11034 static struct TokenInfo auto_setup_tokens[] =
11038 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
11042 static struct TokenInfo server_setup_tokens[] =
11046 &setup.player_uuid, "player_uuid"
11050 &setup.player_version, "player_version"
11054 &setup.use_api_server, TEST_PREFIX "use_api_server"
11058 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
11062 &setup.api_server_password, TEST_PREFIX "api_server_password"
11066 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
11070 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
11074 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
11078 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
11082 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
11086 static struct TokenInfo editor_setup_tokens[] =
11090 &setup.editor.el_classic, "editor.el_classic"
11094 &setup.editor.el_custom, "editor.el_custom"
11098 &setup.editor.el_user_defined, "editor.el_user_defined"
11102 &setup.editor.el_dynamic, "editor.el_dynamic"
11106 &setup.editor.el_headlines, "editor.el_headlines"
11110 &setup.editor.show_element_token, "editor.show_element_token"
11114 &setup.editor.fast_game_start, "editor.fast_game_start"
11118 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
11122 static struct TokenInfo editor_cascade_setup_tokens[] =
11126 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
11130 &setup.editor_cascade.el_bdx, "editor.cascade.el_bdx"
11134 &setup.editor_cascade.el_bdx_effects, "editor.cascade.el_bdx_effects"
11138 &setup.editor_cascade.el_em, "editor.cascade.el_em"
11142 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
11146 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
11150 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
11154 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
11158 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
11162 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
11166 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
11170 &setup.editor_cascade.el_df, "editor.cascade.el_df"
11174 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
11178 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
11182 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
11186 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
11190 &setup.editor_cascade.el_es, "editor.cascade.el_es"
11194 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
11198 &setup.editor_cascade.el_user, "editor.cascade.el_user"
11202 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
11206 static struct TokenInfo shortcut_setup_tokens[] =
11210 &setup.shortcut.save_game, "shortcut.save_game"
11214 &setup.shortcut.load_game, "shortcut.load_game"
11218 &setup.shortcut.restart_game, "shortcut.restart_game"
11222 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
11226 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
11230 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
11234 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
11238 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
11242 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
11246 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
11250 &setup.shortcut.tape_eject, "shortcut.tape_eject"
11254 &setup.shortcut.tape_extra, "shortcut.tape_extra"
11258 &setup.shortcut.tape_stop, "shortcut.tape_stop"
11262 &setup.shortcut.tape_pause, "shortcut.tape_pause"
11266 &setup.shortcut.tape_record, "shortcut.tape_record"
11270 &setup.shortcut.tape_play, "shortcut.tape_play"
11274 &setup.shortcut.sound_simple, "shortcut.sound_simple"
11278 &setup.shortcut.sound_loops, "shortcut.sound_loops"
11282 &setup.shortcut.sound_music, "shortcut.sound_music"
11286 &setup.shortcut.snap_left, "shortcut.snap_left"
11290 &setup.shortcut.snap_right, "shortcut.snap_right"
11294 &setup.shortcut.snap_up, "shortcut.snap_up"
11298 &setup.shortcut.snap_down, "shortcut.snap_down"
11302 &setup.shortcut.speed_fast, "shortcut.speed_fast"
11306 &setup.shortcut.speed_slow, "shortcut.speed_slow"
11310 static struct SetupInputInfo setup_input;
11311 static struct TokenInfo player_setup_tokens[] =
11315 &setup_input.use_joystick, ".use_joystick"
11319 &setup_input.joy.device_name, ".joy.device_name"
11323 &setup_input.joy.xleft, ".joy.xleft"
11327 &setup_input.joy.xmiddle, ".joy.xmiddle"
11331 &setup_input.joy.xright, ".joy.xright"
11335 &setup_input.joy.yupper, ".joy.yupper"
11339 &setup_input.joy.ymiddle, ".joy.ymiddle"
11343 &setup_input.joy.ylower, ".joy.ylower"
11347 &setup_input.joy.snap, ".joy.snap_field"
11351 &setup_input.joy.drop, ".joy.place_bomb"
11355 &setup_input.key.left, ".key.move_left"
11359 &setup_input.key.right, ".key.move_right"
11363 &setup_input.key.up, ".key.move_up"
11367 &setup_input.key.down, ".key.move_down"
11371 &setup_input.key.snap, ".key.snap_field"
11375 &setup_input.key.drop, ".key.place_bomb"
11379 static struct TokenInfo system_setup_tokens[] =
11383 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
11387 &setup.system.sdl_videodriver, "system.sdl_videodriver"
11391 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
11395 &setup.system.audio_fragment_size, "system.audio_fragment_size"
11399 static struct TokenInfo internal_setup_tokens[] =
11403 &setup.internal.program_title, "program_title"
11407 &setup.internal.program_version, "program_version"
11411 &setup.internal.program_author, "program_author"
11415 &setup.internal.program_email, "program_email"
11419 &setup.internal.program_website, "program_website"
11423 &setup.internal.program_copyright, "program_copyright"
11427 &setup.internal.program_company, "program_company"
11431 &setup.internal.program_icon_file, "program_icon_file"
11435 &setup.internal.default_graphics_set, "default_graphics_set"
11439 &setup.internal.default_sounds_set, "default_sounds_set"
11443 &setup.internal.default_music_set, "default_music_set"
11447 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11451 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11455 &setup.internal.fallback_music_file, "fallback_music_file"
11459 &setup.internal.default_level_series, "default_level_series"
11463 &setup.internal.default_window_width, "default_window_width"
11467 &setup.internal.default_window_height, "default_window_height"
11471 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11475 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11479 &setup.internal.create_user_levelset, "create_user_levelset"
11483 &setup.internal.info_screens_from_main, "info_screens_from_main"
11487 &setup.internal.menu_game, "menu_game"
11491 &setup.internal.menu_engines, "menu_engines"
11495 &setup.internal.menu_editor, "menu_editor"
11499 &setup.internal.menu_graphics, "menu_graphics"
11503 &setup.internal.menu_sound, "menu_sound"
11507 &setup.internal.menu_artwork, "menu_artwork"
11511 &setup.internal.menu_input, "menu_input"
11515 &setup.internal.menu_touch, "menu_touch"
11519 &setup.internal.menu_shortcuts, "menu_shortcuts"
11523 &setup.internal.menu_exit, "menu_exit"
11527 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11531 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11535 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11539 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11543 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11547 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11551 &setup.internal.menu_shortcuts_speed, "menu_shortcuts_speed"
11555 &setup.internal.info_title, "info_title"
11559 &setup.internal.info_elements, "info_elements"
11563 &setup.internal.info_music, "info_music"
11567 &setup.internal.info_credits, "info_credits"
11571 &setup.internal.info_program, "info_program"
11575 &setup.internal.info_version, "info_version"
11579 &setup.internal.info_levelset, "info_levelset"
11583 &setup.internal.info_exit, "info_exit"
11587 static struct TokenInfo debug_setup_tokens[] =
11591 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11595 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11599 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11603 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11607 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11611 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11615 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11619 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11623 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11627 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11631 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11635 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11639 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11643 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11647 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11651 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11655 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11659 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11663 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11667 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11671 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11674 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11678 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11681 TYPE_SWITCH_3_STATES,
11682 &setup.debug.xsn_mode, "debug.xsn_mode"
11686 &setup.debug.xsn_percent, "debug.xsn_percent"
11690 static struct TokenInfo options_setup_tokens[] =
11694 &setup.options.verbose, "options.verbose"
11698 &setup.options.debug, "options.debug"
11702 &setup.options.debug_mode, "options.debug_mode"
11706 static void setSetupInfoToDefaults(struct SetupInfo *si)
11710 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11712 si->multiple_users = TRUE;
11715 si->sound_loops = TRUE;
11716 si->sound_music = TRUE;
11717 si->sound_simple = TRUE;
11719 si->global_animations = TRUE;
11720 si->scroll_delay = TRUE;
11721 si->forced_scroll_delay = FALSE;
11722 si->scroll_delay_value = STD_SCROLL_DELAY;
11723 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11724 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11725 si->fade_screens = TRUE;
11726 si->autorecord = TRUE;
11727 si->autorecord_after_replay = TRUE;
11728 si->auto_pause_on_start = FALSE;
11729 si->show_titlescreen = TRUE;
11730 si->quick_doors = FALSE;
11731 si->team_mode = FALSE;
11732 si->handicap = TRUE;
11733 si->skip_levels = TRUE;
11734 si->allow_skipping_levels = STATE_ASK;
11735 si->increment_levels = TRUE;
11736 si->auto_play_next_level = TRUE;
11737 si->count_score_after_game = TRUE;
11738 si->show_scores_after_game = TRUE;
11739 si->time_limit = TRUE;
11740 si->fullscreen = FALSE;
11741 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11742 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11743 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11744 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11745 si->ask_on_escape = TRUE;
11746 si->ask_on_escape_editor = TRUE;
11747 si->ask_on_game_over = TRUE;
11748 si->ask_on_quit_game = TRUE;
11749 si->ask_on_quit_program = TRUE;
11750 si->quick_switch = FALSE;
11751 si->input_on_focus = FALSE;
11752 si->prefer_aga_graphics = TRUE;
11753 si->prefer_lowpass_sounds = FALSE;
11754 si->prefer_extra_panel_items = TRUE;
11755 si->game_speed_extended = FALSE;
11756 si->game_frame_delay = GAME_FRAME_DELAY;
11757 si->default_game_engine_type = GAME_ENGINE_TYPE_RND;
11758 si->bd_skip_uncovering = FALSE;
11759 si->bd_skip_hatching = FALSE;
11760 si->bd_scroll_delay = TRUE;
11761 si->bd_show_invisible_outbox = FALSE;
11762 si->bd_smooth_movements = STATE_TRUE;
11763 si->bd_pushing_graphics = STATE_TRUE;
11764 si->bd_up_down_graphics = STATE_TRUE;
11765 si->bd_falling_sounds = STATE_AUTO;
11766 si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11767 si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11768 si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11769 si->bd_default_color_type = GD_DEFAULT_COLOR_TYPE;
11770 si->bd_random_colors = FALSE;
11771 si->sp_show_border_elements = FALSE;
11772 si->small_game_graphics = FALSE;
11773 si->show_load_save_buttons = FALSE;
11774 si->show_undo_redo_buttons = FALSE;
11775 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11777 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11778 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11779 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11781 si->override_level_graphics = STATE_FALSE;
11782 si->override_level_sounds = STATE_FALSE;
11783 si->override_level_music = STATE_FALSE;
11785 si->volume_simple = 100; // percent
11786 si->volume_loops = 100; // percent
11787 si->volume_music = 100; // percent
11788 si->audio_sample_rate_44100 = FALSE;
11790 si->network_mode = FALSE;
11791 si->network_player_nr = 0; // first player
11792 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11794 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11795 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11796 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11797 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11798 si->touch.draw_outlined = TRUE;
11799 si->touch.draw_pressed = TRUE;
11801 for (i = 0; i < 2; i++)
11803 char *default_grid_button[6][2] =
11809 { "111222", " vv " },
11810 { "111222", " vv " }
11812 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11813 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11814 int min_xsize = MIN(6, grid_xsize);
11815 int min_ysize = MIN(6, grid_ysize);
11816 int startx = grid_xsize - min_xsize;
11817 int starty = grid_ysize - min_ysize;
11820 // virtual buttons grid can only be set to defaults if video is initialized
11821 // (this will be repeated if virtual buttons are not loaded from setup file)
11822 if (video.initialized)
11824 si->touch.grid_xsize[i] = grid_xsize;
11825 si->touch.grid_ysize[i] = grid_ysize;
11829 si->touch.grid_xsize[i] = -1;
11830 si->touch.grid_ysize[i] = -1;
11833 for (x = 0; x < MAX_GRID_XSIZE; x++)
11834 for (y = 0; y < MAX_GRID_YSIZE; y++)
11835 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11837 for (x = 0; x < min_xsize; x++)
11838 for (y = 0; y < min_ysize; y++)
11839 si->touch.grid_button[i][x][starty + y] =
11840 default_grid_button[y][0][x];
11842 for (x = 0; x < min_xsize; x++)
11843 for (y = 0; y < min_ysize; y++)
11844 si->touch.grid_button[i][startx + x][starty + y] =
11845 default_grid_button[y][1][x];
11848 si->touch.grid_initialized = video.initialized;
11850 si->touch.overlay_buttons = FALSE;
11852 si->editor.el_boulderdash = TRUE;
11853 si->editor.el_boulderdash_native = TRUE;
11854 si->editor.el_boulderdash_effects = TRUE;
11855 si->editor.el_emerald_mine = TRUE;
11856 si->editor.el_emerald_mine_club = TRUE;
11857 si->editor.el_more = TRUE;
11858 si->editor.el_sokoban = TRUE;
11859 si->editor.el_supaplex = TRUE;
11860 si->editor.el_diamond_caves = TRUE;
11861 si->editor.el_dx_boulderdash = TRUE;
11863 si->editor.el_mirror_magic = TRUE;
11864 si->editor.el_deflektor = TRUE;
11866 si->editor.el_chars = TRUE;
11867 si->editor.el_steel_chars = TRUE;
11869 si->editor.el_classic = TRUE;
11870 si->editor.el_custom = TRUE;
11872 si->editor.el_user_defined = FALSE;
11873 si->editor.el_dynamic = TRUE;
11875 si->editor.el_headlines = TRUE;
11877 si->editor.show_element_token = FALSE;
11878 si->editor.fast_game_start = FALSE;
11880 si->editor.show_read_only_warning = TRUE;
11882 si->editor.use_template_for_new_levels = TRUE;
11884 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11885 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11886 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11887 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11888 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11890 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11891 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11892 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11893 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11894 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11896 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11897 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11898 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11899 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11900 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11901 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11903 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11904 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11905 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11907 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11908 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11909 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11910 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11912 si->shortcut.speed_fast = DEFAULT_KEY_SPEED_FAST;
11913 si->shortcut.speed_slow = DEFAULT_KEY_SPEED_SLOW;
11915 for (i = 0; i < MAX_PLAYERS; i++)
11917 si->input[i].use_joystick = FALSE;
11918 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11919 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11920 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11921 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11922 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11923 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11924 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11925 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11926 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11927 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11928 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11929 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11930 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11931 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11932 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11935 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11936 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11937 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11938 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11940 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11941 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11942 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11943 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11944 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11945 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11946 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11948 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11950 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11951 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11952 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11954 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11955 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11956 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11958 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11959 si->internal.choose_from_top_leveldir = FALSE;
11960 si->internal.show_scaling_in_title = TRUE;
11961 si->internal.create_user_levelset = TRUE;
11962 si->internal.info_screens_from_main = FALSE;
11964 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11965 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11967 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11968 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11969 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11970 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11971 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11972 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11973 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11974 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11975 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11976 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11978 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11979 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11980 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11981 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11982 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11983 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11984 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11985 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11986 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11987 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11989 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11990 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11992 si->debug.show_frames_per_second = FALSE;
11994 si->debug.xsn_mode = STATE_AUTO;
11995 si->debug.xsn_percent = 0;
11997 si->options.verbose = FALSE;
11998 si->options.debug = FALSE;
11999 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
12001 #if defined(PLATFORM_ANDROID)
12002 si->fullscreen = TRUE;
12003 si->touch.overlay_buttons = TRUE;
12006 setHideSetupEntry(&setup.debug.xsn_mode);
12009 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
12011 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
12014 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
12016 si->player_uuid = NULL; // (will be set later)
12017 si->player_version = 1; // (will be set later)
12019 si->use_api_server = TRUE;
12020 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
12021 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
12022 si->ask_for_uploading_tapes = TRUE;
12023 si->ask_for_remaining_tapes = FALSE;
12024 si->provide_uploading_tapes = TRUE;
12025 si->ask_for_using_api_server = TRUE;
12026 si->has_remaining_tapes = FALSE;
12029 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
12031 si->editor_cascade.el_bd = TRUE;
12032 si->editor_cascade.el_bdx = TRUE;
12033 si->editor_cascade.el_bdx_effects = FALSE;
12034 si->editor_cascade.el_em = TRUE;
12035 si->editor_cascade.el_emc = TRUE;
12036 si->editor_cascade.el_rnd = TRUE;
12037 si->editor_cascade.el_sb = TRUE;
12038 si->editor_cascade.el_sp = TRUE;
12039 si->editor_cascade.el_dc = TRUE;
12040 si->editor_cascade.el_dx = TRUE;
12042 si->editor_cascade.el_mm = TRUE;
12043 si->editor_cascade.el_df = TRUE;
12045 si->editor_cascade.el_chars = FALSE;
12046 si->editor_cascade.el_steel_chars = FALSE;
12047 si->editor_cascade.el_ce = FALSE;
12048 si->editor_cascade.el_ge = FALSE;
12049 si->editor_cascade.el_es = FALSE;
12050 si->editor_cascade.el_ref = FALSE;
12051 si->editor_cascade.el_user = FALSE;
12052 si->editor_cascade.el_dynamic = FALSE;
12055 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
12057 static char *getHideSetupToken(void *setup_value)
12059 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
12061 if (setup_value != NULL)
12062 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
12064 return hide_setup_token;
12067 void setHideSetupEntry(void *setup_value)
12069 char *hide_setup_token = getHideSetupToken(setup_value);
12071 if (hide_setup_hash == NULL)
12072 hide_setup_hash = newSetupFileHash();
12074 if (setup_value != NULL)
12075 setHashEntry(hide_setup_hash, hide_setup_token, "");
12078 void removeHideSetupEntry(void *setup_value)
12080 char *hide_setup_token = getHideSetupToken(setup_value);
12082 if (setup_value != NULL)
12083 removeHashEntry(hide_setup_hash, hide_setup_token);
12086 boolean hideSetupEntry(void *setup_value)
12088 char *hide_setup_token = getHideSetupToken(setup_value);
12090 return (setup_value != NULL &&
12091 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
12094 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
12095 struct TokenInfo *token_info,
12096 int token_nr, char *token_text)
12098 char *token_hide_text = getStringCat2(token_text, ".hide");
12099 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
12101 // set the value of this setup option in the setup option structure
12102 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
12104 // check if this setup option should be hidden in the setup menu
12105 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
12106 setHideSetupEntry(token_info[token_nr].value);
12108 free(token_hide_text);
12111 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
12112 struct TokenInfo *token_info,
12115 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
12116 token_info[token_nr].text);
12119 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
12123 if (!setup_file_hash)
12126 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12127 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
12129 setup.touch.grid_initialized = TRUE;
12130 for (i = 0; i < 2; i++)
12132 int grid_xsize = setup.touch.grid_xsize[i];
12133 int grid_ysize = setup.touch.grid_ysize[i];
12136 // if virtual buttons are not loaded from setup file, repeat initializing
12137 // virtual buttons grid with default values later when video is initialized
12138 if (grid_xsize == -1 ||
12141 setup.touch.grid_initialized = FALSE;
12146 for (y = 0; y < grid_ysize; y++)
12148 char token_string[MAX_LINE_LEN];
12150 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12152 char *value_string = getHashEntry(setup_file_hash, token_string);
12154 if (value_string == NULL)
12157 for (x = 0; x < grid_xsize; x++)
12159 char c = value_string[x];
12161 setup.touch.grid_button[i][x][y] =
12162 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
12167 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12168 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
12170 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12171 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
12173 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12177 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12179 setup_input = setup.input[pnr];
12180 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12182 char full_token[100];
12184 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
12185 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12188 setup.input[pnr] = setup_input;
12191 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12192 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12194 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12195 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12197 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12198 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12200 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12201 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12203 setHideRelatedSetupEntries();
12206 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12210 if (!setup_file_hash)
12213 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12214 setSetupInfo(auto_setup_tokens, i,
12215 getHashEntry(setup_file_hash,
12216 auto_setup_tokens[i].text));
12219 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12223 if (!setup_file_hash)
12226 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12227 setSetupInfo(server_setup_tokens, i,
12228 getHashEntry(setup_file_hash,
12229 server_setup_tokens[i].text));
12232 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12236 if (!setup_file_hash)
12239 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12240 setSetupInfo(editor_cascade_setup_tokens, i,
12241 getHashEntry(setup_file_hash,
12242 editor_cascade_setup_tokens[i].text));
12245 void LoadUserNames(void)
12247 int last_user_nr = user.nr;
12250 if (global.user_names != NULL)
12252 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12253 checked_free(global.user_names[i]);
12255 checked_free(global.user_names);
12258 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12260 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12264 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12266 if (setup_file_hash)
12268 char *player_name = getHashEntry(setup_file_hash, "player_name");
12270 global.user_names[i] = getFixedUserName(player_name);
12272 freeSetupFileHash(setup_file_hash);
12275 if (global.user_names[i] == NULL)
12276 global.user_names[i] = getStringCopy(getDefaultUserName(i));
12279 user.nr = last_user_nr;
12282 void LoadSetupFromFilename(char *filename)
12284 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12286 if (setup_file_hash)
12288 decodeSetupFileHash_Default(setup_file_hash);
12290 freeSetupFileHash(setup_file_hash);
12294 Debug("setup", "using default setup values");
12298 static void LoadSetup_SpecialPostProcessing(void)
12300 char *player_name_new;
12302 // needed to work around problems with fixed length strings
12303 player_name_new = getFixedUserName(setup.player_name);
12304 free(setup.player_name);
12305 setup.player_name = player_name_new;
12307 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12308 if (setup.scroll_delay == FALSE)
12310 setup.scroll_delay_value = MIN_SCROLL_DELAY;
12311 setup.scroll_delay = TRUE; // now always "on"
12314 // make sure that scroll delay value stays inside valid range
12315 setup.scroll_delay_value =
12316 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12319 void LoadSetup_Default(void)
12323 // always start with reliable default values
12324 setSetupInfoToDefaults(&setup);
12326 // try to load setup values from default setup file
12327 filename = getDefaultSetupFilename();
12329 if (fileExists(filename))
12330 LoadSetupFromFilename(filename);
12332 // try to load setup values from platform setup file
12333 filename = getPlatformSetupFilename();
12335 if (fileExists(filename))
12336 LoadSetupFromFilename(filename);
12338 // try to load setup values from user setup file
12339 filename = getSetupFilename();
12341 LoadSetupFromFilename(filename);
12343 LoadSetup_SpecialPostProcessing();
12346 void LoadSetup_AutoSetup(void)
12348 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12349 SetupFileHash *setup_file_hash = NULL;
12351 // always start with reliable default values
12352 setSetupInfoToDefaults_AutoSetup(&setup);
12354 setup_file_hash = loadSetupFileHash(filename);
12356 if (setup_file_hash)
12358 decodeSetupFileHash_AutoSetup(setup_file_hash);
12360 freeSetupFileHash(setup_file_hash);
12366 void LoadSetup_ServerSetup(void)
12368 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12369 SetupFileHash *setup_file_hash = NULL;
12371 // always start with reliable default values
12372 setSetupInfoToDefaults_ServerSetup(&setup);
12374 setup_file_hash = loadSetupFileHash(filename);
12376 if (setup_file_hash)
12378 decodeSetupFileHash_ServerSetup(setup_file_hash);
12380 freeSetupFileHash(setup_file_hash);
12385 if (setup.player_uuid == NULL)
12387 // player UUID does not yet exist in setup file
12388 setup.player_uuid = getStringCopy(getUUID());
12389 setup.player_version = 2;
12391 SaveSetup_ServerSetup();
12395 void LoadSetup_EditorCascade(void)
12397 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12398 SetupFileHash *setup_file_hash = NULL;
12400 // always start with reliable default values
12401 setSetupInfoToDefaults_EditorCascade(&setup);
12403 setup_file_hash = loadSetupFileHash(filename);
12405 if (setup_file_hash)
12407 decodeSetupFileHash_EditorCascade(setup_file_hash);
12409 freeSetupFileHash(setup_file_hash);
12415 void LoadSetup(void)
12417 LoadSetup_Default();
12418 LoadSetup_AutoSetup();
12419 LoadSetup_ServerSetup();
12420 LoadSetup_EditorCascade();
12423 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12424 char *mapping_line)
12426 char mapping_guid[MAX_LINE_LEN];
12427 char *mapping_start, *mapping_end;
12429 // get GUID from game controller mapping line: copy complete line
12430 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12431 mapping_guid[MAX_LINE_LEN - 1] = '\0';
12433 // get GUID from game controller mapping line: cut after GUID part
12434 mapping_start = strchr(mapping_guid, ',');
12435 if (mapping_start != NULL)
12436 *mapping_start = '\0';
12438 // cut newline from game controller mapping line
12439 mapping_end = strchr(mapping_line, '\n');
12440 if (mapping_end != NULL)
12441 *mapping_end = '\0';
12443 // add mapping entry to game controller mappings hash
12444 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12447 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12452 if (!(file = fopen(filename, MODE_READ)))
12454 Warn("cannot read game controller mappings file '%s'", filename);
12459 while (!feof(file))
12461 char line[MAX_LINE_LEN];
12463 if (!fgets(line, MAX_LINE_LEN, file))
12466 addGameControllerMappingToHash(mappings_hash, line);
12472 void SaveSetup_Default(void)
12474 char *filename = getSetupFilename();
12478 InitUserDataDirectory();
12480 if (!(file = fopen(filename, MODE_WRITE)))
12482 Warn("cannot write setup file '%s'", filename);
12487 fprintFileHeader(file, SETUP_FILENAME);
12489 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12491 // just to make things nicer :)
12492 if (global_setup_tokens[i].value == &setup.multiple_users ||
12493 global_setup_tokens[i].value == &setup.sound ||
12494 global_setup_tokens[i].value == &setup.graphics_set ||
12495 global_setup_tokens[i].value == &setup.volume_simple ||
12496 global_setup_tokens[i].value == &setup.network_mode ||
12497 global_setup_tokens[i].value == &setup.touch.control_type ||
12498 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12499 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12500 fprintf(file, "\n");
12502 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12505 for (i = 0; i < 2; i++)
12507 int grid_xsize = setup.touch.grid_xsize[i];
12508 int grid_ysize = setup.touch.grid_ysize[i];
12511 fprintf(file, "\n");
12513 for (y = 0; y < grid_ysize; y++)
12515 char token_string[MAX_LINE_LEN];
12516 char value_string[MAX_LINE_LEN];
12518 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12520 for (x = 0; x < grid_xsize; x++)
12522 char c = setup.touch.grid_button[i][x][y];
12524 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12527 value_string[grid_xsize] = '\0';
12529 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12533 fprintf(file, "\n");
12534 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12535 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12537 fprintf(file, "\n");
12538 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12539 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12541 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12545 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12546 fprintf(file, "\n");
12548 setup_input = setup.input[pnr];
12549 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12550 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12553 fprintf(file, "\n");
12554 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12555 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12557 // (internal setup values not saved to user setup file)
12559 fprintf(file, "\n");
12560 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12561 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12562 setup.debug.xsn_mode != STATE_AUTO)
12563 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12565 fprintf(file, "\n");
12566 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12567 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12571 SetFilePermissions(filename, PERMS_PRIVATE);
12574 void SaveSetup_AutoSetup(void)
12576 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12580 InitUserDataDirectory();
12582 if (!(file = fopen(filename, MODE_WRITE)))
12584 Warn("cannot write auto setup file '%s'", filename);
12591 fprintFileHeader(file, AUTOSETUP_FILENAME);
12593 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12594 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12598 SetFilePermissions(filename, PERMS_PRIVATE);
12603 void SaveSetup_ServerSetup(void)
12605 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12609 InitUserDataDirectory();
12611 if (!(file = fopen(filename, MODE_WRITE)))
12613 Warn("cannot write server setup file '%s'", filename);
12620 fprintFileHeader(file, SERVERSETUP_FILENAME);
12622 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12624 // just to make things nicer :)
12625 if (server_setup_tokens[i].value == &setup.use_api_server)
12626 fprintf(file, "\n");
12628 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12633 SetFilePermissions(filename, PERMS_PRIVATE);
12638 void SaveSetup_EditorCascade(void)
12640 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12644 InitUserDataDirectory();
12646 if (!(file = fopen(filename, MODE_WRITE)))
12648 Warn("cannot write editor cascade state file '%s'", filename);
12655 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12657 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12658 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12662 SetFilePermissions(filename, PERMS_PRIVATE);
12667 void SaveSetup(void)
12669 SaveSetup_Default();
12670 SaveSetup_AutoSetup();
12671 SaveSetup_ServerSetup();
12672 SaveSetup_EditorCascade();
12675 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12680 if (!(file = fopen(filename, MODE_WRITE)))
12682 Warn("cannot write game controller mappings file '%s'", filename);
12687 BEGIN_HASH_ITERATION(mappings_hash, itr)
12689 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12691 END_HASH_ITERATION(mappings_hash, itr)
12696 void SaveSetup_AddGameControllerMapping(char *mapping)
12698 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12699 SetupFileHash *mappings_hash = newSetupFileHash();
12701 InitUserDataDirectory();
12703 // load existing personal game controller mappings
12704 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12706 // add new mapping to personal game controller mappings
12707 addGameControllerMappingToHash(mappings_hash, mapping);
12709 // save updated personal game controller mappings
12710 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12712 freeSetupFileHash(mappings_hash);
12716 void LoadCustomElementDescriptions(void)
12718 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12719 SetupFileHash *setup_file_hash;
12722 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12724 if (element_info[i].custom_description != NULL)
12726 free(element_info[i].custom_description);
12727 element_info[i].custom_description = NULL;
12731 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12734 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12736 char *token = getStringCat2(element_info[i].token_name, ".name");
12737 char *value = getHashEntry(setup_file_hash, token);
12740 element_info[i].custom_description = getStringCopy(value);
12745 freeSetupFileHash(setup_file_hash);
12748 static int getElementFromToken(char *token)
12750 char *value = getHashEntry(element_token_hash, token);
12753 return atoi(value);
12755 Warn("unknown element token '%s'", token);
12757 return EL_UNDEFINED;
12760 void FreeGlobalAnimEventInfo(void)
12762 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12764 if (gaei->event_list == NULL)
12769 for (i = 0; i < gaei->num_event_lists; i++)
12771 checked_free(gaei->event_list[i]->event_value);
12772 checked_free(gaei->event_list[i]);
12775 checked_free(gaei->event_list);
12777 gaei->event_list = NULL;
12778 gaei->num_event_lists = 0;
12781 static int AddGlobalAnimEventList(void)
12783 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12784 int list_pos = gaei->num_event_lists++;
12786 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12787 sizeof(struct GlobalAnimEventListInfo *));
12789 gaei->event_list[list_pos] =
12790 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12792 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12794 gaeli->event_value = NULL;
12795 gaeli->num_event_values = 0;
12800 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12802 // do not add empty global animation events
12803 if (event_value == ANIM_EVENT_NONE)
12806 // if list position is undefined, create new list
12807 if (list_pos == ANIM_EVENT_UNDEFINED)
12808 list_pos = AddGlobalAnimEventList();
12810 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12811 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12812 int value_pos = gaeli->num_event_values++;
12814 gaeli->event_value = checked_realloc(gaeli->event_value,
12815 gaeli->num_event_values * sizeof(int *));
12817 gaeli->event_value[value_pos] = event_value;
12822 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12824 if (list_pos == ANIM_EVENT_UNDEFINED)
12825 return ANIM_EVENT_NONE;
12827 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12828 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12830 return gaeli->event_value[value_pos];
12833 int GetGlobalAnimEventValueCount(int list_pos)
12835 if (list_pos == ANIM_EVENT_UNDEFINED)
12838 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12839 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12841 return gaeli->num_event_values;
12844 // This function checks if a string <s> of the format "string1, string2, ..."
12845 // exactly contains a string <s_contained>.
12847 static boolean string_has_parameter(char *s, char *s_contained)
12851 if (s == NULL || s_contained == NULL)
12854 if (strlen(s_contained) > strlen(s))
12857 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12859 char next_char = s[strlen(s_contained)];
12861 // check if next character is delimiter or whitespace
12862 if (next_char == ',' || next_char == '\0' ||
12863 next_char == ' ' || next_char == '\t')
12867 // check if string contains another parameter string after a comma
12868 substring = strchr(s, ',');
12869 if (substring == NULL) // string does not contain a comma
12872 // advance string pointer to next character after the comma
12875 // skip potential whitespaces after the comma
12876 while (*substring == ' ' || *substring == '\t')
12879 return string_has_parameter(substring, s_contained);
12882 static int get_anim_parameter_value_ce(char *s)
12885 char *pattern_1 = "ce_change:custom_";
12886 char *pattern_2 = ".page_";
12887 int pattern_1_len = strlen(pattern_1);
12888 char *matching_char = strstr(s_ptr, pattern_1);
12889 int result = ANIM_EVENT_NONE;
12891 if (matching_char == NULL)
12892 return ANIM_EVENT_NONE;
12894 result = ANIM_EVENT_CE_CHANGE;
12896 s_ptr = matching_char + pattern_1_len;
12898 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12899 if (*s_ptr >= '0' && *s_ptr <= '9')
12901 int gic_ce_nr = (*s_ptr++ - '0');
12903 if (*s_ptr >= '0' && *s_ptr <= '9')
12905 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12907 if (*s_ptr >= '0' && *s_ptr <= '9')
12908 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12911 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12912 return ANIM_EVENT_NONE;
12914 // custom element stored as 0 to 255
12917 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12921 // invalid custom element number specified
12923 return ANIM_EVENT_NONE;
12926 // check for change page number ("page_X" or "page_XX") (optional)
12927 if (strPrefix(s_ptr, pattern_2))
12929 s_ptr += strlen(pattern_2);
12931 if (*s_ptr >= '0' && *s_ptr <= '9')
12933 int gic_page_nr = (*s_ptr++ - '0');
12935 if (*s_ptr >= '0' && *s_ptr <= '9')
12936 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12938 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12939 return ANIM_EVENT_NONE;
12941 // change page stored as 1 to 32 (0 means "all change pages")
12943 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12947 // invalid animation part number specified
12949 return ANIM_EVENT_NONE;
12953 // discard result if next character is neither delimiter nor whitespace
12954 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12955 *s_ptr == ' ' || *s_ptr == '\t'))
12956 return ANIM_EVENT_NONE;
12961 static int get_anim_parameter_value(char *s)
12963 int event_value[] =
12971 char *pattern_1[] =
12979 char *pattern_2 = ".part_";
12980 char *matching_char = NULL;
12982 int pattern_1_len = 0;
12983 int result = ANIM_EVENT_NONE;
12986 result = get_anim_parameter_value_ce(s);
12988 if (result != ANIM_EVENT_NONE)
12991 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12993 matching_char = strstr(s_ptr, pattern_1[i]);
12994 pattern_1_len = strlen(pattern_1[i]);
12995 result = event_value[i];
12997 if (matching_char != NULL)
13001 if (matching_char == NULL)
13002 return ANIM_EVENT_NONE;
13004 s_ptr = matching_char + pattern_1_len;
13006 // check for main animation number ("anim_X" or "anim_XX")
13007 if (*s_ptr >= '0' && *s_ptr <= '9')
13009 int gic_anim_nr = (*s_ptr++ - '0');
13011 if (*s_ptr >= '0' && *s_ptr <= '9')
13012 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
13014 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
13015 return ANIM_EVENT_NONE;
13017 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
13021 // invalid main animation number specified
13023 return ANIM_EVENT_NONE;
13026 // check for animation part number ("part_X" or "part_XX") (optional)
13027 if (strPrefix(s_ptr, pattern_2))
13029 s_ptr += strlen(pattern_2);
13031 if (*s_ptr >= '0' && *s_ptr <= '9')
13033 int gic_part_nr = (*s_ptr++ - '0');
13035 if (*s_ptr >= '0' && *s_ptr <= '9')
13036 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
13038 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
13039 return ANIM_EVENT_NONE;
13041 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
13045 // invalid animation part number specified
13047 return ANIM_EVENT_NONE;
13051 // discard result if next character is neither delimiter nor whitespace
13052 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
13053 *s_ptr == ' ' || *s_ptr == '\t'))
13054 return ANIM_EVENT_NONE;
13059 static int get_anim_parameter_values(char *s)
13061 int list_pos = ANIM_EVENT_UNDEFINED;
13062 int event_value = ANIM_EVENT_DEFAULT;
13064 if (string_has_parameter(s, "any"))
13065 event_value |= ANIM_EVENT_ANY;
13067 if (string_has_parameter(s, "click:self") ||
13068 string_has_parameter(s, "click") ||
13069 string_has_parameter(s, "self"))
13070 event_value |= ANIM_EVENT_SELF;
13072 if (string_has_parameter(s, "unclick:any"))
13073 event_value |= ANIM_EVENT_UNCLICK_ANY;
13075 // if animation event found, add it to global animation event list
13076 if (event_value != ANIM_EVENT_NONE)
13077 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
13081 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
13082 event_value = get_anim_parameter_value(s);
13084 // if animation event found, add it to global animation event list
13085 if (event_value != ANIM_EVENT_NONE)
13086 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
13088 // continue with next part of the string, starting with next comma
13089 s = strchr(s + 1, ',');
13095 static int get_anim_action_parameter_value(char *token)
13097 // check most common default case first to massively speed things up
13098 if (strEqual(token, ARG_UNDEFINED))
13099 return ANIM_EVENT_ACTION_NONE;
13101 int result = getImageIDFromToken(token);
13105 char *gfx_token = getStringCat2("gfx.", token);
13107 result = getImageIDFromToken(gfx_token);
13109 checked_free(gfx_token);
13114 Key key = getKeyFromX11KeyName(token);
13116 if (key != KSYM_UNDEFINED)
13117 result = -(int)key;
13124 result = get_hash_from_string(token); // unsigned int => int
13125 result = ABS(result); // may be negative now
13126 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
13128 setHashEntry(anim_url_hash, int2str(result, 0), token);
13133 result = ANIM_EVENT_ACTION_NONE;
13138 int get_parameter_value(char *value_raw, char *suffix, int type)
13140 char *value = getStringToLower(value_raw);
13141 int result = 0; // probably a save default value
13143 if (strEqual(suffix, ".direction"))
13145 result = (strEqual(value, "left") ? MV_LEFT :
13146 strEqual(value, "right") ? MV_RIGHT :
13147 strEqual(value, "up") ? MV_UP :
13148 strEqual(value, "down") ? MV_DOWN : MV_NONE);
13150 else if (strEqual(suffix, ".position"))
13152 result = (strEqual(value, "left") ? POS_LEFT :
13153 strEqual(value, "right") ? POS_RIGHT :
13154 strEqual(value, "top") ? POS_TOP :
13155 strEqual(value, "upper") ? POS_UPPER :
13156 strEqual(value, "middle") ? POS_MIDDLE :
13157 strEqual(value, "lower") ? POS_LOWER :
13158 strEqual(value, "bottom") ? POS_BOTTOM :
13159 strEqual(value, "any") ? POS_ANY :
13160 strEqual(value, "ce") ? POS_CE :
13161 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
13162 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
13164 else if (strEqual(suffix, ".align"))
13166 result = (strEqual(value, "left") ? ALIGN_LEFT :
13167 strEqual(value, "right") ? ALIGN_RIGHT :
13168 strEqual(value, "center") ? ALIGN_CENTER :
13169 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
13171 else if (strEqual(suffix, ".valign"))
13173 result = (strEqual(value, "top") ? VALIGN_TOP :
13174 strEqual(value, "bottom") ? VALIGN_BOTTOM :
13175 strEqual(value, "middle") ? VALIGN_MIDDLE :
13176 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
13178 else if (strEqual(suffix, ".anim_mode"))
13180 result = (string_has_parameter(value, "none") ? ANIM_NONE :
13181 string_has_parameter(value, "loop") ? ANIM_LOOP :
13182 string_has_parameter(value, "linear") ? ANIM_LINEAR :
13183 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
13184 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
13185 string_has_parameter(value, "random") ? ANIM_RANDOM :
13186 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13187 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
13188 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
13189 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
13190 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13191 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
13192 string_has_parameter(value, "centered") ? ANIM_CENTERED :
13193 string_has_parameter(value, "all") ? ANIM_ALL :
13194 string_has_parameter(value, "tiled") ? ANIM_TILED :
13195 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
13198 if (string_has_parameter(value, "once"))
13199 result |= ANIM_ONCE;
13201 if (string_has_parameter(value, "reverse"))
13202 result |= ANIM_REVERSE;
13204 if (string_has_parameter(value, "opaque_player"))
13205 result |= ANIM_OPAQUE_PLAYER;
13207 if (string_has_parameter(value, "static_panel"))
13208 result |= ANIM_STATIC_PANEL;
13210 else if (strEqual(suffix, ".init_event") ||
13211 strEqual(suffix, ".anim_event"))
13213 result = get_anim_parameter_values(value);
13215 else if (strEqual(suffix, ".init_delay_action") ||
13216 strEqual(suffix, ".anim_delay_action") ||
13217 strEqual(suffix, ".post_delay_action") ||
13218 strEqual(suffix, ".init_event_action") ||
13219 strEqual(suffix, ".anim_event_action"))
13221 result = get_anim_action_parameter_value(value_raw);
13223 else if (strEqual(suffix, ".class"))
13225 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13226 get_hash_from_string(value));
13228 else if (strEqual(suffix, ".style"))
13230 result = STYLE_DEFAULT;
13232 if (string_has_parameter(value, "accurate_borders"))
13233 result |= STYLE_ACCURATE_BORDERS;
13235 if (string_has_parameter(value, "inner_corners"))
13236 result |= STYLE_INNER_CORNERS;
13238 if (string_has_parameter(value, "reverse"))
13239 result |= STYLE_REVERSE;
13241 if (string_has_parameter(value, "leftmost_position"))
13242 result |= STYLE_LEFTMOST_POSITION;
13244 if (string_has_parameter(value, "block_clicks"))
13245 result |= STYLE_BLOCK;
13247 if (string_has_parameter(value, "passthrough_clicks"))
13248 result |= STYLE_PASSTHROUGH;
13250 if (string_has_parameter(value, "multiple_actions"))
13251 result |= STYLE_MULTIPLE_ACTIONS;
13253 if (string_has_parameter(value, "consume_ce_event"))
13254 result |= STYLE_CONSUME_CE_EVENT;
13256 else if (strEqual(suffix, ".fade_mode"))
13258 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
13259 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
13260 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
13261 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
13262 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
13263 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
13264 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
13265 FADE_MODE_DEFAULT);
13267 else if (strEqual(suffix, ".auto_delay_unit"))
13269 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
13270 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13271 AUTO_DELAY_UNIT_DEFAULT);
13273 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
13275 result = gfx.get_font_from_token_function(value);
13277 else // generic parameter of type integer or boolean
13279 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13280 type == TYPE_INTEGER ? get_integer_from_string(value) :
13281 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13282 ARG_UNDEFINED_VALUE);
13290 static int get_token_parameter_value(char *token, char *value_raw)
13294 if (token == NULL || value_raw == NULL)
13295 return ARG_UNDEFINED_VALUE;
13297 suffix = strrchr(token, '.');
13298 if (suffix == NULL)
13301 if (strEqual(suffix, ".element"))
13302 return getElementFromToken(value_raw);
13304 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13305 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13308 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13309 boolean ignore_defaults)
13313 for (i = 0; image_config_vars[i].token != NULL; i++)
13315 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13317 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13318 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13322 *image_config_vars[i].value =
13323 get_token_parameter_value(image_config_vars[i].token, value);
13327 void InitMenuDesignSettings_Static(void)
13329 // always start with reliable default values from static default config
13330 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13333 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13337 // the following initializes hierarchical values from static configuration
13339 // special case: initialize "ARG_DEFAULT" values in static default config
13340 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13341 titlescreen_initial_first_default.fade_mode =
13342 title_initial_first_default.fade_mode;
13343 titlescreen_initial_first_default.fade_delay =
13344 title_initial_first_default.fade_delay;
13345 titlescreen_initial_first_default.post_delay =
13346 title_initial_first_default.post_delay;
13347 titlescreen_initial_first_default.auto_delay =
13348 title_initial_first_default.auto_delay;
13349 titlescreen_initial_first_default.auto_delay_unit =
13350 title_initial_first_default.auto_delay_unit;
13351 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
13352 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13353 titlescreen_first_default.post_delay = title_first_default.post_delay;
13354 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13355 titlescreen_first_default.auto_delay_unit =
13356 title_first_default.auto_delay_unit;
13357 titlemessage_initial_first_default.fade_mode =
13358 title_initial_first_default.fade_mode;
13359 titlemessage_initial_first_default.fade_delay =
13360 title_initial_first_default.fade_delay;
13361 titlemessage_initial_first_default.post_delay =
13362 title_initial_first_default.post_delay;
13363 titlemessage_initial_first_default.auto_delay =
13364 title_initial_first_default.auto_delay;
13365 titlemessage_initial_first_default.auto_delay_unit =
13366 title_initial_first_default.auto_delay_unit;
13367 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
13368 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13369 titlemessage_first_default.post_delay = title_first_default.post_delay;
13370 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13371 titlemessage_first_default.auto_delay_unit =
13372 title_first_default.auto_delay_unit;
13374 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
13375 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13376 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13377 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13378 titlescreen_initial_default.auto_delay_unit =
13379 title_initial_default.auto_delay_unit;
13380 titlescreen_default.fade_mode = title_default.fade_mode;
13381 titlescreen_default.fade_delay = title_default.fade_delay;
13382 titlescreen_default.post_delay = title_default.post_delay;
13383 titlescreen_default.auto_delay = title_default.auto_delay;
13384 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13385 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
13386 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13387 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13388 titlemessage_initial_default.auto_delay_unit =
13389 title_initial_default.auto_delay_unit;
13390 titlemessage_default.fade_mode = title_default.fade_mode;
13391 titlemessage_default.fade_delay = title_default.fade_delay;
13392 titlemessage_default.post_delay = title_default.post_delay;
13393 titlemessage_default.auto_delay = title_default.auto_delay;
13394 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13396 // special case: initialize "ARG_DEFAULT" values in static default config
13397 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13398 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13400 titlescreen_initial_first[i] = titlescreen_initial_first_default;
13401 titlescreen_first[i] = titlescreen_first_default;
13402 titlemessage_initial_first[i] = titlemessage_initial_first_default;
13403 titlemessage_first[i] = titlemessage_first_default;
13405 titlescreen_initial[i] = titlescreen_initial_default;
13406 titlescreen[i] = titlescreen_default;
13407 titlemessage_initial[i] = titlemessage_initial_default;
13408 titlemessage[i] = titlemessage_default;
13411 // special case: initialize "ARG_DEFAULT" values in static default config
13412 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13413 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13415 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
13418 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13419 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13420 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13423 // special case: initialize "ARG_DEFAULT" values in static default config
13424 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13425 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13427 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13428 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13429 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13431 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
13434 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13438 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13442 struct XY *dst, *src;
13444 game_buttons_xy[] =
13446 { &game.button.save, &game.button.stop },
13447 { &game.button.pause2, &game.button.pause },
13448 { &game.button.load, &game.button.play },
13449 { &game.button.undo, &game.button.stop },
13450 { &game.button.redo, &game.button.play },
13456 // special case: initialize later added SETUP list size from LEVELS value
13457 if (menu.list_size[GAME_MODE_SETUP] == -1)
13458 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13460 // set default position for snapshot buttons to stop/pause/play buttons
13461 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13462 if ((*game_buttons_xy[i].dst).x == -1 &&
13463 (*game_buttons_xy[i].dst).y == -1)
13464 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13466 // --------------------------------------------------------------------------
13467 // dynamic viewports (including playfield margins, borders and alignments)
13468 // --------------------------------------------------------------------------
13470 // dynamic viewports currently only supported for landscape mode
13471 int display_width = MAX(video.display_width, video.display_height);
13472 int display_height = MIN(video.display_width, video.display_height);
13474 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13476 struct RectWithBorder *vp_window = &viewport.window[i];
13477 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13478 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13479 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13480 boolean dynamic_window_width = (vp_window->min_width != -1);
13481 boolean dynamic_window_height = (vp_window->min_height != -1);
13482 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13483 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13485 // adjust window size if min/max width/height is specified
13487 if (vp_window->min_width != -1)
13489 int window_width = display_width;
13491 // when using static window height, use aspect ratio of display
13492 if (vp_window->min_height == -1)
13493 window_width = vp_window->height * display_width / display_height;
13495 vp_window->width = MAX(vp_window->min_width, window_width);
13498 if (vp_window->min_height != -1)
13500 int window_height = display_height;
13502 // when using static window width, use aspect ratio of display
13503 if (vp_window->min_width == -1)
13504 window_height = vp_window->width * display_height / display_width;
13506 vp_window->height = MAX(vp_window->min_height, window_height);
13509 if (vp_window->max_width != -1)
13510 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13512 if (vp_window->max_height != -1)
13513 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13515 int playfield_width = vp_window->width;
13516 int playfield_height = vp_window->height;
13518 // adjust playfield size and position according to specified margins
13520 playfield_width -= vp_playfield->margin_left;
13521 playfield_width -= vp_playfield->margin_right;
13523 playfield_height -= vp_playfield->margin_top;
13524 playfield_height -= vp_playfield->margin_bottom;
13526 // adjust playfield size if min/max width/height is specified
13528 if (vp_playfield->min_width != -1)
13529 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13531 if (vp_playfield->min_height != -1)
13532 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13534 if (vp_playfield->max_width != -1)
13535 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13537 if (vp_playfield->max_height != -1)
13538 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13540 // adjust playfield position according to specified alignment
13542 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13543 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13544 else if (vp_playfield->align == ALIGN_CENTER)
13545 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13546 else if (vp_playfield->align == ALIGN_RIGHT)
13547 vp_playfield->x += playfield_width - vp_playfield->width;
13549 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13550 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13551 else if (vp_playfield->valign == VALIGN_MIDDLE)
13552 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13553 else if (vp_playfield->valign == VALIGN_BOTTOM)
13554 vp_playfield->y += playfield_height - vp_playfield->height;
13556 vp_playfield->x += vp_playfield->margin_left;
13557 vp_playfield->y += vp_playfield->margin_top;
13559 // adjust individual playfield borders if only default border is specified
13561 if (vp_playfield->border_left == -1)
13562 vp_playfield->border_left = vp_playfield->border_size;
13563 if (vp_playfield->border_right == -1)
13564 vp_playfield->border_right = vp_playfield->border_size;
13565 if (vp_playfield->border_top == -1)
13566 vp_playfield->border_top = vp_playfield->border_size;
13567 if (vp_playfield->border_bottom == -1)
13568 vp_playfield->border_bottom = vp_playfield->border_size;
13570 // set dynamic playfield borders if borders are specified as undefined
13571 // (but only if window size was dynamic and playfield size was static)
13573 if (dynamic_window_width && !dynamic_playfield_width)
13575 if (vp_playfield->border_left == -1)
13577 vp_playfield->border_left = (vp_playfield->x -
13578 vp_playfield->margin_left);
13579 vp_playfield->x -= vp_playfield->border_left;
13580 vp_playfield->width += vp_playfield->border_left;
13583 if (vp_playfield->border_right == -1)
13585 vp_playfield->border_right = (vp_window->width -
13587 vp_playfield->width -
13588 vp_playfield->margin_right);
13589 vp_playfield->width += vp_playfield->border_right;
13593 if (dynamic_window_height && !dynamic_playfield_height)
13595 if (vp_playfield->border_top == -1)
13597 vp_playfield->border_top = (vp_playfield->y -
13598 vp_playfield->margin_top);
13599 vp_playfield->y -= vp_playfield->border_top;
13600 vp_playfield->height += vp_playfield->border_top;
13603 if (vp_playfield->border_bottom == -1)
13605 vp_playfield->border_bottom = (vp_window->height -
13607 vp_playfield->height -
13608 vp_playfield->margin_bottom);
13609 vp_playfield->height += vp_playfield->border_bottom;
13613 // adjust playfield size to be a multiple of a defined alignment tile size
13615 int align_size = vp_playfield->align_size;
13616 int playfield_xtiles = vp_playfield->width / align_size;
13617 int playfield_ytiles = vp_playfield->height / align_size;
13618 int playfield_width_corrected = playfield_xtiles * align_size;
13619 int playfield_height_corrected = playfield_ytiles * align_size;
13620 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13621 i == GFX_SPECIAL_ARG_EDITOR);
13623 if (is_playfield_mode &&
13624 dynamic_playfield_width &&
13625 vp_playfield->width != playfield_width_corrected)
13627 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13629 vp_playfield->width = playfield_width_corrected;
13631 if (vp_playfield->align == ALIGN_LEFT)
13633 vp_playfield->border_left += playfield_xdiff;
13635 else if (vp_playfield->align == ALIGN_RIGHT)
13637 vp_playfield->border_right += playfield_xdiff;
13639 else if (vp_playfield->align == ALIGN_CENTER)
13641 int border_left_diff = playfield_xdiff / 2;
13642 int border_right_diff = playfield_xdiff - border_left_diff;
13644 vp_playfield->border_left += border_left_diff;
13645 vp_playfield->border_right += border_right_diff;
13649 if (is_playfield_mode &&
13650 dynamic_playfield_height &&
13651 vp_playfield->height != playfield_height_corrected)
13653 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13655 vp_playfield->height = playfield_height_corrected;
13657 if (vp_playfield->valign == VALIGN_TOP)
13659 vp_playfield->border_top += playfield_ydiff;
13661 else if (vp_playfield->align == VALIGN_BOTTOM)
13663 vp_playfield->border_right += playfield_ydiff;
13665 else if (vp_playfield->align == VALIGN_MIDDLE)
13667 int border_top_diff = playfield_ydiff / 2;
13668 int border_bottom_diff = playfield_ydiff - border_top_diff;
13670 vp_playfield->border_top += border_top_diff;
13671 vp_playfield->border_bottom += border_bottom_diff;
13675 // adjust door positions according to specified alignment
13677 for (j = 0; j < 2; j++)
13679 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13681 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13682 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13683 else if (vp_door->align == ALIGN_CENTER)
13684 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13685 else if (vp_door->align == ALIGN_RIGHT)
13686 vp_door->x += vp_window->width - vp_door->width;
13688 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13689 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13690 else if (vp_door->valign == VALIGN_MIDDLE)
13691 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13692 else if (vp_door->valign == VALIGN_BOTTOM)
13693 vp_door->y += vp_window->height - vp_door->height;
13698 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13702 struct XYTileSize *dst, *src;
13705 editor_buttons_xy[] =
13708 &editor.button.element_left, &editor.palette.element_left,
13709 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13712 &editor.button.element_middle, &editor.palette.element_middle,
13713 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13716 &editor.button.element_right, &editor.palette.element_right,
13717 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13724 // set default position for element buttons to element graphics
13725 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13727 if ((*editor_buttons_xy[i].dst).x == -1 &&
13728 (*editor_buttons_xy[i].dst).y == -1)
13730 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13732 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13734 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13738 // adjust editor palette rows and columns if specified to be dynamic
13740 if (editor.palette.cols == -1)
13742 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13743 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13744 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13746 editor.palette.cols = (vp_width - sc_width) / bt_width;
13748 if (editor.palette.x == -1)
13750 int palette_width = editor.palette.cols * bt_width + sc_width;
13752 editor.palette.x = (vp_width - palette_width) / 2;
13756 if (editor.palette.rows == -1)
13758 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13759 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13760 int tx_height = getFontHeight(FONT_TEXT_2);
13762 editor.palette.rows = (vp_height - tx_height) / bt_height;
13764 if (editor.palette.y == -1)
13766 int palette_height = editor.palette.rows * bt_height + tx_height;
13768 editor.palette.y = (vp_height - palette_height) / 2;
13773 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13774 boolean initialize)
13776 // special case: check if network and preview player positions are redefined,
13777 // to compare this later against the main menu level preview being redefined
13778 struct TokenIntPtrInfo menu_config_players[] =
13780 { "main.network_players.x", &menu.main.network_players.redefined },
13781 { "main.network_players.y", &menu.main.network_players.redefined },
13782 { "main.preview_players.x", &menu.main.preview_players.redefined },
13783 { "main.preview_players.y", &menu.main.preview_players.redefined },
13784 { "preview.x", &preview.redefined },
13785 { "preview.y", &preview.redefined }
13791 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13792 *menu_config_players[i].value = FALSE;
13796 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13797 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13798 *menu_config_players[i].value = TRUE;
13802 static void InitMenuDesignSettings_PreviewPlayers(void)
13804 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13807 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13809 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13812 static void LoadMenuDesignSettingsFromFilename(char *filename)
13814 static struct TitleFadingInfo tfi;
13815 static struct TitleMessageInfo tmi;
13816 static struct TokenInfo title_tokens[] =
13818 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13819 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13820 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13821 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13822 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13826 static struct TokenInfo titlemessage_tokens[] =
13828 { TYPE_INTEGER, &tmi.x, ".x" },
13829 { TYPE_INTEGER, &tmi.y, ".y" },
13830 { TYPE_INTEGER, &tmi.width, ".width" },
13831 { TYPE_INTEGER, &tmi.height, ".height" },
13832 { TYPE_INTEGER, &tmi.chars, ".chars" },
13833 { TYPE_INTEGER, &tmi.lines, ".lines" },
13834 { TYPE_INTEGER, &tmi.align, ".align" },
13835 { TYPE_INTEGER, &tmi.valign, ".valign" },
13836 { TYPE_INTEGER, &tmi.font, ".font" },
13837 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13838 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13839 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13840 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13841 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13842 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13843 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13844 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13845 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13851 struct TitleFadingInfo *info;
13856 // initialize first titles from "enter screen" definitions, if defined
13857 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13858 { &title_first_default, "menu.enter_screen.TITLE" },
13860 // initialize title screens from "next screen" definitions, if defined
13861 { &title_initial_default, "menu.next_screen.TITLE" },
13862 { &title_default, "menu.next_screen.TITLE" },
13868 struct TitleMessageInfo *array;
13871 titlemessage_arrays[] =
13873 // initialize first titles from "enter screen" definitions, if defined
13874 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13875 { titlescreen_first, "menu.enter_screen.TITLE" },
13876 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13877 { titlemessage_first, "menu.enter_screen.TITLE" },
13879 // initialize titles from "next screen" definitions, if defined
13880 { titlescreen_initial, "menu.next_screen.TITLE" },
13881 { titlescreen, "menu.next_screen.TITLE" },
13882 { titlemessage_initial, "menu.next_screen.TITLE" },
13883 { titlemessage, "menu.next_screen.TITLE" },
13885 // overwrite titles with title definitions, if defined
13886 { titlescreen_initial_first, "[title_initial]" },
13887 { titlescreen_first, "[title]" },
13888 { titlemessage_initial_first, "[title_initial]" },
13889 { titlemessage_first, "[title]" },
13891 { titlescreen_initial, "[title_initial]" },
13892 { titlescreen, "[title]" },
13893 { titlemessage_initial, "[title_initial]" },
13894 { titlemessage, "[title]" },
13896 // overwrite titles with title screen/message definitions, if defined
13897 { titlescreen_initial_first, "[titlescreen_initial]" },
13898 { titlescreen_first, "[titlescreen]" },
13899 { titlemessage_initial_first, "[titlemessage_initial]" },
13900 { titlemessage_first, "[titlemessage]" },
13902 { titlescreen_initial, "[titlescreen_initial]" },
13903 { titlescreen, "[titlescreen]" },
13904 { titlemessage_initial, "[titlemessage_initial]" },
13905 { titlemessage, "[titlemessage]" },
13909 SetupFileHash *setup_file_hash;
13912 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13915 // the following initializes hierarchical values from dynamic configuration
13917 // special case: initialize with default values that may be overwritten
13918 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13919 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13921 struct TokenIntPtrInfo menu_config[] =
13923 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13924 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13925 { "menu.list_size", &menu.list_size[i] }
13928 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13930 char *token = menu_config[j].token;
13931 char *value = getHashEntry(setup_file_hash, token);
13934 *menu_config[j].value = get_integer_from_string(value);
13938 // special case: initialize with default values that may be overwritten
13939 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13940 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13942 struct TokenIntPtrInfo menu_config[] =
13944 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13945 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13946 { "menu.list_size.INFO", &menu.list_size_info[i] },
13947 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13948 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13951 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13953 char *token = menu_config[j].token;
13954 char *value = getHashEntry(setup_file_hash, token);
13957 *menu_config[j].value = get_integer_from_string(value);
13961 // special case: initialize with default values that may be overwritten
13962 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13963 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13965 struct TokenIntPtrInfo menu_config[] =
13967 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13968 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13971 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13973 char *token = menu_config[j].token;
13974 char *value = getHashEntry(setup_file_hash, token);
13977 *menu_config[j].value = get_integer_from_string(value);
13981 // special case: initialize with default values that may be overwritten
13982 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13983 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13985 struct TokenIntPtrInfo menu_config[] =
13987 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13988 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13989 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13990 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13991 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13992 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13993 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13994 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13995 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13996 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13999 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
14001 char *token = menu_config[j].token;
14002 char *value = getHashEntry(setup_file_hash, token);
14005 *menu_config[j].value = get_integer_from_string(value);
14009 // special case: initialize with default values that may be overwritten
14010 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
14011 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
14013 struct TokenIntPtrInfo menu_config[] =
14015 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
14016 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
14017 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
14018 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
14019 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
14020 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
14021 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
14022 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
14023 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
14026 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
14028 char *token = menu_config[j].token;
14029 char *value = getHashEntry(setup_file_hash, token);
14032 *menu_config[j].value = get_token_parameter_value(token, value);
14036 // special case: initialize with default values that may be overwritten
14037 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
14038 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
14042 char *token_prefix;
14043 struct RectWithBorder *struct_ptr;
14047 { "viewport.window", &viewport.window[i] },
14048 { "viewport.playfield", &viewport.playfield[i] },
14049 { "viewport.door_1", &viewport.door_1[i] },
14050 { "viewport.door_2", &viewport.door_2[i] }
14053 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
14055 struct TokenIntPtrInfo vp_config[] =
14057 { ".x", &vp_struct[j].struct_ptr->x },
14058 { ".y", &vp_struct[j].struct_ptr->y },
14059 { ".width", &vp_struct[j].struct_ptr->width },
14060 { ".height", &vp_struct[j].struct_ptr->height },
14061 { ".min_width", &vp_struct[j].struct_ptr->min_width },
14062 { ".min_height", &vp_struct[j].struct_ptr->min_height },
14063 { ".max_width", &vp_struct[j].struct_ptr->max_width },
14064 { ".max_height", &vp_struct[j].struct_ptr->max_height },
14065 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
14066 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
14067 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
14068 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
14069 { ".border_left", &vp_struct[j].struct_ptr->border_left },
14070 { ".border_right", &vp_struct[j].struct_ptr->border_right },
14071 { ".border_top", &vp_struct[j].struct_ptr->border_top },
14072 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
14073 { ".border_size", &vp_struct[j].struct_ptr->border_size },
14074 { ".align_size", &vp_struct[j].struct_ptr->align_size },
14075 { ".align", &vp_struct[j].struct_ptr->align },
14076 { ".valign", &vp_struct[j].struct_ptr->valign }
14079 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
14081 char *token = getStringCat2(vp_struct[j].token_prefix,
14082 vp_config[k].token);
14083 char *value = getHashEntry(setup_file_hash, token);
14086 *vp_config[k].value = get_token_parameter_value(token, value);
14093 // special case: initialize with default values that may be overwritten
14094 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
14095 for (i = 0; title_info[i].info != NULL; i++)
14097 struct TitleFadingInfo *info = title_info[i].info;
14098 char *base_token = title_info[i].text;
14100 for (j = 0; title_tokens[j].type != -1; j++)
14102 char *token = getStringCat2(base_token, title_tokens[j].text);
14103 char *value = getHashEntry(setup_file_hash, token);
14107 int parameter_value = get_token_parameter_value(token, value);
14111 *(int *)title_tokens[j].value = (int)parameter_value;
14120 // special case: initialize with default values that may be overwritten
14121 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
14122 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
14124 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
14125 char *base_token = titlemessage_arrays[i].text;
14127 for (j = 0; titlemessage_tokens[j].type != -1; j++)
14129 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
14130 char *value = getHashEntry(setup_file_hash, token);
14134 int parameter_value = get_token_parameter_value(token, value);
14136 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
14140 if (titlemessage_tokens[j].type == TYPE_INTEGER)
14141 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
14143 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
14153 // read (and overwrite with) values that may be specified in config file
14154 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
14156 // special case: check if network and preview player positions are redefined
14157 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
14159 freeSetupFileHash(setup_file_hash);
14162 void LoadMenuDesignSettings(void)
14164 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14166 InitMenuDesignSettings_Static();
14167 InitMenuDesignSettings_SpecialPreProcessing();
14168 InitMenuDesignSettings_PreviewPlayers();
14170 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
14172 // first look for special settings configured in level series config
14173 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
14175 if (fileExists(filename_base))
14176 LoadMenuDesignSettingsFromFilename(filename_base);
14179 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
14181 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14182 LoadMenuDesignSettingsFromFilename(filename_local);
14184 InitMenuDesignSettings_SpecialPostProcessing();
14187 void LoadMenuDesignSettings_AfterGraphics(void)
14189 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14192 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14193 boolean ignore_defaults)
14197 for (i = 0; sound_config_vars[i].token != NULL; i++)
14199 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14201 // (ignore definitions set to "[DEFAULT]" which are already initialized)
14202 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14206 *sound_config_vars[i].value =
14207 get_token_parameter_value(sound_config_vars[i].token, value);
14211 void InitSoundSettings_Static(void)
14213 // always start with reliable default values from static default config
14214 InitSoundSettings_FromHash(sound_config_hash, FALSE);
14217 static void LoadSoundSettingsFromFilename(char *filename)
14219 SetupFileHash *setup_file_hash;
14221 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14224 // read (and overwrite with) values that may be specified in config file
14225 InitSoundSettings_FromHash(setup_file_hash, TRUE);
14227 freeSetupFileHash(setup_file_hash);
14230 void LoadSoundSettings(void)
14232 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14234 InitSoundSettings_Static();
14236 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14238 // first look for special settings configured in level series config
14239 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14241 if (fileExists(filename_base))
14242 LoadSoundSettingsFromFilename(filename_base);
14245 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14247 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14248 LoadSoundSettingsFromFilename(filename_local);
14251 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14253 char *filename = getEditorSetupFilename();
14254 SetupFileList *setup_file_list, *list;
14255 SetupFileHash *element_hash;
14256 int num_unknown_tokens = 0;
14259 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14262 element_hash = newSetupFileHash();
14264 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14265 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14267 // determined size may be larger than needed (due to unknown elements)
14269 for (list = setup_file_list; list != NULL; list = list->next)
14272 // add space for up to 3 more elements for padding that may be needed
14273 *num_elements += 3;
14275 // free memory for old list of elements, if needed
14276 checked_free(*elements);
14278 // allocate memory for new list of elements
14279 *elements = checked_malloc(*num_elements * sizeof(int));
14282 for (list = setup_file_list; list != NULL; list = list->next)
14284 char *value = getHashEntry(element_hash, list->token);
14286 if (value == NULL) // try to find obsolete token mapping
14288 char *mapped_token = get_mapped_token(list->token);
14290 if (mapped_token != NULL)
14292 value = getHashEntry(element_hash, mapped_token);
14294 free(mapped_token);
14300 (*elements)[(*num_elements)++] = atoi(value);
14304 if (num_unknown_tokens == 0)
14307 Warn("unknown token(s) found in config file:");
14308 Warn("- config file: '%s'", filename);
14310 num_unknown_tokens++;
14313 Warn("- token: '%s'", list->token);
14317 if (num_unknown_tokens > 0)
14320 while (*num_elements % 4) // pad with empty elements, if needed
14321 (*elements)[(*num_elements)++] = EL_EMPTY;
14323 freeSetupFileList(setup_file_list);
14324 freeSetupFileHash(element_hash);
14327 for (i = 0; i < *num_elements; i++)
14328 Debug("editor", "element '%s' [%d]\n",
14329 element_info[(*elements)[i]].token_name, (*elements)[i]);
14333 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14336 SetupFileHash *setup_file_hash = NULL;
14337 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14338 char *filename_music, *filename_prefix, *filename_info;
14344 token_to_value_ptr[] =
14346 { "title_header", &tmp_music_file_info.title_header },
14347 { "artist_header", &tmp_music_file_info.artist_header },
14348 { "album_header", &tmp_music_file_info.album_header },
14349 { "year_header", &tmp_music_file_info.year_header },
14350 { "played_header", &tmp_music_file_info.played_header },
14352 { "title", &tmp_music_file_info.title },
14353 { "artist", &tmp_music_file_info.artist },
14354 { "album", &tmp_music_file_info.album },
14355 { "year", &tmp_music_file_info.year },
14356 { "played", &tmp_music_file_info.played },
14362 filename_music = (is_sound ? getCustomSoundFilename(basename) :
14363 getCustomMusicFilename(basename));
14365 if (filename_music == NULL)
14368 // ---------- try to replace file extension ----------
14370 filename_prefix = getStringCopy(filename_music);
14371 if (strrchr(filename_prefix, '.') != NULL)
14372 *strrchr(filename_prefix, '.') = '\0';
14373 filename_info = getStringCat2(filename_prefix, ".txt");
14375 if (fileExists(filename_info))
14376 setup_file_hash = loadSetupFileHash(filename_info);
14378 free(filename_prefix);
14379 free(filename_info);
14381 if (setup_file_hash == NULL)
14383 // ---------- try to add file extension ----------
14385 filename_prefix = getStringCopy(filename_music);
14386 filename_info = getStringCat2(filename_prefix, ".txt");
14388 if (fileExists(filename_info))
14389 setup_file_hash = loadSetupFileHash(filename_info);
14391 free(filename_prefix);
14392 free(filename_info);
14395 if (setup_file_hash == NULL)
14398 // ---------- music file info found ----------
14400 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14402 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14404 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14406 *token_to_value_ptr[i].value_ptr =
14407 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14410 tmp_music_file_info.basename = getStringCopy(basename);
14411 tmp_music_file_info.music = music;
14412 tmp_music_file_info.is_sound = is_sound;
14414 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14415 *new_music_file_info = tmp_music_file_info;
14417 return new_music_file_info;
14420 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14422 return get_music_file_info_ext(basename, music, FALSE);
14425 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14427 return get_music_file_info_ext(basename, sound, TRUE);
14430 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14431 char *basename, boolean is_sound)
14433 for (; list != NULL; list = list->next)
14434 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14440 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14442 return music_info_listed_ext(list, basename, FALSE);
14445 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14447 return music_info_listed_ext(list, basename, TRUE);
14450 void LoadMusicInfo(void)
14452 int num_music_noconf = getMusicListSize_NoConf();
14453 int num_music = getMusicListSize();
14454 int num_sounds = getSoundListSize();
14455 struct FileInfo *music, *sound;
14456 struct MusicFileInfo *next, **new;
14460 while (music_file_info != NULL)
14462 next = music_file_info->next;
14464 checked_free(music_file_info->basename);
14466 checked_free(music_file_info->title_header);
14467 checked_free(music_file_info->artist_header);
14468 checked_free(music_file_info->album_header);
14469 checked_free(music_file_info->year_header);
14470 checked_free(music_file_info->played_header);
14472 checked_free(music_file_info->title);
14473 checked_free(music_file_info->artist);
14474 checked_free(music_file_info->album);
14475 checked_free(music_file_info->year);
14476 checked_free(music_file_info->played);
14478 free(music_file_info);
14480 music_file_info = next;
14483 new = &music_file_info;
14485 // get (configured or unconfigured) music file info for all levels
14486 for (i = leveldir_current->first_level;
14487 i <= leveldir_current->last_level; i++)
14491 if (levelset.music[i] != MUS_UNDEFINED)
14493 // get music file info for configured level music
14494 music_nr = levelset.music[i];
14496 else if (num_music_noconf > 0)
14498 // get music file info for unconfigured level music
14499 int level_pos = i - leveldir_current->first_level;
14501 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14508 char *basename = getMusicInfoEntryFilename(music_nr);
14510 if (basename == NULL)
14513 if (!music_info_listed(music_file_info, basename))
14515 *new = get_music_file_info(basename, music_nr);
14518 new = &(*new)->next;
14522 // get music file info for all remaining configured music files
14523 for (i = 0; i < num_music; i++)
14525 music = getMusicListEntry(i);
14527 if (music->filename == NULL)
14530 if (strEqual(music->filename, UNDEFINED_FILENAME))
14533 // a configured file may be not recognized as music
14534 if (!FileIsMusic(music->filename))
14537 if (!music_info_listed(music_file_info, music->filename))
14539 *new = get_music_file_info(music->filename, i);
14542 new = &(*new)->next;
14546 // get sound file info for all configured sound files
14547 for (i = 0; i < num_sounds; i++)
14549 sound = getSoundListEntry(i);
14551 if (sound->filename == NULL)
14554 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14557 // a configured file may be not recognized as sound
14558 if (!FileIsSound(sound->filename))
14561 if (!sound_info_listed(music_file_info, sound->filename))
14563 *new = get_sound_file_info(sound->filename, i);
14565 new = &(*new)->next;
14569 // add pointers to previous list nodes
14571 struct MusicFileInfo *node = music_file_info;
14573 while (node != NULL)
14576 node->next->prev = node;
14582 static void add_helpanim_entry(int element, int action, int direction,
14583 int delay, int *num_list_entries)
14585 struct HelpAnimInfo *new_list_entry;
14586 (*num_list_entries)++;
14589 checked_realloc(helpanim_info,
14590 *num_list_entries * sizeof(struct HelpAnimInfo));
14591 new_list_entry = &helpanim_info[*num_list_entries - 1];
14593 new_list_entry->element = element;
14594 new_list_entry->action = action;
14595 new_list_entry->direction = direction;
14596 new_list_entry->delay = delay;
14599 static void print_unknown_token(char *filename, char *token, int token_nr)
14604 Warn("unknown token(s) found in config file:");
14605 Warn("- config file: '%s'", filename);
14608 Warn("- token: '%s'", token);
14611 static void print_unknown_token_end(int token_nr)
14617 void LoadHelpAnimInfo(void)
14619 char *filename = getHelpAnimFilename();
14620 SetupFileList *setup_file_list = NULL, *list;
14621 SetupFileHash *element_hash, *action_hash, *direction_hash;
14622 int num_list_entries = 0;
14623 int num_unknown_tokens = 0;
14626 if (fileExists(filename))
14627 setup_file_list = loadSetupFileList(filename);
14629 if (setup_file_list == NULL)
14631 // use reliable default values from static configuration
14632 SetupFileList *insert_ptr;
14634 insert_ptr = setup_file_list =
14635 newSetupFileList(helpanim_config[0].token,
14636 helpanim_config[0].value);
14638 for (i = 1; helpanim_config[i].token; i++)
14639 insert_ptr = addListEntry(insert_ptr,
14640 helpanim_config[i].token,
14641 helpanim_config[i].value);
14644 element_hash = newSetupFileHash();
14645 action_hash = newSetupFileHash();
14646 direction_hash = newSetupFileHash();
14648 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14649 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14651 for (i = 0; i < NUM_ACTIONS; i++)
14652 setHashEntry(action_hash, element_action_info[i].suffix,
14653 i_to_a(element_action_info[i].value));
14655 // do not store direction index (bit) here, but direction value!
14656 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14657 setHashEntry(direction_hash, element_direction_info[i].suffix,
14658 i_to_a(1 << element_direction_info[i].value));
14660 for (list = setup_file_list; list != NULL; list = list->next)
14662 char *element_token, *action_token, *direction_token;
14663 char *element_value, *action_value, *direction_value;
14664 int delay = atoi(list->value);
14666 if (strEqual(list->token, "end"))
14668 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14673 /* first try to break element into element/action/direction parts;
14674 if this does not work, also accept combined "element[.act][.dir]"
14675 elements (like "dynamite.active"), which are unique elements */
14677 if (strchr(list->token, '.') == NULL) // token contains no '.'
14679 element_value = getHashEntry(element_hash, list->token);
14680 if (element_value != NULL) // element found
14681 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14682 &num_list_entries);
14685 // no further suffixes found -- this is not an element
14686 print_unknown_token(filename, list->token, num_unknown_tokens++);
14692 // token has format "<prefix>.<something>"
14694 action_token = strchr(list->token, '.'); // suffix may be action ...
14695 direction_token = action_token; // ... or direction
14697 element_token = getStringCopy(list->token);
14698 *strchr(element_token, '.') = '\0';
14700 element_value = getHashEntry(element_hash, element_token);
14702 if (element_value == NULL) // this is no element
14704 element_value = getHashEntry(element_hash, list->token);
14705 if (element_value != NULL) // combined element found
14706 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14707 &num_list_entries);
14709 print_unknown_token(filename, list->token, num_unknown_tokens++);
14711 free(element_token);
14716 action_value = getHashEntry(action_hash, action_token);
14718 if (action_value != NULL) // action found
14720 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14721 &num_list_entries);
14723 free(element_token);
14728 direction_value = getHashEntry(direction_hash, direction_token);
14730 if (direction_value != NULL) // direction found
14732 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14733 &num_list_entries);
14735 free(element_token);
14740 if (strchr(action_token + 1, '.') == NULL)
14742 // no further suffixes found -- this is not an action nor direction
14744 element_value = getHashEntry(element_hash, list->token);
14745 if (element_value != NULL) // combined element found
14746 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14747 &num_list_entries);
14749 print_unknown_token(filename, list->token, num_unknown_tokens++);
14751 free(element_token);
14756 // token has format "<prefix>.<suffix>.<something>"
14758 direction_token = strchr(action_token + 1, '.');
14760 action_token = getStringCopy(action_token);
14761 *strchr(action_token + 1, '.') = '\0';
14763 action_value = getHashEntry(action_hash, action_token);
14765 if (action_value == NULL) // this is no action
14767 element_value = getHashEntry(element_hash, list->token);
14768 if (element_value != NULL) // combined element found
14769 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14770 &num_list_entries);
14772 print_unknown_token(filename, list->token, num_unknown_tokens++);
14774 free(element_token);
14775 free(action_token);
14780 direction_value = getHashEntry(direction_hash, direction_token);
14782 if (direction_value != NULL) // direction found
14784 add_helpanim_entry(atoi(element_value), atoi(action_value),
14785 atoi(direction_value), delay, &num_list_entries);
14787 free(element_token);
14788 free(action_token);
14793 // this is no direction
14795 element_value = getHashEntry(element_hash, list->token);
14796 if (element_value != NULL) // combined element found
14797 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14798 &num_list_entries);
14800 print_unknown_token(filename, list->token, num_unknown_tokens++);
14802 free(element_token);
14803 free(action_token);
14806 print_unknown_token_end(num_unknown_tokens);
14808 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14809 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14811 freeSetupFileList(setup_file_list);
14812 freeSetupFileHash(element_hash);
14813 freeSetupFileHash(action_hash);
14814 freeSetupFileHash(direction_hash);
14817 for (i = 0; i < num_list_entries; i++)
14818 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14819 EL_NAME(helpanim_info[i].element),
14820 helpanim_info[i].element,
14821 helpanim_info[i].action,
14822 helpanim_info[i].direction,
14823 helpanim_info[i].delay);
14827 void LoadHelpTextInfo(void)
14829 char *filename = getHelpTextFilename();
14832 if (helptext_info != NULL)
14834 freeSetupFileHash(helptext_info);
14835 helptext_info = NULL;
14838 if (fileExists(filename))
14839 helptext_info = loadSetupFileHash(filename);
14841 if (helptext_info == NULL)
14843 // use reliable default values from static configuration
14844 helptext_info = newSetupFileHash();
14846 for (i = 0; helptext_config[i].token; i++)
14847 setHashEntry(helptext_info,
14848 helptext_config[i].token,
14849 helptext_config[i].value);
14853 BEGIN_HASH_ITERATION(helptext_info, itr)
14855 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14856 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14858 END_HASH_ITERATION(hash, itr)
14863 // ----------------------------------------------------------------------------
14865 // ----------------------------------------------------------------------------
14867 #define MAX_NUM_CONVERT_LEVELS 1000
14869 void ConvertLevels(void)
14871 static LevelDirTree *convert_leveldir = NULL;
14872 static int convert_level_nr = -1;
14873 static int num_levels_handled = 0;
14874 static int num_levels_converted = 0;
14875 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14878 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14879 global.convert_leveldir);
14881 if (convert_leveldir == NULL)
14882 Fail("no such level identifier: '%s'", global.convert_leveldir);
14884 leveldir_current = convert_leveldir;
14886 if (global.convert_level_nr != -1)
14888 convert_leveldir->first_level = global.convert_level_nr;
14889 convert_leveldir->last_level = global.convert_level_nr;
14892 convert_level_nr = convert_leveldir->first_level;
14894 PrintLine("=", 79);
14895 Print("Converting levels\n");
14896 PrintLine("-", 79);
14897 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14898 Print("Level series name: '%s'\n", convert_leveldir->name);
14899 Print("Level series author: '%s'\n", convert_leveldir->author);
14900 Print("Number of levels: %d\n", convert_leveldir->levels);
14901 PrintLine("=", 79);
14904 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14905 levels_failed[i] = FALSE;
14907 while (convert_level_nr <= convert_leveldir->last_level)
14909 char *level_filename;
14912 level_nr = convert_level_nr++;
14914 Print("Level %03d: ", level_nr);
14916 LoadLevel(level_nr);
14917 if (level.no_level_file || level.no_valid_file)
14919 Print("(no level)\n");
14923 Print("converting level ... ");
14926 // special case: conversion of some EMC levels as requested by ACME
14927 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14930 level_filename = getDefaultLevelFilename(level_nr);
14931 new_level = !fileExists(level_filename);
14935 SaveLevel(level_nr);
14937 num_levels_converted++;
14939 Print("converted.\n");
14943 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14944 levels_failed[level_nr] = TRUE;
14946 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14949 num_levels_handled++;
14953 PrintLine("=", 79);
14954 Print("Number of levels handled: %d\n", num_levels_handled);
14955 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14956 (num_levels_handled ?
14957 num_levels_converted * 100 / num_levels_handled : 0));
14958 PrintLine("-", 79);
14959 Print("Summary (for automatic parsing by scripts):\n");
14960 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14961 convert_leveldir->identifier, num_levels_converted,
14962 num_levels_handled,
14963 (num_levels_handled ?
14964 num_levels_converted * 100 / num_levels_handled : 0));
14966 if (num_levels_handled != num_levels_converted)
14968 Print(", FAILED:");
14969 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14970 if (levels_failed[i])
14975 PrintLine("=", 79);
14977 CloseAllAndExit(0);
14981 // ----------------------------------------------------------------------------
14982 // create and save images for use in level sketches (raw BMP format)
14983 // ----------------------------------------------------------------------------
14985 void CreateLevelSketchImages(void)
14991 InitElementPropertiesGfxElement();
14993 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14994 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14996 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14998 int element = getMappedElement(i);
14999 char basename1[16];
15000 char basename2[16];
15004 sprintf(basename1, "%04d.bmp", i);
15005 sprintf(basename2, "%04ds.bmp", i);
15007 filename1 = getPath2(global.create_sketch_images_dir, basename1);
15008 filename2 = getPath2(global.create_sketch_images_dir, basename2);
15010 DrawSizedElement(0, 0, element, TILESIZE);
15011 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
15013 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
15014 Fail("cannot save level sketch image file '%s'", filename1);
15016 DrawSizedElement(0, 0, element, MINI_TILESIZE);
15017 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
15019 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
15020 Fail("cannot save level sketch image file '%s'", filename2);
15025 // create corresponding SQL statements (for normal and small images)
15028 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
15029 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
15032 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
15033 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
15035 // optional: create content for forum level sketch demonstration post
15037 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
15040 FreeBitmap(bitmap1);
15041 FreeBitmap(bitmap2);
15044 fprintf(stderr, "\n");
15046 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
15048 CloseAllAndExit(0);
15052 // ----------------------------------------------------------------------------
15053 // create and save images for element collecting animations (raw BMP format)
15054 // ----------------------------------------------------------------------------
15056 static boolean createCollectImage(int element)
15058 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
15061 void CreateCollectElementImages(void)
15065 int anim_frames = num_steps - 1;
15066 int tile_size = TILESIZE;
15067 int anim_width = tile_size * anim_frames;
15068 int anim_height = tile_size;
15069 int num_collect_images = 0;
15070 int pos_collect_images = 0;
15072 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15073 if (createCollectImage(i))
15074 num_collect_images++;
15076 Info("Creating %d element collecting animation images ...",
15077 num_collect_images);
15079 int dst_width = anim_width * 2;
15080 int dst_height = anim_height * num_collect_images / 2;
15081 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
15082 char *basename_bmp = "RocksCollect.bmp";
15083 char *basename_png = "RocksCollect.png";
15084 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
15085 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
15086 int len_filename_bmp = strlen(filename_bmp);
15087 int len_filename_png = strlen(filename_png);
15088 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
15089 char cmd_convert[max_command_len];
15091 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
15095 // force using RGBA surface for destination bitmap
15096 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
15097 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
15099 dst_bitmap->surface =
15100 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15102 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15104 if (!createCollectImage(i))
15107 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
15108 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
15109 int graphic = el2img(i);
15110 char *token_name = element_info[i].token_name;
15111 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
15112 Bitmap *src_bitmap;
15115 Info("- creating collecting image for '%s' ...", token_name);
15117 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
15119 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
15120 tile_size, tile_size, 0, 0);
15122 // force using RGBA surface for temporary bitmap (using transparent black)
15123 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
15124 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
15126 tmp_bitmap->surface =
15127 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15129 tmp_bitmap->surface_masked = tmp_bitmap->surface;
15131 for (j = 0; j < anim_frames; j++)
15133 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
15134 int frame_size = frame_size_final * num_steps;
15135 int offset = (tile_size - frame_size_final) / 2;
15136 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
15138 while (frame_size > frame_size_final)
15142 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
15144 FreeBitmap(frame_bitmap);
15146 frame_bitmap = half_bitmap;
15149 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
15150 frame_size_final, frame_size_final,
15151 dst_x + j * tile_size + offset, dst_y + offset);
15153 FreeBitmap(frame_bitmap);
15156 tmp_bitmap->surface_masked = NULL;
15158 FreeBitmap(tmp_bitmap);
15160 pos_collect_images++;
15163 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
15164 Fail("cannot save element collecting image file '%s'", filename_bmp);
15166 FreeBitmap(dst_bitmap);
15168 Info("Converting image file from BMP to PNG ...");
15170 if (system(cmd_convert) != 0)
15171 Fail("converting image file failed");
15173 unlink(filename_bmp);
15177 CloseAllAndExit(0);
15181 // ----------------------------------------------------------------------------
15182 // create and save images for custom and group elements (raw BMP format)
15183 // ----------------------------------------------------------------------------
15185 void CreateCustomElementImages(char *directory)
15187 char *src_basename = "RocksCE-template.ilbm";
15188 char *dst_basename = "RocksCE.bmp";
15189 char *src_filename = getPath2(directory, src_basename);
15190 char *dst_filename = getPath2(directory, dst_basename);
15191 Bitmap *src_bitmap;
15193 int yoffset_ce = 0;
15194 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15197 InitVideoDefaults();
15199 ReCreateBitmap(&backbuffer, video.width, video.height);
15201 src_bitmap = LoadImage(src_filename);
15203 bitmap = CreateBitmap(TILEX * 16 * 2,
15204 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15207 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15214 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15215 TILEX * x, TILEY * y + yoffset_ce);
15217 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15219 TILEX * x + TILEX * 16,
15220 TILEY * y + yoffset_ce);
15222 for (j = 2; j >= 0; j--)
15226 BlitBitmap(src_bitmap, bitmap,
15227 TILEX + c * 7, 0, 6, 10,
15228 TILEX * x + 6 + j * 7,
15229 TILEY * y + 11 + yoffset_ce);
15231 BlitBitmap(src_bitmap, bitmap,
15232 TILEX + c * 8, TILEY, 6, 10,
15233 TILEX * 16 + TILEX * x + 6 + j * 8,
15234 TILEY * y + 10 + yoffset_ce);
15240 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15247 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15248 TILEX * x, TILEY * y + yoffset_ge);
15250 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15252 TILEX * x + TILEX * 16,
15253 TILEY * y + yoffset_ge);
15255 for (j = 1; j >= 0; j--)
15259 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15260 TILEX * x + 6 + j * 10,
15261 TILEY * y + 11 + yoffset_ge);
15263 BlitBitmap(src_bitmap, bitmap,
15264 TILEX + c * 8, TILEY + 12, 6, 10,
15265 TILEX * 16 + TILEX * x + 10 + j * 8,
15266 TILEY * y + 10 + yoffset_ge);
15272 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15273 Fail("cannot save CE graphics file '%s'", dst_filename);
15275 FreeBitmap(bitmap);
15277 CloseAllAndExit(0);