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 // global variable "leveldir_current" must be modified in the loop below
2933 LevelDirTree *leveldir_current_last = leveldir_current;
2934 char *filename = NULL;
2936 // check for template level in path from current to topmost tree node
2938 while (leveldir_current != NULL)
2940 filename = getDefaultLevelFilename(-1);
2942 if (fileExists(filename))
2945 leveldir_current = leveldir_current->node_parent;
2948 // restore global variable "leveldir_current" modified in above loop
2949 leveldir_current = leveldir_current_last;
2954 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2958 // special case: level number is negative => check for level template file
2961 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2962 getSingleLevelBasename(-1));
2964 // replace local level template filename with global template filename
2965 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2967 // no fallback if template file not existing
2971 // special case: check for file name/pattern specified in "levelinfo.conf"
2972 if (leveldir_current->level_filename != NULL)
2974 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2976 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2977 leveldir_current->level_filename, nr);
2979 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2981 if (fileExists(lfi->filename))
2984 else if (leveldir_current->level_filetype != NULL)
2986 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2988 // check for specified native level file with standard file name
2989 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2990 "%03d.%s", nr, LEVELFILE_EXTENSION);
2991 if (fileExists(lfi->filename))
2995 // check for native Rocks'n'Diamonds level file
2996 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2997 "%03d.%s", nr, LEVELFILE_EXTENSION);
2998 if (fileExists(lfi->filename))
3001 // check for native Boulder Dash level file
3002 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
3003 if (fileExists(lfi->filename))
3006 // check for Emerald Mine level file (V1)
3007 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
3008 'a' + (nr / 10) % 26, '0' + nr % 10);
3009 if (fileExists(lfi->filename))
3011 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
3012 'A' + (nr / 10) % 26, '0' + nr % 10);
3013 if (fileExists(lfi->filename))
3016 // check for Emerald Mine level file (V2 to V5)
3017 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
3018 if (fileExists(lfi->filename))
3021 // check for Emerald Mine level file (V6 / single mode)
3022 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
3023 if (fileExists(lfi->filename))
3025 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
3026 if (fileExists(lfi->filename))
3029 // check for Emerald Mine level file (V6 / teamwork mode)
3030 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
3031 if (fileExists(lfi->filename))
3033 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
3034 if (fileExists(lfi->filename))
3037 // check for various packed level file formats
3038 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
3039 if (fileExists(lfi->filename))
3042 // no known level file found -- use default values (and fail later)
3043 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3044 "%03d.%s", nr, LEVELFILE_EXTENSION);
3047 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3049 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3050 lfi->type = getFileTypeFromBasename(lfi->basename);
3052 if (lfi->type == LEVEL_FILE_TYPE_RND)
3053 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3056 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3058 // always start with reliable default values
3059 setFileInfoToDefaults(level_file_info);
3061 level_file_info->nr = nr; // set requested level number
3063 determineLevelFileInfo_Filename(level_file_info);
3064 determineLevelFileInfo_Filetype(level_file_info);
3067 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3068 struct LevelFileInfo *lfi_to)
3070 lfi_to->nr = lfi_from->nr;
3071 lfi_to->type = lfi_from->type;
3072 lfi_to->packed = lfi_from->packed;
3074 setString(&lfi_to->basename, lfi_from->basename);
3075 setString(&lfi_to->filename, lfi_from->filename);
3078 // ----------------------------------------------------------------------------
3079 // functions for loading R'n'D level
3080 // ----------------------------------------------------------------------------
3082 int getMappedElement(int element)
3084 // remap some (historic, now obsolete) elements
3088 case EL_PLAYER_OBSOLETE:
3089 element = EL_PLAYER_1;
3092 case EL_KEY_OBSOLETE:
3096 case EL_EM_KEY_1_FILE_OBSOLETE:
3097 element = EL_EM_KEY_1;
3100 case EL_EM_KEY_2_FILE_OBSOLETE:
3101 element = EL_EM_KEY_2;
3104 case EL_EM_KEY_3_FILE_OBSOLETE:
3105 element = EL_EM_KEY_3;
3108 case EL_EM_KEY_4_FILE_OBSOLETE:
3109 element = EL_EM_KEY_4;
3112 case EL_ENVELOPE_OBSOLETE:
3113 element = EL_ENVELOPE_1;
3121 if (element >= NUM_FILE_ELEMENTS)
3123 Warn("invalid level element %d", element);
3125 element = EL_UNKNOWN;
3133 static int getMappedElementByVersion(int element, int game_version)
3135 // remap some elements due to certain game version
3137 if (game_version <= VERSION_IDENT(2,2,0,0))
3139 // map game font elements
3140 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
3141 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3142 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
3143 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
3146 if (game_version < VERSION_IDENT(3,0,0,0))
3148 // map Supaplex gravity tube elements
3149 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
3150 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3151 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
3152 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
3159 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3161 level->file_version = getFileVersion(file);
3162 level->game_version = getFileVersion(file);
3167 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3169 level->creation_date.year = getFile16BitBE(file);
3170 level->creation_date.month = getFile8Bit(file);
3171 level->creation_date.day = getFile8Bit(file);
3173 level->creation_date.src = DATE_SRC_LEVELFILE;
3178 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3180 int initial_player_stepsize;
3181 int initial_player_gravity;
3184 level->fieldx = getFile8Bit(file);
3185 level->fieldy = getFile8Bit(file);
3187 level->time = getFile16BitBE(file);
3188 level->gems_needed = getFile16BitBE(file);
3190 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3191 level->name[i] = getFile8Bit(file);
3192 level->name[MAX_LEVEL_NAME_LEN] = 0;
3194 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3195 level->score[i] = getFile8Bit(file);
3197 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3198 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3199 for (y = 0; y < 3; y++)
3200 for (x = 0; x < 3; x++)
3201 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3203 level->amoeba_speed = getFile8Bit(file);
3204 level->time_magic_wall = getFile8Bit(file);
3205 level->time_wheel = getFile8Bit(file);
3206 level->amoeba_content = getMappedElement(getFile8Bit(file));
3208 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3211 for (i = 0; i < MAX_PLAYERS; i++)
3212 level->initial_player_stepsize[i] = initial_player_stepsize;
3214 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3216 for (i = 0; i < MAX_PLAYERS; i++)
3217 level->initial_player_gravity[i] = initial_player_gravity;
3219 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3220 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3222 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3224 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3225 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3226 level->can_move_into_acid_bits = getFile32BitBE(file);
3227 level->dont_collide_with_bits = getFile8Bit(file);
3229 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3230 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3232 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3233 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3234 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3236 level->game_engine_type = getFile8Bit(file);
3238 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3243 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3247 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3248 level->name[i] = getFile8Bit(file);
3249 level->name[MAX_LEVEL_NAME_LEN] = 0;
3254 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3258 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3259 level->author[i] = getFile8Bit(file);
3260 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3265 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3268 int chunk_size_expected = level->fieldx * level->fieldy;
3270 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3271 stored with 16-bit encoding (and should be twice as big then).
3272 Even worse, playfield data was stored 16-bit when only yamyam content
3273 contained 16-bit elements and vice versa. */
3275 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3276 chunk_size_expected *= 2;
3278 if (chunk_size_expected != chunk_size)
3280 ReadUnusedBytesFromFile(file, chunk_size);
3281 return chunk_size_expected;
3284 for (y = 0; y < level->fieldy; y++)
3285 for (x = 0; x < level->fieldx; x++)
3286 level->field[x][y] =
3287 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3292 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3295 int header_size = 4;
3296 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3297 int chunk_size_expected = header_size + content_size;
3299 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3300 stored with 16-bit encoding (and should be twice as big then).
3301 Even worse, playfield data was stored 16-bit when only yamyam content
3302 contained 16-bit elements and vice versa. */
3304 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3305 chunk_size_expected += content_size;
3307 if (chunk_size_expected != chunk_size)
3309 ReadUnusedBytesFromFile(file, chunk_size);
3310 return chunk_size_expected;
3314 level->num_yamyam_contents = getFile8Bit(file);
3318 // correct invalid number of content fields -- should never happen
3319 if (level->num_yamyam_contents < 1 ||
3320 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3321 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3323 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3324 for (y = 0; y < 3; y++)
3325 for (x = 0; x < 3; x++)
3326 level->yamyam_content[i].e[x][y] =
3327 getMappedElement(level->encoding_16bit_field ?
3328 getFile16BitBE(file) : getFile8Bit(file));
3332 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3337 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3339 element = getMappedElement(getFile16BitBE(file));
3340 num_contents = getFile8Bit(file);
3342 getFile8Bit(file); // content x size (unused)
3343 getFile8Bit(file); // content y size (unused)
3345 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3347 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3348 for (y = 0; y < 3; y++)
3349 for (x = 0; x < 3; x++)
3350 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3352 // correct invalid number of content fields -- should never happen
3353 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3354 num_contents = STD_ELEMENT_CONTENTS;
3356 if (element == EL_YAMYAM)
3358 level->num_yamyam_contents = num_contents;
3360 for (i = 0; i < num_contents; i++)
3361 for (y = 0; y < 3; y++)
3362 for (x = 0; x < 3; x++)
3363 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3365 else if (element == EL_BD_AMOEBA)
3367 level->amoeba_content = content_array[0][0][0];
3371 Warn("cannot load content for element '%d'", element);
3377 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3383 int chunk_size_expected;
3385 element = getMappedElement(getFile16BitBE(file));
3386 if (!IS_ENVELOPE(element))
3387 element = EL_ENVELOPE_1;
3389 envelope_nr = element - EL_ENVELOPE_1;
3391 envelope_len = getFile16BitBE(file);
3393 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3394 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3396 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3398 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3399 if (chunk_size_expected != chunk_size)
3401 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3402 return chunk_size_expected;
3405 for (i = 0; i < envelope_len; i++)
3406 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3411 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3413 int num_changed_custom_elements = getFile16BitBE(file);
3414 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3417 if (chunk_size_expected != chunk_size)
3419 ReadUnusedBytesFromFile(file, chunk_size - 2);
3420 return chunk_size_expected;
3423 for (i = 0; i < num_changed_custom_elements; i++)
3425 int element = getMappedElement(getFile16BitBE(file));
3426 int properties = getFile32BitBE(file);
3428 if (IS_CUSTOM_ELEMENT(element))
3429 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3431 Warn("invalid custom element number %d", element);
3433 // older game versions that wrote level files with CUS1 chunks used
3434 // different default push delay values (not yet stored in level file)
3435 element_info[element].push_delay_fixed = 2;
3436 element_info[element].push_delay_random = 8;
3439 level->file_has_custom_elements = TRUE;
3444 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3446 int num_changed_custom_elements = getFile16BitBE(file);
3447 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3450 if (chunk_size_expected != chunk_size)
3452 ReadUnusedBytesFromFile(file, chunk_size - 2);
3453 return chunk_size_expected;
3456 for (i = 0; i < num_changed_custom_elements; i++)
3458 int element = getMappedElement(getFile16BitBE(file));
3459 int custom_target_element = getMappedElement(getFile16BitBE(file));
3461 if (IS_CUSTOM_ELEMENT(element))
3462 element_info[element].change->target_element = custom_target_element;
3464 Warn("invalid custom element number %d", element);
3467 level->file_has_custom_elements = TRUE;
3472 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3474 int num_changed_custom_elements = getFile16BitBE(file);
3475 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3478 if (chunk_size_expected != chunk_size)
3480 ReadUnusedBytesFromFile(file, chunk_size - 2);
3481 return chunk_size_expected;
3484 for (i = 0; i < num_changed_custom_elements; i++)
3486 int element = getMappedElement(getFile16BitBE(file));
3487 struct ElementInfo *ei = &element_info[element];
3488 unsigned int event_bits;
3490 if (!IS_CUSTOM_ELEMENT(element))
3492 Warn("invalid custom element number %d", element);
3494 element = EL_INTERNAL_DUMMY;
3497 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3498 ei->description[j] = getFile8Bit(file);
3499 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3501 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3503 // some free bytes for future properties and padding
3504 ReadUnusedBytesFromFile(file, 7);
3506 ei->use_gfx_element = getFile8Bit(file);
3507 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3509 ei->collect_score_initial = getFile8Bit(file);
3510 ei->collect_count_initial = getFile8Bit(file);
3512 ei->push_delay_fixed = getFile16BitBE(file);
3513 ei->push_delay_random = getFile16BitBE(file);
3514 ei->move_delay_fixed = getFile16BitBE(file);
3515 ei->move_delay_random = getFile16BitBE(file);
3517 ei->move_pattern = getFile16BitBE(file);
3518 ei->move_direction_initial = getFile8Bit(file);
3519 ei->move_stepsize = getFile8Bit(file);
3521 for (y = 0; y < 3; y++)
3522 for (x = 0; x < 3; x++)
3523 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3525 // bits 0 - 31 of "has_event[]"
3526 event_bits = getFile32BitBE(file);
3527 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3528 if (event_bits & (1u << j))
3529 ei->change->has_event[j] = TRUE;
3531 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3533 ei->change->delay_fixed = getFile16BitBE(file);
3534 ei->change->delay_random = getFile16BitBE(file);
3535 ei->change->delay_frames = getFile16BitBE(file);
3537 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3539 ei->change->explode = getFile8Bit(file);
3540 ei->change->use_target_content = getFile8Bit(file);
3541 ei->change->only_if_complete = getFile8Bit(file);
3542 ei->change->use_random_replace = getFile8Bit(file);
3544 ei->change->random_percentage = getFile8Bit(file);
3545 ei->change->replace_when = getFile8Bit(file);
3547 for (y = 0; y < 3; y++)
3548 for (x = 0; x < 3; x++)
3549 ei->change->target_content.e[x][y] =
3550 getMappedElement(getFile16BitBE(file));
3552 ei->slippery_type = getFile8Bit(file);
3554 // some free bytes for future properties and padding
3555 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3557 // mark that this custom element has been modified
3558 ei->modified_settings = TRUE;
3561 level->file_has_custom_elements = TRUE;
3566 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3568 struct ElementInfo *ei;
3569 int chunk_size_expected;
3573 // ---------- custom element base property values (96 bytes) ----------------
3575 element = getMappedElement(getFile16BitBE(file));
3577 if (!IS_CUSTOM_ELEMENT(element))
3579 Warn("invalid custom element number %d", element);
3581 ReadUnusedBytesFromFile(file, chunk_size - 2);
3586 ei = &element_info[element];
3588 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3589 ei->description[i] = getFile8Bit(file);
3590 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3592 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3594 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3596 ei->num_change_pages = getFile8Bit(file);
3598 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3599 if (chunk_size_expected != chunk_size)
3601 ReadUnusedBytesFromFile(file, chunk_size - 43);
3602 return chunk_size_expected;
3605 ei->ce_value_fixed_initial = getFile16BitBE(file);
3606 ei->ce_value_random_initial = getFile16BitBE(file);
3607 ei->use_last_ce_value = getFile8Bit(file);
3609 ei->use_gfx_element = getFile8Bit(file);
3610 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3612 ei->collect_score_initial = getFile8Bit(file);
3613 ei->collect_count_initial = getFile8Bit(file);
3615 ei->drop_delay_fixed = getFile8Bit(file);
3616 ei->push_delay_fixed = getFile8Bit(file);
3617 ei->drop_delay_random = getFile8Bit(file);
3618 ei->push_delay_random = getFile8Bit(file);
3619 ei->move_delay_fixed = getFile16BitBE(file);
3620 ei->move_delay_random = getFile16BitBE(file);
3622 // bits 0 - 15 of "move_pattern" ...
3623 ei->move_pattern = getFile16BitBE(file);
3624 ei->move_direction_initial = getFile8Bit(file);
3625 ei->move_stepsize = getFile8Bit(file);
3627 ei->slippery_type = getFile8Bit(file);
3629 for (y = 0; y < 3; y++)
3630 for (x = 0; x < 3; x++)
3631 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3633 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3634 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3635 ei->move_leave_type = getFile8Bit(file);
3637 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3638 ei->move_pattern |= (getFile16BitBE(file) << 16);
3640 ei->access_direction = getFile8Bit(file);
3642 ei->explosion_delay = getFile8Bit(file);
3643 ei->ignition_delay = getFile8Bit(file);
3644 ei->explosion_type = getFile8Bit(file);
3646 // some free bytes for future custom property values and padding
3647 ReadUnusedBytesFromFile(file, 1);
3649 // ---------- change page property values (48 bytes) ------------------------
3651 setElementChangePages(ei, ei->num_change_pages);
3653 for (i = 0; i < ei->num_change_pages; i++)
3655 struct ElementChangeInfo *change = &ei->change_page[i];
3656 unsigned int event_bits;
3658 // always start with reliable default values
3659 setElementChangeInfoToDefaults(change);
3661 // bits 0 - 31 of "has_event[]" ...
3662 event_bits = getFile32BitBE(file);
3663 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3664 if (event_bits & (1u << j))
3665 change->has_event[j] = TRUE;
3667 change->target_element = getMappedElement(getFile16BitBE(file));
3669 change->delay_fixed = getFile16BitBE(file);
3670 change->delay_random = getFile16BitBE(file);
3671 change->delay_frames = getFile16BitBE(file);
3673 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3675 change->explode = getFile8Bit(file);
3676 change->use_target_content = getFile8Bit(file);
3677 change->only_if_complete = getFile8Bit(file);
3678 change->use_random_replace = getFile8Bit(file);
3680 change->random_percentage = getFile8Bit(file);
3681 change->replace_when = getFile8Bit(file);
3683 for (y = 0; y < 3; y++)
3684 for (x = 0; x < 3; x++)
3685 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3687 change->can_change = getFile8Bit(file);
3689 change->trigger_side = getFile8Bit(file);
3691 change->trigger_player = getFile8Bit(file);
3692 change->trigger_page = getFile8Bit(file);
3694 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3695 CH_PAGE_ANY : (1 << change->trigger_page));
3697 change->has_action = getFile8Bit(file);
3698 change->action_type = getFile8Bit(file);
3699 change->action_mode = getFile8Bit(file);
3700 change->action_arg = getFile16BitBE(file);
3702 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3703 event_bits = getFile8Bit(file);
3704 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3705 if (event_bits & (1u << (j - 32)))
3706 change->has_event[j] = TRUE;
3709 // mark this custom element as modified
3710 ei->modified_settings = TRUE;
3712 level->file_has_custom_elements = TRUE;
3717 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3719 struct ElementInfo *ei;
3720 struct ElementGroupInfo *group;
3724 element = getMappedElement(getFile16BitBE(file));
3726 if (!IS_GROUP_ELEMENT(element))
3728 Warn("invalid group element number %d", element);
3730 ReadUnusedBytesFromFile(file, chunk_size - 2);
3735 ei = &element_info[element];
3737 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3738 ei->description[i] = getFile8Bit(file);
3739 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3741 group = element_info[element].group;
3743 group->num_elements = getFile8Bit(file);
3745 ei->use_gfx_element = getFile8Bit(file);
3746 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3748 group->choice_mode = getFile8Bit(file);
3750 // some free bytes for future values and padding
3751 ReadUnusedBytesFromFile(file, 3);
3753 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3754 group->element[i] = getMappedElement(getFile16BitBE(file));
3756 // mark this group element as modified
3757 element_info[element].modified_settings = TRUE;
3759 level->file_has_custom_elements = TRUE;
3764 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3765 int element, int real_element)
3767 int micro_chunk_size = 0;
3768 int conf_type = getFile8Bit(file);
3769 int byte_mask = conf_type & CONF_MASK_BYTES;
3770 boolean element_found = FALSE;
3773 micro_chunk_size += 1;
3775 if (byte_mask == CONF_MASK_MULTI_BYTES)
3777 int num_bytes = getFile16BitBE(file);
3778 byte *buffer = checked_malloc(num_bytes);
3780 ReadBytesFromFile(file, buffer, num_bytes);
3782 for (i = 0; conf[i].data_type != -1; i++)
3784 if (conf[i].element == element &&
3785 conf[i].conf_type == conf_type)
3787 int data_type = conf[i].data_type;
3788 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3789 int max_num_entities = conf[i].max_num_entities;
3791 if (num_entities > max_num_entities)
3793 Warn("truncating number of entities for element %d from %d to %d",
3794 element, num_entities, max_num_entities);
3796 num_entities = max_num_entities;
3799 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3800 data_type == TYPE_CONTENT_LIST))
3802 // for element and content lists, zero entities are not allowed
3803 Warn("found empty list of entities for element %d", element);
3805 // do not set "num_entities" here to prevent reading behind buffer
3807 *(int *)(conf[i].num_entities) = 1; // at least one is required
3811 *(int *)(conf[i].num_entities) = num_entities;
3814 element_found = TRUE;
3816 if (data_type == TYPE_STRING)
3818 char *string = (char *)(conf[i].value);
3821 for (j = 0; j < max_num_entities; j++)
3822 string[j] = (j < num_entities ? buffer[j] : '\0');
3824 else if (data_type == TYPE_ELEMENT_LIST)
3826 int *element_array = (int *)(conf[i].value);
3829 for (j = 0; j < num_entities; j++)
3831 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3833 else if (data_type == TYPE_CONTENT_LIST)
3835 struct Content *content= (struct Content *)(conf[i].value);
3838 for (c = 0; c < num_entities; c++)
3839 for (y = 0; y < 3; y++)
3840 for (x = 0; x < 3; x++)
3841 content[c].e[x][y] =
3842 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3845 element_found = FALSE;
3851 checked_free(buffer);
3853 micro_chunk_size += 2 + num_bytes;
3855 else // constant size configuration data (1, 2 or 4 bytes)
3857 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3858 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3859 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3861 for (i = 0; conf[i].data_type != -1; i++)
3863 if (conf[i].element == element &&
3864 conf[i].conf_type == conf_type)
3866 int data_type = conf[i].data_type;
3868 if (data_type == TYPE_ELEMENT)
3869 value = getMappedElement(value);
3871 if (data_type == TYPE_BOOLEAN)
3872 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3874 *(int *) (conf[i].value) = value;
3876 element_found = TRUE;
3882 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3887 char *error_conf_chunk_bytes =
3888 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3889 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3890 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3891 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3892 int error_element = real_element;
3894 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3895 error_conf_chunk_bytes, error_conf_chunk_token,
3896 error_element, EL_NAME(error_element));
3899 return micro_chunk_size;
3902 static int LoadLevel_INFO(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 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3912 if (real_chunk_size >= chunk_size)
3916 *level = li; // copy temporary buffer back to level data
3918 return real_chunk_size;
3921 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3923 int real_chunk_size = 0;
3925 li = *level; // copy level data into temporary buffer
3927 while (!checkEndOfFile(file))
3929 int element = getMappedElement(getFile16BitBE(file));
3931 real_chunk_size += 2;
3932 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3934 if (real_chunk_size >= chunk_size)
3938 *level = li; // copy temporary buffer back to level data
3940 return real_chunk_size;
3943 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3945 int real_chunk_size = 0;
3947 li = *level; // copy level data into temporary buffer
3949 while (!checkEndOfFile(file))
3951 int element = getMappedElement(getFile16BitBE(file));
3953 real_chunk_size += 2;
3954 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3956 if (real_chunk_size >= chunk_size)
3960 *level = li; // copy temporary buffer back to level data
3962 return real_chunk_size;
3965 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3967 int element = getMappedElement(getFile16BitBE(file));
3968 int envelope_nr = element - EL_ENVELOPE_1;
3969 int real_chunk_size = 2;
3971 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3973 while (!checkEndOfFile(file))
3975 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3978 if (real_chunk_size >= chunk_size)
3982 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3984 return real_chunk_size;
3987 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3989 int element = getMappedElement(getFile16BitBE(file));
3990 int real_chunk_size = 2;
3991 struct ElementInfo *ei = &element_info[element];
3994 xx_ei = *ei; // copy element data into temporary buffer
3996 xx_ei.num_change_pages = -1;
3998 while (!checkEndOfFile(file))
4000 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
4002 if (xx_ei.num_change_pages != -1)
4005 if (real_chunk_size >= chunk_size)
4011 if (ei->num_change_pages == -1)
4013 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
4016 ei->num_change_pages = 1;
4018 setElementChangePages(ei, 1);
4019 setElementChangeInfoToDefaults(ei->change);
4021 return real_chunk_size;
4024 // initialize number of change pages stored for this custom element
4025 setElementChangePages(ei, ei->num_change_pages);
4026 for (i = 0; i < ei->num_change_pages; i++)
4027 setElementChangeInfoToDefaults(&ei->change_page[i]);
4029 // start with reading properties for the first change page
4030 xx_current_change_page = 0;
4032 while (!checkEndOfFile(file))
4034 // level file might contain invalid change page number
4035 if (xx_current_change_page >= ei->num_change_pages)
4038 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4040 xx_change = *change; // copy change data into temporary buffer
4042 resetEventBits(); // reset bits; change page might have changed
4044 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4047 *change = xx_change;
4049 setEventFlagsFromEventBits(change);
4051 if (real_chunk_size >= chunk_size)
4055 level->file_has_custom_elements = TRUE;
4057 return real_chunk_size;
4060 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4062 int element = getMappedElement(getFile16BitBE(file));
4063 int real_chunk_size = 2;
4064 struct ElementInfo *ei = &element_info[element];
4065 struct ElementGroupInfo *group = ei->group;
4070 xx_ei = *ei; // copy element data into temporary buffer
4071 xx_group = *group; // copy group data into temporary buffer
4073 while (!checkEndOfFile(file))
4075 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4078 if (real_chunk_size >= chunk_size)
4085 level->file_has_custom_elements = TRUE;
4087 return real_chunk_size;
4090 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4092 int element = getMappedElement(getFile16BitBE(file));
4093 int real_chunk_size = 2;
4094 struct ElementInfo *ei = &element_info[element];
4096 xx_ei = *ei; // copy element data into temporary buffer
4098 while (!checkEndOfFile(file))
4100 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4103 if (real_chunk_size >= chunk_size)
4109 level->file_has_custom_elements = TRUE;
4111 return real_chunk_size;
4114 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4115 struct LevelFileInfo *level_file_info,
4116 boolean level_info_only)
4118 char *filename = level_file_info->filename;
4119 char cookie[MAX_LINE_LEN];
4120 char chunk_name[CHUNK_ID_LEN + 1];
4124 if (!(file = openFile(filename, MODE_READ)))
4126 level->no_valid_file = TRUE;
4127 level->no_level_file = TRUE;
4129 if (level_info_only)
4132 Warn("cannot read level '%s' -- using empty level", filename);
4134 if (!setup.editor.use_template_for_new_levels)
4137 // if level file not found, try to initialize level data from template
4138 filename = getGlobalLevelTemplateFilename();
4140 if (!(file = openFile(filename, MODE_READ)))
4143 // default: for empty levels, use level template for custom elements
4144 level->use_custom_template = TRUE;
4146 level->no_valid_file = FALSE;
4149 getFileChunkBE(file, chunk_name, NULL);
4150 if (strEqual(chunk_name, "RND1"))
4152 getFile32BitBE(file); // not used
4154 getFileChunkBE(file, chunk_name, NULL);
4155 if (!strEqual(chunk_name, "CAVE"))
4157 level->no_valid_file = TRUE;
4159 Warn("unknown format of level file '%s'", filename);
4166 else // check for pre-2.0 file format with cookie string
4168 strcpy(cookie, chunk_name);
4169 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4171 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4172 cookie[strlen(cookie) - 1] = '\0';
4174 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4176 level->no_valid_file = TRUE;
4178 Warn("unknown format of level file '%s'", filename);
4185 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4187 level->no_valid_file = TRUE;
4189 Warn("unsupported version of level file '%s'", filename);
4196 // pre-2.0 level files have no game version, so use file version here
4197 level->game_version = level->file_version;
4200 if (level->file_version < FILE_VERSION_1_2)
4202 // level files from versions before 1.2.0 without chunk structure
4203 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4204 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4212 int (*loader)(File *, int, struct LevelInfo *);
4216 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4217 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4218 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4219 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4220 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4221 { "INFO", -1, LoadLevel_INFO },
4222 { "BODY", -1, LoadLevel_BODY },
4223 { "CONT", -1, LoadLevel_CONT },
4224 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4225 { "CNT3", -1, LoadLevel_CNT3 },
4226 { "CUS1", -1, LoadLevel_CUS1 },
4227 { "CUS2", -1, LoadLevel_CUS2 },
4228 { "CUS3", -1, LoadLevel_CUS3 },
4229 { "CUS4", -1, LoadLevel_CUS4 },
4230 { "GRP1", -1, LoadLevel_GRP1 },
4231 { "CONF", -1, LoadLevel_CONF },
4232 { "ELEM", -1, LoadLevel_ELEM },
4233 { "NOTE", -1, LoadLevel_NOTE },
4234 { "CUSX", -1, LoadLevel_CUSX },
4235 { "GRPX", -1, LoadLevel_GRPX },
4236 { "EMPX", -1, LoadLevel_EMPX },
4241 while (getFileChunkBE(file, chunk_name, &chunk_size))
4245 while (chunk_info[i].name != NULL &&
4246 !strEqual(chunk_name, chunk_info[i].name))
4249 if (chunk_info[i].name == NULL)
4251 Warn("unknown chunk '%s' in level file '%s'",
4252 chunk_name, filename);
4254 ReadUnusedBytesFromFile(file, chunk_size);
4256 else if (chunk_info[i].size != -1 &&
4257 chunk_info[i].size != chunk_size)
4259 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4260 chunk_size, chunk_name, filename);
4262 ReadUnusedBytesFromFile(file, chunk_size);
4266 // call function to load this level chunk
4267 int chunk_size_expected =
4268 (chunk_info[i].loader)(file, chunk_size, level);
4270 if (chunk_size_expected < 0)
4272 Warn("error reading chunk '%s' in level file '%s'",
4273 chunk_name, filename);
4278 // the size of some chunks cannot be checked before reading other
4279 // chunks first (like "HEAD" and "BODY") that contain some header
4280 // information, so check them here
4281 if (chunk_size_expected != chunk_size)
4283 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4284 chunk_size, chunk_name, filename);
4296 // ----------------------------------------------------------------------------
4297 // functions for loading BD level
4298 // ----------------------------------------------------------------------------
4300 #define LEVEL_TO_CAVE(e) (map_element_RND_to_BD_cave(e))
4301 #define CAVE_TO_LEVEL(e) (map_element_BD_to_RND_cave(e))
4303 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4305 struct LevelInfo_BD *level_bd = level->native_bd_level;
4306 GdCave *cave = NULL; // will be changed below
4307 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4308 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4311 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4313 // cave and map newly allocated when set to defaults above
4314 cave = level_bd->cave;
4317 cave->intermission = level->bd_intermission;
4320 cave->level_time[0] = level->time;
4321 cave->level_diamonds[0] = level->gems_needed;
4324 cave->scheduling = level->bd_scheduling_type;
4325 cave->pal_timing = level->bd_pal_timing;
4326 cave->level_speed[0] = level->bd_cycle_delay_ms;
4327 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4328 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4329 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4332 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4333 cave->diamond_value = level->score[SC_EMERALD];
4334 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4336 // compatibility settings
4337 cave->lineshift = level->bd_line_shifting_borders;
4338 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4339 cave->short_explosions = level->bd_short_explosions;
4341 // player properties
4342 cave->diagonal_movements = level->bd_diagonal_movements;
4343 cave->active_is_first_found = level->bd_topmost_player_active;
4344 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4345 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4346 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4347 cave->snap_element = LEVEL_TO_CAVE(level->bd_snap_element);
4349 // element properties
4350 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4351 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4352 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4353 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4354 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4355 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4356 cave->level_magic_wall_time[0] = level->bd_magic_wall_time;
4357 cave->magic_timer_zero_is_infinite = level->bd_magic_wall_zero_infinite;
4358 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4359 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4360 cave->magic_wall_breakscan = level->bd_magic_wall_break_scan;
4362 cave->magic_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4363 cave->magic_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4364 cave->magic_mega_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4365 cave->magic_nut_to = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4366 cave->magic_nitro_pack_to = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4367 cave->magic_flying_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4368 cave->magic_flying_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4370 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4371 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4372 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4373 cave->level_amoeba_threshold[0] = level->bd_amoeba_1_threshold_too_big;
4374 cave->level_amoeba_time[0] = level->bd_amoeba_1_slow_growth_time;
4375 cave->amoeba_growth_prob = level->bd_amoeba_1_slow_growth_rate * 10000;
4376 cave->amoeba_fast_growth_prob = level->bd_amoeba_1_fast_growth_rate * 10000;
4377 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4378 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4379 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4380 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4382 cave->amoeba_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_1_content_too_big);
4383 cave->amoeba_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_1_content_enclosed);
4384 cave->amoeba_2_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4385 cave->amoeba_2_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4386 cave->amoeba_2_explosion_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4387 cave->amoeba_2_looks_like = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4389 cave->slime_predictable = level->bd_slime_is_predictable;
4390 cave->slime_correct_random = level->bd_slime_correct_random;
4391 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4392 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4393 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4394 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4395 cave->slime_eats_1 = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4396 cave->slime_converts_1 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4397 cave->slime_eats_2 = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4398 cave->slime_converts_2 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4399 cave->slime_eats_3 = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4400 cave->slime_converts_3 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4402 cave->acid_eats_this = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4403 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4404 cave->acid_turns_to = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4406 cave->biter_delay_frame = level->bd_biter_move_delay;
4407 cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4409 cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4411 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4413 cave->replicators_active = level->bd_replicators_active;
4414 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4416 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4417 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4419 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4421 cave->nut_turns_to_when_crushed = LEVEL_TO_CAVE(level->bd_nut_content);
4423 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4424 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4425 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4427 cave->infinite_rockets = level->bd_infinite_rockets;
4429 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4430 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4432 cave->expanding_wall_looks_like = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4433 cave->dirt_looks_like = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4435 cave->creatures_backwards = level->bd_creatures_start_backwards;
4436 cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4437 cave->creatures_direction_auto_change_time = level->bd_creatures_auto_turn_delay;
4439 cave->gravity = level->bd_gravity_direction;
4440 cave->gravity_switch_active = level->bd_gravity_switch_active;
4441 cave->gravity_change_time = level->bd_gravity_switch_delay;
4442 cave->gravity_affects_all = level->bd_gravity_affects_all;
4444 cave->stone_falling_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4445 cave->stone_bouncing_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4446 cave->diamond_falling_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4447 cave->diamond_bouncing_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4449 cave->firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_1_explodes_to);
4450 cave->alt_firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4451 cave->butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_1_explodes_to);
4452 cave->alt_butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4453 cave->stonefly_explode_to = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4454 cave->dragonfly_explode_to = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4456 cave->diamond_birth_effect = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4457 cave->bomb_explosion_effect = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4458 cave->nitro_explosion_effect = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4459 cave->explosion_effect = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4461 cave->colorb = level->bd_color_b;
4462 cave->color0 = level->bd_color_0;
4463 cave->color1 = level->bd_color_1;
4464 cave->color2 = level->bd_color_2;
4465 cave->color3 = level->bd_color_3;
4466 cave->color4 = level->bd_color_4;
4467 cave->color5 = level->bd_color_5;
4470 strncpy(cave->name, level->name, sizeof(GdString));
4471 cave->name[sizeof(GdString) - 1] = '\0';
4473 // playfield elements
4474 for (x = 0; x < cave->w; x++)
4475 for (y = 0; y < cave->h; y++)
4476 cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4479 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4481 struct LevelInfo_BD *level_bd = level->native_bd_level;
4482 GdCave *cave = level_bd->cave;
4483 int bd_level_nr = level_bd->level_nr;
4486 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4487 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4490 level->bd_intermission = cave->intermission;
4493 level->time = cave->level_time[bd_level_nr];
4494 level->gems_needed = cave->level_diamonds[bd_level_nr];
4497 level->bd_scheduling_type = cave->scheduling;
4498 level->bd_pal_timing = cave->pal_timing;
4499 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4500 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4501 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4502 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4505 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4506 level->score[SC_EMERALD] = cave->diamond_value;
4507 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4509 // compatibility settings
4510 level->bd_line_shifting_borders = cave->lineshift;
4511 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4512 level->bd_short_explosions = cave->short_explosions;
4514 // player properties
4515 level->bd_diagonal_movements = cave->diagonal_movements;
4516 level->bd_topmost_player_active = cave->active_is_first_found;
4517 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4518 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4519 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4520 level->bd_snap_element = CAVE_TO_LEVEL(cave->snap_element);
4522 // element properties
4523 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4524 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4525 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4526 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4527 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4528 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4529 level->bd_magic_wall_time = cave->level_magic_wall_time[bd_level_nr];
4530 level->bd_magic_wall_zero_infinite = cave->magic_timer_zero_is_infinite;
4531 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4532 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4533 level->bd_magic_wall_break_scan = cave->magic_wall_breakscan;
4535 level->bd_magic_wall_diamond_to = CAVE_TO_LEVEL(cave->magic_diamond_to);
4536 level->bd_magic_wall_rock_to = CAVE_TO_LEVEL(cave->magic_stone_to);
4537 level->bd_magic_wall_mega_rock_to = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4538 level->bd_magic_wall_nut_to = CAVE_TO_LEVEL(cave->magic_nut_to);
4539 level->bd_magic_wall_nitro_pack_to = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4540 level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4541 level->bd_magic_wall_flying_rock_to = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4543 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4544 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4545 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4546 level->bd_amoeba_1_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4547 level->bd_amoeba_1_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4548 level->bd_amoeba_1_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4549 level->bd_amoeba_1_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4550 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4551 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4552 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4553 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4555 level->bd_amoeba_1_content_too_big = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4556 level->bd_amoeba_1_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4557 level->bd_amoeba_2_content_too_big = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4558 level->bd_amoeba_2_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4559 level->bd_amoeba_2_content_exploding = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4560 level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4562 level->bd_slime_is_predictable = cave->slime_predictable;
4563 level->bd_slime_correct_random = cave->slime_correct_random;
4564 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4565 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4566 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4567 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4568 level->bd_slime_eats_element_1 = CAVE_TO_LEVEL(cave->slime_eats_1);
4569 level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4570 level->bd_slime_eats_element_2 = CAVE_TO_LEVEL(cave->slime_eats_2);
4571 level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4572 level->bd_slime_eats_element_3 = CAVE_TO_LEVEL(cave->slime_eats_3);
4573 level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4575 level->bd_acid_eats_element = CAVE_TO_LEVEL(cave->acid_eats_this);
4576 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4577 level->bd_acid_turns_to_element = CAVE_TO_LEVEL(cave->acid_turns_to);
4579 level->bd_biter_move_delay = cave->biter_delay_frame;
4580 level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat);
4582 level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4584 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4586 level->bd_replicators_active = cave->replicators_active;
4587 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4589 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4590 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4592 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4594 level->bd_nut_content = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4596 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4597 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4598 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4600 level->bd_infinite_rockets = cave->infinite_rockets;
4602 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4603 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4605 level->bd_expanding_wall_looks_like = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4606 level->bd_sand_looks_like = CAVE_TO_LEVEL(cave->dirt_looks_like);
4608 level->bd_creatures_start_backwards = cave->creatures_backwards;
4609 level->bd_creatures_turn_on_hatching = cave->creatures_direction_auto_change_on_start;
4610 level->bd_creatures_auto_turn_delay = cave->creatures_direction_auto_change_time;
4612 level->bd_gravity_direction = cave->gravity;
4613 level->bd_gravity_switch_active = cave->gravity_switch_active;
4614 level->bd_gravity_switch_delay = cave->gravity_change_time;
4615 level->bd_gravity_affects_all = cave->gravity_affects_all;
4617 level->bd_rock_turns_to_on_falling = CAVE_TO_LEVEL(cave->stone_falling_effect);
4618 level->bd_rock_turns_to_on_impact = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4619 level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4620 level->bd_diamond_turns_to_on_impact = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4622 level->bd_firefly_1_explodes_to = CAVE_TO_LEVEL(cave->firefly_explode_to);
4623 level->bd_firefly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4624 level->bd_butterfly_1_explodes_to = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4625 level->bd_butterfly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4626 level->bd_stonefly_explodes_to = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4627 level->bd_dragonfly_explodes_to = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4629 level->bd_diamond_birth_turns_to = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4630 level->bd_bomb_explosion_turns_to = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4631 level->bd_nitro_explosion_turns_to = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4632 level->bd_explosion_turns_to = CAVE_TO_LEVEL(cave->explosion_effect);
4634 level->bd_color_b = cave->colorb;
4635 level->bd_color_0 = cave->color0;
4636 level->bd_color_1 = cave->color1;
4637 level->bd_color_2 = cave->color2;
4638 level->bd_color_3 = cave->color3;
4639 level->bd_color_4 = cave->color4;
4640 level->bd_color_5 = cave->color5;
4642 // set default color type and colors for BD style level colors
4643 SetDefaultLevelColorType_BD();
4644 SetDefaultLevelColors_BD();
4647 char *cave_name_latin1 = getLatin1FromUTF8(cave->name);
4648 char *cave_name_final = (gd_caveset_has_levels() ?
4649 getStringPrint("%s / %d", cave_name_latin1, bd_level_nr + 1) :
4650 getStringCopy(cave_name_latin1));
4652 strncpy(level->name, cave_name_final, MAX_LEVEL_NAME_LEN);
4653 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4655 // playfield elements
4656 for (x = 0; x < level->fieldx; x++)
4657 for (y = 0; y < level->fieldy; y++)
4658 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4660 checked_free(cave_name_latin1);
4661 checked_free(cave_name_final);
4664 static void setTapeInfoToDefaults(void);
4666 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4668 struct LevelInfo_BD *level_bd = level->native_bd_level;
4669 GdCave *cave = level_bd->cave;
4670 GdReplay *replay = level_bd->replay;
4676 // always start with reliable default values
4677 setTapeInfoToDefaults();
4679 tape.level_nr = level_nr; // (currently not used)
4680 tape.random_seed = replay->seed;
4682 TapeSetDateFromIsoDateString(replay->date);
4685 tape.pos[tape.counter].delay = 0;
4687 tape.bd_replay = TRUE;
4689 // all time calculations only used to display approximate tape time
4690 int cave_speed = cave->speed;
4691 int milliseconds_game = 0;
4692 int milliseconds_elapsed = 20;
4694 for (i = 0; i < replay->movements->len; i++)
4696 int replay_action = replay->movements->data[i];
4697 int tape_action = map_action_BD_to_RND(replay_action);
4698 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4699 boolean success = 0;
4703 success = TapeAddAction(action);
4705 milliseconds_game += milliseconds_elapsed;
4707 if (milliseconds_game >= cave_speed)
4709 milliseconds_game -= cave_speed;
4716 tape.pos[tape.counter].delay = 0;
4717 tape.pos[tape.counter].action[0] = 0;
4721 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4727 TapeHaltRecording();
4729 if (!replay->success)
4730 Warn("BD replay is marked as not successful");
4734 // ----------------------------------------------------------------------------
4735 // functions for loading EM level
4736 // ----------------------------------------------------------------------------
4738 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4740 static int ball_xy[8][2] =
4751 struct LevelInfo_EM *level_em = level->native_em_level;
4752 struct CAVE *cav = level_em->cav;
4755 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4756 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4758 cav->time_seconds = level->time;
4759 cav->gems_needed = level->gems_needed;
4761 cav->emerald_score = level->score[SC_EMERALD];
4762 cav->diamond_score = level->score[SC_DIAMOND];
4763 cav->alien_score = level->score[SC_ROBOT];
4764 cav->tank_score = level->score[SC_SPACESHIP];
4765 cav->bug_score = level->score[SC_BUG];
4766 cav->eater_score = level->score[SC_YAMYAM];
4767 cav->nut_score = level->score[SC_NUT];
4768 cav->dynamite_score = level->score[SC_DYNAMITE];
4769 cav->key_score = level->score[SC_KEY];
4770 cav->exit_score = level->score[SC_TIME_BONUS];
4772 cav->num_eater_arrays = level->num_yamyam_contents;
4774 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4775 for (y = 0; y < 3; y++)
4776 for (x = 0; x < 3; x++)
4777 cav->eater_array[i][y * 3 + x] =
4778 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4780 cav->amoeba_time = level->amoeba_speed;
4781 cav->wonderwall_time = level->time_magic_wall;
4782 cav->wheel_time = level->time_wheel;
4784 cav->android_move_time = level->android_move_time;
4785 cav->android_clone_time = level->android_clone_time;
4786 cav->ball_random = level->ball_random;
4787 cav->ball_active = level->ball_active_initial;
4788 cav->ball_time = level->ball_time;
4789 cav->num_ball_arrays = level->num_ball_contents;
4791 cav->lenses_score = level->lenses_score;
4792 cav->magnify_score = level->magnify_score;
4793 cav->slurp_score = level->slurp_score;
4795 cav->lenses_time = level->lenses_time;
4796 cav->magnify_time = level->magnify_time;
4798 cav->wind_time = 9999;
4799 cav->wind_direction =
4800 map_direction_RND_to_EM(level->wind_direction_initial);
4802 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4803 for (j = 0; j < 8; j++)
4804 cav->ball_array[i][j] =
4805 map_element_RND_to_EM_cave(level->ball_content[i].
4806 e[ball_xy[j][0]][ball_xy[j][1]]);
4808 map_android_clone_elements_RND_to_EM(level);
4810 // first fill the complete playfield with the empty space element
4811 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4812 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4813 cav->cave[x][y] = Cblank;
4815 // then copy the real level contents from level file into the playfield
4816 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4818 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4820 if (level->field[x][y] == EL_AMOEBA_DEAD)
4821 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4823 cav->cave[x][y] = new_element;
4826 for (i = 0; i < MAX_PLAYERS; i++)
4828 cav->player_x[i] = -1;
4829 cav->player_y[i] = -1;
4832 // initialize player positions and delete players from the playfield
4833 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4835 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4837 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4839 cav->player_x[player_nr] = x;
4840 cav->player_y[player_nr] = y;
4842 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4847 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4849 static int ball_xy[8][2] =
4860 struct LevelInfo_EM *level_em = level->native_em_level;
4861 struct CAVE *cav = level_em->cav;
4864 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4865 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4867 level->time = cav->time_seconds;
4868 level->gems_needed = cav->gems_needed;
4870 sprintf(level->name, "Level %d", level->file_info.nr);
4872 level->score[SC_EMERALD] = cav->emerald_score;
4873 level->score[SC_DIAMOND] = cav->diamond_score;
4874 level->score[SC_ROBOT] = cav->alien_score;
4875 level->score[SC_SPACESHIP] = cav->tank_score;
4876 level->score[SC_BUG] = cav->bug_score;
4877 level->score[SC_YAMYAM] = cav->eater_score;
4878 level->score[SC_NUT] = cav->nut_score;
4879 level->score[SC_DYNAMITE] = cav->dynamite_score;
4880 level->score[SC_KEY] = cav->key_score;
4881 level->score[SC_TIME_BONUS] = cav->exit_score;
4883 level->num_yamyam_contents = cav->num_eater_arrays;
4885 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4886 for (y = 0; y < 3; y++)
4887 for (x = 0; x < 3; x++)
4888 level->yamyam_content[i].e[x][y] =
4889 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4891 level->amoeba_speed = cav->amoeba_time;
4892 level->time_magic_wall = cav->wonderwall_time;
4893 level->time_wheel = cav->wheel_time;
4895 level->android_move_time = cav->android_move_time;
4896 level->android_clone_time = cav->android_clone_time;
4897 level->ball_random = cav->ball_random;
4898 level->ball_active_initial = cav->ball_active;
4899 level->ball_time = cav->ball_time;
4900 level->num_ball_contents = cav->num_ball_arrays;
4902 level->lenses_score = cav->lenses_score;
4903 level->magnify_score = cav->magnify_score;
4904 level->slurp_score = cav->slurp_score;
4906 level->lenses_time = cav->lenses_time;
4907 level->magnify_time = cav->magnify_time;
4909 level->wind_direction_initial =
4910 map_direction_EM_to_RND(cav->wind_direction);
4912 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4913 for (j = 0; j < 8; j++)
4914 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4915 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4917 map_android_clone_elements_EM_to_RND(level);
4919 // convert the playfield (some elements need special treatment)
4920 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4922 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4924 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4925 new_element = EL_AMOEBA_DEAD;
4927 level->field[x][y] = new_element;
4930 for (i = 0; i < MAX_PLAYERS; i++)
4932 // in case of all players set to the same field, use the first player
4933 int nr = MAX_PLAYERS - i - 1;
4934 int jx = cav->player_x[nr];
4935 int jy = cav->player_y[nr];
4937 if (jx != -1 && jy != -1)
4938 level->field[jx][jy] = EL_PLAYER_1 + nr;
4941 // time score is counted for each 10 seconds left in Emerald Mine levels
4942 level->time_score_base = 10;
4946 // ----------------------------------------------------------------------------
4947 // functions for loading SP level
4948 // ----------------------------------------------------------------------------
4950 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4952 struct LevelInfo_SP *level_sp = level->native_sp_level;
4953 LevelInfoType *header = &level_sp->header;
4956 level_sp->width = level->fieldx;
4957 level_sp->height = level->fieldy;
4959 for (x = 0; x < level->fieldx; x++)
4960 for (y = 0; y < level->fieldy; y++)
4961 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4963 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4965 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4966 header->LevelTitle[i] = level->name[i];
4967 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4969 header->InfotronsNeeded = level->gems_needed;
4971 header->SpecialPortCount = 0;
4973 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4975 boolean gravity_port_found = FALSE;
4976 boolean gravity_port_valid = FALSE;
4977 int gravity_port_flag;
4978 int gravity_port_base_element;
4979 int element = level->field[x][y];
4981 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4982 element <= EL_SP_GRAVITY_ON_PORT_UP)
4984 gravity_port_found = TRUE;
4985 gravity_port_valid = TRUE;
4986 gravity_port_flag = 1;
4987 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4989 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4990 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4992 gravity_port_found = TRUE;
4993 gravity_port_valid = TRUE;
4994 gravity_port_flag = 0;
4995 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4997 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4998 element <= EL_SP_GRAVITY_PORT_UP)
5000 // change R'n'D style gravity inverting special port to normal port
5001 // (there are no gravity inverting ports in native Supaplex engine)
5003 gravity_port_found = TRUE;
5004 gravity_port_valid = FALSE;
5005 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
5008 if (gravity_port_found)
5010 if (gravity_port_valid &&
5011 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
5013 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
5015 port->PortLocation = (y * level->fieldx + x) * 2;
5016 port->Gravity = gravity_port_flag;
5018 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
5020 header->SpecialPortCount++;
5024 // change special gravity port to normal port
5026 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
5029 level_sp->playfield[x][y] = element - EL_SP_START;
5034 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
5036 struct LevelInfo_SP *level_sp = level->native_sp_level;
5037 LevelInfoType *header = &level_sp->header;
5038 boolean num_invalid_elements = 0;
5041 level->fieldx = level_sp->width;
5042 level->fieldy = level_sp->height;
5044 for (x = 0; x < level->fieldx; x++)
5046 for (y = 0; y < level->fieldy; y++)
5048 int element_old = level_sp->playfield[x][y];
5049 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5051 if (element_new == EL_UNKNOWN)
5053 num_invalid_elements++;
5055 Debug("level:native:SP", "invalid element %d at position %d, %d",
5059 level->field[x][y] = element_new;
5063 if (num_invalid_elements > 0)
5064 Warn("found %d invalid elements%s", num_invalid_elements,
5065 (!options.debug ? " (use '--debug' for more details)" : ""));
5067 for (i = 0; i < MAX_PLAYERS; i++)
5068 level->initial_player_gravity[i] =
5069 (header->InitialGravity == 1 ? TRUE : FALSE);
5071 // skip leading spaces
5072 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5073 if (header->LevelTitle[i] != ' ')
5077 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5078 level->name[j] = header->LevelTitle[i];
5079 level->name[j] = '\0';
5081 // cut trailing spaces
5083 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5084 level->name[j - 1] = '\0';
5086 level->gems_needed = header->InfotronsNeeded;
5088 for (i = 0; i < header->SpecialPortCount; i++)
5090 SpecialPortType *port = &header->SpecialPort[i];
5091 int port_location = port->PortLocation;
5092 int gravity = port->Gravity;
5093 int port_x, port_y, port_element;
5095 port_x = (port_location / 2) % level->fieldx;
5096 port_y = (port_location / 2) / level->fieldx;
5098 if (port_x < 0 || port_x >= level->fieldx ||
5099 port_y < 0 || port_y >= level->fieldy)
5101 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5106 port_element = level->field[port_x][port_y];
5108 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5109 port_element > EL_SP_GRAVITY_PORT_UP)
5111 Warn("no special port at position (%d, %d)", port_x, port_y);
5116 // change previous (wrong) gravity inverting special port to either
5117 // gravity enabling special port or gravity disabling special port
5118 level->field[port_x][port_y] +=
5119 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5120 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5123 // change special gravity ports without database entries to normal ports
5124 for (x = 0; x < level->fieldx; x++)
5125 for (y = 0; y < level->fieldy; y++)
5126 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5127 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5128 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5130 level->time = 0; // no time limit
5131 level->amoeba_speed = 0;
5132 level->time_magic_wall = 0;
5133 level->time_wheel = 0;
5134 level->amoeba_content = EL_EMPTY;
5136 // original Supaplex does not use score values -- rate by playing time
5137 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5138 level->score[i] = 0;
5140 level->rate_time_over_score = TRUE;
5142 // there are no yamyams in supaplex levels
5143 for (i = 0; i < level->num_yamyam_contents; i++)
5144 for (x = 0; x < 3; x++)
5145 for (y = 0; y < 3; y++)
5146 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5149 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5151 struct LevelInfo_SP *level_sp = level->native_sp_level;
5152 struct DemoInfo_SP *demo = &level_sp->demo;
5155 // always start with reliable default values
5156 demo->is_available = FALSE;
5159 if (TAPE_IS_EMPTY(tape))
5162 demo->level_nr = tape.level_nr; // (currently not used)
5164 level_sp->header.DemoRandomSeed = tape.random_seed;
5168 for (i = 0; i < tape.length; i++)
5170 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5171 int demo_repeat = tape.pos[i].delay;
5172 int demo_entries = (demo_repeat + 15) / 16;
5174 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5176 Warn("tape truncated: size exceeds maximum SP demo size %d",
5182 for (j = 0; j < demo_repeat / 16; j++)
5183 demo->data[demo->length++] = 0xf0 | demo_action;
5185 if (demo_repeat % 16)
5186 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5189 demo->is_available = TRUE;
5192 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5194 struct LevelInfo_SP *level_sp = level->native_sp_level;
5195 struct DemoInfo_SP *demo = &level_sp->demo;
5196 char *filename = level->file_info.filename;
5199 // always start with reliable default values
5200 setTapeInfoToDefaults();
5202 if (!demo->is_available)
5205 tape.level_nr = demo->level_nr; // (currently not used)
5206 tape.random_seed = level_sp->header.DemoRandomSeed;
5208 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5211 tape.pos[tape.counter].delay = 0;
5213 for (i = 0; i < demo->length; i++)
5215 int demo_action = demo->data[i] & 0x0f;
5216 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5217 int tape_action = map_key_SP_to_RND(demo_action);
5218 int tape_repeat = demo_repeat + 1;
5219 byte action[MAX_TAPE_ACTIONS] = { tape_action };
5220 boolean success = 0;
5223 for (j = 0; j < tape_repeat; j++)
5224 success = TapeAddAction(action);
5228 Warn("SP demo truncated: size exceeds maximum tape size %d",
5235 TapeHaltRecording();
5239 // ----------------------------------------------------------------------------
5240 // functions for loading MM level
5241 // ----------------------------------------------------------------------------
5243 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5245 struct LevelInfo_MM *level_mm = level->native_mm_level;
5248 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5249 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5251 level_mm->time = level->time;
5252 level_mm->kettles_needed = level->gems_needed;
5253 level_mm->auto_count_kettles = level->auto_count_gems;
5255 level_mm->mm_laser_red = level->mm_laser_red;
5256 level_mm->mm_laser_green = level->mm_laser_green;
5257 level_mm->mm_laser_blue = level->mm_laser_blue;
5259 level_mm->df_laser_red = level->df_laser_red;
5260 level_mm->df_laser_green = level->df_laser_green;
5261 level_mm->df_laser_blue = level->df_laser_blue;
5263 strcpy(level_mm->name, level->name);
5264 strcpy(level_mm->author, level->author);
5266 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
5267 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
5268 level_mm->score[SC_KEY] = level->score[SC_KEY];
5269 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5270 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5272 level_mm->amoeba_speed = level->amoeba_speed;
5273 level_mm->time_fuse = level->mm_time_fuse;
5274 level_mm->time_bomb = level->mm_time_bomb;
5275 level_mm->time_ball = level->mm_time_ball;
5276 level_mm->time_block = level->mm_time_block;
5278 level_mm->num_ball_contents = level->num_mm_ball_contents;
5279 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5280 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5281 level_mm->explode_ball = level->explode_mm_ball;
5283 for (i = 0; i < level->num_mm_ball_contents; i++)
5284 level_mm->ball_content[i] =
5285 map_element_RND_to_MM(level->mm_ball_content[i]);
5287 for (x = 0; x < level->fieldx; x++)
5288 for (y = 0; y < level->fieldy; y++)
5290 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5293 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5295 struct LevelInfo_MM *level_mm = level->native_mm_level;
5298 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5299 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5301 level->time = level_mm->time;
5302 level->gems_needed = level_mm->kettles_needed;
5303 level->auto_count_gems = level_mm->auto_count_kettles;
5305 level->mm_laser_red = level_mm->mm_laser_red;
5306 level->mm_laser_green = level_mm->mm_laser_green;
5307 level->mm_laser_blue = level_mm->mm_laser_blue;
5309 level->df_laser_red = level_mm->df_laser_red;
5310 level->df_laser_green = level_mm->df_laser_green;
5311 level->df_laser_blue = level_mm->df_laser_blue;
5313 strcpy(level->name, level_mm->name);
5315 // only overwrite author from 'levelinfo.conf' if author defined in level
5316 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5317 strcpy(level->author, level_mm->author);
5319 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5320 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5321 level->score[SC_KEY] = level_mm->score[SC_KEY];
5322 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5323 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5325 level->amoeba_speed = level_mm->amoeba_speed;
5326 level->mm_time_fuse = level_mm->time_fuse;
5327 level->mm_time_bomb = level_mm->time_bomb;
5328 level->mm_time_ball = level_mm->time_ball;
5329 level->mm_time_block = level_mm->time_block;
5331 level->num_mm_ball_contents = level_mm->num_ball_contents;
5332 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5333 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5334 level->explode_mm_ball = level_mm->explode_ball;
5336 for (i = 0; i < level->num_mm_ball_contents; i++)
5337 level->mm_ball_content[i] =
5338 map_element_MM_to_RND(level_mm->ball_content[i]);
5340 for (x = 0; x < level->fieldx; x++)
5341 for (y = 0; y < level->fieldy; y++)
5342 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5346 // ----------------------------------------------------------------------------
5347 // functions for loading DC level
5348 // ----------------------------------------------------------------------------
5350 #define DC_LEVEL_HEADER_SIZE 344
5352 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5355 static int last_data_encoded;
5359 int diff_hi, diff_lo;
5360 int data_hi, data_lo;
5361 unsigned short data_decoded;
5365 last_data_encoded = 0;
5372 diff = data_encoded - last_data_encoded;
5373 diff_hi = diff & ~0xff;
5374 diff_lo = diff & 0xff;
5378 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5379 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5380 data_hi = data_hi & 0xff00;
5382 data_decoded = data_hi | data_lo;
5384 last_data_encoded = data_encoded;
5386 offset1 = (offset1 + 1) % 31;
5387 offset2 = offset2 & 0xff;
5389 return data_decoded;
5392 static int getMappedElement_DC(int element)
5400 // 0x0117 - 0x036e: (?)
5403 // 0x042d - 0x0684: (?)
5419 element = EL_CRYSTAL;
5422 case 0x0e77: // quicksand (boulder)
5423 element = EL_QUICKSAND_FAST_FULL;
5426 case 0x0e99: // slow quicksand (boulder)
5427 element = EL_QUICKSAND_FULL;
5431 element = EL_EM_EXIT_OPEN;
5435 element = EL_EM_EXIT_CLOSED;
5439 element = EL_EM_STEEL_EXIT_OPEN;
5443 element = EL_EM_STEEL_EXIT_CLOSED;
5446 case 0x0f4f: // dynamite (lit 1)
5447 element = EL_EM_DYNAMITE_ACTIVE;
5450 case 0x0f57: // dynamite (lit 2)
5451 element = EL_EM_DYNAMITE_ACTIVE;
5454 case 0x0f5f: // dynamite (lit 3)
5455 element = EL_EM_DYNAMITE_ACTIVE;
5458 case 0x0f67: // dynamite (lit 4)
5459 element = EL_EM_DYNAMITE_ACTIVE;
5466 element = EL_AMOEBA_WET;
5470 element = EL_AMOEBA_DROP;
5474 element = EL_DC_MAGIC_WALL;
5478 element = EL_SPACESHIP_UP;
5482 element = EL_SPACESHIP_DOWN;
5486 element = EL_SPACESHIP_LEFT;
5490 element = EL_SPACESHIP_RIGHT;
5494 element = EL_BUG_UP;
5498 element = EL_BUG_DOWN;
5502 element = EL_BUG_LEFT;
5506 element = EL_BUG_RIGHT;
5510 element = EL_MOLE_UP;
5514 element = EL_MOLE_DOWN;
5518 element = EL_MOLE_LEFT;
5522 element = EL_MOLE_RIGHT;
5530 element = EL_YAMYAM_UP;
5534 element = EL_SWITCHGATE_OPEN;
5538 element = EL_SWITCHGATE_CLOSED;
5542 element = EL_DC_SWITCHGATE_SWITCH_UP;
5546 element = EL_TIMEGATE_CLOSED;
5549 case 0x144c: // conveyor belt switch (green)
5550 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5553 case 0x144f: // conveyor belt switch (red)
5554 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5557 case 0x1452: // conveyor belt switch (blue)
5558 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5562 element = EL_CONVEYOR_BELT_3_MIDDLE;
5566 element = EL_CONVEYOR_BELT_3_LEFT;
5570 element = EL_CONVEYOR_BELT_3_RIGHT;
5574 element = EL_CONVEYOR_BELT_1_MIDDLE;
5578 element = EL_CONVEYOR_BELT_1_LEFT;
5582 element = EL_CONVEYOR_BELT_1_RIGHT;
5586 element = EL_CONVEYOR_BELT_4_MIDDLE;
5590 element = EL_CONVEYOR_BELT_4_LEFT;
5594 element = EL_CONVEYOR_BELT_4_RIGHT;
5598 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5602 element = EL_EXPANDABLE_WALL_VERTICAL;
5606 element = EL_EXPANDABLE_WALL_ANY;
5609 case 0x14ce: // growing steel wall (left/right)
5610 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5613 case 0x14df: // growing steel wall (up/down)
5614 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5617 case 0x14e8: // growing steel wall (up/down/left/right)
5618 element = EL_EXPANDABLE_STEELWALL_ANY;
5622 element = EL_SHIELD_DEADLY;
5626 element = EL_EXTRA_TIME;
5634 element = EL_EMPTY_SPACE;
5637 case 0x1578: // quicksand (empty)
5638 element = EL_QUICKSAND_FAST_EMPTY;
5641 case 0x1579: // slow quicksand (empty)
5642 element = EL_QUICKSAND_EMPTY;
5652 element = EL_EM_DYNAMITE;
5655 case 0x15a1: // key (red)
5656 element = EL_EM_KEY_1;
5659 case 0x15a2: // key (yellow)
5660 element = EL_EM_KEY_2;
5663 case 0x15a3: // key (blue)
5664 element = EL_EM_KEY_4;
5667 case 0x15a4: // key (green)
5668 element = EL_EM_KEY_3;
5671 case 0x15a5: // key (white)
5672 element = EL_DC_KEY_WHITE;
5676 element = EL_WALL_SLIPPERY;
5683 case 0x15a8: // wall (not round)
5687 case 0x15a9: // (blue)
5688 element = EL_CHAR_A;
5691 case 0x15aa: // (blue)
5692 element = EL_CHAR_B;
5695 case 0x15ab: // (blue)
5696 element = EL_CHAR_C;
5699 case 0x15ac: // (blue)
5700 element = EL_CHAR_D;
5703 case 0x15ad: // (blue)
5704 element = EL_CHAR_E;
5707 case 0x15ae: // (blue)
5708 element = EL_CHAR_F;
5711 case 0x15af: // (blue)
5712 element = EL_CHAR_G;
5715 case 0x15b0: // (blue)
5716 element = EL_CHAR_H;
5719 case 0x15b1: // (blue)
5720 element = EL_CHAR_I;
5723 case 0x15b2: // (blue)
5724 element = EL_CHAR_J;
5727 case 0x15b3: // (blue)
5728 element = EL_CHAR_K;
5731 case 0x15b4: // (blue)
5732 element = EL_CHAR_L;
5735 case 0x15b5: // (blue)
5736 element = EL_CHAR_M;
5739 case 0x15b6: // (blue)
5740 element = EL_CHAR_N;
5743 case 0x15b7: // (blue)
5744 element = EL_CHAR_O;
5747 case 0x15b8: // (blue)
5748 element = EL_CHAR_P;
5751 case 0x15b9: // (blue)
5752 element = EL_CHAR_Q;
5755 case 0x15ba: // (blue)
5756 element = EL_CHAR_R;
5759 case 0x15bb: // (blue)
5760 element = EL_CHAR_S;
5763 case 0x15bc: // (blue)
5764 element = EL_CHAR_T;
5767 case 0x15bd: // (blue)
5768 element = EL_CHAR_U;
5771 case 0x15be: // (blue)
5772 element = EL_CHAR_V;
5775 case 0x15bf: // (blue)
5776 element = EL_CHAR_W;
5779 case 0x15c0: // (blue)
5780 element = EL_CHAR_X;
5783 case 0x15c1: // (blue)
5784 element = EL_CHAR_Y;
5787 case 0x15c2: // (blue)
5788 element = EL_CHAR_Z;
5791 case 0x15c3: // (blue)
5792 element = EL_CHAR_AUMLAUT;
5795 case 0x15c4: // (blue)
5796 element = EL_CHAR_OUMLAUT;
5799 case 0x15c5: // (blue)
5800 element = EL_CHAR_UUMLAUT;
5803 case 0x15c6: // (blue)
5804 element = EL_CHAR_0;
5807 case 0x15c7: // (blue)
5808 element = EL_CHAR_1;
5811 case 0x15c8: // (blue)
5812 element = EL_CHAR_2;
5815 case 0x15c9: // (blue)
5816 element = EL_CHAR_3;
5819 case 0x15ca: // (blue)
5820 element = EL_CHAR_4;
5823 case 0x15cb: // (blue)
5824 element = EL_CHAR_5;
5827 case 0x15cc: // (blue)
5828 element = EL_CHAR_6;
5831 case 0x15cd: // (blue)
5832 element = EL_CHAR_7;
5835 case 0x15ce: // (blue)
5836 element = EL_CHAR_8;
5839 case 0x15cf: // (blue)
5840 element = EL_CHAR_9;
5843 case 0x15d0: // (blue)
5844 element = EL_CHAR_PERIOD;
5847 case 0x15d1: // (blue)
5848 element = EL_CHAR_EXCLAM;
5851 case 0x15d2: // (blue)
5852 element = EL_CHAR_COLON;
5855 case 0x15d3: // (blue)
5856 element = EL_CHAR_LESS;
5859 case 0x15d4: // (blue)
5860 element = EL_CHAR_GREATER;
5863 case 0x15d5: // (blue)
5864 element = EL_CHAR_QUESTION;
5867 case 0x15d6: // (blue)
5868 element = EL_CHAR_COPYRIGHT;
5871 case 0x15d7: // (blue)
5872 element = EL_CHAR_UP;
5875 case 0x15d8: // (blue)
5876 element = EL_CHAR_DOWN;
5879 case 0x15d9: // (blue)
5880 element = EL_CHAR_BUTTON;
5883 case 0x15da: // (blue)
5884 element = EL_CHAR_PLUS;
5887 case 0x15db: // (blue)
5888 element = EL_CHAR_MINUS;
5891 case 0x15dc: // (blue)
5892 element = EL_CHAR_APOSTROPHE;
5895 case 0x15dd: // (blue)
5896 element = EL_CHAR_PARENLEFT;
5899 case 0x15de: // (blue)
5900 element = EL_CHAR_PARENRIGHT;
5903 case 0x15df: // (green)
5904 element = EL_CHAR_A;
5907 case 0x15e0: // (green)
5908 element = EL_CHAR_B;
5911 case 0x15e1: // (green)
5912 element = EL_CHAR_C;
5915 case 0x15e2: // (green)
5916 element = EL_CHAR_D;
5919 case 0x15e3: // (green)
5920 element = EL_CHAR_E;
5923 case 0x15e4: // (green)
5924 element = EL_CHAR_F;
5927 case 0x15e5: // (green)
5928 element = EL_CHAR_G;
5931 case 0x15e6: // (green)
5932 element = EL_CHAR_H;
5935 case 0x15e7: // (green)
5936 element = EL_CHAR_I;
5939 case 0x15e8: // (green)
5940 element = EL_CHAR_J;
5943 case 0x15e9: // (green)
5944 element = EL_CHAR_K;
5947 case 0x15ea: // (green)
5948 element = EL_CHAR_L;
5951 case 0x15eb: // (green)
5952 element = EL_CHAR_M;
5955 case 0x15ec: // (green)
5956 element = EL_CHAR_N;
5959 case 0x15ed: // (green)
5960 element = EL_CHAR_O;
5963 case 0x15ee: // (green)
5964 element = EL_CHAR_P;
5967 case 0x15ef: // (green)
5968 element = EL_CHAR_Q;
5971 case 0x15f0: // (green)
5972 element = EL_CHAR_R;
5975 case 0x15f1: // (green)
5976 element = EL_CHAR_S;
5979 case 0x15f2: // (green)
5980 element = EL_CHAR_T;
5983 case 0x15f3: // (green)
5984 element = EL_CHAR_U;
5987 case 0x15f4: // (green)
5988 element = EL_CHAR_V;
5991 case 0x15f5: // (green)
5992 element = EL_CHAR_W;
5995 case 0x15f6: // (green)
5996 element = EL_CHAR_X;
5999 case 0x15f7: // (green)
6000 element = EL_CHAR_Y;
6003 case 0x15f8: // (green)
6004 element = EL_CHAR_Z;
6007 case 0x15f9: // (green)
6008 element = EL_CHAR_AUMLAUT;
6011 case 0x15fa: // (green)
6012 element = EL_CHAR_OUMLAUT;
6015 case 0x15fb: // (green)
6016 element = EL_CHAR_UUMLAUT;
6019 case 0x15fc: // (green)
6020 element = EL_CHAR_0;
6023 case 0x15fd: // (green)
6024 element = EL_CHAR_1;
6027 case 0x15fe: // (green)
6028 element = EL_CHAR_2;
6031 case 0x15ff: // (green)
6032 element = EL_CHAR_3;
6035 case 0x1600: // (green)
6036 element = EL_CHAR_4;
6039 case 0x1601: // (green)
6040 element = EL_CHAR_5;
6043 case 0x1602: // (green)
6044 element = EL_CHAR_6;
6047 case 0x1603: // (green)
6048 element = EL_CHAR_7;
6051 case 0x1604: // (green)
6052 element = EL_CHAR_8;
6055 case 0x1605: // (green)
6056 element = EL_CHAR_9;
6059 case 0x1606: // (green)
6060 element = EL_CHAR_PERIOD;
6063 case 0x1607: // (green)
6064 element = EL_CHAR_EXCLAM;
6067 case 0x1608: // (green)
6068 element = EL_CHAR_COLON;
6071 case 0x1609: // (green)
6072 element = EL_CHAR_LESS;
6075 case 0x160a: // (green)
6076 element = EL_CHAR_GREATER;
6079 case 0x160b: // (green)
6080 element = EL_CHAR_QUESTION;
6083 case 0x160c: // (green)
6084 element = EL_CHAR_COPYRIGHT;
6087 case 0x160d: // (green)
6088 element = EL_CHAR_UP;
6091 case 0x160e: // (green)
6092 element = EL_CHAR_DOWN;
6095 case 0x160f: // (green)
6096 element = EL_CHAR_BUTTON;
6099 case 0x1610: // (green)
6100 element = EL_CHAR_PLUS;
6103 case 0x1611: // (green)
6104 element = EL_CHAR_MINUS;
6107 case 0x1612: // (green)
6108 element = EL_CHAR_APOSTROPHE;
6111 case 0x1613: // (green)
6112 element = EL_CHAR_PARENLEFT;
6115 case 0x1614: // (green)
6116 element = EL_CHAR_PARENRIGHT;
6119 case 0x1615: // (blue steel)
6120 element = EL_STEEL_CHAR_A;
6123 case 0x1616: // (blue steel)
6124 element = EL_STEEL_CHAR_B;
6127 case 0x1617: // (blue steel)
6128 element = EL_STEEL_CHAR_C;
6131 case 0x1618: // (blue steel)
6132 element = EL_STEEL_CHAR_D;
6135 case 0x1619: // (blue steel)
6136 element = EL_STEEL_CHAR_E;
6139 case 0x161a: // (blue steel)
6140 element = EL_STEEL_CHAR_F;
6143 case 0x161b: // (blue steel)
6144 element = EL_STEEL_CHAR_G;
6147 case 0x161c: // (blue steel)
6148 element = EL_STEEL_CHAR_H;
6151 case 0x161d: // (blue steel)
6152 element = EL_STEEL_CHAR_I;
6155 case 0x161e: // (blue steel)
6156 element = EL_STEEL_CHAR_J;
6159 case 0x161f: // (blue steel)
6160 element = EL_STEEL_CHAR_K;
6163 case 0x1620: // (blue steel)
6164 element = EL_STEEL_CHAR_L;
6167 case 0x1621: // (blue steel)
6168 element = EL_STEEL_CHAR_M;
6171 case 0x1622: // (blue steel)
6172 element = EL_STEEL_CHAR_N;
6175 case 0x1623: // (blue steel)
6176 element = EL_STEEL_CHAR_O;
6179 case 0x1624: // (blue steel)
6180 element = EL_STEEL_CHAR_P;
6183 case 0x1625: // (blue steel)
6184 element = EL_STEEL_CHAR_Q;
6187 case 0x1626: // (blue steel)
6188 element = EL_STEEL_CHAR_R;
6191 case 0x1627: // (blue steel)
6192 element = EL_STEEL_CHAR_S;
6195 case 0x1628: // (blue steel)
6196 element = EL_STEEL_CHAR_T;
6199 case 0x1629: // (blue steel)
6200 element = EL_STEEL_CHAR_U;
6203 case 0x162a: // (blue steel)
6204 element = EL_STEEL_CHAR_V;
6207 case 0x162b: // (blue steel)
6208 element = EL_STEEL_CHAR_W;
6211 case 0x162c: // (blue steel)
6212 element = EL_STEEL_CHAR_X;
6215 case 0x162d: // (blue steel)
6216 element = EL_STEEL_CHAR_Y;
6219 case 0x162e: // (blue steel)
6220 element = EL_STEEL_CHAR_Z;
6223 case 0x162f: // (blue steel)
6224 element = EL_STEEL_CHAR_AUMLAUT;
6227 case 0x1630: // (blue steel)
6228 element = EL_STEEL_CHAR_OUMLAUT;
6231 case 0x1631: // (blue steel)
6232 element = EL_STEEL_CHAR_UUMLAUT;
6235 case 0x1632: // (blue steel)
6236 element = EL_STEEL_CHAR_0;
6239 case 0x1633: // (blue steel)
6240 element = EL_STEEL_CHAR_1;
6243 case 0x1634: // (blue steel)
6244 element = EL_STEEL_CHAR_2;
6247 case 0x1635: // (blue steel)
6248 element = EL_STEEL_CHAR_3;
6251 case 0x1636: // (blue steel)
6252 element = EL_STEEL_CHAR_4;
6255 case 0x1637: // (blue steel)
6256 element = EL_STEEL_CHAR_5;
6259 case 0x1638: // (blue steel)
6260 element = EL_STEEL_CHAR_6;
6263 case 0x1639: // (blue steel)
6264 element = EL_STEEL_CHAR_7;
6267 case 0x163a: // (blue steel)
6268 element = EL_STEEL_CHAR_8;
6271 case 0x163b: // (blue steel)
6272 element = EL_STEEL_CHAR_9;
6275 case 0x163c: // (blue steel)
6276 element = EL_STEEL_CHAR_PERIOD;
6279 case 0x163d: // (blue steel)
6280 element = EL_STEEL_CHAR_EXCLAM;
6283 case 0x163e: // (blue steel)
6284 element = EL_STEEL_CHAR_COLON;
6287 case 0x163f: // (blue steel)
6288 element = EL_STEEL_CHAR_LESS;
6291 case 0x1640: // (blue steel)
6292 element = EL_STEEL_CHAR_GREATER;
6295 case 0x1641: // (blue steel)
6296 element = EL_STEEL_CHAR_QUESTION;
6299 case 0x1642: // (blue steel)
6300 element = EL_STEEL_CHAR_COPYRIGHT;
6303 case 0x1643: // (blue steel)
6304 element = EL_STEEL_CHAR_UP;
6307 case 0x1644: // (blue steel)
6308 element = EL_STEEL_CHAR_DOWN;
6311 case 0x1645: // (blue steel)
6312 element = EL_STEEL_CHAR_BUTTON;
6315 case 0x1646: // (blue steel)
6316 element = EL_STEEL_CHAR_PLUS;
6319 case 0x1647: // (blue steel)
6320 element = EL_STEEL_CHAR_MINUS;
6323 case 0x1648: // (blue steel)
6324 element = EL_STEEL_CHAR_APOSTROPHE;
6327 case 0x1649: // (blue steel)
6328 element = EL_STEEL_CHAR_PARENLEFT;
6331 case 0x164a: // (blue steel)
6332 element = EL_STEEL_CHAR_PARENRIGHT;
6335 case 0x164b: // (green steel)
6336 element = EL_STEEL_CHAR_A;
6339 case 0x164c: // (green steel)
6340 element = EL_STEEL_CHAR_B;
6343 case 0x164d: // (green steel)
6344 element = EL_STEEL_CHAR_C;
6347 case 0x164e: // (green steel)
6348 element = EL_STEEL_CHAR_D;
6351 case 0x164f: // (green steel)
6352 element = EL_STEEL_CHAR_E;
6355 case 0x1650: // (green steel)
6356 element = EL_STEEL_CHAR_F;
6359 case 0x1651: // (green steel)
6360 element = EL_STEEL_CHAR_G;
6363 case 0x1652: // (green steel)
6364 element = EL_STEEL_CHAR_H;
6367 case 0x1653: // (green steel)
6368 element = EL_STEEL_CHAR_I;
6371 case 0x1654: // (green steel)
6372 element = EL_STEEL_CHAR_J;
6375 case 0x1655: // (green steel)
6376 element = EL_STEEL_CHAR_K;
6379 case 0x1656: // (green steel)
6380 element = EL_STEEL_CHAR_L;
6383 case 0x1657: // (green steel)
6384 element = EL_STEEL_CHAR_M;
6387 case 0x1658: // (green steel)
6388 element = EL_STEEL_CHAR_N;
6391 case 0x1659: // (green steel)
6392 element = EL_STEEL_CHAR_O;
6395 case 0x165a: // (green steel)
6396 element = EL_STEEL_CHAR_P;
6399 case 0x165b: // (green steel)
6400 element = EL_STEEL_CHAR_Q;
6403 case 0x165c: // (green steel)
6404 element = EL_STEEL_CHAR_R;
6407 case 0x165d: // (green steel)
6408 element = EL_STEEL_CHAR_S;
6411 case 0x165e: // (green steel)
6412 element = EL_STEEL_CHAR_T;
6415 case 0x165f: // (green steel)
6416 element = EL_STEEL_CHAR_U;
6419 case 0x1660: // (green steel)
6420 element = EL_STEEL_CHAR_V;
6423 case 0x1661: // (green steel)
6424 element = EL_STEEL_CHAR_W;
6427 case 0x1662: // (green steel)
6428 element = EL_STEEL_CHAR_X;
6431 case 0x1663: // (green steel)
6432 element = EL_STEEL_CHAR_Y;
6435 case 0x1664: // (green steel)
6436 element = EL_STEEL_CHAR_Z;
6439 case 0x1665: // (green steel)
6440 element = EL_STEEL_CHAR_AUMLAUT;
6443 case 0x1666: // (green steel)
6444 element = EL_STEEL_CHAR_OUMLAUT;
6447 case 0x1667: // (green steel)
6448 element = EL_STEEL_CHAR_UUMLAUT;
6451 case 0x1668: // (green steel)
6452 element = EL_STEEL_CHAR_0;
6455 case 0x1669: // (green steel)
6456 element = EL_STEEL_CHAR_1;
6459 case 0x166a: // (green steel)
6460 element = EL_STEEL_CHAR_2;
6463 case 0x166b: // (green steel)
6464 element = EL_STEEL_CHAR_3;
6467 case 0x166c: // (green steel)
6468 element = EL_STEEL_CHAR_4;
6471 case 0x166d: // (green steel)
6472 element = EL_STEEL_CHAR_5;
6475 case 0x166e: // (green steel)
6476 element = EL_STEEL_CHAR_6;
6479 case 0x166f: // (green steel)
6480 element = EL_STEEL_CHAR_7;
6483 case 0x1670: // (green steel)
6484 element = EL_STEEL_CHAR_8;
6487 case 0x1671: // (green steel)
6488 element = EL_STEEL_CHAR_9;
6491 case 0x1672: // (green steel)
6492 element = EL_STEEL_CHAR_PERIOD;
6495 case 0x1673: // (green steel)
6496 element = EL_STEEL_CHAR_EXCLAM;
6499 case 0x1674: // (green steel)
6500 element = EL_STEEL_CHAR_COLON;
6503 case 0x1675: // (green steel)
6504 element = EL_STEEL_CHAR_LESS;
6507 case 0x1676: // (green steel)
6508 element = EL_STEEL_CHAR_GREATER;
6511 case 0x1677: // (green steel)
6512 element = EL_STEEL_CHAR_QUESTION;
6515 case 0x1678: // (green steel)
6516 element = EL_STEEL_CHAR_COPYRIGHT;
6519 case 0x1679: // (green steel)
6520 element = EL_STEEL_CHAR_UP;
6523 case 0x167a: // (green steel)
6524 element = EL_STEEL_CHAR_DOWN;
6527 case 0x167b: // (green steel)
6528 element = EL_STEEL_CHAR_BUTTON;
6531 case 0x167c: // (green steel)
6532 element = EL_STEEL_CHAR_PLUS;
6535 case 0x167d: // (green steel)
6536 element = EL_STEEL_CHAR_MINUS;
6539 case 0x167e: // (green steel)
6540 element = EL_STEEL_CHAR_APOSTROPHE;
6543 case 0x167f: // (green steel)
6544 element = EL_STEEL_CHAR_PARENLEFT;
6547 case 0x1680: // (green steel)
6548 element = EL_STEEL_CHAR_PARENRIGHT;
6551 case 0x1681: // gate (red)
6552 element = EL_EM_GATE_1;
6555 case 0x1682: // secret gate (red)
6556 element = EL_EM_GATE_1_GRAY;
6559 case 0x1683: // gate (yellow)
6560 element = EL_EM_GATE_2;
6563 case 0x1684: // secret gate (yellow)
6564 element = EL_EM_GATE_2_GRAY;
6567 case 0x1685: // gate (blue)
6568 element = EL_EM_GATE_4;
6571 case 0x1686: // secret gate (blue)
6572 element = EL_EM_GATE_4_GRAY;
6575 case 0x1687: // gate (green)
6576 element = EL_EM_GATE_3;
6579 case 0x1688: // secret gate (green)
6580 element = EL_EM_GATE_3_GRAY;
6583 case 0x1689: // gate (white)
6584 element = EL_DC_GATE_WHITE;
6587 case 0x168a: // secret gate (white)
6588 element = EL_DC_GATE_WHITE_GRAY;
6591 case 0x168b: // secret gate (no key)
6592 element = EL_DC_GATE_FAKE_GRAY;
6596 element = EL_ROBOT_WHEEL;
6600 element = EL_DC_TIMEGATE_SWITCH;
6604 element = EL_ACID_POOL_BOTTOM;
6608 element = EL_ACID_POOL_TOPLEFT;
6612 element = EL_ACID_POOL_TOPRIGHT;
6616 element = EL_ACID_POOL_BOTTOMLEFT;
6620 element = EL_ACID_POOL_BOTTOMRIGHT;
6624 element = EL_STEELWALL;
6628 element = EL_STEELWALL_SLIPPERY;
6631 case 0x1695: // steel wall (not round)
6632 element = EL_STEELWALL;
6635 case 0x1696: // steel wall (left)
6636 element = EL_DC_STEELWALL_1_LEFT;
6639 case 0x1697: // steel wall (bottom)
6640 element = EL_DC_STEELWALL_1_BOTTOM;
6643 case 0x1698: // steel wall (right)
6644 element = EL_DC_STEELWALL_1_RIGHT;
6647 case 0x1699: // steel wall (top)
6648 element = EL_DC_STEELWALL_1_TOP;
6651 case 0x169a: // steel wall (left/bottom)
6652 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6655 case 0x169b: // steel wall (right/bottom)
6656 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6659 case 0x169c: // steel wall (right/top)
6660 element = EL_DC_STEELWALL_1_TOPRIGHT;
6663 case 0x169d: // steel wall (left/top)
6664 element = EL_DC_STEELWALL_1_TOPLEFT;
6667 case 0x169e: // steel wall (right/bottom small)
6668 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6671 case 0x169f: // steel wall (left/bottom small)
6672 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6675 case 0x16a0: // steel wall (right/top small)
6676 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6679 case 0x16a1: // steel wall (left/top small)
6680 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6683 case 0x16a2: // steel wall (left/right)
6684 element = EL_DC_STEELWALL_1_VERTICAL;
6687 case 0x16a3: // steel wall (top/bottom)
6688 element = EL_DC_STEELWALL_1_HORIZONTAL;
6691 case 0x16a4: // steel wall 2 (left end)
6692 element = EL_DC_STEELWALL_2_LEFT;
6695 case 0x16a5: // steel wall 2 (right end)
6696 element = EL_DC_STEELWALL_2_RIGHT;
6699 case 0x16a6: // steel wall 2 (top end)
6700 element = EL_DC_STEELWALL_2_TOP;
6703 case 0x16a7: // steel wall 2 (bottom end)
6704 element = EL_DC_STEELWALL_2_BOTTOM;
6707 case 0x16a8: // steel wall 2 (left/right)
6708 element = EL_DC_STEELWALL_2_HORIZONTAL;
6711 case 0x16a9: // steel wall 2 (up/down)
6712 element = EL_DC_STEELWALL_2_VERTICAL;
6715 case 0x16aa: // steel wall 2 (mid)
6716 element = EL_DC_STEELWALL_2_MIDDLE;
6720 element = EL_SIGN_EXCLAMATION;
6724 element = EL_SIGN_RADIOACTIVITY;
6728 element = EL_SIGN_STOP;
6732 element = EL_SIGN_WHEELCHAIR;
6736 element = EL_SIGN_PARKING;
6740 element = EL_SIGN_NO_ENTRY;
6744 element = EL_SIGN_HEART;
6748 element = EL_SIGN_GIVE_WAY;
6752 element = EL_SIGN_ENTRY_FORBIDDEN;
6756 element = EL_SIGN_EMERGENCY_EXIT;
6760 element = EL_SIGN_YIN_YANG;
6764 element = EL_WALL_EMERALD;
6768 element = EL_WALL_DIAMOND;
6772 element = EL_WALL_PEARL;
6776 element = EL_WALL_CRYSTAL;
6780 element = EL_INVISIBLE_WALL;
6784 element = EL_INVISIBLE_STEELWALL;
6788 // EL_INVISIBLE_SAND
6791 element = EL_LIGHT_SWITCH;
6795 element = EL_ENVELOPE_1;
6799 if (element >= 0x0117 && element <= 0x036e) // (?)
6800 element = EL_DIAMOND;
6801 else if (element >= 0x042d && element <= 0x0684) // (?)
6802 element = EL_EMERALD;
6803 else if (element >= 0x157c && element <= 0x158b)
6805 else if (element >= 0x1590 && element <= 0x159f)
6806 element = EL_DC_LANDMINE;
6807 else if (element >= 0x16bc && element <= 0x16cb)
6808 element = EL_INVISIBLE_SAND;
6811 Warn("unknown Diamond Caves element 0x%04x", element);
6813 element = EL_UNKNOWN;
6818 return getMappedElement(element);
6821 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6823 byte header[DC_LEVEL_HEADER_SIZE];
6825 int envelope_header_pos = 62;
6826 int envelope_content_pos = 94;
6827 int level_name_pos = 251;
6828 int level_author_pos = 292;
6829 int envelope_header_len;
6830 int envelope_content_len;
6832 int level_author_len;
6834 int num_yamyam_contents;
6837 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6839 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6841 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6843 header[i * 2 + 0] = header_word >> 8;
6844 header[i * 2 + 1] = header_word & 0xff;
6847 // read some values from level header to check level decoding integrity
6848 fieldx = header[6] | (header[7] << 8);
6849 fieldy = header[8] | (header[9] << 8);
6850 num_yamyam_contents = header[60] | (header[61] << 8);
6852 // do some simple sanity checks to ensure that level was correctly decoded
6853 if (fieldx < 1 || fieldx > 256 ||
6854 fieldy < 1 || fieldy > 256 ||
6855 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6857 level->no_valid_file = TRUE;
6859 Warn("cannot decode level from stream -- using empty level");
6864 // maximum envelope header size is 31 bytes
6865 envelope_header_len = header[envelope_header_pos];
6866 // maximum envelope content size is 110 (156?) bytes
6867 envelope_content_len = header[envelope_content_pos];
6869 // maximum level title size is 40 bytes
6870 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6871 // maximum level author size is 30 (51?) bytes
6872 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6876 for (i = 0; i < envelope_header_len; i++)
6877 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6878 level->envelope[0].text[envelope_size++] =
6879 header[envelope_header_pos + 1 + i];
6881 if (envelope_header_len > 0 && envelope_content_len > 0)
6883 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6884 level->envelope[0].text[envelope_size++] = '\n';
6885 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6886 level->envelope[0].text[envelope_size++] = '\n';
6889 for (i = 0; i < envelope_content_len; i++)
6890 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6891 level->envelope[0].text[envelope_size++] =
6892 header[envelope_content_pos + 1 + i];
6894 level->envelope[0].text[envelope_size] = '\0';
6896 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6897 level->envelope[0].ysize = 10;
6898 level->envelope[0].autowrap = TRUE;
6899 level->envelope[0].centered = TRUE;
6901 for (i = 0; i < level_name_len; i++)
6902 level->name[i] = header[level_name_pos + 1 + i];
6903 level->name[level_name_len] = '\0';
6905 for (i = 0; i < level_author_len; i++)
6906 level->author[i] = header[level_author_pos + 1 + i];
6907 level->author[level_author_len] = '\0';
6909 num_yamyam_contents = header[60] | (header[61] << 8);
6910 level->num_yamyam_contents =
6911 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6913 for (i = 0; i < num_yamyam_contents; i++)
6915 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6917 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6918 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6920 if (i < MAX_ELEMENT_CONTENTS)
6921 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6925 fieldx = header[6] | (header[7] << 8);
6926 fieldy = header[8] | (header[9] << 8);
6927 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6928 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6930 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6932 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6933 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6935 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6936 level->field[x][y] = getMappedElement_DC(element_dc);
6939 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6940 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6941 level->field[x][y] = EL_PLAYER_1;
6943 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6944 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6945 level->field[x][y] = EL_PLAYER_2;
6947 level->gems_needed = header[18] | (header[19] << 8);
6949 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6950 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6951 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6952 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6953 level->score[SC_NUT] = header[28] | (header[29] << 8);
6954 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6955 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6956 level->score[SC_BUG] = header[34] | (header[35] << 8);
6957 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6958 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6959 level->score[SC_KEY] = header[40] | (header[41] << 8);
6960 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6962 level->time = header[44] | (header[45] << 8);
6964 level->amoeba_speed = header[46] | (header[47] << 8);
6965 level->time_light = header[48] | (header[49] << 8);
6966 level->time_timegate = header[50] | (header[51] << 8);
6967 level->time_wheel = header[52] | (header[53] << 8);
6968 level->time_magic_wall = header[54] | (header[55] << 8);
6969 level->extra_time = header[56] | (header[57] << 8);
6970 level->shield_normal_time = header[58] | (header[59] << 8);
6972 // shield and extra time elements do not have a score
6973 level->score[SC_SHIELD] = 0;
6974 level->extra_time_score = 0;
6976 // set time for normal and deadly shields to the same value
6977 level->shield_deadly_time = level->shield_normal_time;
6979 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6980 // can slip down from flat walls, like normal walls and steel walls
6981 level->em_slippery_gems = TRUE;
6983 // time score is counted for each 10 seconds left in Diamond Caves levels
6984 level->time_score_base = 10;
6987 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6988 struct LevelFileInfo *level_file_info,
6989 boolean level_info_only)
6991 char *filename = level_file_info->filename;
6993 int num_magic_bytes = 8;
6994 char magic_bytes[num_magic_bytes + 1];
6995 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6997 if (!(file = openFile(filename, MODE_READ)))
6999 level->no_valid_file = TRUE;
7001 if (!level_info_only)
7002 Warn("cannot read level '%s' -- using empty level", filename);
7007 // fseek(file, 0x0000, SEEK_SET);
7009 if (level_file_info->packed)
7011 // read "magic bytes" from start of file
7012 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
7013 magic_bytes[0] = '\0';
7015 // check "magic bytes" for correct file format
7016 if (!strPrefix(magic_bytes, "DC2"))
7018 level->no_valid_file = TRUE;
7020 Warn("unknown DC level file '%s' -- using empty level", filename);
7025 if (strPrefix(magic_bytes, "DC2Win95") ||
7026 strPrefix(magic_bytes, "DC2Win98"))
7028 int position_first_level = 0x00fa;
7029 int extra_bytes = 4;
7032 // advance file stream to first level inside the level package
7033 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7035 // each block of level data is followed by block of non-level data
7036 num_levels_to_skip *= 2;
7038 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
7039 while (num_levels_to_skip >= 0)
7041 // advance file stream to next level inside the level package
7042 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7044 level->no_valid_file = TRUE;
7046 Warn("cannot fseek in file '%s' -- using empty level", filename);
7051 // skip apparently unused extra bytes following each level
7052 ReadUnusedBytesFromFile(file, extra_bytes);
7054 // read size of next level in level package
7055 skip_bytes = getFile32BitLE(file);
7057 num_levels_to_skip--;
7062 level->no_valid_file = TRUE;
7064 Warn("unknown DC2 level file '%s' -- using empty level", filename);
7070 LoadLevelFromFileStream_DC(file, level);
7076 // ----------------------------------------------------------------------------
7077 // functions for loading SB level
7078 // ----------------------------------------------------------------------------
7080 int getMappedElement_SB(int element_ascii, boolean use_ces)
7088 sb_element_mapping[] =
7090 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
7091 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
7092 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
7093 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
7094 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
7095 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
7096 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
7097 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
7104 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7105 if (element_ascii == sb_element_mapping[i].ascii)
7106 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7108 return EL_UNDEFINED;
7111 static void SetLevelSettings_SB(struct LevelInfo *level)
7115 level->use_step_counter = TRUE;
7118 level->score[SC_TIME_BONUS] = 0;
7119 level->time_score_base = 1;
7120 level->rate_time_over_score = TRUE;
7123 level->auto_exit_sokoban = TRUE;
7126 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7127 struct LevelFileInfo *level_file_info,
7128 boolean level_info_only)
7130 char *filename = level_file_info->filename;
7131 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7132 char last_comment[MAX_LINE_LEN];
7133 char level_name[MAX_LINE_LEN];
7136 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7137 boolean read_continued_line = FALSE;
7138 boolean reading_playfield = FALSE;
7139 boolean got_valid_playfield_line = FALSE;
7140 boolean invalid_playfield_char = FALSE;
7141 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7142 int file_level_nr = 0;
7143 int x = 0, y = 0; // initialized to make compilers happy
7145 last_comment[0] = '\0';
7146 level_name[0] = '\0';
7148 if (!(file = openFile(filename, MODE_READ)))
7150 level->no_valid_file = TRUE;
7152 if (!level_info_only)
7153 Warn("cannot read level '%s' -- using empty level", filename);
7158 while (!checkEndOfFile(file))
7160 // level successfully read, but next level may follow here
7161 if (!got_valid_playfield_line && reading_playfield)
7163 // read playfield from single level file -- skip remaining file
7164 if (!level_file_info->packed)
7167 if (file_level_nr >= num_levels_to_skip)
7172 last_comment[0] = '\0';
7173 level_name[0] = '\0';
7175 reading_playfield = FALSE;
7178 got_valid_playfield_line = FALSE;
7180 // read next line of input file
7181 if (!getStringFromFile(file, line, MAX_LINE_LEN))
7184 // cut trailing line break (this can be newline and/or carriage return)
7185 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7186 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7189 // copy raw input line for later use (mainly debugging output)
7190 strcpy(line_raw, line);
7192 if (read_continued_line)
7194 // append new line to existing line, if there is enough space
7195 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7196 strcat(previous_line, line_ptr);
7198 strcpy(line, previous_line); // copy storage buffer to line
7200 read_continued_line = FALSE;
7203 // if the last character is '\', continue at next line
7204 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7206 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
7207 strcpy(previous_line, line); // copy line to storage buffer
7209 read_continued_line = TRUE;
7215 if (line[0] == '\0')
7218 // extract comment text from comment line
7221 for (line_ptr = line; *line_ptr; line_ptr++)
7222 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7225 strcpy(last_comment, line_ptr);
7230 // extract level title text from line containing level title
7231 if (line[0] == '\'')
7233 strcpy(level_name, &line[1]);
7235 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7236 level_name[strlen(level_name) - 1] = '\0';
7241 // skip lines containing only spaces (or empty lines)
7242 for (line_ptr = line; *line_ptr; line_ptr++)
7243 if (*line_ptr != ' ')
7245 if (*line_ptr == '\0')
7248 // at this point, we have found a line containing part of a playfield
7250 got_valid_playfield_line = TRUE;
7252 if (!reading_playfield)
7254 reading_playfield = TRUE;
7255 invalid_playfield_char = FALSE;
7257 for (x = 0; x < MAX_LEV_FIELDX; x++)
7258 for (y = 0; y < MAX_LEV_FIELDY; y++)
7259 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7264 // start with topmost tile row
7268 // skip playfield line if larger row than allowed
7269 if (y >= MAX_LEV_FIELDY)
7272 // start with leftmost tile column
7275 // read playfield elements from line
7276 for (line_ptr = line; *line_ptr; line_ptr++)
7278 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7280 // stop parsing playfield line if larger column than allowed
7281 if (x >= MAX_LEV_FIELDX)
7284 if (mapped_sb_element == EL_UNDEFINED)
7286 invalid_playfield_char = TRUE;
7291 level->field[x][y] = mapped_sb_element;
7293 // continue with next tile column
7296 level->fieldx = MAX(x, level->fieldx);
7299 if (invalid_playfield_char)
7301 // if first playfield line, treat invalid lines as comment lines
7303 reading_playfield = FALSE;
7308 // continue with next tile row
7316 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7317 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7319 if (!reading_playfield)
7321 level->no_valid_file = TRUE;
7323 Warn("cannot read level '%s' -- using empty level", filename);
7328 if (*level_name != '\0')
7330 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7331 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7333 else if (*last_comment != '\0')
7335 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7336 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7340 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7343 // set all empty fields beyond the border walls to invisible steel wall
7344 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7346 if ((x == 0 || x == level->fieldx - 1 ||
7347 y == 0 || y == level->fieldy - 1) &&
7348 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7349 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7350 level->field, level->fieldx, level->fieldy);
7353 // set special level settings for Sokoban levels
7354 SetLevelSettings_SB(level);
7356 if (load_xsb_to_ces)
7358 // special global settings can now be set in level template
7359 level->use_custom_template = TRUE;
7364 // -------------------------------------------------------------------------
7365 // functions for handling native levels
7366 // -------------------------------------------------------------------------
7368 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7369 struct LevelFileInfo *level_file_info,
7370 boolean level_info_only)
7374 // determine position of requested level inside level package
7375 if (level_file_info->packed)
7376 pos = level_file_info->nr - leveldir_current->first_level;
7378 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7379 level->no_valid_file = TRUE;
7382 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7383 struct LevelFileInfo *level_file_info,
7384 boolean level_info_only)
7386 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7387 level->no_valid_file = TRUE;
7390 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7391 struct LevelFileInfo *level_file_info,
7392 boolean level_info_only)
7396 // determine position of requested level inside level package
7397 if (level_file_info->packed)
7398 pos = level_file_info->nr - leveldir_current->first_level;
7400 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7401 level->no_valid_file = TRUE;
7404 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7405 struct LevelFileInfo *level_file_info,
7406 boolean level_info_only)
7408 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7409 level->no_valid_file = TRUE;
7412 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7414 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7415 CopyNativeLevel_RND_to_BD(level);
7416 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7417 CopyNativeLevel_RND_to_EM(level);
7418 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7419 CopyNativeLevel_RND_to_SP(level);
7420 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7421 CopyNativeLevel_RND_to_MM(level);
7424 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7426 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7427 CopyNativeLevel_BD_to_RND(level);
7428 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7429 CopyNativeLevel_EM_to_RND(level);
7430 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7431 CopyNativeLevel_SP_to_RND(level);
7432 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7433 CopyNativeLevel_MM_to_RND(level);
7436 void SaveNativeLevel(struct LevelInfo *level)
7438 // saving native level files only supported for some game engines
7439 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7440 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7443 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7444 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7445 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7446 char *filename = getLevelFilenameFromBasename(basename);
7448 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7451 boolean success = FALSE;
7453 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7455 CopyNativeLevel_RND_to_BD(level);
7456 // CopyNativeTape_RND_to_BD(level);
7458 success = SaveNativeLevel_BD(filename);
7460 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7462 CopyNativeLevel_RND_to_SP(level);
7463 CopyNativeTape_RND_to_SP(level);
7465 success = SaveNativeLevel_SP(filename);
7469 Request("Native level file saved!", REQ_CONFIRM);
7471 Request("Failed to save native level file!", REQ_CONFIRM);
7475 // ----------------------------------------------------------------------------
7476 // functions for loading generic level
7477 // ----------------------------------------------------------------------------
7479 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7480 struct LevelFileInfo *level_file_info,
7481 boolean level_info_only)
7483 // always start with reliable default values
7484 setLevelInfoToDefaults(level, level_info_only, TRUE);
7486 switch (level_file_info->type)
7488 case LEVEL_FILE_TYPE_RND:
7489 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7492 case LEVEL_FILE_TYPE_BD:
7493 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7494 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7497 case LEVEL_FILE_TYPE_EM:
7498 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7499 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7502 case LEVEL_FILE_TYPE_SP:
7503 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7504 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7507 case LEVEL_FILE_TYPE_MM:
7508 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7509 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7512 case LEVEL_FILE_TYPE_DC:
7513 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7516 case LEVEL_FILE_TYPE_SB:
7517 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7521 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7525 // if level file is invalid, restore level structure to default values
7526 if (level->no_valid_file)
7527 setLevelInfoToDefaults(level, level_info_only, FALSE);
7529 if (check_special_flags("use_native_bd_game_engine"))
7530 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7532 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7533 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7535 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7536 CopyNativeLevel_Native_to_RND(level);
7539 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7541 static struct LevelFileInfo level_file_info;
7543 // always start with reliable default values
7544 setFileInfoToDefaults(&level_file_info);
7546 level_file_info.nr = 0; // unknown level number
7547 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7549 setString(&level_file_info.filename, filename);
7551 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7554 static void LoadLevel_InitVersion(struct LevelInfo *level)
7558 if (leveldir_current == NULL) // only when dumping level
7561 // all engine modifications also valid for levels which use latest engine
7562 if (level->game_version < VERSION_IDENT(3,2,0,5))
7564 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7565 level->time_score_base = 10;
7568 if (leveldir_current->latest_engine)
7570 // ---------- use latest game engine --------------------------------------
7572 /* For all levels which are forced to use the latest game engine version
7573 (normally all but user contributed, private and undefined levels), set
7574 the game engine version to the actual version; this allows for actual
7575 corrections in the game engine to take effect for existing, converted
7576 levels (from "classic" or other existing games) to make the emulation
7577 of the corresponding game more accurate, while (hopefully) not breaking
7578 existing levels created from other players. */
7580 level->game_version = GAME_VERSION_ACTUAL;
7582 /* Set special EM style gems behaviour: EM style gems slip down from
7583 normal, steel and growing wall. As this is a more fundamental change,
7584 it seems better to set the default behaviour to "off" (as it is more
7585 natural) and make it configurable in the level editor (as a property
7586 of gem style elements). Already existing converted levels (neither
7587 private nor contributed levels) are changed to the new behaviour. */
7589 if (level->file_version < FILE_VERSION_2_0)
7590 level->em_slippery_gems = TRUE;
7595 // ---------- use game engine the level was created with --------------------
7597 /* For all levels which are not forced to use the latest game engine
7598 version (normally user contributed, private and undefined levels),
7599 use the version of the game engine the levels were created for.
7601 Since 2.0.1, the game engine version is now directly stored
7602 in the level file (chunk "VERS"), so there is no need anymore
7603 to set the game version from the file version (except for old,
7604 pre-2.0 levels, where the game version is still taken from the
7605 file format version used to store the level -- see above). */
7607 // player was faster than enemies in 1.0.0 and before
7608 if (level->file_version == FILE_VERSION_1_0)
7609 for (i = 0; i < MAX_PLAYERS; i++)
7610 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7612 // default behaviour for EM style gems was "slippery" only in 2.0.1
7613 if (level->game_version == VERSION_IDENT(2,0,1,0))
7614 level->em_slippery_gems = TRUE;
7616 // springs could be pushed over pits before (pre-release version) 2.2.0
7617 if (level->game_version < VERSION_IDENT(2,2,0,0))
7618 level->use_spring_bug = TRUE;
7620 if (level->game_version < VERSION_IDENT(3,2,0,5))
7622 // time orb caused limited time in endless time levels before 3.2.0-5
7623 level->use_time_orb_bug = TRUE;
7625 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7626 level->block_snap_field = FALSE;
7628 // extra time score was same value as time left score before 3.2.0-5
7629 level->extra_time_score = level->score[SC_TIME_BONUS];
7632 if (level->game_version < VERSION_IDENT(3,2,0,7))
7634 // default behaviour for snapping was "not continuous" before 3.2.0-7
7635 level->continuous_snapping = FALSE;
7638 // only few elements were able to actively move into acid before 3.1.0
7639 // trigger settings did not exist before 3.1.0; set to default "any"
7640 if (level->game_version < VERSION_IDENT(3,1,0,0))
7642 // correct "can move into acid" settings (all zero in old levels)
7644 level->can_move_into_acid_bits = 0; // nothing can move into acid
7645 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7647 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7648 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7649 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7650 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7652 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7653 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7655 // correct trigger settings (stored as zero == "none" in old levels)
7657 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7659 int element = EL_CUSTOM_START + i;
7660 struct ElementInfo *ei = &element_info[element];
7662 for (j = 0; j < ei->num_change_pages; j++)
7664 struct ElementChangeInfo *change = &ei->change_page[j];
7666 change->trigger_player = CH_PLAYER_ANY;
7667 change->trigger_page = CH_PAGE_ANY;
7672 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7674 int element = EL_CUSTOM_256;
7675 struct ElementInfo *ei = &element_info[element];
7676 struct ElementChangeInfo *change = &ei->change_page[0];
7678 /* This is needed to fix a problem that was caused by a bugfix in function
7679 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7680 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7681 not replace walkable elements, but instead just placed the player on it,
7682 without placing the Sokoban field under the player). Unfortunately, this
7683 breaks "Snake Bite" style levels when the snake is halfway through a door
7684 that just closes (the snake head is still alive and can be moved in this
7685 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7686 player (without Sokoban element) which then gets killed as designed). */
7688 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7689 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7690 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7691 change->target_element = EL_PLAYER_1;
7694 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7695 if (level->game_version < VERSION_IDENT(3,2,5,0))
7697 /* This is needed to fix a problem that was caused by a bugfix in function
7698 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7699 corrects the behaviour when a custom element changes to another custom
7700 element with a higher element number that has change actions defined.
7701 Normally, only one change per frame is allowed for custom elements.
7702 Therefore, it is checked if a custom element already changed in the
7703 current frame; if it did, subsequent changes are suppressed.
7704 Unfortunately, this is only checked for element changes, but not for
7705 change actions, which are still executed. As the function above loops
7706 through all custom elements from lower to higher, an element change
7707 resulting in a lower CE number won't be checked again, while a target
7708 element with a higher number will also be checked, and potential change
7709 actions will get executed for this CE, too (which is wrong), while
7710 further changes are ignored (which is correct). As this bugfix breaks
7711 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7712 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7713 behaviour for existing levels and tapes that make use of this bug */
7715 level->use_action_after_change_bug = TRUE;
7718 // not centering level after relocating player was default only in 3.2.3
7719 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7720 level->shifted_relocation = TRUE;
7722 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7723 if (level->game_version < VERSION_IDENT(3,2,6,0))
7724 level->em_explodes_by_fire = TRUE;
7726 // levels were solved by the first player entering an exit up to 4.1.0.0
7727 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7728 level->solved_by_one_player = TRUE;
7730 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7731 if (level->game_version < VERSION_IDENT(4,1,1,1))
7732 level->use_life_bugs = TRUE;
7734 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7735 if (level->game_version < VERSION_IDENT(4,1,1,1))
7736 level->sb_objects_needed = FALSE;
7738 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7739 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7740 level->finish_dig_collect = FALSE;
7742 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7743 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7744 level->keep_walkable_ce = TRUE;
7747 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7749 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7752 // check if this level is (not) a Sokoban level
7753 for (y = 0; y < level->fieldy; y++)
7754 for (x = 0; x < level->fieldx; x++)
7755 if (!IS_SB_ELEMENT(Tile[x][y]))
7756 is_sokoban_level = FALSE;
7758 if (is_sokoban_level)
7760 // set special level settings for Sokoban levels
7761 SetLevelSettings_SB(level);
7765 static void LoadLevel_InitSettings(struct LevelInfo *level)
7767 // adjust level settings for (non-native) Sokoban-style levels
7768 LoadLevel_InitSettings_SB(level);
7770 // rename levels with title "nameless level" or if renaming is forced
7771 if (leveldir_current->empty_level_name != NULL &&
7772 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7773 leveldir_current->force_level_name))
7774 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7775 leveldir_current->empty_level_name, level_nr);
7778 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7782 // map elements that have changed in newer versions
7783 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7784 level->game_version);
7785 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7786 for (x = 0; x < 3; x++)
7787 for (y = 0; y < 3; y++)
7788 level->yamyam_content[i].e[x][y] =
7789 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7790 level->game_version);
7794 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7798 // map custom element change events that have changed in newer versions
7799 // (these following values were accidentally changed in version 3.0.1)
7800 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7801 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7803 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7805 int element = EL_CUSTOM_START + i;
7807 // order of checking and copying events to be mapped is important
7808 // (do not change the start and end value -- they are constant)
7809 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7811 if (HAS_CHANGE_EVENT(element, j - 2))
7813 SET_CHANGE_EVENT(element, j - 2, FALSE);
7814 SET_CHANGE_EVENT(element, j, TRUE);
7818 // order of checking and copying events to be mapped is important
7819 // (do not change the start and end value -- they are constant)
7820 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7822 if (HAS_CHANGE_EVENT(element, j - 1))
7824 SET_CHANGE_EVENT(element, j - 1, FALSE);
7825 SET_CHANGE_EVENT(element, j, TRUE);
7831 // initialize "can_change" field for old levels with only one change page
7832 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7834 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7836 int element = EL_CUSTOM_START + i;
7838 if (CAN_CHANGE(element))
7839 element_info[element].change->can_change = TRUE;
7843 // correct custom element values (for old levels without these options)
7844 if (level->game_version < VERSION_IDENT(3,1,1,0))
7846 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7848 int element = EL_CUSTOM_START + i;
7849 struct ElementInfo *ei = &element_info[element];
7851 if (ei->access_direction == MV_NO_DIRECTION)
7852 ei->access_direction = MV_ALL_DIRECTIONS;
7856 // correct custom element values (fix invalid values for all versions)
7859 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7861 int element = EL_CUSTOM_START + i;
7862 struct ElementInfo *ei = &element_info[element];
7864 for (j = 0; j < ei->num_change_pages; j++)
7866 struct ElementChangeInfo *change = &ei->change_page[j];
7868 if (change->trigger_player == CH_PLAYER_NONE)
7869 change->trigger_player = CH_PLAYER_ANY;
7871 if (change->trigger_side == CH_SIDE_NONE)
7872 change->trigger_side = CH_SIDE_ANY;
7877 // initialize "can_explode" field for old levels which did not store this
7878 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7879 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7881 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7883 int element = EL_CUSTOM_START + i;
7885 if (EXPLODES_1X1_OLD(element))
7886 element_info[element].explosion_type = EXPLODES_1X1;
7888 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7889 EXPLODES_SMASHED(element) ||
7890 EXPLODES_IMPACT(element)));
7894 // correct previously hard-coded move delay values for maze runner style
7895 if (level->game_version < VERSION_IDENT(3,1,1,0))
7897 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7899 int element = EL_CUSTOM_START + i;
7901 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7903 // previously hard-coded and therefore ignored
7904 element_info[element].move_delay_fixed = 9;
7905 element_info[element].move_delay_random = 0;
7910 // set some other uninitialized values of custom elements in older levels
7911 if (level->game_version < VERSION_IDENT(3,1,0,0))
7913 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7915 int element = EL_CUSTOM_START + i;
7917 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7919 element_info[element].explosion_delay = 17;
7920 element_info[element].ignition_delay = 8;
7924 // set mouse click change events to work for left/middle/right mouse button
7925 if (level->game_version < VERSION_IDENT(4,2,3,0))
7927 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7929 int element = EL_CUSTOM_START + i;
7930 struct ElementInfo *ei = &element_info[element];
7932 for (j = 0; j < ei->num_change_pages; j++)
7934 struct ElementChangeInfo *change = &ei->change_page[j];
7936 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7937 change->has_event[CE_PRESSED_BY_MOUSE] ||
7938 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7939 change->has_event[CE_MOUSE_PRESSED_ON_X])
7940 change->trigger_side = CH_SIDE_ANY;
7946 static void LoadLevel_InitElements(struct LevelInfo *level)
7948 LoadLevel_InitStandardElements(level);
7950 if (level->file_has_custom_elements)
7951 LoadLevel_InitCustomElements(level);
7953 // initialize element properties for level editor etc.
7954 InitElementPropertiesEngine(level->game_version);
7955 InitElementPropertiesGfxElement();
7958 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7962 // map elements that have changed in newer versions
7963 for (y = 0; y < level->fieldy; y++)
7964 for (x = 0; x < level->fieldx; x++)
7965 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7966 level->game_version);
7968 // clear unused playfield data (nicer if level gets resized in editor)
7969 for (x = 0; x < MAX_LEV_FIELDX; x++)
7970 for (y = 0; y < MAX_LEV_FIELDY; y++)
7971 if (x >= level->fieldx || y >= level->fieldy)
7972 level->field[x][y] = EL_EMPTY;
7974 // copy elements to runtime playfield array
7975 for (x = 0; x < MAX_LEV_FIELDX; x++)
7976 for (y = 0; y < MAX_LEV_FIELDY; y++)
7977 Tile[x][y] = level->field[x][y];
7979 // initialize level size variables for faster access
7980 lev_fieldx = level->fieldx;
7981 lev_fieldy = level->fieldy;
7983 // determine border element for this level
7984 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7985 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7990 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7992 struct LevelFileInfo *level_file_info = &level->file_info;
7994 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7995 CopyNativeLevel_RND_to_Native(level);
7998 static void LoadLevelTemplate_LoadAndInit(void)
8000 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
8002 LoadLevel_InitVersion(&level_template);
8003 LoadLevel_InitElements(&level_template);
8004 LoadLevel_InitSettings(&level_template);
8006 ActivateLevelTemplate();
8009 void LoadLevelTemplate(int nr)
8011 if (!fileExists(getGlobalLevelTemplateFilename()))
8013 Warn("no level template found for this level");
8018 setLevelFileInfo(&level_template.file_info, nr);
8020 LoadLevelTemplate_LoadAndInit();
8023 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
8025 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
8027 LoadLevelTemplate_LoadAndInit();
8030 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
8032 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
8034 if (level.use_custom_template)
8036 if (network_level != NULL)
8037 LoadNetworkLevelTemplate(network_level);
8039 LoadLevelTemplate(-1);
8042 LoadLevel_InitVersion(&level);
8043 LoadLevel_InitElements(&level);
8044 LoadLevel_InitPlayfield(&level);
8045 LoadLevel_InitSettings(&level);
8047 LoadLevel_InitNativeEngines(&level);
8050 void LoadLevel(int nr)
8052 SetLevelSetInfo(leveldir_current->identifier, nr);
8054 setLevelFileInfo(&level.file_info, nr);
8056 LoadLevel_LoadAndInit(NULL);
8059 void LoadLevelInfoOnly(int nr)
8061 setLevelFileInfo(&level.file_info, nr);
8063 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8066 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8068 SetLevelSetInfo(network_level->leveldir_identifier,
8069 network_level->file_info.nr);
8071 copyLevelFileInfo(&network_level->file_info, &level.file_info);
8073 LoadLevel_LoadAndInit(network_level);
8076 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8080 chunk_size += putFileVersion(file, level->file_version);
8081 chunk_size += putFileVersion(file, level->game_version);
8086 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8090 chunk_size += putFile16BitBE(file, level->creation_date.year);
8091 chunk_size += putFile8Bit(file, level->creation_date.month);
8092 chunk_size += putFile8Bit(file, level->creation_date.day);
8097 #if ENABLE_HISTORIC_CHUNKS
8098 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8102 putFile8Bit(file, level->fieldx);
8103 putFile8Bit(file, level->fieldy);
8105 putFile16BitBE(file, level->time);
8106 putFile16BitBE(file, level->gems_needed);
8108 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8109 putFile8Bit(file, level->name[i]);
8111 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8112 putFile8Bit(file, level->score[i]);
8114 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8115 for (y = 0; y < 3; y++)
8116 for (x = 0; x < 3; x++)
8117 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8118 level->yamyam_content[i].e[x][y]));
8119 putFile8Bit(file, level->amoeba_speed);
8120 putFile8Bit(file, level->time_magic_wall);
8121 putFile8Bit(file, level->time_wheel);
8122 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8123 level->amoeba_content));
8124 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8125 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8126 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8127 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8129 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8131 putFile8Bit(file, (level->block_last_field ? 1 : 0));
8132 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8133 putFile32BitBE(file, level->can_move_into_acid_bits);
8134 putFile8Bit(file, level->dont_collide_with_bits);
8136 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8137 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8139 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8140 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8141 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8143 putFile8Bit(file, level->game_engine_type);
8145 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8149 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8154 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8155 chunk_size += putFile8Bit(file, level->name[i]);
8160 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8165 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8166 chunk_size += putFile8Bit(file, level->author[i]);
8171 #if ENABLE_HISTORIC_CHUNKS
8172 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8177 for (y = 0; y < level->fieldy; y++)
8178 for (x = 0; x < level->fieldx; x++)
8179 if (level->encoding_16bit_field)
8180 chunk_size += putFile16BitBE(file, level->field[x][y]);
8182 chunk_size += putFile8Bit(file, level->field[x][y]);
8188 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8193 for (y = 0; y < level->fieldy; y++)
8194 for (x = 0; x < level->fieldx; x++)
8195 chunk_size += putFile16BitBE(file, level->field[x][y]);
8200 #if ENABLE_HISTORIC_CHUNKS
8201 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8205 putFile8Bit(file, EL_YAMYAM);
8206 putFile8Bit(file, level->num_yamyam_contents);
8207 putFile8Bit(file, 0);
8208 putFile8Bit(file, 0);
8210 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8211 for (y = 0; y < 3; y++)
8212 for (x = 0; x < 3; x++)
8213 if (level->encoding_16bit_field)
8214 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8216 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8220 #if ENABLE_HISTORIC_CHUNKS
8221 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8224 int num_contents, content_xsize, content_ysize;
8225 int content_array[MAX_ELEMENT_CONTENTS][3][3];
8227 if (element == EL_YAMYAM)
8229 num_contents = level->num_yamyam_contents;
8233 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8234 for (y = 0; y < 3; y++)
8235 for (x = 0; x < 3; x++)
8236 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8238 else if (element == EL_BD_AMOEBA)
8244 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8245 for (y = 0; y < 3; y++)
8246 for (x = 0; x < 3; x++)
8247 content_array[i][x][y] = EL_EMPTY;
8248 content_array[0][0][0] = level->amoeba_content;
8252 // chunk header already written -- write empty chunk data
8253 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8255 Warn("cannot save content for element '%d'", element);
8260 putFile16BitBE(file, element);
8261 putFile8Bit(file, num_contents);
8262 putFile8Bit(file, content_xsize);
8263 putFile8Bit(file, content_ysize);
8265 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8267 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8268 for (y = 0; y < 3; y++)
8269 for (x = 0; x < 3; x++)
8270 putFile16BitBE(file, content_array[i][x][y]);
8274 #if ENABLE_HISTORIC_CHUNKS
8275 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8277 int envelope_nr = element - EL_ENVELOPE_1;
8278 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8282 chunk_size += putFile16BitBE(file, element);
8283 chunk_size += putFile16BitBE(file, envelope_len);
8284 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8285 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8287 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8288 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8290 for (i = 0; i < envelope_len; i++)
8291 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8297 #if ENABLE_HISTORIC_CHUNKS
8298 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8299 int num_changed_custom_elements)
8303 putFile16BitBE(file, num_changed_custom_elements);
8305 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8307 int element = EL_CUSTOM_START + i;
8309 struct ElementInfo *ei = &element_info[element];
8311 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8313 if (check < num_changed_custom_elements)
8315 putFile16BitBE(file, element);
8316 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8323 if (check != num_changed_custom_elements) // should not happen
8324 Warn("inconsistent number of custom element properties");
8328 #if ENABLE_HISTORIC_CHUNKS
8329 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8330 int num_changed_custom_elements)
8334 putFile16BitBE(file, num_changed_custom_elements);
8336 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8338 int element = EL_CUSTOM_START + i;
8340 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8342 if (check < num_changed_custom_elements)
8344 putFile16BitBE(file, element);
8345 putFile16BitBE(file, element_info[element].change->target_element);
8352 if (check != num_changed_custom_elements) // should not happen
8353 Warn("inconsistent number of custom target elements");
8357 #if ENABLE_HISTORIC_CHUNKS
8358 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8359 int num_changed_custom_elements)
8361 int i, j, x, y, check = 0;
8363 putFile16BitBE(file, num_changed_custom_elements);
8365 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8367 int element = EL_CUSTOM_START + i;
8368 struct ElementInfo *ei = &element_info[element];
8370 if (ei->modified_settings)
8372 if (check < num_changed_custom_elements)
8374 putFile16BitBE(file, element);
8376 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8377 putFile8Bit(file, ei->description[j]);
8379 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8381 // some free bytes for future properties and padding
8382 WriteUnusedBytesToFile(file, 7);
8384 putFile8Bit(file, ei->use_gfx_element);
8385 putFile16BitBE(file, ei->gfx_element_initial);
8387 putFile8Bit(file, ei->collect_score_initial);
8388 putFile8Bit(file, ei->collect_count_initial);
8390 putFile16BitBE(file, ei->push_delay_fixed);
8391 putFile16BitBE(file, ei->push_delay_random);
8392 putFile16BitBE(file, ei->move_delay_fixed);
8393 putFile16BitBE(file, ei->move_delay_random);
8395 putFile16BitBE(file, ei->move_pattern);
8396 putFile8Bit(file, ei->move_direction_initial);
8397 putFile8Bit(file, ei->move_stepsize);
8399 for (y = 0; y < 3; y++)
8400 for (x = 0; x < 3; x++)
8401 putFile16BitBE(file, ei->content.e[x][y]);
8403 putFile32BitBE(file, ei->change->events);
8405 putFile16BitBE(file, ei->change->target_element);
8407 putFile16BitBE(file, ei->change->delay_fixed);
8408 putFile16BitBE(file, ei->change->delay_random);
8409 putFile16BitBE(file, ei->change->delay_frames);
8411 putFile16BitBE(file, ei->change->initial_trigger_element);
8413 putFile8Bit(file, ei->change->explode);
8414 putFile8Bit(file, ei->change->use_target_content);
8415 putFile8Bit(file, ei->change->only_if_complete);
8416 putFile8Bit(file, ei->change->use_random_replace);
8418 putFile8Bit(file, ei->change->random_percentage);
8419 putFile8Bit(file, ei->change->replace_when);
8421 for (y = 0; y < 3; y++)
8422 for (x = 0; x < 3; x++)
8423 putFile16BitBE(file, ei->change->content.e[x][y]);
8425 putFile8Bit(file, ei->slippery_type);
8427 // some free bytes for future properties and padding
8428 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8435 if (check != num_changed_custom_elements) // should not happen
8436 Warn("inconsistent number of custom element properties");
8440 #if ENABLE_HISTORIC_CHUNKS
8441 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8443 struct ElementInfo *ei = &element_info[element];
8446 // ---------- custom element base property values (96 bytes) ----------------
8448 putFile16BitBE(file, element);
8450 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8451 putFile8Bit(file, ei->description[i]);
8453 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8455 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8457 putFile8Bit(file, ei->num_change_pages);
8459 putFile16BitBE(file, ei->ce_value_fixed_initial);
8460 putFile16BitBE(file, ei->ce_value_random_initial);
8461 putFile8Bit(file, ei->use_last_ce_value);
8463 putFile8Bit(file, ei->use_gfx_element);
8464 putFile16BitBE(file, ei->gfx_element_initial);
8466 putFile8Bit(file, ei->collect_score_initial);
8467 putFile8Bit(file, ei->collect_count_initial);
8469 putFile8Bit(file, ei->drop_delay_fixed);
8470 putFile8Bit(file, ei->push_delay_fixed);
8471 putFile8Bit(file, ei->drop_delay_random);
8472 putFile8Bit(file, ei->push_delay_random);
8473 putFile16BitBE(file, ei->move_delay_fixed);
8474 putFile16BitBE(file, ei->move_delay_random);
8476 // bits 0 - 15 of "move_pattern" ...
8477 putFile16BitBE(file, ei->move_pattern & 0xffff);
8478 putFile8Bit(file, ei->move_direction_initial);
8479 putFile8Bit(file, ei->move_stepsize);
8481 putFile8Bit(file, ei->slippery_type);
8483 for (y = 0; y < 3; y++)
8484 for (x = 0; x < 3; x++)
8485 putFile16BitBE(file, ei->content.e[x][y]);
8487 putFile16BitBE(file, ei->move_enter_element);
8488 putFile16BitBE(file, ei->move_leave_element);
8489 putFile8Bit(file, ei->move_leave_type);
8491 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8492 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8494 putFile8Bit(file, ei->access_direction);
8496 putFile8Bit(file, ei->explosion_delay);
8497 putFile8Bit(file, ei->ignition_delay);
8498 putFile8Bit(file, ei->explosion_type);
8500 // some free bytes for future custom property values and padding
8501 WriteUnusedBytesToFile(file, 1);
8503 // ---------- change page property values (48 bytes) ------------------------
8505 for (i = 0; i < ei->num_change_pages; i++)
8507 struct ElementChangeInfo *change = &ei->change_page[i];
8508 unsigned int event_bits;
8510 // bits 0 - 31 of "has_event[]" ...
8512 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8513 if (change->has_event[j])
8514 event_bits |= (1u << j);
8515 putFile32BitBE(file, event_bits);
8517 putFile16BitBE(file, change->target_element);
8519 putFile16BitBE(file, change->delay_fixed);
8520 putFile16BitBE(file, change->delay_random);
8521 putFile16BitBE(file, change->delay_frames);
8523 putFile16BitBE(file, change->initial_trigger_element);
8525 putFile8Bit(file, change->explode);
8526 putFile8Bit(file, change->use_target_content);
8527 putFile8Bit(file, change->only_if_complete);
8528 putFile8Bit(file, change->use_random_replace);
8530 putFile8Bit(file, change->random_percentage);
8531 putFile8Bit(file, change->replace_when);
8533 for (y = 0; y < 3; y++)
8534 for (x = 0; x < 3; x++)
8535 putFile16BitBE(file, change->target_content.e[x][y]);
8537 putFile8Bit(file, change->can_change);
8539 putFile8Bit(file, change->trigger_side);
8541 putFile8Bit(file, change->trigger_player);
8542 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8543 log_2(change->trigger_page)));
8545 putFile8Bit(file, change->has_action);
8546 putFile8Bit(file, change->action_type);
8547 putFile8Bit(file, change->action_mode);
8548 putFile16BitBE(file, change->action_arg);
8550 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8552 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8553 if (change->has_event[j])
8554 event_bits |= (1u << (j - 32));
8555 putFile8Bit(file, event_bits);
8560 #if ENABLE_HISTORIC_CHUNKS
8561 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8563 struct ElementInfo *ei = &element_info[element];
8564 struct ElementGroupInfo *group = ei->group;
8567 putFile16BitBE(file, element);
8569 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8570 putFile8Bit(file, ei->description[i]);
8572 putFile8Bit(file, group->num_elements);
8574 putFile8Bit(file, ei->use_gfx_element);
8575 putFile16BitBE(file, ei->gfx_element_initial);
8577 putFile8Bit(file, group->choice_mode);
8579 // some free bytes for future values and padding
8580 WriteUnusedBytesToFile(file, 3);
8582 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8583 putFile16BitBE(file, group->element[i]);
8587 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8588 boolean write_element)
8590 int save_type = entry->save_type;
8591 int data_type = entry->data_type;
8592 int conf_type = entry->conf_type;
8593 int byte_mask = conf_type & CONF_MASK_BYTES;
8594 int element = entry->element;
8595 int default_value = entry->default_value;
8597 boolean modified = FALSE;
8599 if (byte_mask != CONF_MASK_MULTI_BYTES)
8601 void *value_ptr = entry->value;
8602 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8605 // check if any settings have been modified before saving them
8606 if (value != default_value)
8609 // do not save if explicitly told or if unmodified default settings
8610 if ((save_type == SAVE_CONF_NEVER) ||
8611 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8615 num_bytes += putFile16BitBE(file, element);
8617 num_bytes += putFile8Bit(file, conf_type);
8618 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8619 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8620 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8623 else if (data_type == TYPE_STRING)
8625 char *default_string = entry->default_string;
8626 char *string = (char *)(entry->value);
8627 int string_length = strlen(string);
8630 // check if any settings have been modified before saving them
8631 if (!strEqual(string, default_string))
8634 // do not save if explicitly told or if unmodified default settings
8635 if ((save_type == SAVE_CONF_NEVER) ||
8636 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8640 num_bytes += putFile16BitBE(file, element);
8642 num_bytes += putFile8Bit(file, conf_type);
8643 num_bytes += putFile16BitBE(file, string_length);
8645 for (i = 0; i < string_length; i++)
8646 num_bytes += putFile8Bit(file, string[i]);
8648 else if (data_type == TYPE_ELEMENT_LIST)
8650 int *element_array = (int *)(entry->value);
8651 int num_elements = *(int *)(entry->num_entities);
8654 // check if any settings have been modified before saving them
8655 for (i = 0; i < num_elements; i++)
8656 if (element_array[i] != default_value)
8659 // do not save if explicitly told or if unmodified default settings
8660 if ((save_type == SAVE_CONF_NEVER) ||
8661 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8665 num_bytes += putFile16BitBE(file, element);
8667 num_bytes += putFile8Bit(file, conf_type);
8668 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8670 for (i = 0; i < num_elements; i++)
8671 num_bytes += putFile16BitBE(file, element_array[i]);
8673 else if (data_type == TYPE_CONTENT_LIST)
8675 struct Content *content = (struct Content *)(entry->value);
8676 int num_contents = *(int *)(entry->num_entities);
8679 // check if any settings have been modified before saving them
8680 for (i = 0; i < num_contents; i++)
8681 for (y = 0; y < 3; y++)
8682 for (x = 0; x < 3; x++)
8683 if (content[i].e[x][y] != default_value)
8686 // do not save if explicitly told or if unmodified default settings
8687 if ((save_type == SAVE_CONF_NEVER) ||
8688 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8692 num_bytes += putFile16BitBE(file, element);
8694 num_bytes += putFile8Bit(file, conf_type);
8695 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8697 for (i = 0; i < num_contents; i++)
8698 for (y = 0; y < 3; y++)
8699 for (x = 0; x < 3; x++)
8700 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8706 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8711 li = *level; // copy level data into temporary buffer
8713 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8714 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8719 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8724 li = *level; // copy level data into temporary buffer
8726 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8727 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8732 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8734 int envelope_nr = element - EL_ENVELOPE_1;
8738 chunk_size += putFile16BitBE(file, element);
8740 // copy envelope data into temporary buffer
8741 xx_envelope = level->envelope[envelope_nr];
8743 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8744 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8749 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8751 struct ElementInfo *ei = &element_info[element];
8755 chunk_size += putFile16BitBE(file, element);
8757 xx_ei = *ei; // copy element data into temporary buffer
8759 // set default description string for this specific element
8760 strcpy(xx_default_description, getDefaultElementDescription(ei));
8762 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8763 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8765 for (i = 0; i < ei->num_change_pages; i++)
8767 struct ElementChangeInfo *change = &ei->change_page[i];
8769 xx_current_change_page = i;
8771 xx_change = *change; // copy change data into temporary buffer
8774 setEventBitsFromEventFlags(change);
8776 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8777 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8784 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8786 struct ElementInfo *ei = &element_info[element];
8787 struct ElementGroupInfo *group = ei->group;
8791 chunk_size += putFile16BitBE(file, element);
8793 xx_ei = *ei; // copy element data into temporary buffer
8794 xx_group = *group; // copy group data into temporary buffer
8796 // set default description string for this specific element
8797 strcpy(xx_default_description, getDefaultElementDescription(ei));
8799 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8800 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8805 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8807 struct ElementInfo *ei = &element_info[element];
8811 chunk_size += putFile16BitBE(file, element);
8813 xx_ei = *ei; // copy element data into temporary buffer
8815 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8816 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8821 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8822 boolean save_as_template)
8828 if (!(file = fopen(filename, MODE_WRITE)))
8830 Warn("cannot save level file '%s'", filename);
8835 level->file_version = FILE_VERSION_ACTUAL;
8836 level->game_version = GAME_VERSION_ACTUAL;
8838 level->creation_date = getCurrentDate();
8840 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8841 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8843 chunk_size = SaveLevel_VERS(NULL, level);
8844 putFileChunkBE(file, "VERS", chunk_size);
8845 SaveLevel_VERS(file, level);
8847 chunk_size = SaveLevel_DATE(NULL, level);
8848 putFileChunkBE(file, "DATE", chunk_size);
8849 SaveLevel_DATE(file, level);
8851 chunk_size = SaveLevel_NAME(NULL, level);
8852 putFileChunkBE(file, "NAME", chunk_size);
8853 SaveLevel_NAME(file, level);
8855 chunk_size = SaveLevel_AUTH(NULL, level);
8856 putFileChunkBE(file, "AUTH", chunk_size);
8857 SaveLevel_AUTH(file, level);
8859 chunk_size = SaveLevel_INFO(NULL, level);
8860 putFileChunkBE(file, "INFO", chunk_size);
8861 SaveLevel_INFO(file, level);
8863 chunk_size = SaveLevel_BODY(NULL, level);
8864 putFileChunkBE(file, "BODY", chunk_size);
8865 SaveLevel_BODY(file, level);
8867 chunk_size = SaveLevel_ELEM(NULL, level);
8868 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8870 putFileChunkBE(file, "ELEM", chunk_size);
8871 SaveLevel_ELEM(file, level);
8874 for (i = 0; i < NUM_ENVELOPES; i++)
8876 int element = EL_ENVELOPE_1 + i;
8878 chunk_size = SaveLevel_NOTE(NULL, level, element);
8879 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8881 putFileChunkBE(file, "NOTE", chunk_size);
8882 SaveLevel_NOTE(file, level, element);
8886 // if not using template level, check for non-default custom/group elements
8887 if (!level->use_custom_template || save_as_template)
8889 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8891 int element = EL_CUSTOM_START + i;
8893 chunk_size = SaveLevel_CUSX(NULL, level, element);
8894 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8896 putFileChunkBE(file, "CUSX", chunk_size);
8897 SaveLevel_CUSX(file, level, element);
8901 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8903 int element = EL_GROUP_START + i;
8905 chunk_size = SaveLevel_GRPX(NULL, level, element);
8906 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8908 putFileChunkBE(file, "GRPX", chunk_size);
8909 SaveLevel_GRPX(file, level, element);
8913 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8915 int element = GET_EMPTY_ELEMENT(i);
8917 chunk_size = SaveLevel_EMPX(NULL, level, element);
8918 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8920 putFileChunkBE(file, "EMPX", chunk_size);
8921 SaveLevel_EMPX(file, level, element);
8928 SetFilePermissions(filename, PERMS_PRIVATE);
8931 void SaveLevel(int nr)
8933 char *filename = getDefaultLevelFilename(nr);
8935 SaveLevelFromFilename(&level, filename, FALSE);
8938 void SaveLevelTemplate(void)
8940 char *filename = getLocalLevelTemplateFilename();
8942 SaveLevelFromFilename(&level, filename, TRUE);
8945 boolean SaveLevelChecked(int nr)
8947 char *filename = getDefaultLevelFilename(nr);
8948 boolean new_level = !fileExists(filename);
8949 boolean level_saved = FALSE;
8951 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8956 Request("Level saved!", REQ_CONFIRM);
8964 void DumpLevel(struct LevelInfo *level)
8966 if (level->no_level_file || level->no_valid_file)
8968 Warn("cannot dump -- no valid level file found");
8974 Print("Level xxx (file version %08d, game version %08d)\n",
8975 level->file_version, level->game_version);
8978 Print("Level author: '%s'\n", level->author);
8979 Print("Level title: '%s'\n", level->name);
8981 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8983 Print("Level time: %d seconds\n", level->time);
8984 Print("Gems needed: %d\n", level->gems_needed);
8986 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8987 Print("Time for wheel: %d seconds\n", level->time_wheel);
8988 Print("Time for light: %d seconds\n", level->time_light);
8989 Print("Time for timegate: %d seconds\n", level->time_timegate);
8991 Print("Amoeba speed: %d\n", level->amoeba_speed);
8994 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8995 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8996 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8997 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8998 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8999 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
9005 for (i = 0; i < NUM_ENVELOPES; i++)
9007 char *text = level->envelope[i].text;
9008 int text_len = strlen(text);
9009 boolean has_text = FALSE;
9011 for (j = 0; j < text_len; j++)
9012 if (text[j] != ' ' && text[j] != '\n')
9018 Print("Envelope %d:\n'%s'\n", i + 1, text);
9026 void DumpLevels(void)
9028 static LevelDirTree *dumplevel_leveldir = NULL;
9030 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9031 global.dumplevel_leveldir);
9033 if (dumplevel_leveldir == NULL)
9034 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
9036 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
9037 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
9038 Fail("no such level number: %d", global.dumplevel_level_nr);
9040 leveldir_current = dumplevel_leveldir;
9042 LoadLevel(global.dumplevel_level_nr);
9048 void DumpLevelsetFromFilename_BD(char *filename)
9050 if (leveldir_current == NULL) // no levelsets loaded yet
9053 if (!LoadNativeLevel_BD(filename, 0, FALSE))
9054 CloseAllAndExit(0); // function has already printed warning
9057 Print("Levelset '%s'\n", filename);
9067 void DumpLevelset(void)
9069 static LevelDirTree *dumplevelset_leveldir = NULL;
9071 dumplevelset_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9072 global.dumplevelset_leveldir);
9073 if (dumplevelset_leveldir == NULL)
9074 Fail("no such level identifier: '%s'", global.dumplevelset_leveldir);
9077 Print("Levelset '%s'\n", dumplevelset_leveldir->identifier);
9080 Print("Number of levels: %d\n", dumplevelset_leveldir->levels);
9081 Print("First level number: %d\n", dumplevelset_leveldir->first_level);
9089 // ============================================================================
9090 // tape file functions
9091 // ============================================================================
9093 static void setTapeInfoToDefaults(void)
9097 // always start with reliable default values (empty tape)
9100 // default values (also for pre-1.2 tapes) with only the first player
9101 tape.player_participates[0] = TRUE;
9102 for (i = 1; i < MAX_PLAYERS; i++)
9103 tape.player_participates[i] = FALSE;
9105 // at least one (default: the first) player participates in every tape
9106 tape.num_participating_players = 1;
9108 tape.property_bits = TAPE_PROPERTY_NONE;
9110 tape.level_nr = level_nr;
9112 tape.changed = FALSE;
9113 tape.solved = FALSE;
9115 tape.recording = FALSE;
9116 tape.playing = FALSE;
9117 tape.pausing = FALSE;
9119 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9120 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9122 tape.no_info_chunk = TRUE;
9123 tape.no_valid_file = FALSE;
9126 static int getTapePosSize(struct TapeInfo *tape)
9128 int tape_pos_size = 0;
9130 if (tape->use_key_actions)
9131 tape_pos_size += tape->num_participating_players;
9133 if (tape->use_mouse_actions)
9134 tape_pos_size += 3; // x and y position and mouse button mask
9136 tape_pos_size += 1; // tape action delay value
9138 return tape_pos_size;
9141 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9143 tape->use_key_actions = FALSE;
9144 tape->use_mouse_actions = FALSE;
9146 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9147 tape->use_key_actions = TRUE;
9149 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9150 tape->use_mouse_actions = TRUE;
9153 static int getTapeActionValue(struct TapeInfo *tape)
9155 return (tape->use_key_actions &&
9156 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9157 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
9158 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9159 TAPE_ACTIONS_DEFAULT);
9162 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9164 tape->file_version = getFileVersion(file);
9165 tape->game_version = getFileVersion(file);
9170 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9174 tape->random_seed = getFile32BitBE(file);
9175 tape->date = getFile32BitBE(file);
9176 tape->length = getFile32BitBE(file);
9178 // read header fields that are new since version 1.2
9179 if (tape->file_version >= FILE_VERSION_1_2)
9181 byte store_participating_players = getFile8Bit(file);
9184 // since version 1.2, tapes store which players participate in the tape
9185 tape->num_participating_players = 0;
9186 for (i = 0; i < MAX_PLAYERS; i++)
9188 tape->player_participates[i] = FALSE;
9190 if (store_participating_players & (1 << i))
9192 tape->player_participates[i] = TRUE;
9193 tape->num_participating_players++;
9197 setTapeActionFlags(tape, getFile8Bit(file));
9199 tape->property_bits = getFile8Bit(file);
9200 tape->solved = getFile8Bit(file);
9202 engine_version = getFileVersion(file);
9203 if (engine_version > 0)
9204 tape->engine_version = engine_version;
9206 tape->engine_version = tape->game_version;
9212 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9214 tape->scr_fieldx = getFile8Bit(file);
9215 tape->scr_fieldy = getFile8Bit(file);
9220 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9222 char *level_identifier = NULL;
9223 int level_identifier_size;
9226 tape->no_info_chunk = FALSE;
9228 level_identifier_size = getFile16BitBE(file);
9230 level_identifier = checked_malloc(level_identifier_size);
9232 for (i = 0; i < level_identifier_size; i++)
9233 level_identifier[i] = getFile8Bit(file);
9235 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9236 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9238 checked_free(level_identifier);
9240 tape->level_nr = getFile16BitBE(file);
9242 chunk_size = 2 + level_identifier_size + 2;
9247 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9250 int tape_pos_size = getTapePosSize(tape);
9251 int chunk_size_expected = tape_pos_size * tape->length;
9253 if (chunk_size_expected != chunk_size)
9255 ReadUnusedBytesFromFile(file, chunk_size);
9256 return chunk_size_expected;
9259 for (i = 0; i < tape->length; i++)
9261 if (i >= MAX_TAPE_LEN)
9263 Warn("tape truncated -- size exceeds maximum tape size %d",
9266 // tape too large; read and ignore remaining tape data from this chunk
9267 for (;i < tape->length; i++)
9268 ReadUnusedBytesFromFile(file, tape_pos_size);
9273 if (tape->use_key_actions)
9275 for (j = 0; j < MAX_PLAYERS; j++)
9277 tape->pos[i].action[j] = MV_NONE;
9279 if (tape->player_participates[j])
9280 tape->pos[i].action[j] = getFile8Bit(file);
9284 if (tape->use_mouse_actions)
9286 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
9287 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
9288 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9291 tape->pos[i].delay = getFile8Bit(file);
9293 if (tape->file_version == FILE_VERSION_1_0)
9295 // eliminate possible diagonal moves in old tapes
9296 // this is only for backward compatibility
9298 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9299 byte action = tape->pos[i].action[0];
9300 int k, num_moves = 0;
9302 for (k = 0; k < 4; k++)
9304 if (action & joy_dir[k])
9306 tape->pos[i + num_moves].action[0] = joy_dir[k];
9308 tape->pos[i + num_moves].delay = 0;
9317 tape->length += num_moves;
9320 else if (tape->file_version < FILE_VERSION_2_0)
9322 // convert pre-2.0 tapes to new tape format
9324 if (tape->pos[i].delay > 1)
9327 tape->pos[i + 1] = tape->pos[i];
9328 tape->pos[i + 1].delay = 1;
9331 for (j = 0; j < MAX_PLAYERS; j++)
9332 tape->pos[i].action[j] = MV_NONE;
9333 tape->pos[i].delay--;
9340 if (checkEndOfFile(file))
9344 if (i != tape->length)
9345 chunk_size = tape_pos_size * i;
9350 static void LoadTape_SokobanSolution(char *filename)
9353 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9355 if (!(file = openFile(filename, MODE_READ)))
9357 tape.no_valid_file = TRUE;
9362 while (!checkEndOfFile(file))
9364 unsigned char c = getByteFromFile(file);
9366 if (checkEndOfFile(file))
9373 tape.pos[tape.length].action[0] = MV_UP;
9374 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9380 tape.pos[tape.length].action[0] = MV_DOWN;
9381 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9387 tape.pos[tape.length].action[0] = MV_LEFT;
9388 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9394 tape.pos[tape.length].action[0] = MV_RIGHT;
9395 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9403 // ignore white-space characters
9407 tape.no_valid_file = TRUE;
9409 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9417 if (tape.no_valid_file)
9420 tape.length_frames = GetTapeLengthFrames();
9421 tape.length_seconds = GetTapeLengthSeconds();
9424 void LoadTapeFromFilename(char *filename)
9426 char cookie[MAX_LINE_LEN];
9427 char chunk_name[CHUNK_ID_LEN + 1];
9431 // always start with reliable default values
9432 setTapeInfoToDefaults();
9434 if (strSuffix(filename, ".sln"))
9436 LoadTape_SokobanSolution(filename);
9441 if (!(file = openFile(filename, MODE_READ)))
9443 tape.no_valid_file = TRUE;
9448 getFileChunkBE(file, chunk_name, NULL);
9449 if (strEqual(chunk_name, "RND1"))
9451 getFile32BitBE(file); // not used
9453 getFileChunkBE(file, chunk_name, NULL);
9454 if (!strEqual(chunk_name, "TAPE"))
9456 tape.no_valid_file = TRUE;
9458 Warn("unknown format of tape file '%s'", filename);
9465 else // check for pre-2.0 file format with cookie string
9467 strcpy(cookie, chunk_name);
9468 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9470 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9471 cookie[strlen(cookie) - 1] = '\0';
9473 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9475 tape.no_valid_file = TRUE;
9477 Warn("unknown format of tape file '%s'", filename);
9484 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9486 tape.no_valid_file = TRUE;
9488 Warn("unsupported version of tape file '%s'", filename);
9495 // pre-2.0 tape files have no game version, so use file version here
9496 tape.game_version = tape.file_version;
9499 if (tape.file_version < FILE_VERSION_1_2)
9501 // tape files from versions before 1.2.0 without chunk structure
9502 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9503 LoadTape_BODY(file, 2 * tape.length, &tape);
9511 int (*loader)(File *, int, struct TapeInfo *);
9515 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9516 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9517 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9518 { "INFO", -1, LoadTape_INFO },
9519 { "BODY", -1, LoadTape_BODY },
9523 while (getFileChunkBE(file, chunk_name, &chunk_size))
9527 while (chunk_info[i].name != NULL &&
9528 !strEqual(chunk_name, chunk_info[i].name))
9531 if (chunk_info[i].name == NULL)
9533 Warn("unknown chunk '%s' in tape file '%s'",
9534 chunk_name, filename);
9536 ReadUnusedBytesFromFile(file, chunk_size);
9538 else if (chunk_info[i].size != -1 &&
9539 chunk_info[i].size != chunk_size)
9541 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9542 chunk_size, chunk_name, filename);
9544 ReadUnusedBytesFromFile(file, chunk_size);
9548 // call function to load this tape chunk
9549 int chunk_size_expected =
9550 (chunk_info[i].loader)(file, chunk_size, &tape);
9552 // the size of some chunks cannot be checked before reading other
9553 // chunks first (like "HEAD" and "BODY") that contain some header
9554 // information, so check them here
9555 if (chunk_size_expected != chunk_size)
9557 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9558 chunk_size, chunk_name, filename);
9566 tape.length_frames = GetTapeLengthFrames();
9567 tape.length_seconds = GetTapeLengthSeconds();
9570 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9572 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9574 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9575 tape.engine_version);
9579 void LoadTape(int nr)
9581 char *filename = getTapeFilename(nr);
9583 LoadTapeFromFilename(filename);
9586 void LoadSolutionTape(int nr)
9588 char *filename = getSolutionTapeFilename(nr);
9590 LoadTapeFromFilename(filename);
9592 if (TAPE_IS_EMPTY(tape))
9594 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9595 level.native_bd_level->replay != NULL)
9596 CopyNativeTape_BD_to_RND(&level);
9597 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9598 level.native_sp_level->demo.is_available)
9599 CopyNativeTape_SP_to_RND(&level);
9603 void LoadScoreTape(char *score_tape_basename, int nr)
9605 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9607 LoadTapeFromFilename(filename);
9610 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9612 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9614 LoadTapeFromFilename(filename);
9617 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9619 // chunk required for team mode tapes with non-default screen size
9620 return (tape->num_participating_players > 1 &&
9621 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9622 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9625 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9627 putFileVersion(file, tape->file_version);
9628 putFileVersion(file, tape->game_version);
9631 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9634 byte store_participating_players = 0;
9636 // set bits for participating players for compact storage
9637 for (i = 0; i < MAX_PLAYERS; i++)
9638 if (tape->player_participates[i])
9639 store_participating_players |= (1 << i);
9641 putFile32BitBE(file, tape->random_seed);
9642 putFile32BitBE(file, tape->date);
9643 putFile32BitBE(file, tape->length);
9645 putFile8Bit(file, store_participating_players);
9647 putFile8Bit(file, getTapeActionValue(tape));
9649 putFile8Bit(file, tape->property_bits);
9650 putFile8Bit(file, tape->solved);
9652 putFileVersion(file, tape->engine_version);
9655 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9657 putFile8Bit(file, tape->scr_fieldx);
9658 putFile8Bit(file, tape->scr_fieldy);
9661 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9663 int level_identifier_size = strlen(tape->level_identifier) + 1;
9666 putFile16BitBE(file, level_identifier_size);
9668 for (i = 0; i < level_identifier_size; i++)
9669 putFile8Bit(file, tape->level_identifier[i]);
9671 putFile16BitBE(file, tape->level_nr);
9674 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9678 for (i = 0; i < tape->length; i++)
9680 if (tape->use_key_actions)
9682 for (j = 0; j < MAX_PLAYERS; j++)
9683 if (tape->player_participates[j])
9684 putFile8Bit(file, tape->pos[i].action[j]);
9687 if (tape->use_mouse_actions)
9689 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9690 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9691 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9694 putFile8Bit(file, tape->pos[i].delay);
9698 void SaveTapeToFilename(char *filename)
9702 int info_chunk_size;
9703 int body_chunk_size;
9705 if (!(file = fopen(filename, MODE_WRITE)))
9707 Warn("cannot save level recording file '%s'", filename);
9712 tape_pos_size = getTapePosSize(&tape);
9714 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9715 body_chunk_size = tape_pos_size * tape.length;
9717 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9718 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9720 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9721 SaveTape_VERS(file, &tape);
9723 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9724 SaveTape_HEAD(file, &tape);
9726 if (checkSaveTape_SCRN(&tape))
9728 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9729 SaveTape_SCRN(file, &tape);
9732 putFileChunkBE(file, "INFO", info_chunk_size);
9733 SaveTape_INFO(file, &tape);
9735 putFileChunkBE(file, "BODY", body_chunk_size);
9736 SaveTape_BODY(file, &tape);
9740 SetFilePermissions(filename, PERMS_PRIVATE);
9743 static void SaveTapeExt(char *filename)
9747 tape.file_version = FILE_VERSION_ACTUAL;
9748 tape.game_version = GAME_VERSION_ACTUAL;
9750 tape.num_participating_players = 0;
9752 // count number of participating players
9753 for (i = 0; i < MAX_PLAYERS; i++)
9754 if (tape.player_participates[i])
9755 tape.num_participating_players++;
9757 SaveTapeToFilename(filename);
9759 tape.changed = FALSE;
9762 void SaveTape(int nr)
9764 char *filename = getTapeFilename(nr);
9766 InitTapeDirectory(leveldir_current->subdir);
9768 SaveTapeExt(filename);
9771 void SaveScoreTape(int nr)
9773 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9775 // used instead of "leveldir_current->subdir" (for network games)
9776 InitScoreTapeDirectory(levelset.identifier, nr);
9778 SaveTapeExt(filename);
9781 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9782 unsigned int req_state_added)
9784 char *filename = getTapeFilename(nr);
9785 boolean new_tape = !fileExists(filename);
9786 boolean tape_saved = FALSE;
9788 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9793 Request(msg_saved, REQ_CONFIRM | req_state_added);
9801 boolean SaveTapeChecked(int nr)
9803 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9806 boolean SaveTapeChecked_LevelSolved(int nr)
9808 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9809 "Level solved! Tape saved!", REQ_STAY_OPEN);
9812 void DumpTape(struct TapeInfo *tape)
9814 int tape_frame_counter;
9817 if (tape->no_valid_file)
9819 Warn("cannot dump -- no valid tape file found");
9826 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9827 tape->level_nr, tape->file_version, tape->game_version);
9828 Print(" (effective engine version %08d)\n",
9829 tape->engine_version);
9830 Print("Level series identifier: '%s'\n", tape->level_identifier);
9832 Print("Solution tape: %s\n",
9833 tape->solved ? "yes" :
9834 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9836 Print("Special tape properties: ");
9837 if (tape->property_bits == TAPE_PROPERTY_NONE)
9839 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9840 Print("[em_random_bug]");
9841 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9842 Print("[game_speed]");
9843 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9845 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9846 Print("[single_step]");
9847 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9848 Print("[snapshot]");
9849 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9850 Print("[replayed]");
9851 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9852 Print("[tas_keys]");
9853 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9854 Print("[small_graphics]");
9857 int year2 = tape->date / 10000;
9858 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9859 int month_index_raw = (tape->date / 100) % 100;
9860 int month_index = month_index_raw % 12; // prevent invalid index
9861 int month = month_index + 1;
9862 int day = tape->date % 100;
9864 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9868 tape_frame_counter = 0;
9870 for (i = 0; i < tape->length; i++)
9872 if (i >= MAX_TAPE_LEN)
9877 for (j = 0; j < MAX_PLAYERS; j++)
9879 if (tape->player_participates[j])
9881 int action = tape->pos[i].action[j];
9883 Print("%d:%02x ", j, action);
9884 Print("[%c%c%c%c|%c%c] - ",
9885 (action & JOY_LEFT ? '<' : ' '),
9886 (action & JOY_RIGHT ? '>' : ' '),
9887 (action & JOY_UP ? '^' : ' '),
9888 (action & JOY_DOWN ? 'v' : ' '),
9889 (action & JOY_BUTTON_1 ? '1' : ' '),
9890 (action & JOY_BUTTON_2 ? '2' : ' '));
9894 Print("(%03d) ", tape->pos[i].delay);
9895 Print("[%05d]\n", tape_frame_counter);
9897 tape_frame_counter += tape->pos[i].delay;
9903 void DumpTapes(void)
9905 static LevelDirTree *dumptape_leveldir = NULL;
9907 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9908 global.dumptape_leveldir);
9910 if (dumptape_leveldir == NULL)
9911 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9913 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9914 global.dumptape_level_nr > dumptape_leveldir->last_level)
9915 Fail("no such level number: %d", global.dumptape_level_nr);
9917 leveldir_current = dumptape_leveldir;
9919 if (options.mytapes)
9920 LoadTape(global.dumptape_level_nr);
9922 LoadSolutionTape(global.dumptape_level_nr);
9930 // ============================================================================
9931 // score file functions
9932 // ============================================================================
9934 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9938 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9940 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9941 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9942 scores->entry[i].score = 0;
9943 scores->entry[i].time = 0;
9945 scores->entry[i].id = -1;
9946 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9947 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9948 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9949 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9950 strcpy(scores->entry[i].country_code, "??");
9953 scores->num_entries = 0;
9954 scores->last_added = -1;
9955 scores->last_added_local = -1;
9957 scores->updated = FALSE;
9958 scores->uploaded = FALSE;
9959 scores->tape_downloaded = FALSE;
9960 scores->force_last_added = FALSE;
9962 // The following values are intentionally not reset here:
9966 // - continue_playing
9967 // - continue_on_return
9970 static void setScoreInfoToDefaults(void)
9972 setScoreInfoToDefaultsExt(&scores);
9975 static void setServerScoreInfoToDefaults(void)
9977 setScoreInfoToDefaultsExt(&server_scores);
9980 static void LoadScore_OLD(int nr)
9983 char *filename = getScoreFilename(nr);
9984 char cookie[MAX_LINE_LEN];
9985 char line[MAX_LINE_LEN];
9989 if (!(file = fopen(filename, MODE_READ)))
9992 // check file identifier
9993 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9995 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9996 cookie[strlen(cookie) - 1] = '\0';
9998 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10000 Warn("unknown format of score file '%s'", filename);
10007 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10009 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
10010 Warn("fscanf() failed; %s", strerror(errno));
10012 if (fgets(line, MAX_LINE_LEN, file) == NULL)
10015 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
10016 line[strlen(line) - 1] = '\0';
10018 for (line_ptr = line; *line_ptr; line_ptr++)
10020 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
10022 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
10023 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10032 static void ConvertScore_OLD(void)
10034 // only convert score to time for levels that rate playing time over score
10035 if (!level.rate_time_over_score)
10038 // convert old score to playing time for score-less levels (like Supaplex)
10039 int time_final_max = 999;
10042 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10044 int score = scores.entry[i].score;
10046 if (score > 0 && score < time_final_max)
10047 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
10051 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
10053 scores->file_version = getFileVersion(file);
10054 scores->game_version = getFileVersion(file);
10059 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
10061 char *level_identifier = NULL;
10062 int level_identifier_size;
10065 level_identifier_size = getFile16BitBE(file);
10067 level_identifier = checked_malloc(level_identifier_size);
10069 for (i = 0; i < level_identifier_size; i++)
10070 level_identifier[i] = getFile8Bit(file);
10072 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
10073 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
10075 checked_free(level_identifier);
10077 scores->level_nr = getFile16BitBE(file);
10078 scores->num_entries = getFile16BitBE(file);
10080 chunk_size = 2 + level_identifier_size + 2 + 2;
10085 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10089 for (i = 0; i < scores->num_entries; i++)
10091 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10092 scores->entry[i].name[j] = getFile8Bit(file);
10094 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10097 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10102 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10106 for (i = 0; i < scores->num_entries; i++)
10107 scores->entry[i].score = getFile16BitBE(file);
10109 chunk_size = scores->num_entries * 2;
10114 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10118 for (i = 0; i < scores->num_entries; i++)
10119 scores->entry[i].score = getFile32BitBE(file);
10121 chunk_size = scores->num_entries * 4;
10126 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10130 for (i = 0; i < scores->num_entries; i++)
10131 scores->entry[i].time = getFile32BitBE(file);
10133 chunk_size = scores->num_entries * 4;
10138 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10142 for (i = 0; i < scores->num_entries; i++)
10144 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10145 scores->entry[i].tape_basename[j] = getFile8Bit(file);
10147 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10150 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10155 void LoadScore(int nr)
10157 char *filename = getScoreFilename(nr);
10158 char cookie[MAX_LINE_LEN];
10159 char chunk_name[CHUNK_ID_LEN + 1];
10161 boolean old_score_file_format = FALSE;
10164 // always start with reliable default values
10165 setScoreInfoToDefaults();
10167 if (!(file = openFile(filename, MODE_READ)))
10170 getFileChunkBE(file, chunk_name, NULL);
10171 if (strEqual(chunk_name, "RND1"))
10173 getFile32BitBE(file); // not used
10175 getFileChunkBE(file, chunk_name, NULL);
10176 if (!strEqual(chunk_name, "SCOR"))
10178 Warn("unknown format of score file '%s'", filename);
10185 else // check for old file format with cookie string
10187 strcpy(cookie, chunk_name);
10188 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10190 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10191 cookie[strlen(cookie) - 1] = '\0';
10193 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10195 Warn("unknown format of score file '%s'", filename);
10202 old_score_file_format = TRUE;
10205 if (old_score_file_format)
10207 // score files from versions before 4.2.4.0 without chunk structure
10210 // convert score to time, if possible (mainly for Supaplex levels)
10211 ConvertScore_OLD();
10219 int (*loader)(File *, int, struct ScoreInfo *);
10223 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
10224 { "INFO", -1, LoadScore_INFO },
10225 { "NAME", -1, LoadScore_NAME },
10226 { "SCOR", -1, LoadScore_SCOR },
10227 { "SC4R", -1, LoadScore_SC4R },
10228 { "TIME", -1, LoadScore_TIME },
10229 { "TAPE", -1, LoadScore_TAPE },
10234 while (getFileChunkBE(file, chunk_name, &chunk_size))
10238 while (chunk_info[i].name != NULL &&
10239 !strEqual(chunk_name, chunk_info[i].name))
10242 if (chunk_info[i].name == NULL)
10244 Warn("unknown chunk '%s' in score file '%s'",
10245 chunk_name, filename);
10247 ReadUnusedBytesFromFile(file, chunk_size);
10249 else if (chunk_info[i].size != -1 &&
10250 chunk_info[i].size != chunk_size)
10252 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10253 chunk_size, chunk_name, filename);
10255 ReadUnusedBytesFromFile(file, chunk_size);
10259 // call function to load this score chunk
10260 int chunk_size_expected =
10261 (chunk_info[i].loader)(file, chunk_size, &scores);
10263 // the size of some chunks cannot be checked before reading other
10264 // chunks first (like "HEAD" and "BODY") that contain some header
10265 // information, so check them here
10266 if (chunk_size_expected != chunk_size)
10268 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10269 chunk_size, chunk_name, filename);
10278 #if ENABLE_HISTORIC_CHUNKS
10279 void SaveScore_OLD(int nr)
10282 char *filename = getScoreFilename(nr);
10285 // used instead of "leveldir_current->subdir" (for network games)
10286 InitScoreDirectory(levelset.identifier);
10288 if (!(file = fopen(filename, MODE_WRITE)))
10290 Warn("cannot save score for level %d", nr);
10295 fprintf(file, "%s\n\n", SCORE_COOKIE);
10297 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10298 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10302 SetFilePermissions(filename, PERMS_PRIVATE);
10306 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10308 putFileVersion(file, scores->file_version);
10309 putFileVersion(file, scores->game_version);
10312 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10314 int level_identifier_size = strlen(scores->level_identifier) + 1;
10317 putFile16BitBE(file, level_identifier_size);
10319 for (i = 0; i < level_identifier_size; i++)
10320 putFile8Bit(file, scores->level_identifier[i]);
10322 putFile16BitBE(file, scores->level_nr);
10323 putFile16BitBE(file, scores->num_entries);
10326 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10330 for (i = 0; i < scores->num_entries; i++)
10332 int name_size = strlen(scores->entry[i].name);
10334 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10335 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10339 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10343 for (i = 0; i < scores->num_entries; i++)
10344 putFile16BitBE(file, scores->entry[i].score);
10347 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10351 for (i = 0; i < scores->num_entries; i++)
10352 putFile32BitBE(file, scores->entry[i].score);
10355 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10359 for (i = 0; i < scores->num_entries; i++)
10360 putFile32BitBE(file, scores->entry[i].time);
10363 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10367 for (i = 0; i < scores->num_entries; i++)
10369 int size = strlen(scores->entry[i].tape_basename);
10371 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10372 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10376 static void SaveScoreToFilename(char *filename)
10379 int info_chunk_size;
10380 int name_chunk_size;
10381 int scor_chunk_size;
10382 int sc4r_chunk_size;
10383 int time_chunk_size;
10384 int tape_chunk_size;
10385 boolean has_large_score_values;
10388 if (!(file = fopen(filename, MODE_WRITE)))
10390 Warn("cannot save score file '%s'", filename);
10395 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10396 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10397 scor_chunk_size = scores.num_entries * 2;
10398 sc4r_chunk_size = scores.num_entries * 4;
10399 time_chunk_size = scores.num_entries * 4;
10400 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10402 has_large_score_values = FALSE;
10403 for (i = 0; i < scores.num_entries; i++)
10404 if (scores.entry[i].score > 0xffff)
10405 has_large_score_values = TRUE;
10407 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10408 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10410 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10411 SaveScore_VERS(file, &scores);
10413 putFileChunkBE(file, "INFO", info_chunk_size);
10414 SaveScore_INFO(file, &scores);
10416 putFileChunkBE(file, "NAME", name_chunk_size);
10417 SaveScore_NAME(file, &scores);
10419 if (has_large_score_values)
10421 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10422 SaveScore_SC4R(file, &scores);
10426 putFileChunkBE(file, "SCOR", scor_chunk_size);
10427 SaveScore_SCOR(file, &scores);
10430 putFileChunkBE(file, "TIME", time_chunk_size);
10431 SaveScore_TIME(file, &scores);
10433 putFileChunkBE(file, "TAPE", tape_chunk_size);
10434 SaveScore_TAPE(file, &scores);
10438 SetFilePermissions(filename, PERMS_PRIVATE);
10441 void SaveScore(int nr)
10443 char *filename = getScoreFilename(nr);
10446 // used instead of "leveldir_current->subdir" (for network games)
10447 InitScoreDirectory(levelset.identifier);
10449 scores.file_version = FILE_VERSION_ACTUAL;
10450 scores.game_version = GAME_VERSION_ACTUAL;
10452 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10453 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10454 scores.level_nr = level_nr;
10456 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10457 if (scores.entry[i].score == 0 &&
10458 scores.entry[i].time == 0 &&
10459 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10462 scores.num_entries = i;
10464 if (scores.num_entries == 0)
10467 SaveScoreToFilename(filename);
10470 static void LoadServerScoreFromCache(int nr)
10472 struct ScoreEntry score_entry;
10481 { &score_entry.score, FALSE, 0 },
10482 { &score_entry.time, FALSE, 0 },
10483 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10484 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10485 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10486 { &score_entry.id, FALSE, 0 },
10487 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10488 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10489 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10490 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10494 char *filename = getScoreCacheFilename(nr);
10495 SetupFileHash *score_hash = loadSetupFileHash(filename);
10498 server_scores.num_entries = 0;
10500 if (score_hash == NULL)
10503 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10505 score_entry = server_scores.entry[i];
10507 for (j = 0; score_mapping[j].value != NULL; j++)
10511 sprintf(token, "%02d.%d", i, j);
10513 char *value = getHashEntry(score_hash, token);
10518 if (score_mapping[j].is_string)
10520 char *score_value = (char *)score_mapping[j].value;
10521 int value_size = score_mapping[j].string_size;
10523 strncpy(score_value, value, value_size);
10524 score_value[value_size] = '\0';
10528 int *score_value = (int *)score_mapping[j].value;
10530 *score_value = atoi(value);
10533 server_scores.num_entries = i + 1;
10536 server_scores.entry[i] = score_entry;
10539 freeSetupFileHash(score_hash);
10542 void LoadServerScore(int nr, boolean download_score)
10544 if (!setup.use_api_server)
10547 // always start with reliable default values
10548 setServerScoreInfoToDefaults();
10550 // 1st step: load server scores from cache file (which may not exist)
10551 // (this should prevent reading it while the thread is writing to it)
10552 LoadServerScoreFromCache(nr);
10554 if (download_score && runtime.use_api_server)
10556 // 2nd step: download server scores from score server to cache file
10557 // (as thread, as it might time out if the server is not reachable)
10558 ApiGetScoreAsThread(nr);
10562 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10564 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10566 // if score tape not uploaded, ask for uploading missing tapes later
10567 if (!setup.has_remaining_tapes)
10568 setup.ask_for_remaining_tapes = TRUE;
10570 setup.provide_uploading_tapes = TRUE;
10571 setup.has_remaining_tapes = TRUE;
10573 SaveSetup_ServerSetup();
10576 void SaveServerScore(int nr, boolean tape_saved)
10578 if (!runtime.use_api_server)
10580 PrepareScoreTapesForUpload(leveldir_current->subdir);
10585 ApiAddScoreAsThread(nr, tape_saved, NULL);
10588 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10589 char *score_tape_filename)
10591 if (!runtime.use_api_server)
10594 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10597 void LoadLocalAndServerScore(int nr, boolean download_score)
10599 int last_added_local = scores.last_added_local;
10600 boolean force_last_added = scores.force_last_added;
10602 // needed if only showing server scores
10603 setScoreInfoToDefaults();
10605 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10608 // restore last added local score entry (before merging server scores)
10609 scores.last_added = scores.last_added_local = last_added_local;
10611 if (setup.use_api_server &&
10612 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10614 // load server scores from cache file and trigger update from server
10615 LoadServerScore(nr, download_score);
10617 // merge local scores with scores from server
10618 MergeServerScore();
10621 if (force_last_added)
10622 scores.force_last_added = force_last_added;
10626 // ============================================================================
10627 // setup file functions
10628 // ============================================================================
10630 #define TOKEN_STR_PLAYER_PREFIX "player_"
10633 static struct TokenInfo global_setup_tokens[] =
10637 &setup.player_name, "player_name"
10641 &setup.multiple_users, "multiple_users"
10645 &setup.sound, "sound"
10649 &setup.sound_loops, "repeating_sound_loops"
10653 &setup.sound_music, "background_music"
10657 &setup.sound_simple, "simple_sound_effects"
10661 &setup.toons, "toons"
10665 &setup.global_animations, "global_animations"
10669 &setup.scroll_delay, "scroll_delay"
10673 &setup.forced_scroll_delay, "forced_scroll_delay"
10677 &setup.scroll_delay_value, "scroll_delay_value"
10681 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10685 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10689 &setup.fade_screens, "fade_screens"
10693 &setup.autorecord, "automatic_tape_recording"
10697 &setup.autorecord_after_replay, "autorecord_after_replay"
10701 &setup.auto_pause_on_start, "auto_pause_on_start"
10705 &setup.show_titlescreen, "show_titlescreen"
10709 &setup.quick_doors, "quick_doors"
10713 &setup.team_mode, "team_mode"
10717 &setup.handicap, "handicap"
10721 &setup.skip_levels, "skip_levels"
10724 TYPE_SWITCH_3_STATES,
10725 &setup.allow_skipping_levels, "allow_skipping_levels"
10729 &setup.increment_levels, "increment_levels"
10733 &setup.auto_play_next_level, "auto_play_next_level"
10737 &setup.count_score_after_game, "count_score_after_game"
10741 &setup.show_scores_after_game, "show_scores_after_game"
10745 &setup.time_limit, "time_limit"
10749 &setup.fullscreen, "fullscreen"
10753 &setup.window_scaling_percent, "window_scaling_percent"
10757 &setup.window_scaling_quality, "window_scaling_quality"
10761 &setup.screen_rendering_mode, "screen_rendering_mode"
10765 &setup.vsync_mode, "vsync_mode"
10769 &setup.ask_on_escape, "ask_on_escape"
10773 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10777 &setup.ask_on_game_over, "ask_on_game_over"
10781 &setup.ask_on_quit_game, "ask_on_quit_game"
10785 &setup.ask_on_quit_program, "ask_on_quit_program"
10789 &setup.quick_switch, "quick_player_switch"
10793 &setup.input_on_focus, "input_on_focus"
10797 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10801 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10805 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10809 &setup.game_speed_extended, "game_speed_extended"
10813 &setup.game_frame_delay, "game_frame_delay"
10817 &setup.default_game_engine_type, "default_game_engine_type"
10821 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10825 &setup.bd_skip_hatching, "bd_skip_hatching"
10829 &setup.bd_scroll_delay, "bd_scroll_delay"
10833 &setup.bd_show_invisible_outbox, "bd_show_invisible_outbox"
10836 TYPE_SWITCH_3_STATES,
10837 &setup.bd_smooth_movements, "bd_smooth_movements"
10840 TYPE_SWITCH_3_STATES,
10841 &setup.bd_pushing_graphics, "bd_pushing_graphics"
10844 TYPE_SWITCH_3_STATES,
10845 &setup.bd_up_down_graphics, "bd_up_down_graphics"
10848 TYPE_SWITCH_3_STATES,
10849 &setup.bd_falling_sounds, "bd_falling_sounds"
10853 &setup.bd_palette_c64, "bd_palette_c64"
10857 &setup.bd_palette_c64dtv, "bd_palette_c64dtv"
10861 &setup.bd_palette_atari, "bd_palette_atari"
10865 &setup.bd_default_color_type, "bd_default_color_type"
10869 &setup.bd_random_colors, "bd_random_colors"
10873 &setup.sp_show_border_elements, "sp_show_border_elements"
10877 &setup.small_game_graphics, "small_game_graphics"
10881 &setup.show_load_save_buttons, "show_load_save_buttons"
10885 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10889 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10893 &setup.graphics_set, "graphics_set"
10897 &setup.sounds_set, "sounds_set"
10901 &setup.music_set, "music_set"
10904 TYPE_SWITCH_3_STATES,
10905 &setup.override_level_graphics, "override_level_graphics"
10908 TYPE_SWITCH_3_STATES,
10909 &setup.override_level_sounds, "override_level_sounds"
10912 TYPE_SWITCH_3_STATES,
10913 &setup.override_level_music, "override_level_music"
10917 &setup.volume_simple, "volume_simple"
10921 &setup.volume_loops, "volume_loops"
10925 &setup.volume_music, "volume_music"
10929 &setup.audio_sample_rate_44100, "audio_sample_rate_44100"
10933 &setup.network_mode, "network_mode"
10937 &setup.network_player_nr, "network_player"
10941 &setup.network_server_hostname, "network_server_hostname"
10945 &setup.touch.control_type, "touch.control_type"
10949 &setup.touch.move_distance, "touch.move_distance"
10953 &setup.touch.drop_distance, "touch.drop_distance"
10957 &setup.touch.transparency, "touch.transparency"
10961 &setup.touch.draw_outlined, "touch.draw_outlined"
10965 &setup.touch.draw_pressed, "touch.draw_pressed"
10969 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10973 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10977 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10981 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10985 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10989 static struct TokenInfo auto_setup_tokens[] =
10993 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10997 static struct TokenInfo server_setup_tokens[] =
11001 &setup.player_uuid, "player_uuid"
11005 &setup.player_version, "player_version"
11009 &setup.use_api_server, TEST_PREFIX "use_api_server"
11013 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
11017 &setup.api_server_password, TEST_PREFIX "api_server_password"
11021 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
11025 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
11029 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
11033 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
11037 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
11041 static struct TokenInfo editor_setup_tokens[] =
11045 &setup.editor.el_classic, "editor.el_classic"
11049 &setup.editor.el_custom, "editor.el_custom"
11053 &setup.editor.el_user_defined, "editor.el_user_defined"
11057 &setup.editor.el_dynamic, "editor.el_dynamic"
11061 &setup.editor.el_headlines, "editor.el_headlines"
11065 &setup.editor.show_element_token, "editor.show_element_token"
11069 &setup.editor.fast_game_start, "editor.fast_game_start"
11073 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
11077 static struct TokenInfo editor_cascade_setup_tokens[] =
11081 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
11085 &setup.editor_cascade.el_bdx, "editor.cascade.el_bdx"
11089 &setup.editor_cascade.el_bdx_effects, "editor.cascade.el_bdx_effects"
11093 &setup.editor_cascade.el_em, "editor.cascade.el_em"
11097 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
11101 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
11105 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
11109 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
11113 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
11117 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
11121 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
11125 &setup.editor_cascade.el_df, "editor.cascade.el_df"
11129 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
11133 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
11137 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
11141 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
11145 &setup.editor_cascade.el_es, "editor.cascade.el_es"
11149 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
11153 &setup.editor_cascade.el_user, "editor.cascade.el_user"
11157 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
11161 static struct TokenInfo shortcut_setup_tokens[] =
11165 &setup.shortcut.save_game, "shortcut.save_game"
11169 &setup.shortcut.load_game, "shortcut.load_game"
11173 &setup.shortcut.restart_game, "shortcut.restart_game"
11177 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
11181 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
11185 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
11189 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
11193 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
11197 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
11201 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
11205 &setup.shortcut.tape_eject, "shortcut.tape_eject"
11209 &setup.shortcut.tape_extra, "shortcut.tape_extra"
11213 &setup.shortcut.tape_stop, "shortcut.tape_stop"
11217 &setup.shortcut.tape_pause, "shortcut.tape_pause"
11221 &setup.shortcut.tape_record, "shortcut.tape_record"
11225 &setup.shortcut.tape_play, "shortcut.tape_play"
11229 &setup.shortcut.sound_simple, "shortcut.sound_simple"
11233 &setup.shortcut.sound_loops, "shortcut.sound_loops"
11237 &setup.shortcut.sound_music, "shortcut.sound_music"
11241 &setup.shortcut.snap_left, "shortcut.snap_left"
11245 &setup.shortcut.snap_right, "shortcut.snap_right"
11249 &setup.shortcut.snap_up, "shortcut.snap_up"
11253 &setup.shortcut.snap_down, "shortcut.snap_down"
11257 &setup.shortcut.speed_fast, "shortcut.speed_fast"
11261 &setup.shortcut.speed_slow, "shortcut.speed_slow"
11265 static struct SetupInputInfo setup_input;
11266 static struct TokenInfo player_setup_tokens[] =
11270 &setup_input.use_joystick, ".use_joystick"
11274 &setup_input.joy.device_name, ".joy.device_name"
11278 &setup_input.joy.xleft, ".joy.xleft"
11282 &setup_input.joy.xmiddle, ".joy.xmiddle"
11286 &setup_input.joy.xright, ".joy.xright"
11290 &setup_input.joy.yupper, ".joy.yupper"
11294 &setup_input.joy.ymiddle, ".joy.ymiddle"
11298 &setup_input.joy.ylower, ".joy.ylower"
11302 &setup_input.joy.snap, ".joy.snap_field"
11306 &setup_input.joy.drop, ".joy.place_bomb"
11310 &setup_input.key.left, ".key.move_left"
11314 &setup_input.key.right, ".key.move_right"
11318 &setup_input.key.up, ".key.move_up"
11322 &setup_input.key.down, ".key.move_down"
11326 &setup_input.key.snap, ".key.snap_field"
11330 &setup_input.key.drop, ".key.place_bomb"
11334 static struct TokenInfo system_setup_tokens[] =
11338 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
11342 &setup.system.sdl_videodriver, "system.sdl_videodriver"
11346 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
11350 &setup.system.audio_fragment_size, "system.audio_fragment_size"
11354 static struct TokenInfo internal_setup_tokens[] =
11358 &setup.internal.program_title, "program_title"
11362 &setup.internal.program_version, "program_version"
11366 &setup.internal.program_author, "program_author"
11370 &setup.internal.program_email, "program_email"
11374 &setup.internal.program_website, "program_website"
11378 &setup.internal.program_copyright, "program_copyright"
11382 &setup.internal.program_company, "program_company"
11386 &setup.internal.program_icon_file, "program_icon_file"
11390 &setup.internal.default_graphics_set, "default_graphics_set"
11394 &setup.internal.default_sounds_set, "default_sounds_set"
11398 &setup.internal.default_music_set, "default_music_set"
11402 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11406 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11410 &setup.internal.fallback_music_file, "fallback_music_file"
11414 &setup.internal.default_level_series, "default_level_series"
11418 &setup.internal.default_window_width, "default_window_width"
11422 &setup.internal.default_window_height, "default_window_height"
11426 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11430 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11434 &setup.internal.create_user_levelset, "create_user_levelset"
11438 &setup.internal.info_screens_from_main, "info_screens_from_main"
11442 &setup.internal.menu_game, "menu_game"
11446 &setup.internal.menu_engines, "menu_engines"
11450 &setup.internal.menu_editor, "menu_editor"
11454 &setup.internal.menu_graphics, "menu_graphics"
11458 &setup.internal.menu_sound, "menu_sound"
11462 &setup.internal.menu_artwork, "menu_artwork"
11466 &setup.internal.menu_input, "menu_input"
11470 &setup.internal.menu_touch, "menu_touch"
11474 &setup.internal.menu_shortcuts, "menu_shortcuts"
11478 &setup.internal.menu_exit, "menu_exit"
11482 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11486 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11490 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11494 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11498 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11502 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11506 &setup.internal.menu_shortcuts_speed, "menu_shortcuts_speed"
11510 &setup.internal.info_title, "info_title"
11514 &setup.internal.info_elements, "info_elements"
11518 &setup.internal.info_music, "info_music"
11522 &setup.internal.info_credits, "info_credits"
11526 &setup.internal.info_program, "info_program"
11530 &setup.internal.info_version, "info_version"
11534 &setup.internal.info_levelset, "info_levelset"
11538 &setup.internal.info_exit, "info_exit"
11542 static struct TokenInfo debug_setup_tokens[] =
11546 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11550 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11554 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11558 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11562 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11566 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11570 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11574 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11578 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11582 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11586 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11590 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11594 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11598 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11602 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11606 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11610 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11614 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11618 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11622 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11626 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11629 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11633 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11636 TYPE_SWITCH_3_STATES,
11637 &setup.debug.xsn_mode, "debug.xsn_mode"
11641 &setup.debug.xsn_percent, "debug.xsn_percent"
11645 static struct TokenInfo options_setup_tokens[] =
11649 &setup.options.verbose, "options.verbose"
11653 &setup.options.debug, "options.debug"
11657 &setup.options.debug_mode, "options.debug_mode"
11661 static void setSetupInfoToDefaults(struct SetupInfo *si)
11665 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11667 si->multiple_users = TRUE;
11670 si->sound_loops = TRUE;
11671 si->sound_music = TRUE;
11672 si->sound_simple = TRUE;
11674 si->global_animations = TRUE;
11675 si->scroll_delay = TRUE;
11676 si->forced_scroll_delay = FALSE;
11677 si->scroll_delay_value = STD_SCROLL_DELAY;
11678 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11679 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11680 si->fade_screens = TRUE;
11681 si->autorecord = TRUE;
11682 si->autorecord_after_replay = TRUE;
11683 si->auto_pause_on_start = FALSE;
11684 si->show_titlescreen = TRUE;
11685 si->quick_doors = FALSE;
11686 si->team_mode = FALSE;
11687 si->handicap = TRUE;
11688 si->skip_levels = TRUE;
11689 si->allow_skipping_levels = STATE_ASK;
11690 si->increment_levels = TRUE;
11691 si->auto_play_next_level = TRUE;
11692 si->count_score_after_game = TRUE;
11693 si->show_scores_after_game = TRUE;
11694 si->time_limit = TRUE;
11695 si->fullscreen = FALSE;
11696 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11697 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11698 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11699 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11700 si->ask_on_escape = TRUE;
11701 si->ask_on_escape_editor = TRUE;
11702 si->ask_on_game_over = TRUE;
11703 si->ask_on_quit_game = TRUE;
11704 si->ask_on_quit_program = TRUE;
11705 si->quick_switch = FALSE;
11706 si->input_on_focus = FALSE;
11707 si->prefer_aga_graphics = TRUE;
11708 si->prefer_lowpass_sounds = FALSE;
11709 si->prefer_extra_panel_items = TRUE;
11710 si->game_speed_extended = FALSE;
11711 si->game_frame_delay = GAME_FRAME_DELAY;
11712 si->default_game_engine_type = GAME_ENGINE_TYPE_RND;
11713 si->bd_skip_uncovering = FALSE;
11714 si->bd_skip_hatching = FALSE;
11715 si->bd_scroll_delay = TRUE;
11716 si->bd_show_invisible_outbox = FALSE;
11717 si->bd_smooth_movements = STATE_TRUE;
11718 si->bd_pushing_graphics = STATE_TRUE;
11719 si->bd_up_down_graphics = STATE_TRUE;
11720 si->bd_falling_sounds = STATE_AUTO;
11721 si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11722 si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11723 si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11724 si->bd_default_color_type = GD_DEFAULT_COLOR_TYPE;
11725 si->bd_random_colors = FALSE;
11726 si->sp_show_border_elements = FALSE;
11727 si->small_game_graphics = FALSE;
11728 si->show_load_save_buttons = FALSE;
11729 si->show_undo_redo_buttons = FALSE;
11730 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11732 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11733 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11734 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11736 si->override_level_graphics = STATE_FALSE;
11737 si->override_level_sounds = STATE_FALSE;
11738 si->override_level_music = STATE_FALSE;
11740 si->volume_simple = 100; // percent
11741 si->volume_loops = 100; // percent
11742 si->volume_music = 100; // percent
11743 si->audio_sample_rate_44100 = FALSE;
11745 si->network_mode = FALSE;
11746 si->network_player_nr = 0; // first player
11747 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11749 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11750 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11751 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11752 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11753 si->touch.draw_outlined = TRUE;
11754 si->touch.draw_pressed = TRUE;
11756 for (i = 0; i < 2; i++)
11758 char *default_grid_button[6][2] =
11764 { "111222", " vv " },
11765 { "111222", " vv " }
11767 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11768 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11769 int min_xsize = MIN(6, grid_xsize);
11770 int min_ysize = MIN(6, grid_ysize);
11771 int startx = grid_xsize - min_xsize;
11772 int starty = grid_ysize - min_ysize;
11775 // virtual buttons grid can only be set to defaults if video is initialized
11776 // (this will be repeated if virtual buttons are not loaded from setup file)
11777 if (video.initialized)
11779 si->touch.grid_xsize[i] = grid_xsize;
11780 si->touch.grid_ysize[i] = grid_ysize;
11784 si->touch.grid_xsize[i] = -1;
11785 si->touch.grid_ysize[i] = -1;
11788 for (x = 0; x < MAX_GRID_XSIZE; x++)
11789 for (y = 0; y < MAX_GRID_YSIZE; y++)
11790 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11792 for (x = 0; x < min_xsize; x++)
11793 for (y = 0; y < min_ysize; y++)
11794 si->touch.grid_button[i][x][starty + y] =
11795 default_grid_button[y][0][x];
11797 for (x = 0; x < min_xsize; x++)
11798 for (y = 0; y < min_ysize; y++)
11799 si->touch.grid_button[i][startx + x][starty + y] =
11800 default_grid_button[y][1][x];
11803 si->touch.grid_initialized = video.initialized;
11805 si->touch.overlay_buttons = FALSE;
11807 si->editor.el_boulderdash = TRUE;
11808 si->editor.el_boulderdash_native = TRUE;
11809 si->editor.el_boulderdash_effects = TRUE;
11810 si->editor.el_emerald_mine = TRUE;
11811 si->editor.el_emerald_mine_club = TRUE;
11812 si->editor.el_more = TRUE;
11813 si->editor.el_sokoban = TRUE;
11814 si->editor.el_supaplex = TRUE;
11815 si->editor.el_diamond_caves = TRUE;
11816 si->editor.el_dx_boulderdash = TRUE;
11818 si->editor.el_mirror_magic = TRUE;
11819 si->editor.el_deflektor = TRUE;
11821 si->editor.el_chars = TRUE;
11822 si->editor.el_steel_chars = TRUE;
11824 si->editor.el_classic = TRUE;
11825 si->editor.el_custom = TRUE;
11827 si->editor.el_user_defined = FALSE;
11828 si->editor.el_dynamic = TRUE;
11830 si->editor.el_headlines = TRUE;
11832 si->editor.show_element_token = FALSE;
11833 si->editor.fast_game_start = FALSE;
11835 si->editor.show_read_only_warning = TRUE;
11837 si->editor.use_template_for_new_levels = TRUE;
11839 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11840 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11841 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11842 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11843 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11845 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11846 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11847 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11848 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11849 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11851 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11852 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11853 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11854 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11855 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11856 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11858 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11859 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11860 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11862 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11863 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11864 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11865 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11867 si->shortcut.speed_fast = DEFAULT_KEY_SPEED_FAST;
11868 si->shortcut.speed_slow = DEFAULT_KEY_SPEED_SLOW;
11870 for (i = 0; i < MAX_PLAYERS; i++)
11872 si->input[i].use_joystick = FALSE;
11873 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11874 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11875 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11876 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11877 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11878 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11879 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11880 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11881 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11882 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11883 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11884 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11885 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11886 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11887 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11890 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11891 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11892 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11893 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11895 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11896 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11897 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11898 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11899 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11900 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11901 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11903 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11905 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11906 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11907 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11909 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11910 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11911 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11913 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11914 si->internal.choose_from_top_leveldir = FALSE;
11915 si->internal.show_scaling_in_title = TRUE;
11916 si->internal.create_user_levelset = TRUE;
11917 si->internal.info_screens_from_main = FALSE;
11919 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11920 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11922 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11923 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11924 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11925 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11926 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11927 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11928 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11929 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11930 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11931 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11933 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11934 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11935 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11936 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11937 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11938 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11939 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11940 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11941 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11942 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11944 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11945 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11947 si->debug.show_frames_per_second = FALSE;
11949 si->debug.xsn_mode = STATE_AUTO;
11950 si->debug.xsn_percent = 0;
11952 si->options.verbose = FALSE;
11953 si->options.debug = FALSE;
11954 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11956 #if defined(PLATFORM_ANDROID)
11957 si->fullscreen = TRUE;
11958 si->touch.overlay_buttons = TRUE;
11961 setHideSetupEntry(&setup.debug.xsn_mode);
11964 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11966 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11969 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11971 si->player_uuid = NULL; // (will be set later)
11972 si->player_version = 1; // (will be set later)
11974 si->use_api_server = TRUE;
11975 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11976 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11977 si->ask_for_uploading_tapes = TRUE;
11978 si->ask_for_remaining_tapes = FALSE;
11979 si->provide_uploading_tapes = TRUE;
11980 si->ask_for_using_api_server = TRUE;
11981 si->has_remaining_tapes = FALSE;
11984 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11986 si->editor_cascade.el_bd = TRUE;
11987 si->editor_cascade.el_bdx = TRUE;
11988 si->editor_cascade.el_bdx_effects = FALSE;
11989 si->editor_cascade.el_em = TRUE;
11990 si->editor_cascade.el_emc = TRUE;
11991 si->editor_cascade.el_rnd = TRUE;
11992 si->editor_cascade.el_sb = TRUE;
11993 si->editor_cascade.el_sp = TRUE;
11994 si->editor_cascade.el_dc = TRUE;
11995 si->editor_cascade.el_dx = TRUE;
11997 si->editor_cascade.el_mm = TRUE;
11998 si->editor_cascade.el_df = TRUE;
12000 si->editor_cascade.el_chars = FALSE;
12001 si->editor_cascade.el_steel_chars = FALSE;
12002 si->editor_cascade.el_ce = FALSE;
12003 si->editor_cascade.el_ge = FALSE;
12004 si->editor_cascade.el_es = FALSE;
12005 si->editor_cascade.el_ref = FALSE;
12006 si->editor_cascade.el_user = FALSE;
12007 si->editor_cascade.el_dynamic = FALSE;
12010 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
12012 static char *getHideSetupToken(void *setup_value)
12014 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
12016 if (setup_value != NULL)
12017 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
12019 return hide_setup_token;
12022 void setHideSetupEntry(void *setup_value)
12024 char *hide_setup_token = getHideSetupToken(setup_value);
12026 if (hide_setup_hash == NULL)
12027 hide_setup_hash = newSetupFileHash();
12029 if (setup_value != NULL)
12030 setHashEntry(hide_setup_hash, hide_setup_token, "");
12033 void removeHideSetupEntry(void *setup_value)
12035 char *hide_setup_token = getHideSetupToken(setup_value);
12037 if (setup_value != NULL)
12038 removeHashEntry(hide_setup_hash, hide_setup_token);
12041 boolean hideSetupEntry(void *setup_value)
12043 char *hide_setup_token = getHideSetupToken(setup_value);
12045 return (setup_value != NULL &&
12046 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
12049 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
12050 struct TokenInfo *token_info,
12051 int token_nr, char *token_text)
12053 char *token_hide_text = getStringCat2(token_text, ".hide");
12054 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
12056 // set the value of this setup option in the setup option structure
12057 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
12059 // check if this setup option should be hidden in the setup menu
12060 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
12061 setHideSetupEntry(token_info[token_nr].value);
12063 free(token_hide_text);
12066 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
12067 struct TokenInfo *token_info,
12070 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
12071 token_info[token_nr].text);
12074 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
12078 if (!setup_file_hash)
12081 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12082 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
12084 setup.touch.grid_initialized = TRUE;
12085 for (i = 0; i < 2; i++)
12087 int grid_xsize = setup.touch.grid_xsize[i];
12088 int grid_ysize = setup.touch.grid_ysize[i];
12091 // if virtual buttons are not loaded from setup file, repeat initializing
12092 // virtual buttons grid with default values later when video is initialized
12093 if (grid_xsize == -1 ||
12096 setup.touch.grid_initialized = FALSE;
12101 for (y = 0; y < grid_ysize; y++)
12103 char token_string[MAX_LINE_LEN];
12105 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12107 char *value_string = getHashEntry(setup_file_hash, token_string);
12109 if (value_string == NULL)
12112 for (x = 0; x < grid_xsize; x++)
12114 char c = value_string[x];
12116 setup.touch.grid_button[i][x][y] =
12117 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
12122 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12123 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
12125 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12126 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
12128 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12132 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12134 setup_input = setup.input[pnr];
12135 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12137 char full_token[100];
12139 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
12140 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12143 setup.input[pnr] = setup_input;
12146 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12147 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12149 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12150 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12152 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12153 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12155 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12156 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12158 setHideRelatedSetupEntries();
12161 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12165 if (!setup_file_hash)
12168 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12169 setSetupInfo(auto_setup_tokens, i,
12170 getHashEntry(setup_file_hash,
12171 auto_setup_tokens[i].text));
12174 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12178 if (!setup_file_hash)
12181 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12182 setSetupInfo(server_setup_tokens, i,
12183 getHashEntry(setup_file_hash,
12184 server_setup_tokens[i].text));
12187 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12191 if (!setup_file_hash)
12194 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12195 setSetupInfo(editor_cascade_setup_tokens, i,
12196 getHashEntry(setup_file_hash,
12197 editor_cascade_setup_tokens[i].text));
12200 void LoadUserNames(void)
12202 int last_user_nr = user.nr;
12205 if (global.user_names != NULL)
12207 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12208 checked_free(global.user_names[i]);
12210 checked_free(global.user_names);
12213 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12215 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12219 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12221 if (setup_file_hash)
12223 char *player_name = getHashEntry(setup_file_hash, "player_name");
12225 global.user_names[i] = getFixedUserName(player_name);
12227 freeSetupFileHash(setup_file_hash);
12230 if (global.user_names[i] == NULL)
12231 global.user_names[i] = getStringCopy(getDefaultUserName(i));
12234 user.nr = last_user_nr;
12237 void LoadSetupFromFilename(char *filename)
12239 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12241 if (setup_file_hash)
12243 decodeSetupFileHash_Default(setup_file_hash);
12245 freeSetupFileHash(setup_file_hash);
12249 Debug("setup", "using default setup values");
12253 static void LoadSetup_SpecialPostProcessing(void)
12255 char *player_name_new;
12257 // needed to work around problems with fixed length strings
12258 player_name_new = getFixedUserName(setup.player_name);
12259 free(setup.player_name);
12260 setup.player_name = player_name_new;
12262 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12263 if (setup.scroll_delay == FALSE)
12265 setup.scroll_delay_value = MIN_SCROLL_DELAY;
12266 setup.scroll_delay = TRUE; // now always "on"
12269 // make sure that scroll delay value stays inside valid range
12270 setup.scroll_delay_value =
12271 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12274 void LoadSetup_Default(void)
12278 // always start with reliable default values
12279 setSetupInfoToDefaults(&setup);
12281 // try to load setup values from default setup file
12282 filename = getDefaultSetupFilename();
12284 if (fileExists(filename))
12285 LoadSetupFromFilename(filename);
12287 // try to load setup values from platform setup file
12288 filename = getPlatformSetupFilename();
12290 if (fileExists(filename))
12291 LoadSetupFromFilename(filename);
12293 // try to load setup values from user setup file
12294 filename = getSetupFilename();
12296 LoadSetupFromFilename(filename);
12298 LoadSetup_SpecialPostProcessing();
12301 void LoadSetup_AutoSetup(void)
12303 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12304 SetupFileHash *setup_file_hash = NULL;
12306 // always start with reliable default values
12307 setSetupInfoToDefaults_AutoSetup(&setup);
12309 setup_file_hash = loadSetupFileHash(filename);
12311 if (setup_file_hash)
12313 decodeSetupFileHash_AutoSetup(setup_file_hash);
12315 freeSetupFileHash(setup_file_hash);
12321 void LoadSetup_ServerSetup(void)
12323 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12324 SetupFileHash *setup_file_hash = NULL;
12326 // always start with reliable default values
12327 setSetupInfoToDefaults_ServerSetup(&setup);
12329 setup_file_hash = loadSetupFileHash(filename);
12331 if (setup_file_hash)
12333 decodeSetupFileHash_ServerSetup(setup_file_hash);
12335 freeSetupFileHash(setup_file_hash);
12340 if (setup.player_uuid == NULL)
12342 // player UUID does not yet exist in setup file
12343 setup.player_uuid = getStringCopy(getUUID());
12344 setup.player_version = 2;
12346 SaveSetup_ServerSetup();
12350 void LoadSetup_EditorCascade(void)
12352 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12353 SetupFileHash *setup_file_hash = NULL;
12355 // always start with reliable default values
12356 setSetupInfoToDefaults_EditorCascade(&setup);
12358 setup_file_hash = loadSetupFileHash(filename);
12360 if (setup_file_hash)
12362 decodeSetupFileHash_EditorCascade(setup_file_hash);
12364 freeSetupFileHash(setup_file_hash);
12370 void LoadSetup(void)
12372 LoadSetup_Default();
12373 LoadSetup_AutoSetup();
12374 LoadSetup_ServerSetup();
12375 LoadSetup_EditorCascade();
12378 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12379 char *mapping_line)
12381 char mapping_guid[MAX_LINE_LEN];
12382 char *mapping_start, *mapping_end;
12384 // get GUID from game controller mapping line: copy complete line
12385 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12386 mapping_guid[MAX_LINE_LEN - 1] = '\0';
12388 // get GUID from game controller mapping line: cut after GUID part
12389 mapping_start = strchr(mapping_guid, ',');
12390 if (mapping_start != NULL)
12391 *mapping_start = '\0';
12393 // cut newline from game controller mapping line
12394 mapping_end = strchr(mapping_line, '\n');
12395 if (mapping_end != NULL)
12396 *mapping_end = '\0';
12398 // add mapping entry to game controller mappings hash
12399 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12402 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12407 if (!(file = fopen(filename, MODE_READ)))
12409 Warn("cannot read game controller mappings file '%s'", filename);
12414 while (!feof(file))
12416 char line[MAX_LINE_LEN];
12418 if (!fgets(line, MAX_LINE_LEN, file))
12421 addGameControllerMappingToHash(mappings_hash, line);
12427 void SaveSetup_Default(void)
12429 char *filename = getSetupFilename();
12433 InitUserDataDirectory();
12435 if (!(file = fopen(filename, MODE_WRITE)))
12437 Warn("cannot write setup file '%s'", filename);
12442 fprintFileHeader(file, SETUP_FILENAME);
12444 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12446 // just to make things nicer :)
12447 if (global_setup_tokens[i].value == &setup.multiple_users ||
12448 global_setup_tokens[i].value == &setup.sound ||
12449 global_setup_tokens[i].value == &setup.graphics_set ||
12450 global_setup_tokens[i].value == &setup.volume_simple ||
12451 global_setup_tokens[i].value == &setup.network_mode ||
12452 global_setup_tokens[i].value == &setup.touch.control_type ||
12453 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12454 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12455 fprintf(file, "\n");
12457 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12460 for (i = 0; i < 2; i++)
12462 int grid_xsize = setup.touch.grid_xsize[i];
12463 int grid_ysize = setup.touch.grid_ysize[i];
12466 fprintf(file, "\n");
12468 for (y = 0; y < grid_ysize; y++)
12470 char token_string[MAX_LINE_LEN];
12471 char value_string[MAX_LINE_LEN];
12473 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12475 for (x = 0; x < grid_xsize; x++)
12477 char c = setup.touch.grid_button[i][x][y];
12479 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12482 value_string[grid_xsize] = '\0';
12484 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12488 fprintf(file, "\n");
12489 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12490 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12492 fprintf(file, "\n");
12493 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12494 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12496 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12500 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12501 fprintf(file, "\n");
12503 setup_input = setup.input[pnr];
12504 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12505 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12508 fprintf(file, "\n");
12509 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12510 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12512 // (internal setup values not saved to user setup file)
12514 fprintf(file, "\n");
12515 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12516 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12517 setup.debug.xsn_mode != STATE_AUTO)
12518 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12520 fprintf(file, "\n");
12521 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12522 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12526 SetFilePermissions(filename, PERMS_PRIVATE);
12529 void SaveSetup_AutoSetup(void)
12531 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12535 InitUserDataDirectory();
12537 if (!(file = fopen(filename, MODE_WRITE)))
12539 Warn("cannot write auto setup file '%s'", filename);
12546 fprintFileHeader(file, AUTOSETUP_FILENAME);
12548 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12549 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12553 SetFilePermissions(filename, PERMS_PRIVATE);
12558 void SaveSetup_ServerSetup(void)
12560 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12564 InitUserDataDirectory();
12566 if (!(file = fopen(filename, MODE_WRITE)))
12568 Warn("cannot write server setup file '%s'", filename);
12575 fprintFileHeader(file, SERVERSETUP_FILENAME);
12577 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12579 // just to make things nicer :)
12580 if (server_setup_tokens[i].value == &setup.use_api_server)
12581 fprintf(file, "\n");
12583 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12588 SetFilePermissions(filename, PERMS_PRIVATE);
12593 void SaveSetup_EditorCascade(void)
12595 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12599 InitUserDataDirectory();
12601 if (!(file = fopen(filename, MODE_WRITE)))
12603 Warn("cannot write editor cascade state file '%s'", filename);
12610 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12612 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12613 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12617 SetFilePermissions(filename, PERMS_PRIVATE);
12622 void SaveSetup(void)
12624 SaveSetup_Default();
12625 SaveSetup_AutoSetup();
12626 SaveSetup_ServerSetup();
12627 SaveSetup_EditorCascade();
12630 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12635 if (!(file = fopen(filename, MODE_WRITE)))
12637 Warn("cannot write game controller mappings file '%s'", filename);
12642 BEGIN_HASH_ITERATION(mappings_hash, itr)
12644 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12646 END_HASH_ITERATION(mappings_hash, itr)
12651 void SaveSetup_AddGameControllerMapping(char *mapping)
12653 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12654 SetupFileHash *mappings_hash = newSetupFileHash();
12656 InitUserDataDirectory();
12658 // load existing personal game controller mappings
12659 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12661 // add new mapping to personal game controller mappings
12662 addGameControllerMappingToHash(mappings_hash, mapping);
12664 // save updated personal game controller mappings
12665 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12667 freeSetupFileHash(mappings_hash);
12671 void LoadCustomElementDescriptions(void)
12673 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12674 SetupFileHash *setup_file_hash;
12677 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12679 if (element_info[i].custom_description != NULL)
12681 free(element_info[i].custom_description);
12682 element_info[i].custom_description = NULL;
12686 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12689 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12691 char *token = getStringCat2(element_info[i].token_name, ".name");
12692 char *value = getHashEntry(setup_file_hash, token);
12695 element_info[i].custom_description = getStringCopy(value);
12700 freeSetupFileHash(setup_file_hash);
12703 static int getElementFromToken(char *token)
12705 char *value = getHashEntry(element_token_hash, token);
12708 return atoi(value);
12710 Warn("unknown element token '%s'", token);
12712 return EL_UNDEFINED;
12715 void FreeGlobalAnimEventInfo(void)
12717 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12719 if (gaei->event_list == NULL)
12724 for (i = 0; i < gaei->num_event_lists; i++)
12726 checked_free(gaei->event_list[i]->event_value);
12727 checked_free(gaei->event_list[i]);
12730 checked_free(gaei->event_list);
12732 gaei->event_list = NULL;
12733 gaei->num_event_lists = 0;
12736 static int AddGlobalAnimEventList(void)
12738 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12739 int list_pos = gaei->num_event_lists++;
12741 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12742 sizeof(struct GlobalAnimEventListInfo *));
12744 gaei->event_list[list_pos] =
12745 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12747 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12749 gaeli->event_value = NULL;
12750 gaeli->num_event_values = 0;
12755 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12757 // do not add empty global animation events
12758 if (event_value == ANIM_EVENT_NONE)
12761 // if list position is undefined, create new list
12762 if (list_pos == ANIM_EVENT_UNDEFINED)
12763 list_pos = AddGlobalAnimEventList();
12765 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12766 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12767 int value_pos = gaeli->num_event_values++;
12769 gaeli->event_value = checked_realloc(gaeli->event_value,
12770 gaeli->num_event_values * sizeof(int *));
12772 gaeli->event_value[value_pos] = event_value;
12777 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12779 if (list_pos == ANIM_EVENT_UNDEFINED)
12780 return ANIM_EVENT_NONE;
12782 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12783 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12785 return gaeli->event_value[value_pos];
12788 int GetGlobalAnimEventValueCount(int list_pos)
12790 if (list_pos == ANIM_EVENT_UNDEFINED)
12793 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12794 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12796 return gaeli->num_event_values;
12799 // This function checks if a string <s> of the format "string1, string2, ..."
12800 // exactly contains a string <s_contained>.
12802 static boolean string_has_parameter(char *s, char *s_contained)
12806 if (s == NULL || s_contained == NULL)
12809 if (strlen(s_contained) > strlen(s))
12812 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12814 char next_char = s[strlen(s_contained)];
12816 // check if next character is delimiter or whitespace
12817 if (next_char == ',' || next_char == '\0' ||
12818 next_char == ' ' || next_char == '\t')
12822 // check if string contains another parameter string after a comma
12823 substring = strchr(s, ',');
12824 if (substring == NULL) // string does not contain a comma
12827 // advance string pointer to next character after the comma
12830 // skip potential whitespaces after the comma
12831 while (*substring == ' ' || *substring == '\t')
12834 return string_has_parameter(substring, s_contained);
12837 static int get_anim_parameter_value_ce(char *s)
12840 char *pattern_1 = "ce_change:custom_";
12841 char *pattern_2 = ".page_";
12842 int pattern_1_len = strlen(pattern_1);
12843 char *matching_char = strstr(s_ptr, pattern_1);
12844 int result = ANIM_EVENT_NONE;
12846 if (matching_char == NULL)
12847 return ANIM_EVENT_NONE;
12849 result = ANIM_EVENT_CE_CHANGE;
12851 s_ptr = matching_char + pattern_1_len;
12853 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12854 if (*s_ptr >= '0' && *s_ptr <= '9')
12856 int gic_ce_nr = (*s_ptr++ - '0');
12858 if (*s_ptr >= '0' && *s_ptr <= '9')
12860 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12862 if (*s_ptr >= '0' && *s_ptr <= '9')
12863 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12866 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12867 return ANIM_EVENT_NONE;
12869 // custom element stored as 0 to 255
12872 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12876 // invalid custom element number specified
12878 return ANIM_EVENT_NONE;
12881 // check for change page number ("page_X" or "page_XX") (optional)
12882 if (strPrefix(s_ptr, pattern_2))
12884 s_ptr += strlen(pattern_2);
12886 if (*s_ptr >= '0' && *s_ptr <= '9')
12888 int gic_page_nr = (*s_ptr++ - '0');
12890 if (*s_ptr >= '0' && *s_ptr <= '9')
12891 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12893 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12894 return ANIM_EVENT_NONE;
12896 // change page stored as 1 to 32 (0 means "all change pages")
12898 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12902 // invalid animation part number specified
12904 return ANIM_EVENT_NONE;
12908 // discard result if next character is neither delimiter nor whitespace
12909 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12910 *s_ptr == ' ' || *s_ptr == '\t'))
12911 return ANIM_EVENT_NONE;
12916 static int get_anim_parameter_value(char *s)
12918 int event_value[] =
12926 char *pattern_1[] =
12934 char *pattern_2 = ".part_";
12935 char *matching_char = NULL;
12937 int pattern_1_len = 0;
12938 int result = ANIM_EVENT_NONE;
12941 result = get_anim_parameter_value_ce(s);
12943 if (result != ANIM_EVENT_NONE)
12946 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12948 matching_char = strstr(s_ptr, pattern_1[i]);
12949 pattern_1_len = strlen(pattern_1[i]);
12950 result = event_value[i];
12952 if (matching_char != NULL)
12956 if (matching_char == NULL)
12957 return ANIM_EVENT_NONE;
12959 s_ptr = matching_char + pattern_1_len;
12961 // check for main animation number ("anim_X" or "anim_XX")
12962 if (*s_ptr >= '0' && *s_ptr <= '9')
12964 int gic_anim_nr = (*s_ptr++ - '0');
12966 if (*s_ptr >= '0' && *s_ptr <= '9')
12967 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12969 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12970 return ANIM_EVENT_NONE;
12972 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12976 // invalid main animation number specified
12978 return ANIM_EVENT_NONE;
12981 // check for animation part number ("part_X" or "part_XX") (optional)
12982 if (strPrefix(s_ptr, pattern_2))
12984 s_ptr += strlen(pattern_2);
12986 if (*s_ptr >= '0' && *s_ptr <= '9')
12988 int gic_part_nr = (*s_ptr++ - '0');
12990 if (*s_ptr >= '0' && *s_ptr <= '9')
12991 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12993 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12994 return ANIM_EVENT_NONE;
12996 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
13000 // invalid animation part number specified
13002 return ANIM_EVENT_NONE;
13006 // discard result if next character is neither delimiter nor whitespace
13007 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
13008 *s_ptr == ' ' || *s_ptr == '\t'))
13009 return ANIM_EVENT_NONE;
13014 static int get_anim_parameter_values(char *s)
13016 int list_pos = ANIM_EVENT_UNDEFINED;
13017 int event_value = ANIM_EVENT_DEFAULT;
13019 if (string_has_parameter(s, "any"))
13020 event_value |= ANIM_EVENT_ANY;
13022 if (string_has_parameter(s, "click:self") ||
13023 string_has_parameter(s, "click") ||
13024 string_has_parameter(s, "self"))
13025 event_value |= ANIM_EVENT_SELF;
13027 if (string_has_parameter(s, "unclick:any"))
13028 event_value |= ANIM_EVENT_UNCLICK_ANY;
13030 // if animation event found, add it to global animation event list
13031 if (event_value != ANIM_EVENT_NONE)
13032 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
13036 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
13037 event_value = get_anim_parameter_value(s);
13039 // if animation event found, add it to global animation event list
13040 if (event_value != ANIM_EVENT_NONE)
13041 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
13043 // continue with next part of the string, starting with next comma
13044 s = strchr(s + 1, ',');
13050 static int get_anim_action_parameter_value(char *token)
13052 // check most common default case first to massively speed things up
13053 if (strEqual(token, ARG_UNDEFINED))
13054 return ANIM_EVENT_ACTION_NONE;
13056 int result = getImageIDFromToken(token);
13060 char *gfx_token = getStringCat2("gfx.", token);
13062 result = getImageIDFromToken(gfx_token);
13064 checked_free(gfx_token);
13069 Key key = getKeyFromX11KeyName(token);
13071 if (key != KSYM_UNDEFINED)
13072 result = -(int)key;
13079 result = get_hash_from_string(token); // unsigned int => int
13080 result = ABS(result); // may be negative now
13081 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
13083 setHashEntry(anim_url_hash, int2str(result, 0), token);
13088 result = ANIM_EVENT_ACTION_NONE;
13093 int get_parameter_value(char *value_raw, char *suffix, int type)
13095 char *value = getStringToLower(value_raw);
13096 int result = 0; // probably a save default value
13098 if (strEqual(suffix, ".direction"))
13100 result = (strEqual(value, "left") ? MV_LEFT :
13101 strEqual(value, "right") ? MV_RIGHT :
13102 strEqual(value, "up") ? MV_UP :
13103 strEqual(value, "down") ? MV_DOWN : MV_NONE);
13105 else if (strEqual(suffix, ".position"))
13107 result = (strEqual(value, "left") ? POS_LEFT :
13108 strEqual(value, "right") ? POS_RIGHT :
13109 strEqual(value, "top") ? POS_TOP :
13110 strEqual(value, "upper") ? POS_UPPER :
13111 strEqual(value, "middle") ? POS_MIDDLE :
13112 strEqual(value, "lower") ? POS_LOWER :
13113 strEqual(value, "bottom") ? POS_BOTTOM :
13114 strEqual(value, "any") ? POS_ANY :
13115 strEqual(value, "ce") ? POS_CE :
13116 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
13117 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
13119 else if (strEqual(suffix, ".align"))
13121 result = (strEqual(value, "left") ? ALIGN_LEFT :
13122 strEqual(value, "right") ? ALIGN_RIGHT :
13123 strEqual(value, "center") ? ALIGN_CENTER :
13124 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
13126 else if (strEqual(suffix, ".valign"))
13128 result = (strEqual(value, "top") ? VALIGN_TOP :
13129 strEqual(value, "bottom") ? VALIGN_BOTTOM :
13130 strEqual(value, "middle") ? VALIGN_MIDDLE :
13131 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
13133 else if (strEqual(suffix, ".anim_mode"))
13135 result = (string_has_parameter(value, "none") ? ANIM_NONE :
13136 string_has_parameter(value, "loop") ? ANIM_LOOP :
13137 string_has_parameter(value, "linear") ? ANIM_LINEAR :
13138 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
13139 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
13140 string_has_parameter(value, "random") ? ANIM_RANDOM :
13141 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13142 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
13143 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
13144 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
13145 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13146 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
13147 string_has_parameter(value, "centered") ? ANIM_CENTERED :
13148 string_has_parameter(value, "all") ? ANIM_ALL :
13149 string_has_parameter(value, "tiled") ? ANIM_TILED :
13150 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
13153 if (string_has_parameter(value, "once"))
13154 result |= ANIM_ONCE;
13156 if (string_has_parameter(value, "reverse"))
13157 result |= ANIM_REVERSE;
13159 if (string_has_parameter(value, "opaque_player"))
13160 result |= ANIM_OPAQUE_PLAYER;
13162 if (string_has_parameter(value, "static_panel"))
13163 result |= ANIM_STATIC_PANEL;
13165 else if (strEqual(suffix, ".init_event") ||
13166 strEqual(suffix, ".anim_event"))
13168 result = get_anim_parameter_values(value);
13170 else if (strEqual(suffix, ".init_delay_action") ||
13171 strEqual(suffix, ".anim_delay_action") ||
13172 strEqual(suffix, ".post_delay_action") ||
13173 strEqual(suffix, ".init_event_action") ||
13174 strEqual(suffix, ".anim_event_action"))
13176 result = get_anim_action_parameter_value(value_raw);
13178 else if (strEqual(suffix, ".class"))
13180 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13181 get_hash_from_string(value));
13183 else if (strEqual(suffix, ".style"))
13185 result = STYLE_DEFAULT;
13187 if (string_has_parameter(value, "accurate_borders"))
13188 result |= STYLE_ACCURATE_BORDERS;
13190 if (string_has_parameter(value, "inner_corners"))
13191 result |= STYLE_INNER_CORNERS;
13193 if (string_has_parameter(value, "reverse"))
13194 result |= STYLE_REVERSE;
13196 if (string_has_parameter(value, "leftmost_position"))
13197 result |= STYLE_LEFTMOST_POSITION;
13199 if (string_has_parameter(value, "block_clicks"))
13200 result |= STYLE_BLOCK;
13202 if (string_has_parameter(value, "passthrough_clicks"))
13203 result |= STYLE_PASSTHROUGH;
13205 if (string_has_parameter(value, "multiple_actions"))
13206 result |= STYLE_MULTIPLE_ACTIONS;
13208 if (string_has_parameter(value, "consume_ce_event"))
13209 result |= STYLE_CONSUME_CE_EVENT;
13211 else if (strEqual(suffix, ".fade_mode"))
13213 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
13214 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
13215 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
13216 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
13217 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
13218 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
13219 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
13220 FADE_MODE_DEFAULT);
13222 else if (strEqual(suffix, ".auto_delay_unit"))
13224 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
13225 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13226 AUTO_DELAY_UNIT_DEFAULT);
13228 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
13230 result = gfx.get_font_from_token_function(value);
13232 else // generic parameter of type integer or boolean
13234 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13235 type == TYPE_INTEGER ? get_integer_from_string(value) :
13236 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13237 ARG_UNDEFINED_VALUE);
13245 static int get_token_parameter_value(char *token, char *value_raw)
13249 if (token == NULL || value_raw == NULL)
13250 return ARG_UNDEFINED_VALUE;
13252 suffix = strrchr(token, '.');
13253 if (suffix == NULL)
13256 if (strEqual(suffix, ".element"))
13257 return getElementFromToken(value_raw);
13259 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13260 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13263 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13264 boolean ignore_defaults)
13268 for (i = 0; image_config_vars[i].token != NULL; i++)
13270 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13272 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13273 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13277 *image_config_vars[i].value =
13278 get_token_parameter_value(image_config_vars[i].token, value);
13282 void InitMenuDesignSettings_Static(void)
13284 // always start with reliable default values from static default config
13285 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13288 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13292 // the following initializes hierarchical values from static configuration
13294 // special case: initialize "ARG_DEFAULT" values in static default config
13295 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13296 titlescreen_initial_first_default.fade_mode =
13297 title_initial_first_default.fade_mode;
13298 titlescreen_initial_first_default.fade_delay =
13299 title_initial_first_default.fade_delay;
13300 titlescreen_initial_first_default.post_delay =
13301 title_initial_first_default.post_delay;
13302 titlescreen_initial_first_default.auto_delay =
13303 title_initial_first_default.auto_delay;
13304 titlescreen_initial_first_default.auto_delay_unit =
13305 title_initial_first_default.auto_delay_unit;
13306 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
13307 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13308 titlescreen_first_default.post_delay = title_first_default.post_delay;
13309 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13310 titlescreen_first_default.auto_delay_unit =
13311 title_first_default.auto_delay_unit;
13312 titlemessage_initial_first_default.fade_mode =
13313 title_initial_first_default.fade_mode;
13314 titlemessage_initial_first_default.fade_delay =
13315 title_initial_first_default.fade_delay;
13316 titlemessage_initial_first_default.post_delay =
13317 title_initial_first_default.post_delay;
13318 titlemessage_initial_first_default.auto_delay =
13319 title_initial_first_default.auto_delay;
13320 titlemessage_initial_first_default.auto_delay_unit =
13321 title_initial_first_default.auto_delay_unit;
13322 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
13323 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13324 titlemessage_first_default.post_delay = title_first_default.post_delay;
13325 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13326 titlemessage_first_default.auto_delay_unit =
13327 title_first_default.auto_delay_unit;
13329 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
13330 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13331 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13332 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13333 titlescreen_initial_default.auto_delay_unit =
13334 title_initial_default.auto_delay_unit;
13335 titlescreen_default.fade_mode = title_default.fade_mode;
13336 titlescreen_default.fade_delay = title_default.fade_delay;
13337 titlescreen_default.post_delay = title_default.post_delay;
13338 titlescreen_default.auto_delay = title_default.auto_delay;
13339 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13340 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
13341 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13342 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13343 titlemessage_initial_default.auto_delay_unit =
13344 title_initial_default.auto_delay_unit;
13345 titlemessage_default.fade_mode = title_default.fade_mode;
13346 titlemessage_default.fade_delay = title_default.fade_delay;
13347 titlemessage_default.post_delay = title_default.post_delay;
13348 titlemessage_default.auto_delay = title_default.auto_delay;
13349 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13351 // special case: initialize "ARG_DEFAULT" values in static default config
13352 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13353 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13355 titlescreen_initial_first[i] = titlescreen_initial_first_default;
13356 titlescreen_first[i] = titlescreen_first_default;
13357 titlemessage_initial_first[i] = titlemessage_initial_first_default;
13358 titlemessage_first[i] = titlemessage_first_default;
13360 titlescreen_initial[i] = titlescreen_initial_default;
13361 titlescreen[i] = titlescreen_default;
13362 titlemessage_initial[i] = titlemessage_initial_default;
13363 titlemessage[i] = titlemessage_default;
13366 // special case: initialize "ARG_DEFAULT" values in static default config
13367 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13368 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13370 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
13373 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13374 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13375 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13378 // special case: initialize "ARG_DEFAULT" values in static default config
13379 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13380 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13382 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13383 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13384 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13386 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
13389 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13393 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13397 struct XY *dst, *src;
13399 game_buttons_xy[] =
13401 { &game.button.save, &game.button.stop },
13402 { &game.button.pause2, &game.button.pause },
13403 { &game.button.load, &game.button.play },
13404 { &game.button.undo, &game.button.stop },
13405 { &game.button.redo, &game.button.play },
13411 // special case: initialize later added SETUP list size from LEVELS value
13412 if (menu.list_size[GAME_MODE_SETUP] == -1)
13413 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13415 // set default position for snapshot buttons to stop/pause/play buttons
13416 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13417 if ((*game_buttons_xy[i].dst).x == -1 &&
13418 (*game_buttons_xy[i].dst).y == -1)
13419 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13421 // --------------------------------------------------------------------------
13422 // dynamic viewports (including playfield margins, borders and alignments)
13423 // --------------------------------------------------------------------------
13425 // dynamic viewports currently only supported for landscape mode
13426 int display_width = MAX(video.display_width, video.display_height);
13427 int display_height = MIN(video.display_width, video.display_height);
13429 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13431 struct RectWithBorder *vp_window = &viewport.window[i];
13432 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13433 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13434 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13435 boolean dynamic_window_width = (vp_window->min_width != -1);
13436 boolean dynamic_window_height = (vp_window->min_height != -1);
13437 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13438 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13440 // adjust window size if min/max width/height is specified
13442 if (vp_window->min_width != -1)
13444 int window_width = display_width;
13446 // when using static window height, use aspect ratio of display
13447 if (vp_window->min_height == -1)
13448 window_width = vp_window->height * display_width / display_height;
13450 vp_window->width = MAX(vp_window->min_width, window_width);
13453 if (vp_window->min_height != -1)
13455 int window_height = display_height;
13457 // when using static window width, use aspect ratio of display
13458 if (vp_window->min_width == -1)
13459 window_height = vp_window->width * display_height / display_width;
13461 vp_window->height = MAX(vp_window->min_height, window_height);
13464 if (vp_window->max_width != -1)
13465 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13467 if (vp_window->max_height != -1)
13468 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13470 int playfield_width = vp_window->width;
13471 int playfield_height = vp_window->height;
13473 // adjust playfield size and position according to specified margins
13475 playfield_width -= vp_playfield->margin_left;
13476 playfield_width -= vp_playfield->margin_right;
13478 playfield_height -= vp_playfield->margin_top;
13479 playfield_height -= vp_playfield->margin_bottom;
13481 // adjust playfield size if min/max width/height is specified
13483 if (vp_playfield->min_width != -1)
13484 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13486 if (vp_playfield->min_height != -1)
13487 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13489 if (vp_playfield->max_width != -1)
13490 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13492 if (vp_playfield->max_height != -1)
13493 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13495 // adjust playfield position according to specified alignment
13497 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13498 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13499 else if (vp_playfield->align == ALIGN_CENTER)
13500 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13501 else if (vp_playfield->align == ALIGN_RIGHT)
13502 vp_playfield->x += playfield_width - vp_playfield->width;
13504 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13505 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13506 else if (vp_playfield->valign == VALIGN_MIDDLE)
13507 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13508 else if (vp_playfield->valign == VALIGN_BOTTOM)
13509 vp_playfield->y += playfield_height - vp_playfield->height;
13511 vp_playfield->x += vp_playfield->margin_left;
13512 vp_playfield->y += vp_playfield->margin_top;
13514 // adjust individual playfield borders if only default border is specified
13516 if (vp_playfield->border_left == -1)
13517 vp_playfield->border_left = vp_playfield->border_size;
13518 if (vp_playfield->border_right == -1)
13519 vp_playfield->border_right = vp_playfield->border_size;
13520 if (vp_playfield->border_top == -1)
13521 vp_playfield->border_top = vp_playfield->border_size;
13522 if (vp_playfield->border_bottom == -1)
13523 vp_playfield->border_bottom = vp_playfield->border_size;
13525 // set dynamic playfield borders if borders are specified as undefined
13526 // (but only if window size was dynamic and playfield size was static)
13528 if (dynamic_window_width && !dynamic_playfield_width)
13530 if (vp_playfield->border_left == -1)
13532 vp_playfield->border_left = (vp_playfield->x -
13533 vp_playfield->margin_left);
13534 vp_playfield->x -= vp_playfield->border_left;
13535 vp_playfield->width += vp_playfield->border_left;
13538 if (vp_playfield->border_right == -1)
13540 vp_playfield->border_right = (vp_window->width -
13542 vp_playfield->width -
13543 vp_playfield->margin_right);
13544 vp_playfield->width += vp_playfield->border_right;
13548 if (dynamic_window_height && !dynamic_playfield_height)
13550 if (vp_playfield->border_top == -1)
13552 vp_playfield->border_top = (vp_playfield->y -
13553 vp_playfield->margin_top);
13554 vp_playfield->y -= vp_playfield->border_top;
13555 vp_playfield->height += vp_playfield->border_top;
13558 if (vp_playfield->border_bottom == -1)
13560 vp_playfield->border_bottom = (vp_window->height -
13562 vp_playfield->height -
13563 vp_playfield->margin_bottom);
13564 vp_playfield->height += vp_playfield->border_bottom;
13568 // adjust playfield size to be a multiple of a defined alignment tile size
13570 int align_size = vp_playfield->align_size;
13571 int playfield_xtiles = vp_playfield->width / align_size;
13572 int playfield_ytiles = vp_playfield->height / align_size;
13573 int playfield_width_corrected = playfield_xtiles * align_size;
13574 int playfield_height_corrected = playfield_ytiles * align_size;
13575 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13576 i == GFX_SPECIAL_ARG_EDITOR);
13578 if (is_playfield_mode &&
13579 dynamic_playfield_width &&
13580 vp_playfield->width != playfield_width_corrected)
13582 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13584 vp_playfield->width = playfield_width_corrected;
13586 if (vp_playfield->align == ALIGN_LEFT)
13588 vp_playfield->border_left += playfield_xdiff;
13590 else if (vp_playfield->align == ALIGN_RIGHT)
13592 vp_playfield->border_right += playfield_xdiff;
13594 else if (vp_playfield->align == ALIGN_CENTER)
13596 int border_left_diff = playfield_xdiff / 2;
13597 int border_right_diff = playfield_xdiff - border_left_diff;
13599 vp_playfield->border_left += border_left_diff;
13600 vp_playfield->border_right += border_right_diff;
13604 if (is_playfield_mode &&
13605 dynamic_playfield_height &&
13606 vp_playfield->height != playfield_height_corrected)
13608 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13610 vp_playfield->height = playfield_height_corrected;
13612 if (vp_playfield->valign == VALIGN_TOP)
13614 vp_playfield->border_top += playfield_ydiff;
13616 else if (vp_playfield->align == VALIGN_BOTTOM)
13618 vp_playfield->border_right += playfield_ydiff;
13620 else if (vp_playfield->align == VALIGN_MIDDLE)
13622 int border_top_diff = playfield_ydiff / 2;
13623 int border_bottom_diff = playfield_ydiff - border_top_diff;
13625 vp_playfield->border_top += border_top_diff;
13626 vp_playfield->border_bottom += border_bottom_diff;
13630 // adjust door positions according to specified alignment
13632 for (j = 0; j < 2; j++)
13634 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13636 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13637 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13638 else if (vp_door->align == ALIGN_CENTER)
13639 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13640 else if (vp_door->align == ALIGN_RIGHT)
13641 vp_door->x += vp_window->width - vp_door->width;
13643 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13644 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13645 else if (vp_door->valign == VALIGN_MIDDLE)
13646 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13647 else if (vp_door->valign == VALIGN_BOTTOM)
13648 vp_door->y += vp_window->height - vp_door->height;
13653 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13657 struct XYTileSize *dst, *src;
13660 editor_buttons_xy[] =
13663 &editor.button.element_left, &editor.palette.element_left,
13664 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13667 &editor.button.element_middle, &editor.palette.element_middle,
13668 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13671 &editor.button.element_right, &editor.palette.element_right,
13672 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13679 // set default position for element buttons to element graphics
13680 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13682 if ((*editor_buttons_xy[i].dst).x == -1 &&
13683 (*editor_buttons_xy[i].dst).y == -1)
13685 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13687 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13689 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13693 // adjust editor palette rows and columns if specified to be dynamic
13695 if (editor.palette.cols == -1)
13697 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13698 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13699 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13701 editor.palette.cols = (vp_width - sc_width) / bt_width;
13703 if (editor.palette.x == -1)
13705 int palette_width = editor.palette.cols * bt_width + sc_width;
13707 editor.palette.x = (vp_width - palette_width) / 2;
13711 if (editor.palette.rows == -1)
13713 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13714 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13715 int tx_height = getFontHeight(FONT_TEXT_2);
13717 editor.palette.rows = (vp_height - tx_height) / bt_height;
13719 if (editor.palette.y == -1)
13721 int palette_height = editor.palette.rows * bt_height + tx_height;
13723 editor.palette.y = (vp_height - palette_height) / 2;
13728 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13729 boolean initialize)
13731 // special case: check if network and preview player positions are redefined,
13732 // to compare this later against the main menu level preview being redefined
13733 struct TokenIntPtrInfo menu_config_players[] =
13735 { "main.network_players.x", &menu.main.network_players.redefined },
13736 { "main.network_players.y", &menu.main.network_players.redefined },
13737 { "main.preview_players.x", &menu.main.preview_players.redefined },
13738 { "main.preview_players.y", &menu.main.preview_players.redefined },
13739 { "preview.x", &preview.redefined },
13740 { "preview.y", &preview.redefined }
13746 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13747 *menu_config_players[i].value = FALSE;
13751 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13752 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13753 *menu_config_players[i].value = TRUE;
13757 static void InitMenuDesignSettings_PreviewPlayers(void)
13759 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13762 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13764 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13767 static void LoadMenuDesignSettingsFromFilename(char *filename)
13769 static struct TitleFadingInfo tfi;
13770 static struct TitleMessageInfo tmi;
13771 static struct TokenInfo title_tokens[] =
13773 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13774 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13775 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13776 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13777 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13781 static struct TokenInfo titlemessage_tokens[] =
13783 { TYPE_INTEGER, &tmi.x, ".x" },
13784 { TYPE_INTEGER, &tmi.y, ".y" },
13785 { TYPE_INTEGER, &tmi.width, ".width" },
13786 { TYPE_INTEGER, &tmi.height, ".height" },
13787 { TYPE_INTEGER, &tmi.chars, ".chars" },
13788 { TYPE_INTEGER, &tmi.lines, ".lines" },
13789 { TYPE_INTEGER, &tmi.align, ".align" },
13790 { TYPE_INTEGER, &tmi.valign, ".valign" },
13791 { TYPE_INTEGER, &tmi.font, ".font" },
13792 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13793 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13794 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13795 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13796 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13797 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13798 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13799 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13800 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13806 struct TitleFadingInfo *info;
13811 // initialize first titles from "enter screen" definitions, if defined
13812 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13813 { &title_first_default, "menu.enter_screen.TITLE" },
13815 // initialize title screens from "next screen" definitions, if defined
13816 { &title_initial_default, "menu.next_screen.TITLE" },
13817 { &title_default, "menu.next_screen.TITLE" },
13823 struct TitleMessageInfo *array;
13826 titlemessage_arrays[] =
13828 // initialize first titles from "enter screen" definitions, if defined
13829 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13830 { titlescreen_first, "menu.enter_screen.TITLE" },
13831 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13832 { titlemessage_first, "menu.enter_screen.TITLE" },
13834 // initialize titles from "next screen" definitions, if defined
13835 { titlescreen_initial, "menu.next_screen.TITLE" },
13836 { titlescreen, "menu.next_screen.TITLE" },
13837 { titlemessage_initial, "menu.next_screen.TITLE" },
13838 { titlemessage, "menu.next_screen.TITLE" },
13840 // overwrite titles with title definitions, if defined
13841 { titlescreen_initial_first, "[title_initial]" },
13842 { titlescreen_first, "[title]" },
13843 { titlemessage_initial_first, "[title_initial]" },
13844 { titlemessage_first, "[title]" },
13846 { titlescreen_initial, "[title_initial]" },
13847 { titlescreen, "[title]" },
13848 { titlemessage_initial, "[title_initial]" },
13849 { titlemessage, "[title]" },
13851 // overwrite titles with title screen/message definitions, if defined
13852 { titlescreen_initial_first, "[titlescreen_initial]" },
13853 { titlescreen_first, "[titlescreen]" },
13854 { titlemessage_initial_first, "[titlemessage_initial]" },
13855 { titlemessage_first, "[titlemessage]" },
13857 { titlescreen_initial, "[titlescreen_initial]" },
13858 { titlescreen, "[titlescreen]" },
13859 { titlemessage_initial, "[titlemessage_initial]" },
13860 { titlemessage, "[titlemessage]" },
13864 SetupFileHash *setup_file_hash;
13867 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13870 // the following initializes hierarchical values from dynamic configuration
13872 // special case: initialize with default values that may be overwritten
13873 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13874 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13876 struct TokenIntPtrInfo menu_config[] =
13878 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13879 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13880 { "menu.list_size", &menu.list_size[i] }
13883 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13885 char *token = menu_config[j].token;
13886 char *value = getHashEntry(setup_file_hash, token);
13889 *menu_config[j].value = get_integer_from_string(value);
13893 // special case: initialize with default values that may be overwritten
13894 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13895 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13897 struct TokenIntPtrInfo menu_config[] =
13899 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13900 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13901 { "menu.list_size.INFO", &menu.list_size_info[i] },
13902 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13903 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13906 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13908 char *token = menu_config[j].token;
13909 char *value = getHashEntry(setup_file_hash, token);
13912 *menu_config[j].value = get_integer_from_string(value);
13916 // special case: initialize with default values that may be overwritten
13917 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13918 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13920 struct TokenIntPtrInfo menu_config[] =
13922 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13923 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13926 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13928 char *token = menu_config[j].token;
13929 char *value = getHashEntry(setup_file_hash, token);
13932 *menu_config[j].value = get_integer_from_string(value);
13936 // special case: initialize with default values that may be overwritten
13937 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13938 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13940 struct TokenIntPtrInfo menu_config[] =
13942 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13943 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13944 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13945 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13946 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13947 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13948 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13949 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13950 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13951 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13954 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13956 char *token = menu_config[j].token;
13957 char *value = getHashEntry(setup_file_hash, token);
13960 *menu_config[j].value = get_integer_from_string(value);
13964 // special case: initialize with default values that may be overwritten
13965 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13966 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13968 struct TokenIntPtrInfo menu_config[] =
13970 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13971 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13972 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13973 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13974 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13975 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13976 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13977 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13978 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13981 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13983 char *token = menu_config[j].token;
13984 char *value = getHashEntry(setup_file_hash, token);
13987 *menu_config[j].value = get_token_parameter_value(token, value);
13991 // special case: initialize with default values that may be overwritten
13992 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13993 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13997 char *token_prefix;
13998 struct RectWithBorder *struct_ptr;
14002 { "viewport.window", &viewport.window[i] },
14003 { "viewport.playfield", &viewport.playfield[i] },
14004 { "viewport.door_1", &viewport.door_1[i] },
14005 { "viewport.door_2", &viewport.door_2[i] }
14008 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
14010 struct TokenIntPtrInfo vp_config[] =
14012 { ".x", &vp_struct[j].struct_ptr->x },
14013 { ".y", &vp_struct[j].struct_ptr->y },
14014 { ".width", &vp_struct[j].struct_ptr->width },
14015 { ".height", &vp_struct[j].struct_ptr->height },
14016 { ".min_width", &vp_struct[j].struct_ptr->min_width },
14017 { ".min_height", &vp_struct[j].struct_ptr->min_height },
14018 { ".max_width", &vp_struct[j].struct_ptr->max_width },
14019 { ".max_height", &vp_struct[j].struct_ptr->max_height },
14020 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
14021 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
14022 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
14023 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
14024 { ".border_left", &vp_struct[j].struct_ptr->border_left },
14025 { ".border_right", &vp_struct[j].struct_ptr->border_right },
14026 { ".border_top", &vp_struct[j].struct_ptr->border_top },
14027 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
14028 { ".border_size", &vp_struct[j].struct_ptr->border_size },
14029 { ".align_size", &vp_struct[j].struct_ptr->align_size },
14030 { ".align", &vp_struct[j].struct_ptr->align },
14031 { ".valign", &vp_struct[j].struct_ptr->valign }
14034 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
14036 char *token = getStringCat2(vp_struct[j].token_prefix,
14037 vp_config[k].token);
14038 char *value = getHashEntry(setup_file_hash, token);
14041 *vp_config[k].value = get_token_parameter_value(token, value);
14048 // special case: initialize with default values that may be overwritten
14049 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
14050 for (i = 0; title_info[i].info != NULL; i++)
14052 struct TitleFadingInfo *info = title_info[i].info;
14053 char *base_token = title_info[i].text;
14055 for (j = 0; title_tokens[j].type != -1; j++)
14057 char *token = getStringCat2(base_token, title_tokens[j].text);
14058 char *value = getHashEntry(setup_file_hash, token);
14062 int parameter_value = get_token_parameter_value(token, value);
14066 *(int *)title_tokens[j].value = (int)parameter_value;
14075 // special case: initialize with default values that may be overwritten
14076 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
14077 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
14079 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
14080 char *base_token = titlemessage_arrays[i].text;
14082 for (j = 0; titlemessage_tokens[j].type != -1; j++)
14084 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
14085 char *value = getHashEntry(setup_file_hash, token);
14089 int parameter_value = get_token_parameter_value(token, value);
14091 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
14095 if (titlemessage_tokens[j].type == TYPE_INTEGER)
14096 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
14098 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
14108 // read (and overwrite with) values that may be specified in config file
14109 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
14111 // special case: check if network and preview player positions are redefined
14112 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
14114 freeSetupFileHash(setup_file_hash);
14117 void LoadMenuDesignSettings(void)
14119 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14121 InitMenuDesignSettings_Static();
14122 InitMenuDesignSettings_SpecialPreProcessing();
14123 InitMenuDesignSettings_PreviewPlayers();
14125 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
14127 // first look for special settings configured in level series config
14128 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
14130 if (fileExists(filename_base))
14131 LoadMenuDesignSettingsFromFilename(filename_base);
14134 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
14136 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14137 LoadMenuDesignSettingsFromFilename(filename_local);
14139 InitMenuDesignSettings_SpecialPostProcessing();
14142 void LoadMenuDesignSettings_AfterGraphics(void)
14144 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14147 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14148 boolean ignore_defaults)
14152 for (i = 0; sound_config_vars[i].token != NULL; i++)
14154 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14156 // (ignore definitions set to "[DEFAULT]" which are already initialized)
14157 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14161 *sound_config_vars[i].value =
14162 get_token_parameter_value(sound_config_vars[i].token, value);
14166 void InitSoundSettings_Static(void)
14168 // always start with reliable default values from static default config
14169 InitSoundSettings_FromHash(sound_config_hash, FALSE);
14172 static void LoadSoundSettingsFromFilename(char *filename)
14174 SetupFileHash *setup_file_hash;
14176 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14179 // read (and overwrite with) values that may be specified in config file
14180 InitSoundSettings_FromHash(setup_file_hash, TRUE);
14182 freeSetupFileHash(setup_file_hash);
14185 void LoadSoundSettings(void)
14187 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14189 InitSoundSettings_Static();
14191 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14193 // first look for special settings configured in level series config
14194 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14196 if (fileExists(filename_base))
14197 LoadSoundSettingsFromFilename(filename_base);
14200 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14202 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14203 LoadSoundSettingsFromFilename(filename_local);
14206 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14208 char *filename = getEditorSetupFilename();
14209 SetupFileList *setup_file_list, *list;
14210 SetupFileHash *element_hash;
14211 int num_unknown_tokens = 0;
14214 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14217 element_hash = newSetupFileHash();
14219 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14220 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14222 // determined size may be larger than needed (due to unknown elements)
14224 for (list = setup_file_list; list != NULL; list = list->next)
14227 // add space for up to 3 more elements for padding that may be needed
14228 *num_elements += 3;
14230 // free memory for old list of elements, if needed
14231 checked_free(*elements);
14233 // allocate memory for new list of elements
14234 *elements = checked_malloc(*num_elements * sizeof(int));
14237 for (list = setup_file_list; list != NULL; list = list->next)
14239 char *value = getHashEntry(element_hash, list->token);
14241 if (value == NULL) // try to find obsolete token mapping
14243 char *mapped_token = get_mapped_token(list->token);
14245 if (mapped_token != NULL)
14247 value = getHashEntry(element_hash, mapped_token);
14249 free(mapped_token);
14255 (*elements)[(*num_elements)++] = atoi(value);
14259 if (num_unknown_tokens == 0)
14262 Warn("unknown token(s) found in config file:");
14263 Warn("- config file: '%s'", filename);
14265 num_unknown_tokens++;
14268 Warn("- token: '%s'", list->token);
14272 if (num_unknown_tokens > 0)
14275 while (*num_elements % 4) // pad with empty elements, if needed
14276 (*elements)[(*num_elements)++] = EL_EMPTY;
14278 freeSetupFileList(setup_file_list);
14279 freeSetupFileHash(element_hash);
14282 for (i = 0; i < *num_elements; i++)
14283 Debug("editor", "element '%s' [%d]\n",
14284 element_info[(*elements)[i]].token_name, (*elements)[i]);
14288 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14291 SetupFileHash *setup_file_hash = NULL;
14292 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14293 char *filename_music, *filename_prefix, *filename_info;
14299 token_to_value_ptr[] =
14301 { "title_header", &tmp_music_file_info.title_header },
14302 { "artist_header", &tmp_music_file_info.artist_header },
14303 { "album_header", &tmp_music_file_info.album_header },
14304 { "year_header", &tmp_music_file_info.year_header },
14305 { "played_header", &tmp_music_file_info.played_header },
14307 { "title", &tmp_music_file_info.title },
14308 { "artist", &tmp_music_file_info.artist },
14309 { "album", &tmp_music_file_info.album },
14310 { "year", &tmp_music_file_info.year },
14311 { "played", &tmp_music_file_info.played },
14317 filename_music = (is_sound ? getCustomSoundFilename(basename) :
14318 getCustomMusicFilename(basename));
14320 if (filename_music == NULL)
14323 // ---------- try to replace file extension ----------
14325 filename_prefix = getStringCopy(filename_music);
14326 if (strrchr(filename_prefix, '.') != NULL)
14327 *strrchr(filename_prefix, '.') = '\0';
14328 filename_info = getStringCat2(filename_prefix, ".txt");
14330 if (fileExists(filename_info))
14331 setup_file_hash = loadSetupFileHash(filename_info);
14333 free(filename_prefix);
14334 free(filename_info);
14336 if (setup_file_hash == NULL)
14338 // ---------- try to add file extension ----------
14340 filename_prefix = getStringCopy(filename_music);
14341 filename_info = getStringCat2(filename_prefix, ".txt");
14343 if (fileExists(filename_info))
14344 setup_file_hash = loadSetupFileHash(filename_info);
14346 free(filename_prefix);
14347 free(filename_info);
14350 if (setup_file_hash == NULL)
14353 // ---------- music file info found ----------
14355 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14357 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14359 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14361 *token_to_value_ptr[i].value_ptr =
14362 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14365 tmp_music_file_info.basename = getStringCopy(basename);
14366 tmp_music_file_info.music = music;
14367 tmp_music_file_info.is_sound = is_sound;
14369 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14370 *new_music_file_info = tmp_music_file_info;
14372 return new_music_file_info;
14375 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14377 return get_music_file_info_ext(basename, music, FALSE);
14380 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14382 return get_music_file_info_ext(basename, sound, TRUE);
14385 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14386 char *basename, boolean is_sound)
14388 for (; list != NULL; list = list->next)
14389 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14395 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14397 return music_info_listed_ext(list, basename, FALSE);
14400 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14402 return music_info_listed_ext(list, basename, TRUE);
14405 void LoadMusicInfo(void)
14407 int num_music_noconf = getMusicListSize_NoConf();
14408 int num_music = getMusicListSize();
14409 int num_sounds = getSoundListSize();
14410 struct FileInfo *music, *sound;
14411 struct MusicFileInfo *next, **new;
14415 while (music_file_info != NULL)
14417 next = music_file_info->next;
14419 checked_free(music_file_info->basename);
14421 checked_free(music_file_info->title_header);
14422 checked_free(music_file_info->artist_header);
14423 checked_free(music_file_info->album_header);
14424 checked_free(music_file_info->year_header);
14425 checked_free(music_file_info->played_header);
14427 checked_free(music_file_info->title);
14428 checked_free(music_file_info->artist);
14429 checked_free(music_file_info->album);
14430 checked_free(music_file_info->year);
14431 checked_free(music_file_info->played);
14433 free(music_file_info);
14435 music_file_info = next;
14438 new = &music_file_info;
14440 // get (configured or unconfigured) music file info for all levels
14441 for (i = leveldir_current->first_level;
14442 i <= leveldir_current->last_level; i++)
14446 if (levelset.music[i] != MUS_UNDEFINED)
14448 // get music file info for configured level music
14449 music_nr = levelset.music[i];
14451 else if (num_music_noconf > 0)
14453 // get music file info for unconfigured level music
14454 int level_pos = i - leveldir_current->first_level;
14456 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14463 char *basename = getMusicInfoEntryFilename(music_nr);
14465 if (basename == NULL)
14468 if (!music_info_listed(music_file_info, basename))
14470 *new = get_music_file_info(basename, music_nr);
14473 new = &(*new)->next;
14477 // get music file info for all remaining configured music files
14478 for (i = 0; i < num_music; i++)
14480 music = getMusicListEntry(i);
14482 if (music->filename == NULL)
14485 if (strEqual(music->filename, UNDEFINED_FILENAME))
14488 // a configured file may be not recognized as music
14489 if (!FileIsMusic(music->filename))
14492 if (!music_info_listed(music_file_info, music->filename))
14494 *new = get_music_file_info(music->filename, i);
14497 new = &(*new)->next;
14501 // get sound file info for all configured sound files
14502 for (i = 0; i < num_sounds; i++)
14504 sound = getSoundListEntry(i);
14506 if (sound->filename == NULL)
14509 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14512 // a configured file may be not recognized as sound
14513 if (!FileIsSound(sound->filename))
14516 if (!sound_info_listed(music_file_info, sound->filename))
14518 *new = get_sound_file_info(sound->filename, i);
14520 new = &(*new)->next;
14524 // add pointers to previous list nodes
14526 struct MusicFileInfo *node = music_file_info;
14528 while (node != NULL)
14531 node->next->prev = node;
14537 static void add_helpanim_entry(int element, int action, int direction,
14538 int delay, int *num_list_entries)
14540 struct HelpAnimInfo *new_list_entry;
14541 (*num_list_entries)++;
14544 checked_realloc(helpanim_info,
14545 *num_list_entries * sizeof(struct HelpAnimInfo));
14546 new_list_entry = &helpanim_info[*num_list_entries - 1];
14548 new_list_entry->element = element;
14549 new_list_entry->action = action;
14550 new_list_entry->direction = direction;
14551 new_list_entry->delay = delay;
14554 static void print_unknown_token(char *filename, char *token, int token_nr)
14559 Warn("unknown token(s) found in config file:");
14560 Warn("- config file: '%s'", filename);
14563 Warn("- token: '%s'", token);
14566 static void print_unknown_token_end(int token_nr)
14572 void LoadHelpAnimInfo(void)
14574 char *filename = getHelpAnimFilename();
14575 SetupFileList *setup_file_list = NULL, *list;
14576 SetupFileHash *element_hash, *action_hash, *direction_hash;
14577 int num_list_entries = 0;
14578 int num_unknown_tokens = 0;
14581 if (fileExists(filename))
14582 setup_file_list = loadSetupFileList(filename);
14584 if (setup_file_list == NULL)
14586 // use reliable default values from static configuration
14587 SetupFileList *insert_ptr;
14589 insert_ptr = setup_file_list =
14590 newSetupFileList(helpanim_config[0].token,
14591 helpanim_config[0].value);
14593 for (i = 1; helpanim_config[i].token; i++)
14594 insert_ptr = addListEntry(insert_ptr,
14595 helpanim_config[i].token,
14596 helpanim_config[i].value);
14599 element_hash = newSetupFileHash();
14600 action_hash = newSetupFileHash();
14601 direction_hash = newSetupFileHash();
14603 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14604 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14606 for (i = 0; i < NUM_ACTIONS; i++)
14607 setHashEntry(action_hash, element_action_info[i].suffix,
14608 i_to_a(element_action_info[i].value));
14610 // do not store direction index (bit) here, but direction value!
14611 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14612 setHashEntry(direction_hash, element_direction_info[i].suffix,
14613 i_to_a(1 << element_direction_info[i].value));
14615 for (list = setup_file_list; list != NULL; list = list->next)
14617 char *element_token, *action_token, *direction_token;
14618 char *element_value, *action_value, *direction_value;
14619 int delay = atoi(list->value);
14621 if (strEqual(list->token, "end"))
14623 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14628 /* first try to break element into element/action/direction parts;
14629 if this does not work, also accept combined "element[.act][.dir]"
14630 elements (like "dynamite.active"), which are unique elements */
14632 if (strchr(list->token, '.') == NULL) // token contains no '.'
14634 element_value = getHashEntry(element_hash, list->token);
14635 if (element_value != NULL) // element found
14636 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14637 &num_list_entries);
14640 // no further suffixes found -- this is not an element
14641 print_unknown_token(filename, list->token, num_unknown_tokens++);
14647 // token has format "<prefix>.<something>"
14649 action_token = strchr(list->token, '.'); // suffix may be action ...
14650 direction_token = action_token; // ... or direction
14652 element_token = getStringCopy(list->token);
14653 *strchr(element_token, '.') = '\0';
14655 element_value = getHashEntry(element_hash, element_token);
14657 if (element_value == NULL) // this is no element
14659 element_value = getHashEntry(element_hash, list->token);
14660 if (element_value != NULL) // combined element found
14661 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14662 &num_list_entries);
14664 print_unknown_token(filename, list->token, num_unknown_tokens++);
14666 free(element_token);
14671 action_value = getHashEntry(action_hash, action_token);
14673 if (action_value != NULL) // action found
14675 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14676 &num_list_entries);
14678 free(element_token);
14683 direction_value = getHashEntry(direction_hash, direction_token);
14685 if (direction_value != NULL) // direction found
14687 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14688 &num_list_entries);
14690 free(element_token);
14695 if (strchr(action_token + 1, '.') == NULL)
14697 // no further suffixes found -- this is not an action nor direction
14699 element_value = getHashEntry(element_hash, list->token);
14700 if (element_value != NULL) // combined element found
14701 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14702 &num_list_entries);
14704 print_unknown_token(filename, list->token, num_unknown_tokens++);
14706 free(element_token);
14711 // token has format "<prefix>.<suffix>.<something>"
14713 direction_token = strchr(action_token + 1, '.');
14715 action_token = getStringCopy(action_token);
14716 *strchr(action_token + 1, '.') = '\0';
14718 action_value = getHashEntry(action_hash, action_token);
14720 if (action_value == NULL) // this is no action
14722 element_value = getHashEntry(element_hash, list->token);
14723 if (element_value != NULL) // combined element found
14724 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14725 &num_list_entries);
14727 print_unknown_token(filename, list->token, num_unknown_tokens++);
14729 free(element_token);
14730 free(action_token);
14735 direction_value = getHashEntry(direction_hash, direction_token);
14737 if (direction_value != NULL) // direction found
14739 add_helpanim_entry(atoi(element_value), atoi(action_value),
14740 atoi(direction_value), delay, &num_list_entries);
14742 free(element_token);
14743 free(action_token);
14748 // this is no direction
14750 element_value = getHashEntry(element_hash, list->token);
14751 if (element_value != NULL) // combined element found
14752 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14753 &num_list_entries);
14755 print_unknown_token(filename, list->token, num_unknown_tokens++);
14757 free(element_token);
14758 free(action_token);
14761 print_unknown_token_end(num_unknown_tokens);
14763 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14764 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14766 freeSetupFileList(setup_file_list);
14767 freeSetupFileHash(element_hash);
14768 freeSetupFileHash(action_hash);
14769 freeSetupFileHash(direction_hash);
14772 for (i = 0; i < num_list_entries; i++)
14773 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14774 EL_NAME(helpanim_info[i].element),
14775 helpanim_info[i].element,
14776 helpanim_info[i].action,
14777 helpanim_info[i].direction,
14778 helpanim_info[i].delay);
14782 void LoadHelpTextInfo(void)
14784 char *filename = getHelpTextFilename();
14787 if (helptext_info != NULL)
14789 freeSetupFileHash(helptext_info);
14790 helptext_info = NULL;
14793 if (fileExists(filename))
14794 helptext_info = loadSetupFileHash(filename);
14796 if (helptext_info == NULL)
14798 // use reliable default values from static configuration
14799 helptext_info = newSetupFileHash();
14801 for (i = 0; helptext_config[i].token; i++)
14802 setHashEntry(helptext_info,
14803 helptext_config[i].token,
14804 helptext_config[i].value);
14808 BEGIN_HASH_ITERATION(helptext_info, itr)
14810 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14811 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14813 END_HASH_ITERATION(hash, itr)
14818 // ----------------------------------------------------------------------------
14820 // ----------------------------------------------------------------------------
14822 #define MAX_NUM_CONVERT_LEVELS 1000
14824 void ConvertLevels(void)
14826 static LevelDirTree *convert_leveldir = NULL;
14827 static int convert_level_nr = -1;
14828 static int num_levels_handled = 0;
14829 static int num_levels_converted = 0;
14830 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14833 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14834 global.convert_leveldir);
14836 if (convert_leveldir == NULL)
14837 Fail("no such level identifier: '%s'", global.convert_leveldir);
14839 leveldir_current = convert_leveldir;
14841 if (global.convert_level_nr != -1)
14843 convert_leveldir->first_level = global.convert_level_nr;
14844 convert_leveldir->last_level = global.convert_level_nr;
14847 convert_level_nr = convert_leveldir->first_level;
14849 PrintLine("=", 79);
14850 Print("Converting levels\n");
14851 PrintLine("-", 79);
14852 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14853 Print("Level series name: '%s'\n", convert_leveldir->name);
14854 Print("Level series author: '%s'\n", convert_leveldir->author);
14855 Print("Number of levels: %d\n", convert_leveldir->levels);
14856 PrintLine("=", 79);
14859 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14860 levels_failed[i] = FALSE;
14862 while (convert_level_nr <= convert_leveldir->last_level)
14864 char *level_filename;
14867 level_nr = convert_level_nr++;
14869 Print("Level %03d: ", level_nr);
14871 LoadLevel(level_nr);
14872 if (level.no_level_file || level.no_valid_file)
14874 Print("(no level)\n");
14878 Print("converting level ... ");
14881 // special case: conversion of some EMC levels as requested by ACME
14882 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14885 level_filename = getDefaultLevelFilename(level_nr);
14886 new_level = !fileExists(level_filename);
14890 SaveLevel(level_nr);
14892 num_levels_converted++;
14894 Print("converted.\n");
14898 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14899 levels_failed[level_nr] = TRUE;
14901 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14904 num_levels_handled++;
14908 PrintLine("=", 79);
14909 Print("Number of levels handled: %d\n", num_levels_handled);
14910 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14911 (num_levels_handled ?
14912 num_levels_converted * 100 / num_levels_handled : 0));
14913 PrintLine("-", 79);
14914 Print("Summary (for automatic parsing by scripts):\n");
14915 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14916 convert_leveldir->identifier, num_levels_converted,
14917 num_levels_handled,
14918 (num_levels_handled ?
14919 num_levels_converted * 100 / num_levels_handled : 0));
14921 if (num_levels_handled != num_levels_converted)
14923 Print(", FAILED:");
14924 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14925 if (levels_failed[i])
14930 PrintLine("=", 79);
14932 CloseAllAndExit(0);
14936 // ----------------------------------------------------------------------------
14937 // create and save images for use in level sketches (raw BMP format)
14938 // ----------------------------------------------------------------------------
14940 void CreateLevelSketchImages(void)
14946 InitElementPropertiesGfxElement();
14948 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14949 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14951 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14953 int element = getMappedElement(i);
14954 char basename1[16];
14955 char basename2[16];
14959 sprintf(basename1, "%04d.bmp", i);
14960 sprintf(basename2, "%04ds.bmp", i);
14962 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14963 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14965 DrawSizedElement(0, 0, element, TILESIZE);
14966 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14968 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14969 Fail("cannot save level sketch image file '%s'", filename1);
14971 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14972 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14974 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14975 Fail("cannot save level sketch image file '%s'", filename2);
14980 // create corresponding SQL statements (for normal and small images)
14983 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14984 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14987 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14988 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14990 // optional: create content for forum level sketch demonstration post
14992 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14995 FreeBitmap(bitmap1);
14996 FreeBitmap(bitmap2);
14999 fprintf(stderr, "\n");
15001 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
15003 CloseAllAndExit(0);
15007 // ----------------------------------------------------------------------------
15008 // create and save images for element collecting animations (raw BMP format)
15009 // ----------------------------------------------------------------------------
15011 static boolean createCollectImage(int element)
15013 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
15016 void CreateCollectElementImages(void)
15020 int anim_frames = num_steps - 1;
15021 int tile_size = TILESIZE;
15022 int anim_width = tile_size * anim_frames;
15023 int anim_height = tile_size;
15024 int num_collect_images = 0;
15025 int pos_collect_images = 0;
15027 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15028 if (createCollectImage(i))
15029 num_collect_images++;
15031 Info("Creating %d element collecting animation images ...",
15032 num_collect_images);
15034 int dst_width = anim_width * 2;
15035 int dst_height = anim_height * num_collect_images / 2;
15036 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
15037 char *basename_bmp = "RocksCollect.bmp";
15038 char *basename_png = "RocksCollect.png";
15039 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
15040 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
15041 int len_filename_bmp = strlen(filename_bmp);
15042 int len_filename_png = strlen(filename_png);
15043 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
15044 char cmd_convert[max_command_len];
15046 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
15050 // force using RGBA surface for destination bitmap
15051 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
15052 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
15054 dst_bitmap->surface =
15055 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15057 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15059 if (!createCollectImage(i))
15062 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
15063 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
15064 int graphic = el2img(i);
15065 char *token_name = element_info[i].token_name;
15066 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
15067 Bitmap *src_bitmap;
15070 Info("- creating collecting image for '%s' ...", token_name);
15072 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
15074 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
15075 tile_size, tile_size, 0, 0);
15077 // force using RGBA surface for temporary bitmap (using transparent black)
15078 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
15079 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
15081 tmp_bitmap->surface =
15082 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15084 tmp_bitmap->surface_masked = tmp_bitmap->surface;
15086 for (j = 0; j < anim_frames; j++)
15088 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
15089 int frame_size = frame_size_final * num_steps;
15090 int offset = (tile_size - frame_size_final) / 2;
15091 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
15093 while (frame_size > frame_size_final)
15097 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
15099 FreeBitmap(frame_bitmap);
15101 frame_bitmap = half_bitmap;
15104 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
15105 frame_size_final, frame_size_final,
15106 dst_x + j * tile_size + offset, dst_y + offset);
15108 FreeBitmap(frame_bitmap);
15111 tmp_bitmap->surface_masked = NULL;
15113 FreeBitmap(tmp_bitmap);
15115 pos_collect_images++;
15118 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
15119 Fail("cannot save element collecting image file '%s'", filename_bmp);
15121 FreeBitmap(dst_bitmap);
15123 Info("Converting image file from BMP to PNG ...");
15125 if (system(cmd_convert) != 0)
15126 Fail("converting image file failed");
15128 unlink(filename_bmp);
15132 CloseAllAndExit(0);
15136 // ----------------------------------------------------------------------------
15137 // create and save images for custom and group elements (raw BMP format)
15138 // ----------------------------------------------------------------------------
15140 void CreateCustomElementImages(char *directory)
15142 char *src_basename = "RocksCE-template.ilbm";
15143 char *dst_basename = "RocksCE.bmp";
15144 char *src_filename = getPath2(directory, src_basename);
15145 char *dst_filename = getPath2(directory, dst_basename);
15146 Bitmap *src_bitmap;
15148 int yoffset_ce = 0;
15149 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15152 InitVideoDefaults();
15154 ReCreateBitmap(&backbuffer, video.width, video.height);
15156 src_bitmap = LoadImage(src_filename);
15158 bitmap = CreateBitmap(TILEX * 16 * 2,
15159 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15162 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15169 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15170 TILEX * x, TILEY * y + yoffset_ce);
15172 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15174 TILEX * x + TILEX * 16,
15175 TILEY * y + yoffset_ce);
15177 for (j = 2; j >= 0; j--)
15181 BlitBitmap(src_bitmap, bitmap,
15182 TILEX + c * 7, 0, 6, 10,
15183 TILEX * x + 6 + j * 7,
15184 TILEY * y + 11 + yoffset_ce);
15186 BlitBitmap(src_bitmap, bitmap,
15187 TILEX + c * 8, TILEY, 6, 10,
15188 TILEX * 16 + TILEX * x + 6 + j * 8,
15189 TILEY * y + 10 + yoffset_ce);
15195 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15202 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15203 TILEX * x, TILEY * y + yoffset_ge);
15205 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15207 TILEX * x + TILEX * 16,
15208 TILEY * y + yoffset_ge);
15210 for (j = 1; j >= 0; j--)
15214 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15215 TILEX * x + 6 + j * 10,
15216 TILEY * y + 11 + yoffset_ge);
15218 BlitBitmap(src_bitmap, bitmap,
15219 TILEX + c * 8, TILEY + 12, 6, 10,
15220 TILEX * 16 + TILEX * x + 10 + j * 8,
15221 TILEY * y + 10 + yoffset_ge);
15227 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15228 Fail("cannot save CE graphics file '%s'", dst_filename);
15230 FreeBitmap(bitmap);
15232 CloseAllAndExit(0);