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, 200
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;
4649 if (gd_caveset_has_levels())
4650 cave_name_final = getStringPrint("%s / %d", cave_name_latin1, bd_level_nr + 1);
4652 cave_name_final = getStringCopy(cave_name_latin1);
4654 strncpy(level->name, cave_name_final, MAX_LEVEL_NAME_LEN);
4655 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4657 // playfield elements
4658 for (x = 0; x < level->fieldx; x++)
4659 for (y = 0; y < level->fieldy; y++)
4660 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4662 checked_free(cave_name_latin1);
4663 checked_free(cave_name_final);
4666 static void setTapeInfoToDefaults(void);
4668 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4670 struct LevelInfo_BD *level_bd = level->native_bd_level;
4671 GdCave *cave = level_bd->cave;
4672 GdReplay *replay = level_bd->replay;
4678 // always start with reliable default values
4679 setTapeInfoToDefaults();
4681 tape.level_nr = level_nr; // (currently not used)
4682 tape.random_seed = replay->seed;
4684 TapeSetDateFromIsoDateString(replay->date);
4687 tape.pos[tape.counter].delay = 0;
4689 tape.bd_replay = TRUE;
4691 // all time calculations only used to display approximate tape time
4692 int cave_speed = cave->speed;
4693 int milliseconds_game = 0;
4694 int milliseconds_elapsed = 20;
4696 for (i = 0; i < replay->movements->len; i++)
4698 int replay_action = replay->movements->data[i];
4699 int tape_action = map_action_BD_to_RND(replay_action);
4700 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4701 boolean success = 0;
4705 success = TapeAddAction(action);
4707 milliseconds_game += milliseconds_elapsed;
4709 if (milliseconds_game >= cave_speed)
4711 milliseconds_game -= cave_speed;
4718 tape.pos[tape.counter].delay = 0;
4719 tape.pos[tape.counter].action[0] = 0;
4723 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4729 TapeHaltRecording();
4731 if (!replay->success)
4732 Warn("BD replay is marked as not successful");
4736 // ----------------------------------------------------------------------------
4737 // functions for loading EM level
4738 // ----------------------------------------------------------------------------
4740 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4742 static int ball_xy[8][2] =
4753 struct LevelInfo_EM *level_em = level->native_em_level;
4754 struct CAVE *cav = level_em->cav;
4757 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4758 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4760 cav->time_seconds = level->time;
4761 cav->gems_needed = level->gems_needed;
4763 cav->emerald_score = level->score[SC_EMERALD];
4764 cav->diamond_score = level->score[SC_DIAMOND];
4765 cav->alien_score = level->score[SC_ROBOT];
4766 cav->tank_score = level->score[SC_SPACESHIP];
4767 cav->bug_score = level->score[SC_BUG];
4768 cav->eater_score = level->score[SC_YAMYAM];
4769 cav->nut_score = level->score[SC_NUT];
4770 cav->dynamite_score = level->score[SC_DYNAMITE];
4771 cav->key_score = level->score[SC_KEY];
4772 cav->exit_score = level->score[SC_TIME_BONUS];
4774 cav->num_eater_arrays = level->num_yamyam_contents;
4776 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4777 for (y = 0; y < 3; y++)
4778 for (x = 0; x < 3; x++)
4779 cav->eater_array[i][y * 3 + x] =
4780 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4782 cav->amoeba_time = level->amoeba_speed;
4783 cav->wonderwall_time = level->time_magic_wall;
4784 cav->wheel_time = level->time_wheel;
4786 cav->android_move_time = level->android_move_time;
4787 cav->android_clone_time = level->android_clone_time;
4788 cav->ball_random = level->ball_random;
4789 cav->ball_active = level->ball_active_initial;
4790 cav->ball_time = level->ball_time;
4791 cav->num_ball_arrays = level->num_ball_contents;
4793 cav->lenses_score = level->lenses_score;
4794 cav->magnify_score = level->magnify_score;
4795 cav->slurp_score = level->slurp_score;
4797 cav->lenses_time = level->lenses_time;
4798 cav->magnify_time = level->magnify_time;
4800 cav->wind_time = 9999;
4801 cav->wind_direction =
4802 map_direction_RND_to_EM(level->wind_direction_initial);
4804 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4805 for (j = 0; j < 8; j++)
4806 cav->ball_array[i][j] =
4807 map_element_RND_to_EM_cave(level->ball_content[i].
4808 e[ball_xy[j][0]][ball_xy[j][1]]);
4810 map_android_clone_elements_RND_to_EM(level);
4812 // first fill the complete playfield with the empty space element
4813 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4814 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4815 cav->cave[x][y] = Cblank;
4817 // then copy the real level contents from level file into the playfield
4818 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4820 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4822 if (level->field[x][y] == EL_AMOEBA_DEAD)
4823 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4825 cav->cave[x][y] = new_element;
4828 for (i = 0; i < MAX_PLAYERS; i++)
4830 cav->player_x[i] = -1;
4831 cav->player_y[i] = -1;
4834 // initialize player positions and delete players from the playfield
4835 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4837 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4839 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4841 cav->player_x[player_nr] = x;
4842 cav->player_y[player_nr] = y;
4844 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4849 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4851 static int ball_xy[8][2] =
4862 struct LevelInfo_EM *level_em = level->native_em_level;
4863 struct CAVE *cav = level_em->cav;
4866 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4867 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4869 level->time = cav->time_seconds;
4870 level->gems_needed = cav->gems_needed;
4872 sprintf(level->name, "Level %d", level->file_info.nr);
4874 level->score[SC_EMERALD] = cav->emerald_score;
4875 level->score[SC_DIAMOND] = cav->diamond_score;
4876 level->score[SC_ROBOT] = cav->alien_score;
4877 level->score[SC_SPACESHIP] = cav->tank_score;
4878 level->score[SC_BUG] = cav->bug_score;
4879 level->score[SC_YAMYAM] = cav->eater_score;
4880 level->score[SC_NUT] = cav->nut_score;
4881 level->score[SC_DYNAMITE] = cav->dynamite_score;
4882 level->score[SC_KEY] = cav->key_score;
4883 level->score[SC_TIME_BONUS] = cav->exit_score;
4885 level->num_yamyam_contents = cav->num_eater_arrays;
4887 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4888 for (y = 0; y < 3; y++)
4889 for (x = 0; x < 3; x++)
4890 level->yamyam_content[i].e[x][y] =
4891 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4893 level->amoeba_speed = cav->amoeba_time;
4894 level->time_magic_wall = cav->wonderwall_time;
4895 level->time_wheel = cav->wheel_time;
4897 level->android_move_time = cav->android_move_time;
4898 level->android_clone_time = cav->android_clone_time;
4899 level->ball_random = cav->ball_random;
4900 level->ball_active_initial = cav->ball_active;
4901 level->ball_time = cav->ball_time;
4902 level->num_ball_contents = cav->num_ball_arrays;
4904 level->lenses_score = cav->lenses_score;
4905 level->magnify_score = cav->magnify_score;
4906 level->slurp_score = cav->slurp_score;
4908 level->lenses_time = cav->lenses_time;
4909 level->magnify_time = cav->magnify_time;
4911 level->wind_direction_initial =
4912 map_direction_EM_to_RND(cav->wind_direction);
4914 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4915 for (j = 0; j < 8; j++)
4916 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4917 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4919 map_android_clone_elements_EM_to_RND(level);
4921 // convert the playfield (some elements need special treatment)
4922 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4924 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4926 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4927 new_element = EL_AMOEBA_DEAD;
4929 level->field[x][y] = new_element;
4932 for (i = 0; i < MAX_PLAYERS; i++)
4934 // in case of all players set to the same field, use the first player
4935 int nr = MAX_PLAYERS - i - 1;
4936 int jx = cav->player_x[nr];
4937 int jy = cav->player_y[nr];
4939 if (jx != -1 && jy != -1)
4940 level->field[jx][jy] = EL_PLAYER_1 + nr;
4943 // time score is counted for each 10 seconds left in Emerald Mine levels
4944 level->time_score_base = 10;
4948 // ----------------------------------------------------------------------------
4949 // functions for loading SP level
4950 // ----------------------------------------------------------------------------
4952 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4954 struct LevelInfo_SP *level_sp = level->native_sp_level;
4955 LevelInfoType *header = &level_sp->header;
4958 level_sp->width = level->fieldx;
4959 level_sp->height = level->fieldy;
4961 for (x = 0; x < level->fieldx; x++)
4962 for (y = 0; y < level->fieldy; y++)
4963 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4965 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4967 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4968 header->LevelTitle[i] = level->name[i];
4969 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4971 header->InfotronsNeeded = level->gems_needed;
4973 header->SpecialPortCount = 0;
4975 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4977 boolean gravity_port_found = FALSE;
4978 boolean gravity_port_valid = FALSE;
4979 int gravity_port_flag;
4980 int gravity_port_base_element;
4981 int element = level->field[x][y];
4983 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4984 element <= EL_SP_GRAVITY_ON_PORT_UP)
4986 gravity_port_found = TRUE;
4987 gravity_port_valid = TRUE;
4988 gravity_port_flag = 1;
4989 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4991 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4992 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4994 gravity_port_found = TRUE;
4995 gravity_port_valid = TRUE;
4996 gravity_port_flag = 0;
4997 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4999 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
5000 element <= EL_SP_GRAVITY_PORT_UP)
5002 // change R'n'D style gravity inverting special port to normal port
5003 // (there are no gravity inverting ports in native Supaplex engine)
5005 gravity_port_found = TRUE;
5006 gravity_port_valid = FALSE;
5007 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
5010 if (gravity_port_found)
5012 if (gravity_port_valid &&
5013 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
5015 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
5017 port->PortLocation = (y * level->fieldx + x) * 2;
5018 port->Gravity = gravity_port_flag;
5020 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
5022 header->SpecialPortCount++;
5026 // change special gravity port to normal port
5028 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
5031 level_sp->playfield[x][y] = element - EL_SP_START;
5036 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
5038 struct LevelInfo_SP *level_sp = level->native_sp_level;
5039 LevelInfoType *header = &level_sp->header;
5040 boolean num_invalid_elements = 0;
5043 level->fieldx = level_sp->width;
5044 level->fieldy = level_sp->height;
5046 for (x = 0; x < level->fieldx; x++)
5048 for (y = 0; y < level->fieldy; y++)
5050 int element_old = level_sp->playfield[x][y];
5051 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5053 if (element_new == EL_UNKNOWN)
5055 num_invalid_elements++;
5057 Debug("level:native:SP", "invalid element %d at position %d, %d",
5061 level->field[x][y] = element_new;
5065 if (num_invalid_elements > 0)
5066 Warn("found %d invalid elements%s", num_invalid_elements,
5067 (!options.debug ? " (use '--debug' for more details)" : ""));
5069 for (i = 0; i < MAX_PLAYERS; i++)
5070 level->initial_player_gravity[i] =
5071 (header->InitialGravity == 1 ? TRUE : FALSE);
5073 // skip leading spaces
5074 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5075 if (header->LevelTitle[i] != ' ')
5079 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5080 level->name[j] = header->LevelTitle[i];
5081 level->name[j] = '\0';
5083 // cut trailing spaces
5085 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5086 level->name[j - 1] = '\0';
5088 level->gems_needed = header->InfotronsNeeded;
5090 for (i = 0; i < header->SpecialPortCount; i++)
5092 SpecialPortType *port = &header->SpecialPort[i];
5093 int port_location = port->PortLocation;
5094 int gravity = port->Gravity;
5095 int port_x, port_y, port_element;
5097 port_x = (port_location / 2) % level->fieldx;
5098 port_y = (port_location / 2) / level->fieldx;
5100 if (port_x < 0 || port_x >= level->fieldx ||
5101 port_y < 0 || port_y >= level->fieldy)
5103 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5108 port_element = level->field[port_x][port_y];
5110 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5111 port_element > EL_SP_GRAVITY_PORT_UP)
5113 Warn("no special port at position (%d, %d)", port_x, port_y);
5118 // change previous (wrong) gravity inverting special port to either
5119 // gravity enabling special port or gravity disabling special port
5120 level->field[port_x][port_y] +=
5121 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5122 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5125 // change special gravity ports without database entries to normal ports
5126 for (x = 0; x < level->fieldx; x++)
5127 for (y = 0; y < level->fieldy; y++)
5128 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5129 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5130 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5132 level->time = 0; // no time limit
5133 level->amoeba_speed = 0;
5134 level->time_magic_wall = 0;
5135 level->time_wheel = 0;
5136 level->amoeba_content = EL_EMPTY;
5138 // original Supaplex does not use score values -- rate by playing time
5139 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5140 level->score[i] = 0;
5142 level->rate_time_over_score = TRUE;
5144 // there are no yamyams in supaplex levels
5145 for (i = 0; i < level->num_yamyam_contents; i++)
5146 for (x = 0; x < 3; x++)
5147 for (y = 0; y < 3; y++)
5148 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5151 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5153 struct LevelInfo_SP *level_sp = level->native_sp_level;
5154 struct DemoInfo_SP *demo = &level_sp->demo;
5157 // always start with reliable default values
5158 demo->is_available = FALSE;
5161 if (TAPE_IS_EMPTY(tape))
5164 demo->level_nr = tape.level_nr; // (currently not used)
5166 level_sp->header.DemoRandomSeed = tape.random_seed;
5170 for (i = 0; i < tape.length; i++)
5172 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5173 int demo_repeat = tape.pos[i].delay;
5174 int demo_entries = (demo_repeat + 15) / 16;
5176 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5178 Warn("tape truncated: size exceeds maximum SP demo size %d",
5184 for (j = 0; j < demo_repeat / 16; j++)
5185 demo->data[demo->length++] = 0xf0 | demo_action;
5187 if (demo_repeat % 16)
5188 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5191 demo->is_available = TRUE;
5194 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5196 struct LevelInfo_SP *level_sp = level->native_sp_level;
5197 struct DemoInfo_SP *demo = &level_sp->demo;
5198 char *filename = level->file_info.filename;
5201 // always start with reliable default values
5202 setTapeInfoToDefaults();
5204 if (!demo->is_available)
5207 tape.level_nr = demo->level_nr; // (currently not used)
5208 tape.random_seed = level_sp->header.DemoRandomSeed;
5210 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5213 tape.pos[tape.counter].delay = 0;
5215 for (i = 0; i < demo->length; i++)
5217 int demo_action = demo->data[i] & 0x0f;
5218 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5219 int tape_action = map_key_SP_to_RND(demo_action);
5220 int tape_repeat = demo_repeat + 1;
5221 byte action[MAX_TAPE_ACTIONS] = { tape_action };
5222 boolean success = 0;
5225 for (j = 0; j < tape_repeat; j++)
5226 success = TapeAddAction(action);
5230 Warn("SP demo truncated: size exceeds maximum tape size %d",
5237 TapeHaltRecording();
5241 // ----------------------------------------------------------------------------
5242 // functions for loading MM level
5243 // ----------------------------------------------------------------------------
5245 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5247 struct LevelInfo_MM *level_mm = level->native_mm_level;
5250 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5251 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5253 level_mm->time = level->time;
5254 level_mm->kettles_needed = level->gems_needed;
5255 level_mm->auto_count_kettles = level->auto_count_gems;
5257 level_mm->mm_laser_red = level->mm_laser_red;
5258 level_mm->mm_laser_green = level->mm_laser_green;
5259 level_mm->mm_laser_blue = level->mm_laser_blue;
5261 level_mm->df_laser_red = level->df_laser_red;
5262 level_mm->df_laser_green = level->df_laser_green;
5263 level_mm->df_laser_blue = level->df_laser_blue;
5265 strcpy(level_mm->name, level->name);
5266 strcpy(level_mm->author, level->author);
5268 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
5269 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
5270 level_mm->score[SC_KEY] = level->score[SC_KEY];
5271 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5272 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5274 level_mm->amoeba_speed = level->amoeba_speed;
5275 level_mm->time_fuse = level->mm_time_fuse;
5276 level_mm->time_bomb = level->mm_time_bomb;
5277 level_mm->time_ball = level->mm_time_ball;
5278 level_mm->time_block = level->mm_time_block;
5280 level_mm->num_ball_contents = level->num_mm_ball_contents;
5281 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5282 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5283 level_mm->explode_ball = level->explode_mm_ball;
5285 for (i = 0; i < level->num_mm_ball_contents; i++)
5286 level_mm->ball_content[i] =
5287 map_element_RND_to_MM(level->mm_ball_content[i]);
5289 for (x = 0; x < level->fieldx; x++)
5290 for (y = 0; y < level->fieldy; y++)
5292 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5295 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5297 struct LevelInfo_MM *level_mm = level->native_mm_level;
5300 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5301 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5303 level->time = level_mm->time;
5304 level->gems_needed = level_mm->kettles_needed;
5305 level->auto_count_gems = level_mm->auto_count_kettles;
5307 level->mm_laser_red = level_mm->mm_laser_red;
5308 level->mm_laser_green = level_mm->mm_laser_green;
5309 level->mm_laser_blue = level_mm->mm_laser_blue;
5311 level->df_laser_red = level_mm->df_laser_red;
5312 level->df_laser_green = level_mm->df_laser_green;
5313 level->df_laser_blue = level_mm->df_laser_blue;
5315 strcpy(level->name, level_mm->name);
5317 // only overwrite author from 'levelinfo.conf' if author defined in level
5318 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5319 strcpy(level->author, level_mm->author);
5321 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5322 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5323 level->score[SC_KEY] = level_mm->score[SC_KEY];
5324 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5325 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5327 level->amoeba_speed = level_mm->amoeba_speed;
5328 level->mm_time_fuse = level_mm->time_fuse;
5329 level->mm_time_bomb = level_mm->time_bomb;
5330 level->mm_time_ball = level_mm->time_ball;
5331 level->mm_time_block = level_mm->time_block;
5333 level->num_mm_ball_contents = level_mm->num_ball_contents;
5334 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5335 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5336 level->explode_mm_ball = level_mm->explode_ball;
5338 for (i = 0; i < level->num_mm_ball_contents; i++)
5339 level->mm_ball_content[i] =
5340 map_element_MM_to_RND(level_mm->ball_content[i]);
5342 for (x = 0; x < level->fieldx; x++)
5343 for (y = 0; y < level->fieldy; y++)
5344 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5348 // ----------------------------------------------------------------------------
5349 // functions for loading DC level
5350 // ----------------------------------------------------------------------------
5352 #define DC_LEVEL_HEADER_SIZE 344
5354 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5357 static int last_data_encoded;
5361 int diff_hi, diff_lo;
5362 int data_hi, data_lo;
5363 unsigned short data_decoded;
5367 last_data_encoded = 0;
5374 diff = data_encoded - last_data_encoded;
5375 diff_hi = diff & ~0xff;
5376 diff_lo = diff & 0xff;
5380 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5381 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5382 data_hi = data_hi & 0xff00;
5384 data_decoded = data_hi | data_lo;
5386 last_data_encoded = data_encoded;
5388 offset1 = (offset1 + 1) % 31;
5389 offset2 = offset2 & 0xff;
5391 return data_decoded;
5394 static int getMappedElement_DC(int element)
5402 // 0x0117 - 0x036e: (?)
5405 // 0x042d - 0x0684: (?)
5421 element = EL_CRYSTAL;
5424 case 0x0e77: // quicksand (boulder)
5425 element = EL_QUICKSAND_FAST_FULL;
5428 case 0x0e99: // slow quicksand (boulder)
5429 element = EL_QUICKSAND_FULL;
5433 element = EL_EM_EXIT_OPEN;
5437 element = EL_EM_EXIT_CLOSED;
5441 element = EL_EM_STEEL_EXIT_OPEN;
5445 element = EL_EM_STEEL_EXIT_CLOSED;
5448 case 0x0f4f: // dynamite (lit 1)
5449 element = EL_EM_DYNAMITE_ACTIVE;
5452 case 0x0f57: // dynamite (lit 2)
5453 element = EL_EM_DYNAMITE_ACTIVE;
5456 case 0x0f5f: // dynamite (lit 3)
5457 element = EL_EM_DYNAMITE_ACTIVE;
5460 case 0x0f67: // dynamite (lit 4)
5461 element = EL_EM_DYNAMITE_ACTIVE;
5468 element = EL_AMOEBA_WET;
5472 element = EL_AMOEBA_DROP;
5476 element = EL_DC_MAGIC_WALL;
5480 element = EL_SPACESHIP_UP;
5484 element = EL_SPACESHIP_DOWN;
5488 element = EL_SPACESHIP_LEFT;
5492 element = EL_SPACESHIP_RIGHT;
5496 element = EL_BUG_UP;
5500 element = EL_BUG_DOWN;
5504 element = EL_BUG_LEFT;
5508 element = EL_BUG_RIGHT;
5512 element = EL_MOLE_UP;
5516 element = EL_MOLE_DOWN;
5520 element = EL_MOLE_LEFT;
5524 element = EL_MOLE_RIGHT;
5532 element = EL_YAMYAM_UP;
5536 element = EL_SWITCHGATE_OPEN;
5540 element = EL_SWITCHGATE_CLOSED;
5544 element = EL_DC_SWITCHGATE_SWITCH_UP;
5548 element = EL_TIMEGATE_CLOSED;
5551 case 0x144c: // conveyor belt switch (green)
5552 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5555 case 0x144f: // conveyor belt switch (red)
5556 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5559 case 0x1452: // conveyor belt switch (blue)
5560 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5564 element = EL_CONVEYOR_BELT_3_MIDDLE;
5568 element = EL_CONVEYOR_BELT_3_LEFT;
5572 element = EL_CONVEYOR_BELT_3_RIGHT;
5576 element = EL_CONVEYOR_BELT_1_MIDDLE;
5580 element = EL_CONVEYOR_BELT_1_LEFT;
5584 element = EL_CONVEYOR_BELT_1_RIGHT;
5588 element = EL_CONVEYOR_BELT_4_MIDDLE;
5592 element = EL_CONVEYOR_BELT_4_LEFT;
5596 element = EL_CONVEYOR_BELT_4_RIGHT;
5600 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5604 element = EL_EXPANDABLE_WALL_VERTICAL;
5608 element = EL_EXPANDABLE_WALL_ANY;
5611 case 0x14ce: // growing steel wall (left/right)
5612 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5615 case 0x14df: // growing steel wall (up/down)
5616 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5619 case 0x14e8: // growing steel wall (up/down/left/right)
5620 element = EL_EXPANDABLE_STEELWALL_ANY;
5624 element = EL_SHIELD_DEADLY;
5628 element = EL_EXTRA_TIME;
5636 element = EL_EMPTY_SPACE;
5639 case 0x1578: // quicksand (empty)
5640 element = EL_QUICKSAND_FAST_EMPTY;
5643 case 0x1579: // slow quicksand (empty)
5644 element = EL_QUICKSAND_EMPTY;
5654 element = EL_EM_DYNAMITE;
5657 case 0x15a1: // key (red)
5658 element = EL_EM_KEY_1;
5661 case 0x15a2: // key (yellow)
5662 element = EL_EM_KEY_2;
5665 case 0x15a3: // key (blue)
5666 element = EL_EM_KEY_4;
5669 case 0x15a4: // key (green)
5670 element = EL_EM_KEY_3;
5673 case 0x15a5: // key (white)
5674 element = EL_DC_KEY_WHITE;
5678 element = EL_WALL_SLIPPERY;
5685 case 0x15a8: // wall (not round)
5689 case 0x15a9: // (blue)
5690 element = EL_CHAR_A;
5693 case 0x15aa: // (blue)
5694 element = EL_CHAR_B;
5697 case 0x15ab: // (blue)
5698 element = EL_CHAR_C;
5701 case 0x15ac: // (blue)
5702 element = EL_CHAR_D;
5705 case 0x15ad: // (blue)
5706 element = EL_CHAR_E;
5709 case 0x15ae: // (blue)
5710 element = EL_CHAR_F;
5713 case 0x15af: // (blue)
5714 element = EL_CHAR_G;
5717 case 0x15b0: // (blue)
5718 element = EL_CHAR_H;
5721 case 0x15b1: // (blue)
5722 element = EL_CHAR_I;
5725 case 0x15b2: // (blue)
5726 element = EL_CHAR_J;
5729 case 0x15b3: // (blue)
5730 element = EL_CHAR_K;
5733 case 0x15b4: // (blue)
5734 element = EL_CHAR_L;
5737 case 0x15b5: // (blue)
5738 element = EL_CHAR_M;
5741 case 0x15b6: // (blue)
5742 element = EL_CHAR_N;
5745 case 0x15b7: // (blue)
5746 element = EL_CHAR_O;
5749 case 0x15b8: // (blue)
5750 element = EL_CHAR_P;
5753 case 0x15b9: // (blue)
5754 element = EL_CHAR_Q;
5757 case 0x15ba: // (blue)
5758 element = EL_CHAR_R;
5761 case 0x15bb: // (blue)
5762 element = EL_CHAR_S;
5765 case 0x15bc: // (blue)
5766 element = EL_CHAR_T;
5769 case 0x15bd: // (blue)
5770 element = EL_CHAR_U;
5773 case 0x15be: // (blue)
5774 element = EL_CHAR_V;
5777 case 0x15bf: // (blue)
5778 element = EL_CHAR_W;
5781 case 0x15c0: // (blue)
5782 element = EL_CHAR_X;
5785 case 0x15c1: // (blue)
5786 element = EL_CHAR_Y;
5789 case 0x15c2: // (blue)
5790 element = EL_CHAR_Z;
5793 case 0x15c3: // (blue)
5794 element = EL_CHAR_AUMLAUT;
5797 case 0x15c4: // (blue)
5798 element = EL_CHAR_OUMLAUT;
5801 case 0x15c5: // (blue)
5802 element = EL_CHAR_UUMLAUT;
5805 case 0x15c6: // (blue)
5806 element = EL_CHAR_0;
5809 case 0x15c7: // (blue)
5810 element = EL_CHAR_1;
5813 case 0x15c8: // (blue)
5814 element = EL_CHAR_2;
5817 case 0x15c9: // (blue)
5818 element = EL_CHAR_3;
5821 case 0x15ca: // (blue)
5822 element = EL_CHAR_4;
5825 case 0x15cb: // (blue)
5826 element = EL_CHAR_5;
5829 case 0x15cc: // (blue)
5830 element = EL_CHAR_6;
5833 case 0x15cd: // (blue)
5834 element = EL_CHAR_7;
5837 case 0x15ce: // (blue)
5838 element = EL_CHAR_8;
5841 case 0x15cf: // (blue)
5842 element = EL_CHAR_9;
5845 case 0x15d0: // (blue)
5846 element = EL_CHAR_PERIOD;
5849 case 0x15d1: // (blue)
5850 element = EL_CHAR_EXCLAM;
5853 case 0x15d2: // (blue)
5854 element = EL_CHAR_COLON;
5857 case 0x15d3: // (blue)
5858 element = EL_CHAR_LESS;
5861 case 0x15d4: // (blue)
5862 element = EL_CHAR_GREATER;
5865 case 0x15d5: // (blue)
5866 element = EL_CHAR_QUESTION;
5869 case 0x15d6: // (blue)
5870 element = EL_CHAR_COPYRIGHT;
5873 case 0x15d7: // (blue)
5874 element = EL_CHAR_UP;
5877 case 0x15d8: // (blue)
5878 element = EL_CHAR_DOWN;
5881 case 0x15d9: // (blue)
5882 element = EL_CHAR_BUTTON;
5885 case 0x15da: // (blue)
5886 element = EL_CHAR_PLUS;
5889 case 0x15db: // (blue)
5890 element = EL_CHAR_MINUS;
5893 case 0x15dc: // (blue)
5894 element = EL_CHAR_APOSTROPHE;
5897 case 0x15dd: // (blue)
5898 element = EL_CHAR_PARENLEFT;
5901 case 0x15de: // (blue)
5902 element = EL_CHAR_PARENRIGHT;
5905 case 0x15df: // (green)
5906 element = EL_CHAR_A;
5909 case 0x15e0: // (green)
5910 element = EL_CHAR_B;
5913 case 0x15e1: // (green)
5914 element = EL_CHAR_C;
5917 case 0x15e2: // (green)
5918 element = EL_CHAR_D;
5921 case 0x15e3: // (green)
5922 element = EL_CHAR_E;
5925 case 0x15e4: // (green)
5926 element = EL_CHAR_F;
5929 case 0x15e5: // (green)
5930 element = EL_CHAR_G;
5933 case 0x15e6: // (green)
5934 element = EL_CHAR_H;
5937 case 0x15e7: // (green)
5938 element = EL_CHAR_I;
5941 case 0x15e8: // (green)
5942 element = EL_CHAR_J;
5945 case 0x15e9: // (green)
5946 element = EL_CHAR_K;
5949 case 0x15ea: // (green)
5950 element = EL_CHAR_L;
5953 case 0x15eb: // (green)
5954 element = EL_CHAR_M;
5957 case 0x15ec: // (green)
5958 element = EL_CHAR_N;
5961 case 0x15ed: // (green)
5962 element = EL_CHAR_O;
5965 case 0x15ee: // (green)
5966 element = EL_CHAR_P;
5969 case 0x15ef: // (green)
5970 element = EL_CHAR_Q;
5973 case 0x15f0: // (green)
5974 element = EL_CHAR_R;
5977 case 0x15f1: // (green)
5978 element = EL_CHAR_S;
5981 case 0x15f2: // (green)
5982 element = EL_CHAR_T;
5985 case 0x15f3: // (green)
5986 element = EL_CHAR_U;
5989 case 0x15f4: // (green)
5990 element = EL_CHAR_V;
5993 case 0x15f5: // (green)
5994 element = EL_CHAR_W;
5997 case 0x15f6: // (green)
5998 element = EL_CHAR_X;
6001 case 0x15f7: // (green)
6002 element = EL_CHAR_Y;
6005 case 0x15f8: // (green)
6006 element = EL_CHAR_Z;
6009 case 0x15f9: // (green)
6010 element = EL_CHAR_AUMLAUT;
6013 case 0x15fa: // (green)
6014 element = EL_CHAR_OUMLAUT;
6017 case 0x15fb: // (green)
6018 element = EL_CHAR_UUMLAUT;
6021 case 0x15fc: // (green)
6022 element = EL_CHAR_0;
6025 case 0x15fd: // (green)
6026 element = EL_CHAR_1;
6029 case 0x15fe: // (green)
6030 element = EL_CHAR_2;
6033 case 0x15ff: // (green)
6034 element = EL_CHAR_3;
6037 case 0x1600: // (green)
6038 element = EL_CHAR_4;
6041 case 0x1601: // (green)
6042 element = EL_CHAR_5;
6045 case 0x1602: // (green)
6046 element = EL_CHAR_6;
6049 case 0x1603: // (green)
6050 element = EL_CHAR_7;
6053 case 0x1604: // (green)
6054 element = EL_CHAR_8;
6057 case 0x1605: // (green)
6058 element = EL_CHAR_9;
6061 case 0x1606: // (green)
6062 element = EL_CHAR_PERIOD;
6065 case 0x1607: // (green)
6066 element = EL_CHAR_EXCLAM;
6069 case 0x1608: // (green)
6070 element = EL_CHAR_COLON;
6073 case 0x1609: // (green)
6074 element = EL_CHAR_LESS;
6077 case 0x160a: // (green)
6078 element = EL_CHAR_GREATER;
6081 case 0x160b: // (green)
6082 element = EL_CHAR_QUESTION;
6085 case 0x160c: // (green)
6086 element = EL_CHAR_COPYRIGHT;
6089 case 0x160d: // (green)
6090 element = EL_CHAR_UP;
6093 case 0x160e: // (green)
6094 element = EL_CHAR_DOWN;
6097 case 0x160f: // (green)
6098 element = EL_CHAR_BUTTON;
6101 case 0x1610: // (green)
6102 element = EL_CHAR_PLUS;
6105 case 0x1611: // (green)
6106 element = EL_CHAR_MINUS;
6109 case 0x1612: // (green)
6110 element = EL_CHAR_APOSTROPHE;
6113 case 0x1613: // (green)
6114 element = EL_CHAR_PARENLEFT;
6117 case 0x1614: // (green)
6118 element = EL_CHAR_PARENRIGHT;
6121 case 0x1615: // (blue steel)
6122 element = EL_STEEL_CHAR_A;
6125 case 0x1616: // (blue steel)
6126 element = EL_STEEL_CHAR_B;
6129 case 0x1617: // (blue steel)
6130 element = EL_STEEL_CHAR_C;
6133 case 0x1618: // (blue steel)
6134 element = EL_STEEL_CHAR_D;
6137 case 0x1619: // (blue steel)
6138 element = EL_STEEL_CHAR_E;
6141 case 0x161a: // (blue steel)
6142 element = EL_STEEL_CHAR_F;
6145 case 0x161b: // (blue steel)
6146 element = EL_STEEL_CHAR_G;
6149 case 0x161c: // (blue steel)
6150 element = EL_STEEL_CHAR_H;
6153 case 0x161d: // (blue steel)
6154 element = EL_STEEL_CHAR_I;
6157 case 0x161e: // (blue steel)
6158 element = EL_STEEL_CHAR_J;
6161 case 0x161f: // (blue steel)
6162 element = EL_STEEL_CHAR_K;
6165 case 0x1620: // (blue steel)
6166 element = EL_STEEL_CHAR_L;
6169 case 0x1621: // (blue steel)
6170 element = EL_STEEL_CHAR_M;
6173 case 0x1622: // (blue steel)
6174 element = EL_STEEL_CHAR_N;
6177 case 0x1623: // (blue steel)
6178 element = EL_STEEL_CHAR_O;
6181 case 0x1624: // (blue steel)
6182 element = EL_STEEL_CHAR_P;
6185 case 0x1625: // (blue steel)
6186 element = EL_STEEL_CHAR_Q;
6189 case 0x1626: // (blue steel)
6190 element = EL_STEEL_CHAR_R;
6193 case 0x1627: // (blue steel)
6194 element = EL_STEEL_CHAR_S;
6197 case 0x1628: // (blue steel)
6198 element = EL_STEEL_CHAR_T;
6201 case 0x1629: // (blue steel)
6202 element = EL_STEEL_CHAR_U;
6205 case 0x162a: // (blue steel)
6206 element = EL_STEEL_CHAR_V;
6209 case 0x162b: // (blue steel)
6210 element = EL_STEEL_CHAR_W;
6213 case 0x162c: // (blue steel)
6214 element = EL_STEEL_CHAR_X;
6217 case 0x162d: // (blue steel)
6218 element = EL_STEEL_CHAR_Y;
6221 case 0x162e: // (blue steel)
6222 element = EL_STEEL_CHAR_Z;
6225 case 0x162f: // (blue steel)
6226 element = EL_STEEL_CHAR_AUMLAUT;
6229 case 0x1630: // (blue steel)
6230 element = EL_STEEL_CHAR_OUMLAUT;
6233 case 0x1631: // (blue steel)
6234 element = EL_STEEL_CHAR_UUMLAUT;
6237 case 0x1632: // (blue steel)
6238 element = EL_STEEL_CHAR_0;
6241 case 0x1633: // (blue steel)
6242 element = EL_STEEL_CHAR_1;
6245 case 0x1634: // (blue steel)
6246 element = EL_STEEL_CHAR_2;
6249 case 0x1635: // (blue steel)
6250 element = EL_STEEL_CHAR_3;
6253 case 0x1636: // (blue steel)
6254 element = EL_STEEL_CHAR_4;
6257 case 0x1637: // (blue steel)
6258 element = EL_STEEL_CHAR_5;
6261 case 0x1638: // (blue steel)
6262 element = EL_STEEL_CHAR_6;
6265 case 0x1639: // (blue steel)
6266 element = EL_STEEL_CHAR_7;
6269 case 0x163a: // (blue steel)
6270 element = EL_STEEL_CHAR_8;
6273 case 0x163b: // (blue steel)
6274 element = EL_STEEL_CHAR_9;
6277 case 0x163c: // (blue steel)
6278 element = EL_STEEL_CHAR_PERIOD;
6281 case 0x163d: // (blue steel)
6282 element = EL_STEEL_CHAR_EXCLAM;
6285 case 0x163e: // (blue steel)
6286 element = EL_STEEL_CHAR_COLON;
6289 case 0x163f: // (blue steel)
6290 element = EL_STEEL_CHAR_LESS;
6293 case 0x1640: // (blue steel)
6294 element = EL_STEEL_CHAR_GREATER;
6297 case 0x1641: // (blue steel)
6298 element = EL_STEEL_CHAR_QUESTION;
6301 case 0x1642: // (blue steel)
6302 element = EL_STEEL_CHAR_COPYRIGHT;
6305 case 0x1643: // (blue steel)
6306 element = EL_STEEL_CHAR_UP;
6309 case 0x1644: // (blue steel)
6310 element = EL_STEEL_CHAR_DOWN;
6313 case 0x1645: // (blue steel)
6314 element = EL_STEEL_CHAR_BUTTON;
6317 case 0x1646: // (blue steel)
6318 element = EL_STEEL_CHAR_PLUS;
6321 case 0x1647: // (blue steel)
6322 element = EL_STEEL_CHAR_MINUS;
6325 case 0x1648: // (blue steel)
6326 element = EL_STEEL_CHAR_APOSTROPHE;
6329 case 0x1649: // (blue steel)
6330 element = EL_STEEL_CHAR_PARENLEFT;
6333 case 0x164a: // (blue steel)
6334 element = EL_STEEL_CHAR_PARENRIGHT;
6337 case 0x164b: // (green steel)
6338 element = EL_STEEL_CHAR_A;
6341 case 0x164c: // (green steel)
6342 element = EL_STEEL_CHAR_B;
6345 case 0x164d: // (green steel)
6346 element = EL_STEEL_CHAR_C;
6349 case 0x164e: // (green steel)
6350 element = EL_STEEL_CHAR_D;
6353 case 0x164f: // (green steel)
6354 element = EL_STEEL_CHAR_E;
6357 case 0x1650: // (green steel)
6358 element = EL_STEEL_CHAR_F;
6361 case 0x1651: // (green steel)
6362 element = EL_STEEL_CHAR_G;
6365 case 0x1652: // (green steel)
6366 element = EL_STEEL_CHAR_H;
6369 case 0x1653: // (green steel)
6370 element = EL_STEEL_CHAR_I;
6373 case 0x1654: // (green steel)
6374 element = EL_STEEL_CHAR_J;
6377 case 0x1655: // (green steel)
6378 element = EL_STEEL_CHAR_K;
6381 case 0x1656: // (green steel)
6382 element = EL_STEEL_CHAR_L;
6385 case 0x1657: // (green steel)
6386 element = EL_STEEL_CHAR_M;
6389 case 0x1658: // (green steel)
6390 element = EL_STEEL_CHAR_N;
6393 case 0x1659: // (green steel)
6394 element = EL_STEEL_CHAR_O;
6397 case 0x165a: // (green steel)
6398 element = EL_STEEL_CHAR_P;
6401 case 0x165b: // (green steel)
6402 element = EL_STEEL_CHAR_Q;
6405 case 0x165c: // (green steel)
6406 element = EL_STEEL_CHAR_R;
6409 case 0x165d: // (green steel)
6410 element = EL_STEEL_CHAR_S;
6413 case 0x165e: // (green steel)
6414 element = EL_STEEL_CHAR_T;
6417 case 0x165f: // (green steel)
6418 element = EL_STEEL_CHAR_U;
6421 case 0x1660: // (green steel)
6422 element = EL_STEEL_CHAR_V;
6425 case 0x1661: // (green steel)
6426 element = EL_STEEL_CHAR_W;
6429 case 0x1662: // (green steel)
6430 element = EL_STEEL_CHAR_X;
6433 case 0x1663: // (green steel)
6434 element = EL_STEEL_CHAR_Y;
6437 case 0x1664: // (green steel)
6438 element = EL_STEEL_CHAR_Z;
6441 case 0x1665: // (green steel)
6442 element = EL_STEEL_CHAR_AUMLAUT;
6445 case 0x1666: // (green steel)
6446 element = EL_STEEL_CHAR_OUMLAUT;
6449 case 0x1667: // (green steel)
6450 element = EL_STEEL_CHAR_UUMLAUT;
6453 case 0x1668: // (green steel)
6454 element = EL_STEEL_CHAR_0;
6457 case 0x1669: // (green steel)
6458 element = EL_STEEL_CHAR_1;
6461 case 0x166a: // (green steel)
6462 element = EL_STEEL_CHAR_2;
6465 case 0x166b: // (green steel)
6466 element = EL_STEEL_CHAR_3;
6469 case 0x166c: // (green steel)
6470 element = EL_STEEL_CHAR_4;
6473 case 0x166d: // (green steel)
6474 element = EL_STEEL_CHAR_5;
6477 case 0x166e: // (green steel)
6478 element = EL_STEEL_CHAR_6;
6481 case 0x166f: // (green steel)
6482 element = EL_STEEL_CHAR_7;
6485 case 0x1670: // (green steel)
6486 element = EL_STEEL_CHAR_8;
6489 case 0x1671: // (green steel)
6490 element = EL_STEEL_CHAR_9;
6493 case 0x1672: // (green steel)
6494 element = EL_STEEL_CHAR_PERIOD;
6497 case 0x1673: // (green steel)
6498 element = EL_STEEL_CHAR_EXCLAM;
6501 case 0x1674: // (green steel)
6502 element = EL_STEEL_CHAR_COLON;
6505 case 0x1675: // (green steel)
6506 element = EL_STEEL_CHAR_LESS;
6509 case 0x1676: // (green steel)
6510 element = EL_STEEL_CHAR_GREATER;
6513 case 0x1677: // (green steel)
6514 element = EL_STEEL_CHAR_QUESTION;
6517 case 0x1678: // (green steel)
6518 element = EL_STEEL_CHAR_COPYRIGHT;
6521 case 0x1679: // (green steel)
6522 element = EL_STEEL_CHAR_UP;
6525 case 0x167a: // (green steel)
6526 element = EL_STEEL_CHAR_DOWN;
6529 case 0x167b: // (green steel)
6530 element = EL_STEEL_CHAR_BUTTON;
6533 case 0x167c: // (green steel)
6534 element = EL_STEEL_CHAR_PLUS;
6537 case 0x167d: // (green steel)
6538 element = EL_STEEL_CHAR_MINUS;
6541 case 0x167e: // (green steel)
6542 element = EL_STEEL_CHAR_APOSTROPHE;
6545 case 0x167f: // (green steel)
6546 element = EL_STEEL_CHAR_PARENLEFT;
6549 case 0x1680: // (green steel)
6550 element = EL_STEEL_CHAR_PARENRIGHT;
6553 case 0x1681: // gate (red)
6554 element = EL_EM_GATE_1;
6557 case 0x1682: // secret gate (red)
6558 element = EL_EM_GATE_1_GRAY;
6561 case 0x1683: // gate (yellow)
6562 element = EL_EM_GATE_2;
6565 case 0x1684: // secret gate (yellow)
6566 element = EL_EM_GATE_2_GRAY;
6569 case 0x1685: // gate (blue)
6570 element = EL_EM_GATE_4;
6573 case 0x1686: // secret gate (blue)
6574 element = EL_EM_GATE_4_GRAY;
6577 case 0x1687: // gate (green)
6578 element = EL_EM_GATE_3;
6581 case 0x1688: // secret gate (green)
6582 element = EL_EM_GATE_3_GRAY;
6585 case 0x1689: // gate (white)
6586 element = EL_DC_GATE_WHITE;
6589 case 0x168a: // secret gate (white)
6590 element = EL_DC_GATE_WHITE_GRAY;
6593 case 0x168b: // secret gate (no key)
6594 element = EL_DC_GATE_FAKE_GRAY;
6598 element = EL_ROBOT_WHEEL;
6602 element = EL_DC_TIMEGATE_SWITCH;
6606 element = EL_ACID_POOL_BOTTOM;
6610 element = EL_ACID_POOL_TOPLEFT;
6614 element = EL_ACID_POOL_TOPRIGHT;
6618 element = EL_ACID_POOL_BOTTOMLEFT;
6622 element = EL_ACID_POOL_BOTTOMRIGHT;
6626 element = EL_STEELWALL;
6630 element = EL_STEELWALL_SLIPPERY;
6633 case 0x1695: // steel wall (not round)
6634 element = EL_STEELWALL;
6637 case 0x1696: // steel wall (left)
6638 element = EL_DC_STEELWALL_1_LEFT;
6641 case 0x1697: // steel wall (bottom)
6642 element = EL_DC_STEELWALL_1_BOTTOM;
6645 case 0x1698: // steel wall (right)
6646 element = EL_DC_STEELWALL_1_RIGHT;
6649 case 0x1699: // steel wall (top)
6650 element = EL_DC_STEELWALL_1_TOP;
6653 case 0x169a: // steel wall (left/bottom)
6654 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6657 case 0x169b: // steel wall (right/bottom)
6658 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6661 case 0x169c: // steel wall (right/top)
6662 element = EL_DC_STEELWALL_1_TOPRIGHT;
6665 case 0x169d: // steel wall (left/top)
6666 element = EL_DC_STEELWALL_1_TOPLEFT;
6669 case 0x169e: // steel wall (right/bottom small)
6670 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6673 case 0x169f: // steel wall (left/bottom small)
6674 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6677 case 0x16a0: // steel wall (right/top small)
6678 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6681 case 0x16a1: // steel wall (left/top small)
6682 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6685 case 0x16a2: // steel wall (left/right)
6686 element = EL_DC_STEELWALL_1_VERTICAL;
6689 case 0x16a3: // steel wall (top/bottom)
6690 element = EL_DC_STEELWALL_1_HORIZONTAL;
6693 case 0x16a4: // steel wall 2 (left end)
6694 element = EL_DC_STEELWALL_2_LEFT;
6697 case 0x16a5: // steel wall 2 (right end)
6698 element = EL_DC_STEELWALL_2_RIGHT;
6701 case 0x16a6: // steel wall 2 (top end)
6702 element = EL_DC_STEELWALL_2_TOP;
6705 case 0x16a7: // steel wall 2 (bottom end)
6706 element = EL_DC_STEELWALL_2_BOTTOM;
6709 case 0x16a8: // steel wall 2 (left/right)
6710 element = EL_DC_STEELWALL_2_HORIZONTAL;
6713 case 0x16a9: // steel wall 2 (up/down)
6714 element = EL_DC_STEELWALL_2_VERTICAL;
6717 case 0x16aa: // steel wall 2 (mid)
6718 element = EL_DC_STEELWALL_2_MIDDLE;
6722 element = EL_SIGN_EXCLAMATION;
6726 element = EL_SIGN_RADIOACTIVITY;
6730 element = EL_SIGN_STOP;
6734 element = EL_SIGN_WHEELCHAIR;
6738 element = EL_SIGN_PARKING;
6742 element = EL_SIGN_NO_ENTRY;
6746 element = EL_SIGN_HEART;
6750 element = EL_SIGN_GIVE_WAY;
6754 element = EL_SIGN_ENTRY_FORBIDDEN;
6758 element = EL_SIGN_EMERGENCY_EXIT;
6762 element = EL_SIGN_YIN_YANG;
6766 element = EL_WALL_EMERALD;
6770 element = EL_WALL_DIAMOND;
6774 element = EL_WALL_PEARL;
6778 element = EL_WALL_CRYSTAL;
6782 element = EL_INVISIBLE_WALL;
6786 element = EL_INVISIBLE_STEELWALL;
6790 // EL_INVISIBLE_SAND
6793 element = EL_LIGHT_SWITCH;
6797 element = EL_ENVELOPE_1;
6801 if (element >= 0x0117 && element <= 0x036e) // (?)
6802 element = EL_DIAMOND;
6803 else if (element >= 0x042d && element <= 0x0684) // (?)
6804 element = EL_EMERALD;
6805 else if (element >= 0x157c && element <= 0x158b)
6807 else if (element >= 0x1590 && element <= 0x159f)
6808 element = EL_DC_LANDMINE;
6809 else if (element >= 0x16bc && element <= 0x16cb)
6810 element = EL_INVISIBLE_SAND;
6813 Warn("unknown Diamond Caves element 0x%04x", element);
6815 element = EL_UNKNOWN;
6820 return getMappedElement(element);
6823 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6825 byte header[DC_LEVEL_HEADER_SIZE];
6827 int envelope_header_pos = 62;
6828 int envelope_content_pos = 94;
6829 int level_name_pos = 251;
6830 int level_author_pos = 292;
6831 int envelope_header_len;
6832 int envelope_content_len;
6834 int level_author_len;
6836 int num_yamyam_contents;
6839 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6841 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6843 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6845 header[i * 2 + 0] = header_word >> 8;
6846 header[i * 2 + 1] = header_word & 0xff;
6849 // read some values from level header to check level decoding integrity
6850 fieldx = header[6] | (header[7] << 8);
6851 fieldy = header[8] | (header[9] << 8);
6852 num_yamyam_contents = header[60] | (header[61] << 8);
6854 // do some simple sanity checks to ensure that level was correctly decoded
6855 if (fieldx < 1 || fieldx > 256 ||
6856 fieldy < 1 || fieldy > 256 ||
6857 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6859 level->no_valid_file = TRUE;
6861 Warn("cannot decode level from stream -- using empty level");
6866 // maximum envelope header size is 31 bytes
6867 envelope_header_len = header[envelope_header_pos];
6868 // maximum envelope content size is 110 (156?) bytes
6869 envelope_content_len = header[envelope_content_pos];
6871 // maximum level title size is 40 bytes
6872 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6873 // maximum level author size is 30 (51?) bytes
6874 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6878 for (i = 0; i < envelope_header_len; i++)
6879 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6880 level->envelope[0].text[envelope_size++] =
6881 header[envelope_header_pos + 1 + i];
6883 if (envelope_header_len > 0 && envelope_content_len > 0)
6885 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6886 level->envelope[0].text[envelope_size++] = '\n';
6887 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6888 level->envelope[0].text[envelope_size++] = '\n';
6891 for (i = 0; i < envelope_content_len; i++)
6892 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6893 level->envelope[0].text[envelope_size++] =
6894 header[envelope_content_pos + 1 + i];
6896 level->envelope[0].text[envelope_size] = '\0';
6898 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6899 level->envelope[0].ysize = 10;
6900 level->envelope[0].autowrap = TRUE;
6901 level->envelope[0].centered = TRUE;
6903 for (i = 0; i < level_name_len; i++)
6904 level->name[i] = header[level_name_pos + 1 + i];
6905 level->name[level_name_len] = '\0';
6907 for (i = 0; i < level_author_len; i++)
6908 level->author[i] = header[level_author_pos + 1 + i];
6909 level->author[level_author_len] = '\0';
6911 num_yamyam_contents = header[60] | (header[61] << 8);
6912 level->num_yamyam_contents =
6913 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6915 for (i = 0; i < num_yamyam_contents; i++)
6917 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6919 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6920 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6922 if (i < MAX_ELEMENT_CONTENTS)
6923 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6927 fieldx = header[6] | (header[7] << 8);
6928 fieldy = header[8] | (header[9] << 8);
6929 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6930 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6932 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6934 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6935 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6937 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6938 level->field[x][y] = getMappedElement_DC(element_dc);
6941 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6942 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6943 level->field[x][y] = EL_PLAYER_1;
6945 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6946 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6947 level->field[x][y] = EL_PLAYER_2;
6949 level->gems_needed = header[18] | (header[19] << 8);
6951 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6952 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6953 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6954 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6955 level->score[SC_NUT] = header[28] | (header[29] << 8);
6956 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6957 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6958 level->score[SC_BUG] = header[34] | (header[35] << 8);
6959 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6960 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6961 level->score[SC_KEY] = header[40] | (header[41] << 8);
6962 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6964 level->time = header[44] | (header[45] << 8);
6966 level->amoeba_speed = header[46] | (header[47] << 8);
6967 level->time_light = header[48] | (header[49] << 8);
6968 level->time_timegate = header[50] | (header[51] << 8);
6969 level->time_wheel = header[52] | (header[53] << 8);
6970 level->time_magic_wall = header[54] | (header[55] << 8);
6971 level->extra_time = header[56] | (header[57] << 8);
6972 level->shield_normal_time = header[58] | (header[59] << 8);
6974 // shield and extra time elements do not have a score
6975 level->score[SC_SHIELD] = 0;
6976 level->extra_time_score = 0;
6978 // set time for normal and deadly shields to the same value
6979 level->shield_deadly_time = level->shield_normal_time;
6981 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6982 // can slip down from flat walls, like normal walls and steel walls
6983 level->em_slippery_gems = TRUE;
6985 // time score is counted for each 10 seconds left in Diamond Caves levels
6986 level->time_score_base = 10;
6989 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6990 struct LevelFileInfo *level_file_info,
6991 boolean level_info_only)
6993 char *filename = level_file_info->filename;
6995 int num_magic_bytes = 8;
6996 char magic_bytes[num_magic_bytes + 1];
6997 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6999 if (!(file = openFile(filename, MODE_READ)))
7001 level->no_valid_file = TRUE;
7003 if (!level_info_only)
7004 Warn("cannot read level '%s' -- using empty level", filename);
7009 // fseek(file, 0x0000, SEEK_SET);
7011 if (level_file_info->packed)
7013 // read "magic bytes" from start of file
7014 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
7015 magic_bytes[0] = '\0';
7017 // check "magic bytes" for correct file format
7018 if (!strPrefix(magic_bytes, "DC2"))
7020 level->no_valid_file = TRUE;
7022 Warn("unknown DC level file '%s' -- using empty level", filename);
7027 if (strPrefix(magic_bytes, "DC2Win95") ||
7028 strPrefix(magic_bytes, "DC2Win98"))
7030 int position_first_level = 0x00fa;
7031 int extra_bytes = 4;
7034 // advance file stream to first level inside the level package
7035 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7037 // each block of level data is followed by block of non-level data
7038 num_levels_to_skip *= 2;
7040 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
7041 while (num_levels_to_skip >= 0)
7043 // advance file stream to next level inside the level package
7044 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7046 level->no_valid_file = TRUE;
7048 Warn("cannot fseek in file '%s' -- using empty level", filename);
7053 // skip apparently unused extra bytes following each level
7054 ReadUnusedBytesFromFile(file, extra_bytes);
7056 // read size of next level in level package
7057 skip_bytes = getFile32BitLE(file);
7059 num_levels_to_skip--;
7064 level->no_valid_file = TRUE;
7066 Warn("unknown DC2 level file '%s' -- using empty level", filename);
7072 LoadLevelFromFileStream_DC(file, level);
7078 // ----------------------------------------------------------------------------
7079 // functions for loading SB level
7080 // ----------------------------------------------------------------------------
7082 int getMappedElement_SB(int element_ascii, boolean use_ces)
7090 sb_element_mapping[] =
7092 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
7093 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
7094 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
7095 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
7096 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
7097 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
7098 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
7099 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
7106 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7107 if (element_ascii == sb_element_mapping[i].ascii)
7108 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7110 return EL_UNDEFINED;
7113 static void SetLevelSettings_SB(struct LevelInfo *level)
7117 level->use_step_counter = TRUE;
7120 level->score[SC_TIME_BONUS] = 0;
7121 level->time_score_base = 1;
7122 level->rate_time_over_score = TRUE;
7125 level->auto_exit_sokoban = TRUE;
7128 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7129 struct LevelFileInfo *level_file_info,
7130 boolean level_info_only)
7132 char *filename = level_file_info->filename;
7133 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7134 char last_comment[MAX_LINE_LEN];
7135 char level_name[MAX_LINE_LEN];
7138 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7139 boolean read_continued_line = FALSE;
7140 boolean reading_playfield = FALSE;
7141 boolean got_valid_playfield_line = FALSE;
7142 boolean invalid_playfield_char = FALSE;
7143 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7144 int file_level_nr = 0;
7145 int x = 0, y = 0; // initialized to make compilers happy
7147 last_comment[0] = '\0';
7148 level_name[0] = '\0';
7150 if (!(file = openFile(filename, MODE_READ)))
7152 level->no_valid_file = TRUE;
7154 if (!level_info_only)
7155 Warn("cannot read level '%s' -- using empty level", filename);
7160 while (!checkEndOfFile(file))
7162 // level successfully read, but next level may follow here
7163 if (!got_valid_playfield_line && reading_playfield)
7165 // read playfield from single level file -- skip remaining file
7166 if (!level_file_info->packed)
7169 if (file_level_nr >= num_levels_to_skip)
7174 last_comment[0] = '\0';
7175 level_name[0] = '\0';
7177 reading_playfield = FALSE;
7180 got_valid_playfield_line = FALSE;
7182 // read next line of input file
7183 if (!getStringFromFile(file, line, MAX_LINE_LEN))
7186 // cut trailing line break (this can be newline and/or carriage return)
7187 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7188 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7191 // copy raw input line for later use (mainly debugging output)
7192 strcpy(line_raw, line);
7194 if (read_continued_line)
7196 // append new line to existing line, if there is enough space
7197 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7198 strcat(previous_line, line_ptr);
7200 strcpy(line, previous_line); // copy storage buffer to line
7202 read_continued_line = FALSE;
7205 // if the last character is '\', continue at next line
7206 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7208 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
7209 strcpy(previous_line, line); // copy line to storage buffer
7211 read_continued_line = TRUE;
7217 if (line[0] == '\0')
7220 // extract comment text from comment line
7223 for (line_ptr = line; *line_ptr; line_ptr++)
7224 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7227 strcpy(last_comment, line_ptr);
7232 // extract level title text from line containing level title
7233 if (line[0] == '\'')
7235 strcpy(level_name, &line[1]);
7237 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7238 level_name[strlen(level_name) - 1] = '\0';
7243 // skip lines containing only spaces (or empty lines)
7244 for (line_ptr = line; *line_ptr; line_ptr++)
7245 if (*line_ptr != ' ')
7247 if (*line_ptr == '\0')
7250 // at this point, we have found a line containing part of a playfield
7252 got_valid_playfield_line = TRUE;
7254 if (!reading_playfield)
7256 reading_playfield = TRUE;
7257 invalid_playfield_char = FALSE;
7259 for (x = 0; x < MAX_LEV_FIELDX; x++)
7260 for (y = 0; y < MAX_LEV_FIELDY; y++)
7261 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7266 // start with topmost tile row
7270 // skip playfield line if larger row than allowed
7271 if (y >= MAX_LEV_FIELDY)
7274 // start with leftmost tile column
7277 // read playfield elements from line
7278 for (line_ptr = line; *line_ptr; line_ptr++)
7280 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7282 // stop parsing playfield line if larger column than allowed
7283 if (x >= MAX_LEV_FIELDX)
7286 if (mapped_sb_element == EL_UNDEFINED)
7288 invalid_playfield_char = TRUE;
7293 level->field[x][y] = mapped_sb_element;
7295 // continue with next tile column
7298 level->fieldx = MAX(x, level->fieldx);
7301 if (invalid_playfield_char)
7303 // if first playfield line, treat invalid lines as comment lines
7305 reading_playfield = FALSE;
7310 // continue with next tile row
7318 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7319 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7321 if (!reading_playfield)
7323 level->no_valid_file = TRUE;
7325 Warn("cannot read level '%s' -- using empty level", filename);
7330 if (*level_name != '\0')
7332 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7333 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7335 else if (*last_comment != '\0')
7337 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7338 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7342 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7345 // set all empty fields beyond the border walls to invisible steel wall
7346 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7348 if ((x == 0 || x == level->fieldx - 1 ||
7349 y == 0 || y == level->fieldy - 1) &&
7350 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7351 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7352 level->field, level->fieldx, level->fieldy);
7355 // set special level settings for Sokoban levels
7356 SetLevelSettings_SB(level);
7358 if (load_xsb_to_ces)
7360 // special global settings can now be set in level template
7361 level->use_custom_template = TRUE;
7366 // -------------------------------------------------------------------------
7367 // functions for handling native levels
7368 // -------------------------------------------------------------------------
7370 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7371 struct LevelFileInfo *level_file_info,
7372 boolean level_info_only)
7376 // determine position of requested level inside level package
7377 if (level_file_info->packed)
7378 pos = level_file_info->nr - leveldir_current->first_level;
7380 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7381 level->no_valid_file = TRUE;
7384 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7385 struct LevelFileInfo *level_file_info,
7386 boolean level_info_only)
7388 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7389 level->no_valid_file = TRUE;
7392 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7393 struct LevelFileInfo *level_file_info,
7394 boolean level_info_only)
7398 // determine position of requested level inside level package
7399 if (level_file_info->packed)
7400 pos = level_file_info->nr - leveldir_current->first_level;
7402 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7403 level->no_valid_file = TRUE;
7406 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7407 struct LevelFileInfo *level_file_info,
7408 boolean level_info_only)
7410 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7411 level->no_valid_file = TRUE;
7414 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7416 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7417 CopyNativeLevel_RND_to_BD(level);
7418 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7419 CopyNativeLevel_RND_to_EM(level);
7420 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7421 CopyNativeLevel_RND_to_SP(level);
7422 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7423 CopyNativeLevel_RND_to_MM(level);
7426 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7428 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7429 CopyNativeLevel_BD_to_RND(level);
7430 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7431 CopyNativeLevel_EM_to_RND(level);
7432 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7433 CopyNativeLevel_SP_to_RND(level);
7434 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7435 CopyNativeLevel_MM_to_RND(level);
7438 void SaveNativeLevel(struct LevelInfo *level)
7440 // saving native level files only supported for some game engines
7441 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7442 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7445 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7446 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7447 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7448 char *filename = getLevelFilenameFromBasename(basename);
7450 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7453 boolean success = FALSE;
7455 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7457 CopyNativeLevel_RND_to_BD(level);
7458 // CopyNativeTape_RND_to_BD(level);
7460 success = SaveNativeLevel_BD(filename);
7462 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7464 CopyNativeLevel_RND_to_SP(level);
7465 CopyNativeTape_RND_to_SP(level);
7467 success = SaveNativeLevel_SP(filename);
7471 Request("Native level file saved!", REQ_CONFIRM);
7473 Request("Failed to save native level file!", REQ_CONFIRM);
7477 // ----------------------------------------------------------------------------
7478 // functions for loading generic level
7479 // ----------------------------------------------------------------------------
7481 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7482 struct LevelFileInfo *level_file_info,
7483 boolean level_info_only)
7485 // always start with reliable default values
7486 setLevelInfoToDefaults(level, level_info_only, TRUE);
7488 switch (level_file_info->type)
7490 case LEVEL_FILE_TYPE_RND:
7491 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7494 case LEVEL_FILE_TYPE_BD:
7495 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7496 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7499 case LEVEL_FILE_TYPE_EM:
7500 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7501 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7504 case LEVEL_FILE_TYPE_SP:
7505 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7506 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7509 case LEVEL_FILE_TYPE_MM:
7510 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7511 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7514 case LEVEL_FILE_TYPE_DC:
7515 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7518 case LEVEL_FILE_TYPE_SB:
7519 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7523 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7527 // if level file is invalid, restore level structure to default values
7528 if (level->no_valid_file)
7529 setLevelInfoToDefaults(level, level_info_only, FALSE);
7531 if (check_special_flags("use_native_bd_game_engine"))
7532 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7534 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7535 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7537 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7538 CopyNativeLevel_Native_to_RND(level);
7541 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7543 static struct LevelFileInfo level_file_info;
7545 // always start with reliable default values
7546 setFileInfoToDefaults(&level_file_info);
7548 level_file_info.nr = 0; // unknown level number
7549 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7551 setString(&level_file_info.filename, filename);
7553 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7556 static void LoadLevel_InitVersion(struct LevelInfo *level)
7560 if (leveldir_current == NULL) // only when dumping level
7563 // all engine modifications also valid for levels which use latest engine
7564 if (level->game_version < VERSION_IDENT(3,2,0,5))
7566 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7567 level->time_score_base = 10;
7570 if (leveldir_current->latest_engine)
7572 // ---------- use latest game engine --------------------------------------
7574 /* For all levels which are forced to use the latest game engine version
7575 (normally all but user contributed, private and undefined levels), set
7576 the game engine version to the actual version; this allows for actual
7577 corrections in the game engine to take effect for existing, converted
7578 levels (from "classic" or other existing games) to make the emulation
7579 of the corresponding game more accurate, while (hopefully) not breaking
7580 existing levels created from other players. */
7582 level->game_version = GAME_VERSION_ACTUAL;
7584 /* Set special EM style gems behaviour: EM style gems slip down from
7585 normal, steel and growing wall. As this is a more fundamental change,
7586 it seems better to set the default behaviour to "off" (as it is more
7587 natural) and make it configurable in the level editor (as a property
7588 of gem style elements). Already existing converted levels (neither
7589 private nor contributed levels) are changed to the new behaviour. */
7591 if (level->file_version < FILE_VERSION_2_0)
7592 level->em_slippery_gems = TRUE;
7597 // ---------- use game engine the level was created with --------------------
7599 /* For all levels which are not forced to use the latest game engine
7600 version (normally user contributed, private and undefined levels),
7601 use the version of the game engine the levels were created for.
7603 Since 2.0.1, the game engine version is now directly stored
7604 in the level file (chunk "VERS"), so there is no need anymore
7605 to set the game version from the file version (except for old,
7606 pre-2.0 levels, where the game version is still taken from the
7607 file format version used to store the level -- see above). */
7609 // player was faster than enemies in 1.0.0 and before
7610 if (level->file_version == FILE_VERSION_1_0)
7611 for (i = 0; i < MAX_PLAYERS; i++)
7612 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7614 // default behaviour for EM style gems was "slippery" only in 2.0.1
7615 if (level->game_version == VERSION_IDENT(2,0,1,0))
7616 level->em_slippery_gems = TRUE;
7618 // springs could be pushed over pits before (pre-release version) 2.2.0
7619 if (level->game_version < VERSION_IDENT(2,2,0,0))
7620 level->use_spring_bug = TRUE;
7622 if (level->game_version < VERSION_IDENT(3,2,0,5))
7624 // time orb caused limited time in endless time levels before 3.2.0-5
7625 level->use_time_orb_bug = TRUE;
7627 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7628 level->block_snap_field = FALSE;
7630 // extra time score was same value as time left score before 3.2.0-5
7631 level->extra_time_score = level->score[SC_TIME_BONUS];
7634 if (level->game_version < VERSION_IDENT(3,2,0,7))
7636 // default behaviour for snapping was "not continuous" before 3.2.0-7
7637 level->continuous_snapping = FALSE;
7640 // only few elements were able to actively move into acid before 3.1.0
7641 // trigger settings did not exist before 3.1.0; set to default "any"
7642 if (level->game_version < VERSION_IDENT(3,1,0,0))
7644 // correct "can move into acid" settings (all zero in old levels)
7646 level->can_move_into_acid_bits = 0; // nothing can move into acid
7647 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7649 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7650 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7651 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7652 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7654 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7655 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7657 // correct trigger settings (stored as zero == "none" in old levels)
7659 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7661 int element = EL_CUSTOM_START + i;
7662 struct ElementInfo *ei = &element_info[element];
7664 for (j = 0; j < ei->num_change_pages; j++)
7666 struct ElementChangeInfo *change = &ei->change_page[j];
7668 change->trigger_player = CH_PLAYER_ANY;
7669 change->trigger_page = CH_PAGE_ANY;
7674 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7676 int element = EL_CUSTOM_256;
7677 struct ElementInfo *ei = &element_info[element];
7678 struct ElementChangeInfo *change = &ei->change_page[0];
7680 /* This is needed to fix a problem that was caused by a bugfix in function
7681 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7682 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7683 not replace walkable elements, but instead just placed the player on it,
7684 without placing the Sokoban field under the player). Unfortunately, this
7685 breaks "Snake Bite" style levels when the snake is halfway through a door
7686 that just closes (the snake head is still alive and can be moved in this
7687 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7688 player (without Sokoban element) which then gets killed as designed). */
7690 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7691 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7692 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7693 change->target_element = EL_PLAYER_1;
7696 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7697 if (level->game_version < VERSION_IDENT(3,2,5,0))
7699 /* This is needed to fix a problem that was caused by a bugfix in function
7700 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7701 corrects the behaviour when a custom element changes to another custom
7702 element with a higher element number that has change actions defined.
7703 Normally, only one change per frame is allowed for custom elements.
7704 Therefore, it is checked if a custom element already changed in the
7705 current frame; if it did, subsequent changes are suppressed.
7706 Unfortunately, this is only checked for element changes, but not for
7707 change actions, which are still executed. As the function above loops
7708 through all custom elements from lower to higher, an element change
7709 resulting in a lower CE number won't be checked again, while a target
7710 element with a higher number will also be checked, and potential change
7711 actions will get executed for this CE, too (which is wrong), while
7712 further changes are ignored (which is correct). As this bugfix breaks
7713 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7714 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7715 behaviour for existing levels and tapes that make use of this bug */
7717 level->use_action_after_change_bug = TRUE;
7720 // not centering level after relocating player was default only in 3.2.3
7721 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7722 level->shifted_relocation = TRUE;
7724 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7725 if (level->game_version < VERSION_IDENT(3,2,6,0))
7726 level->em_explodes_by_fire = TRUE;
7728 // levels were solved by the first player entering an exit up to 4.1.0.0
7729 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7730 level->solved_by_one_player = TRUE;
7732 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7733 if (level->game_version < VERSION_IDENT(4,1,1,1))
7734 level->use_life_bugs = TRUE;
7736 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7737 if (level->game_version < VERSION_IDENT(4,1,1,1))
7738 level->sb_objects_needed = FALSE;
7740 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7741 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7742 level->finish_dig_collect = FALSE;
7744 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7745 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7746 level->keep_walkable_ce = TRUE;
7749 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7751 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7754 // check if this level is (not) a Sokoban level
7755 for (y = 0; y < level->fieldy; y++)
7756 for (x = 0; x < level->fieldx; x++)
7757 if (!IS_SB_ELEMENT(Tile[x][y]))
7758 is_sokoban_level = FALSE;
7760 if (is_sokoban_level)
7762 // set special level settings for Sokoban levels
7763 SetLevelSettings_SB(level);
7767 static void LoadLevel_InitSettings(struct LevelInfo *level)
7769 // adjust level settings for (non-native) Sokoban-style levels
7770 LoadLevel_InitSettings_SB(level);
7772 // rename levels with title "nameless level" or if renaming is forced
7773 if (leveldir_current->empty_level_name != NULL &&
7774 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7775 leveldir_current->force_level_name))
7776 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7777 leveldir_current->empty_level_name, level_nr);
7780 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7784 // map elements that have changed in newer versions
7785 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7786 level->game_version);
7787 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7788 for (x = 0; x < 3; x++)
7789 for (y = 0; y < 3; y++)
7790 level->yamyam_content[i].e[x][y] =
7791 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7792 level->game_version);
7796 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7800 // map custom element change events that have changed in newer versions
7801 // (these following values were accidentally changed in version 3.0.1)
7802 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7803 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7805 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7807 int element = EL_CUSTOM_START + i;
7809 // order of checking and copying events to be mapped is important
7810 // (do not change the start and end value -- they are constant)
7811 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7813 if (HAS_CHANGE_EVENT(element, j - 2))
7815 SET_CHANGE_EVENT(element, j - 2, FALSE);
7816 SET_CHANGE_EVENT(element, j, TRUE);
7820 // order of checking and copying events to be mapped is important
7821 // (do not change the start and end value -- they are constant)
7822 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7824 if (HAS_CHANGE_EVENT(element, j - 1))
7826 SET_CHANGE_EVENT(element, j - 1, FALSE);
7827 SET_CHANGE_EVENT(element, j, TRUE);
7833 // initialize "can_change" field for old levels with only one change page
7834 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7836 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7838 int element = EL_CUSTOM_START + i;
7840 if (CAN_CHANGE(element))
7841 element_info[element].change->can_change = TRUE;
7845 // correct custom element values (for old levels without these options)
7846 if (level->game_version < VERSION_IDENT(3,1,1,0))
7848 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7850 int element = EL_CUSTOM_START + i;
7851 struct ElementInfo *ei = &element_info[element];
7853 if (ei->access_direction == MV_NO_DIRECTION)
7854 ei->access_direction = MV_ALL_DIRECTIONS;
7858 // correct custom element values (fix invalid values for all versions)
7861 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7863 int element = EL_CUSTOM_START + i;
7864 struct ElementInfo *ei = &element_info[element];
7866 for (j = 0; j < ei->num_change_pages; j++)
7868 struct ElementChangeInfo *change = &ei->change_page[j];
7870 if (change->trigger_player == CH_PLAYER_NONE)
7871 change->trigger_player = CH_PLAYER_ANY;
7873 if (change->trigger_side == CH_SIDE_NONE)
7874 change->trigger_side = CH_SIDE_ANY;
7879 // initialize "can_explode" field for old levels which did not store this
7880 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7881 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7883 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7885 int element = EL_CUSTOM_START + i;
7887 if (EXPLODES_1X1_OLD(element))
7888 element_info[element].explosion_type = EXPLODES_1X1;
7890 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7891 EXPLODES_SMASHED(element) ||
7892 EXPLODES_IMPACT(element)));
7896 // correct previously hard-coded move delay values for maze runner style
7897 if (level->game_version < VERSION_IDENT(3,1,1,0))
7899 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7901 int element = EL_CUSTOM_START + i;
7903 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7905 // previously hard-coded and therefore ignored
7906 element_info[element].move_delay_fixed = 9;
7907 element_info[element].move_delay_random = 0;
7912 // set some other uninitialized values of custom elements in older levels
7913 if (level->game_version < VERSION_IDENT(3,1,0,0))
7915 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7917 int element = EL_CUSTOM_START + i;
7919 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7921 element_info[element].explosion_delay = 17;
7922 element_info[element].ignition_delay = 8;
7926 // set mouse click change events to work for left/middle/right mouse button
7927 if (level->game_version < VERSION_IDENT(4,2,3,0))
7929 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7931 int element = EL_CUSTOM_START + i;
7932 struct ElementInfo *ei = &element_info[element];
7934 for (j = 0; j < ei->num_change_pages; j++)
7936 struct ElementChangeInfo *change = &ei->change_page[j];
7938 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7939 change->has_event[CE_PRESSED_BY_MOUSE] ||
7940 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7941 change->has_event[CE_MOUSE_PRESSED_ON_X])
7942 change->trigger_side = CH_SIDE_ANY;
7948 static void LoadLevel_InitElements(struct LevelInfo *level)
7950 LoadLevel_InitStandardElements(level);
7952 if (level->file_has_custom_elements)
7953 LoadLevel_InitCustomElements(level);
7955 // initialize element properties for level editor etc.
7956 InitElementPropertiesEngine(level->game_version);
7957 InitElementPropertiesGfxElement();
7960 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7964 // map elements that have changed in newer versions
7965 for (y = 0; y < level->fieldy; y++)
7966 for (x = 0; x < level->fieldx; x++)
7967 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7968 level->game_version);
7970 // clear unused playfield data (nicer if level gets resized in editor)
7971 for (x = 0; x < MAX_LEV_FIELDX; x++)
7972 for (y = 0; y < MAX_LEV_FIELDY; y++)
7973 if (x >= level->fieldx || y >= level->fieldy)
7974 level->field[x][y] = EL_EMPTY;
7976 // copy elements to runtime playfield array
7977 for (x = 0; x < MAX_LEV_FIELDX; x++)
7978 for (y = 0; y < MAX_LEV_FIELDY; y++)
7979 Tile[x][y] = level->field[x][y];
7981 // initialize level size variables for faster access
7982 lev_fieldx = level->fieldx;
7983 lev_fieldy = level->fieldy;
7985 // determine border element for this level
7986 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7987 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7992 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7994 struct LevelFileInfo *level_file_info = &level->file_info;
7996 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7997 CopyNativeLevel_RND_to_Native(level);
8000 static void LoadLevelTemplate_LoadAndInit(void)
8002 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
8004 LoadLevel_InitVersion(&level_template);
8005 LoadLevel_InitElements(&level_template);
8006 LoadLevel_InitSettings(&level_template);
8008 ActivateLevelTemplate();
8011 void LoadLevelTemplate(int nr)
8013 if (!fileExists(getGlobalLevelTemplateFilename()))
8015 Warn("no level template found for this level");
8020 setLevelFileInfo(&level_template.file_info, nr);
8022 LoadLevelTemplate_LoadAndInit();
8025 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
8027 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
8029 LoadLevelTemplate_LoadAndInit();
8032 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
8034 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
8036 if (level.use_custom_template)
8038 if (network_level != NULL)
8039 LoadNetworkLevelTemplate(network_level);
8041 LoadLevelTemplate(-1);
8044 LoadLevel_InitVersion(&level);
8045 LoadLevel_InitElements(&level);
8046 LoadLevel_InitPlayfield(&level);
8047 LoadLevel_InitSettings(&level);
8049 LoadLevel_InitNativeEngines(&level);
8052 void LoadLevel(int nr)
8054 SetLevelSetInfo(leveldir_current->identifier, nr);
8056 setLevelFileInfo(&level.file_info, nr);
8058 LoadLevel_LoadAndInit(NULL);
8061 void LoadLevelInfoOnly(int nr)
8063 setLevelFileInfo(&level.file_info, nr);
8065 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8068 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8070 SetLevelSetInfo(network_level->leveldir_identifier,
8071 network_level->file_info.nr);
8073 copyLevelFileInfo(&network_level->file_info, &level.file_info);
8075 LoadLevel_LoadAndInit(network_level);
8078 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8082 chunk_size += putFileVersion(file, level->file_version);
8083 chunk_size += putFileVersion(file, level->game_version);
8088 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8092 chunk_size += putFile16BitBE(file, level->creation_date.year);
8093 chunk_size += putFile8Bit(file, level->creation_date.month);
8094 chunk_size += putFile8Bit(file, level->creation_date.day);
8099 #if ENABLE_HISTORIC_CHUNKS
8100 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8104 putFile8Bit(file, level->fieldx);
8105 putFile8Bit(file, level->fieldy);
8107 putFile16BitBE(file, level->time);
8108 putFile16BitBE(file, level->gems_needed);
8110 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8111 putFile8Bit(file, level->name[i]);
8113 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8114 putFile8Bit(file, level->score[i]);
8116 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8117 for (y = 0; y < 3; y++)
8118 for (x = 0; x < 3; x++)
8119 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8120 level->yamyam_content[i].e[x][y]));
8121 putFile8Bit(file, level->amoeba_speed);
8122 putFile8Bit(file, level->time_magic_wall);
8123 putFile8Bit(file, level->time_wheel);
8124 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8125 level->amoeba_content));
8126 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8127 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8128 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8129 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8131 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8133 putFile8Bit(file, (level->block_last_field ? 1 : 0));
8134 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8135 putFile32BitBE(file, level->can_move_into_acid_bits);
8136 putFile8Bit(file, level->dont_collide_with_bits);
8138 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8139 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8141 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8142 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8143 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8145 putFile8Bit(file, level->game_engine_type);
8147 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8151 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8156 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8157 chunk_size += putFile8Bit(file, level->name[i]);
8162 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8167 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8168 chunk_size += putFile8Bit(file, level->author[i]);
8173 #if ENABLE_HISTORIC_CHUNKS
8174 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8179 for (y = 0; y < level->fieldy; y++)
8180 for (x = 0; x < level->fieldx; x++)
8181 if (level->encoding_16bit_field)
8182 chunk_size += putFile16BitBE(file, level->field[x][y]);
8184 chunk_size += putFile8Bit(file, level->field[x][y]);
8190 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8195 for (y = 0; y < level->fieldy; y++)
8196 for (x = 0; x < level->fieldx; x++)
8197 chunk_size += putFile16BitBE(file, level->field[x][y]);
8202 #if ENABLE_HISTORIC_CHUNKS
8203 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8207 putFile8Bit(file, EL_YAMYAM);
8208 putFile8Bit(file, level->num_yamyam_contents);
8209 putFile8Bit(file, 0);
8210 putFile8Bit(file, 0);
8212 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8213 for (y = 0; y < 3; y++)
8214 for (x = 0; x < 3; x++)
8215 if (level->encoding_16bit_field)
8216 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8218 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8222 #if ENABLE_HISTORIC_CHUNKS
8223 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8226 int num_contents, content_xsize, content_ysize;
8227 int content_array[MAX_ELEMENT_CONTENTS][3][3];
8229 if (element == EL_YAMYAM)
8231 num_contents = level->num_yamyam_contents;
8235 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8236 for (y = 0; y < 3; y++)
8237 for (x = 0; x < 3; x++)
8238 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8240 else if (element == EL_BD_AMOEBA)
8246 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8247 for (y = 0; y < 3; y++)
8248 for (x = 0; x < 3; x++)
8249 content_array[i][x][y] = EL_EMPTY;
8250 content_array[0][0][0] = level->amoeba_content;
8254 // chunk header already written -- write empty chunk data
8255 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8257 Warn("cannot save content for element '%d'", element);
8262 putFile16BitBE(file, element);
8263 putFile8Bit(file, num_contents);
8264 putFile8Bit(file, content_xsize);
8265 putFile8Bit(file, content_ysize);
8267 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8269 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8270 for (y = 0; y < 3; y++)
8271 for (x = 0; x < 3; x++)
8272 putFile16BitBE(file, content_array[i][x][y]);
8276 #if ENABLE_HISTORIC_CHUNKS
8277 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8279 int envelope_nr = element - EL_ENVELOPE_1;
8280 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8284 chunk_size += putFile16BitBE(file, element);
8285 chunk_size += putFile16BitBE(file, envelope_len);
8286 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8287 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8289 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8290 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8292 for (i = 0; i < envelope_len; i++)
8293 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8299 #if ENABLE_HISTORIC_CHUNKS
8300 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8301 int num_changed_custom_elements)
8305 putFile16BitBE(file, num_changed_custom_elements);
8307 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8309 int element = EL_CUSTOM_START + i;
8311 struct ElementInfo *ei = &element_info[element];
8313 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8315 if (check < num_changed_custom_elements)
8317 putFile16BitBE(file, element);
8318 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8325 if (check != num_changed_custom_elements) // should not happen
8326 Warn("inconsistent number of custom element properties");
8330 #if ENABLE_HISTORIC_CHUNKS
8331 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8332 int num_changed_custom_elements)
8336 putFile16BitBE(file, num_changed_custom_elements);
8338 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8340 int element = EL_CUSTOM_START + i;
8342 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8344 if (check < num_changed_custom_elements)
8346 putFile16BitBE(file, element);
8347 putFile16BitBE(file, element_info[element].change->target_element);
8354 if (check != num_changed_custom_elements) // should not happen
8355 Warn("inconsistent number of custom target elements");
8359 #if ENABLE_HISTORIC_CHUNKS
8360 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8361 int num_changed_custom_elements)
8363 int i, j, x, y, check = 0;
8365 putFile16BitBE(file, num_changed_custom_elements);
8367 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8369 int element = EL_CUSTOM_START + i;
8370 struct ElementInfo *ei = &element_info[element];
8372 if (ei->modified_settings)
8374 if (check < num_changed_custom_elements)
8376 putFile16BitBE(file, element);
8378 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8379 putFile8Bit(file, ei->description[j]);
8381 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8383 // some free bytes for future properties and padding
8384 WriteUnusedBytesToFile(file, 7);
8386 putFile8Bit(file, ei->use_gfx_element);
8387 putFile16BitBE(file, ei->gfx_element_initial);
8389 putFile8Bit(file, ei->collect_score_initial);
8390 putFile8Bit(file, ei->collect_count_initial);
8392 putFile16BitBE(file, ei->push_delay_fixed);
8393 putFile16BitBE(file, ei->push_delay_random);
8394 putFile16BitBE(file, ei->move_delay_fixed);
8395 putFile16BitBE(file, ei->move_delay_random);
8397 putFile16BitBE(file, ei->move_pattern);
8398 putFile8Bit(file, ei->move_direction_initial);
8399 putFile8Bit(file, ei->move_stepsize);
8401 for (y = 0; y < 3; y++)
8402 for (x = 0; x < 3; x++)
8403 putFile16BitBE(file, ei->content.e[x][y]);
8405 putFile32BitBE(file, ei->change->events);
8407 putFile16BitBE(file, ei->change->target_element);
8409 putFile16BitBE(file, ei->change->delay_fixed);
8410 putFile16BitBE(file, ei->change->delay_random);
8411 putFile16BitBE(file, ei->change->delay_frames);
8413 putFile16BitBE(file, ei->change->initial_trigger_element);
8415 putFile8Bit(file, ei->change->explode);
8416 putFile8Bit(file, ei->change->use_target_content);
8417 putFile8Bit(file, ei->change->only_if_complete);
8418 putFile8Bit(file, ei->change->use_random_replace);
8420 putFile8Bit(file, ei->change->random_percentage);
8421 putFile8Bit(file, ei->change->replace_when);
8423 for (y = 0; y < 3; y++)
8424 for (x = 0; x < 3; x++)
8425 putFile16BitBE(file, ei->change->content.e[x][y]);
8427 putFile8Bit(file, ei->slippery_type);
8429 // some free bytes for future properties and padding
8430 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8437 if (check != num_changed_custom_elements) // should not happen
8438 Warn("inconsistent number of custom element properties");
8442 #if ENABLE_HISTORIC_CHUNKS
8443 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8445 struct ElementInfo *ei = &element_info[element];
8448 // ---------- custom element base property values (96 bytes) ----------------
8450 putFile16BitBE(file, element);
8452 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8453 putFile8Bit(file, ei->description[i]);
8455 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8457 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8459 putFile8Bit(file, ei->num_change_pages);
8461 putFile16BitBE(file, ei->ce_value_fixed_initial);
8462 putFile16BitBE(file, ei->ce_value_random_initial);
8463 putFile8Bit(file, ei->use_last_ce_value);
8465 putFile8Bit(file, ei->use_gfx_element);
8466 putFile16BitBE(file, ei->gfx_element_initial);
8468 putFile8Bit(file, ei->collect_score_initial);
8469 putFile8Bit(file, ei->collect_count_initial);
8471 putFile8Bit(file, ei->drop_delay_fixed);
8472 putFile8Bit(file, ei->push_delay_fixed);
8473 putFile8Bit(file, ei->drop_delay_random);
8474 putFile8Bit(file, ei->push_delay_random);
8475 putFile16BitBE(file, ei->move_delay_fixed);
8476 putFile16BitBE(file, ei->move_delay_random);
8478 // bits 0 - 15 of "move_pattern" ...
8479 putFile16BitBE(file, ei->move_pattern & 0xffff);
8480 putFile8Bit(file, ei->move_direction_initial);
8481 putFile8Bit(file, ei->move_stepsize);
8483 putFile8Bit(file, ei->slippery_type);
8485 for (y = 0; y < 3; y++)
8486 for (x = 0; x < 3; x++)
8487 putFile16BitBE(file, ei->content.e[x][y]);
8489 putFile16BitBE(file, ei->move_enter_element);
8490 putFile16BitBE(file, ei->move_leave_element);
8491 putFile8Bit(file, ei->move_leave_type);
8493 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8494 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8496 putFile8Bit(file, ei->access_direction);
8498 putFile8Bit(file, ei->explosion_delay);
8499 putFile8Bit(file, ei->ignition_delay);
8500 putFile8Bit(file, ei->explosion_type);
8502 // some free bytes for future custom property values and padding
8503 WriteUnusedBytesToFile(file, 1);
8505 // ---------- change page property values (48 bytes) ------------------------
8507 for (i = 0; i < ei->num_change_pages; i++)
8509 struct ElementChangeInfo *change = &ei->change_page[i];
8510 unsigned int event_bits;
8512 // bits 0 - 31 of "has_event[]" ...
8514 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8515 if (change->has_event[j])
8516 event_bits |= (1u << j);
8517 putFile32BitBE(file, event_bits);
8519 putFile16BitBE(file, change->target_element);
8521 putFile16BitBE(file, change->delay_fixed);
8522 putFile16BitBE(file, change->delay_random);
8523 putFile16BitBE(file, change->delay_frames);
8525 putFile16BitBE(file, change->initial_trigger_element);
8527 putFile8Bit(file, change->explode);
8528 putFile8Bit(file, change->use_target_content);
8529 putFile8Bit(file, change->only_if_complete);
8530 putFile8Bit(file, change->use_random_replace);
8532 putFile8Bit(file, change->random_percentage);
8533 putFile8Bit(file, change->replace_when);
8535 for (y = 0; y < 3; y++)
8536 for (x = 0; x < 3; x++)
8537 putFile16BitBE(file, change->target_content.e[x][y]);
8539 putFile8Bit(file, change->can_change);
8541 putFile8Bit(file, change->trigger_side);
8543 putFile8Bit(file, change->trigger_player);
8544 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8545 log_2(change->trigger_page)));
8547 putFile8Bit(file, change->has_action);
8548 putFile8Bit(file, change->action_type);
8549 putFile8Bit(file, change->action_mode);
8550 putFile16BitBE(file, change->action_arg);
8552 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8554 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8555 if (change->has_event[j])
8556 event_bits |= (1u << (j - 32));
8557 putFile8Bit(file, event_bits);
8562 #if ENABLE_HISTORIC_CHUNKS
8563 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8565 struct ElementInfo *ei = &element_info[element];
8566 struct ElementGroupInfo *group = ei->group;
8569 putFile16BitBE(file, element);
8571 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8572 putFile8Bit(file, ei->description[i]);
8574 putFile8Bit(file, group->num_elements);
8576 putFile8Bit(file, ei->use_gfx_element);
8577 putFile16BitBE(file, ei->gfx_element_initial);
8579 putFile8Bit(file, group->choice_mode);
8581 // some free bytes for future values and padding
8582 WriteUnusedBytesToFile(file, 3);
8584 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8585 putFile16BitBE(file, group->element[i]);
8589 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8590 boolean write_element)
8592 int save_type = entry->save_type;
8593 int data_type = entry->data_type;
8594 int conf_type = entry->conf_type;
8595 int byte_mask = conf_type & CONF_MASK_BYTES;
8596 int element = entry->element;
8597 int default_value = entry->default_value;
8599 boolean modified = FALSE;
8601 if (byte_mask != CONF_MASK_MULTI_BYTES)
8603 void *value_ptr = entry->value;
8604 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8607 // check if any settings have been modified before saving them
8608 if (value != default_value)
8611 // do not save if explicitly told or if unmodified default settings
8612 if ((save_type == SAVE_CONF_NEVER) ||
8613 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8617 num_bytes += putFile16BitBE(file, element);
8619 num_bytes += putFile8Bit(file, conf_type);
8620 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8621 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8622 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8625 else if (data_type == TYPE_STRING)
8627 char *default_string = entry->default_string;
8628 char *string = (char *)(entry->value);
8629 int string_length = strlen(string);
8632 // check if any settings have been modified before saving them
8633 if (!strEqual(string, default_string))
8636 // do not save if explicitly told or if unmodified default settings
8637 if ((save_type == SAVE_CONF_NEVER) ||
8638 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8642 num_bytes += putFile16BitBE(file, element);
8644 num_bytes += putFile8Bit(file, conf_type);
8645 num_bytes += putFile16BitBE(file, string_length);
8647 for (i = 0; i < string_length; i++)
8648 num_bytes += putFile8Bit(file, string[i]);
8650 else if (data_type == TYPE_ELEMENT_LIST)
8652 int *element_array = (int *)(entry->value);
8653 int num_elements = *(int *)(entry->num_entities);
8656 // check if any settings have been modified before saving them
8657 for (i = 0; i < num_elements; i++)
8658 if (element_array[i] != default_value)
8661 // do not save if explicitly told or if unmodified default settings
8662 if ((save_type == SAVE_CONF_NEVER) ||
8663 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8667 num_bytes += putFile16BitBE(file, element);
8669 num_bytes += putFile8Bit(file, conf_type);
8670 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8672 for (i = 0; i < num_elements; i++)
8673 num_bytes += putFile16BitBE(file, element_array[i]);
8675 else if (data_type == TYPE_CONTENT_LIST)
8677 struct Content *content = (struct Content *)(entry->value);
8678 int num_contents = *(int *)(entry->num_entities);
8681 // check if any settings have been modified before saving them
8682 for (i = 0; i < num_contents; i++)
8683 for (y = 0; y < 3; y++)
8684 for (x = 0; x < 3; x++)
8685 if (content[i].e[x][y] != default_value)
8688 // do not save if explicitly told or if unmodified default settings
8689 if ((save_type == SAVE_CONF_NEVER) ||
8690 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8694 num_bytes += putFile16BitBE(file, element);
8696 num_bytes += putFile8Bit(file, conf_type);
8697 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8699 for (i = 0; i < num_contents; i++)
8700 for (y = 0; y < 3; y++)
8701 for (x = 0; x < 3; x++)
8702 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8708 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8713 li = *level; // copy level data into temporary buffer
8715 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8716 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8721 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8726 li = *level; // copy level data into temporary buffer
8728 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8729 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8734 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8736 int envelope_nr = element - EL_ENVELOPE_1;
8740 chunk_size += putFile16BitBE(file, element);
8742 // copy envelope data into temporary buffer
8743 xx_envelope = level->envelope[envelope_nr];
8745 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8746 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8751 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8753 struct ElementInfo *ei = &element_info[element];
8757 chunk_size += putFile16BitBE(file, element);
8759 xx_ei = *ei; // copy element data into temporary buffer
8761 // set default description string for this specific element
8762 strcpy(xx_default_description, getDefaultElementDescription(ei));
8764 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8765 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8767 for (i = 0; i < ei->num_change_pages; i++)
8769 struct ElementChangeInfo *change = &ei->change_page[i];
8771 xx_current_change_page = i;
8773 xx_change = *change; // copy change data into temporary buffer
8776 setEventBitsFromEventFlags(change);
8778 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8779 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8786 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8788 struct ElementInfo *ei = &element_info[element];
8789 struct ElementGroupInfo *group = ei->group;
8793 chunk_size += putFile16BitBE(file, element);
8795 xx_ei = *ei; // copy element data into temporary buffer
8796 xx_group = *group; // copy group data into temporary buffer
8798 // set default description string for this specific element
8799 strcpy(xx_default_description, getDefaultElementDescription(ei));
8801 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8802 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8807 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8809 struct ElementInfo *ei = &element_info[element];
8813 chunk_size += putFile16BitBE(file, element);
8815 xx_ei = *ei; // copy element data into temporary buffer
8817 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8818 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8823 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8824 boolean save_as_template)
8830 if (!(file = fopen(filename, MODE_WRITE)))
8832 Warn("cannot save level file '%s'", filename);
8837 level->file_version = FILE_VERSION_ACTUAL;
8838 level->game_version = GAME_VERSION_ACTUAL;
8840 level->creation_date = getCurrentDate();
8842 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8843 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8845 chunk_size = SaveLevel_VERS(NULL, level);
8846 putFileChunkBE(file, "VERS", chunk_size);
8847 SaveLevel_VERS(file, level);
8849 chunk_size = SaveLevel_DATE(NULL, level);
8850 putFileChunkBE(file, "DATE", chunk_size);
8851 SaveLevel_DATE(file, level);
8853 chunk_size = SaveLevel_NAME(NULL, level);
8854 putFileChunkBE(file, "NAME", chunk_size);
8855 SaveLevel_NAME(file, level);
8857 chunk_size = SaveLevel_AUTH(NULL, level);
8858 putFileChunkBE(file, "AUTH", chunk_size);
8859 SaveLevel_AUTH(file, level);
8861 chunk_size = SaveLevel_INFO(NULL, level);
8862 putFileChunkBE(file, "INFO", chunk_size);
8863 SaveLevel_INFO(file, level);
8865 chunk_size = SaveLevel_BODY(NULL, level);
8866 putFileChunkBE(file, "BODY", chunk_size);
8867 SaveLevel_BODY(file, level);
8869 chunk_size = SaveLevel_ELEM(NULL, level);
8870 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8872 putFileChunkBE(file, "ELEM", chunk_size);
8873 SaveLevel_ELEM(file, level);
8876 for (i = 0; i < NUM_ENVELOPES; i++)
8878 int element = EL_ENVELOPE_1 + i;
8880 chunk_size = SaveLevel_NOTE(NULL, level, element);
8881 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8883 putFileChunkBE(file, "NOTE", chunk_size);
8884 SaveLevel_NOTE(file, level, element);
8888 // if not using template level, check for non-default custom/group elements
8889 if (!level->use_custom_template || save_as_template)
8891 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8893 int element = EL_CUSTOM_START + i;
8895 chunk_size = SaveLevel_CUSX(NULL, level, element);
8896 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8898 putFileChunkBE(file, "CUSX", chunk_size);
8899 SaveLevel_CUSX(file, level, element);
8903 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8905 int element = EL_GROUP_START + i;
8907 chunk_size = SaveLevel_GRPX(NULL, level, element);
8908 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8910 putFileChunkBE(file, "GRPX", chunk_size);
8911 SaveLevel_GRPX(file, level, element);
8915 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8917 int element = GET_EMPTY_ELEMENT(i);
8919 chunk_size = SaveLevel_EMPX(NULL, level, element);
8920 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8922 putFileChunkBE(file, "EMPX", chunk_size);
8923 SaveLevel_EMPX(file, level, element);
8930 SetFilePermissions(filename, PERMS_PRIVATE);
8933 void SaveLevel(int nr)
8935 char *filename = getDefaultLevelFilename(nr);
8937 SaveLevelFromFilename(&level, filename, FALSE);
8940 void SaveLevelTemplate(void)
8942 char *filename = getLocalLevelTemplateFilename();
8944 SaveLevelFromFilename(&level, filename, TRUE);
8947 boolean SaveLevelChecked(int nr)
8949 char *filename = getDefaultLevelFilename(nr);
8950 boolean new_level = !fileExists(filename);
8951 boolean level_saved = FALSE;
8953 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8958 Request("Level saved!", REQ_CONFIRM);
8966 void DumpLevel(struct LevelInfo *level)
8968 if (level->no_level_file || level->no_valid_file)
8970 Warn("cannot dump -- no valid level file found");
8976 Print("Level xxx (file version %08d, game version %08d)\n",
8977 level->file_version, level->game_version);
8980 Print("Level author: '%s'\n", level->author);
8981 Print("Level title: '%s'\n", level->name);
8983 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8985 Print("Level time: %d seconds\n", level->time);
8986 Print("Gems needed: %d\n", level->gems_needed);
8988 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8989 Print("Time for wheel: %d seconds\n", level->time_wheel);
8990 Print("Time for light: %d seconds\n", level->time_light);
8991 Print("Time for timegate: %d seconds\n", level->time_timegate);
8993 Print("Amoeba speed: %d\n", level->amoeba_speed);
8996 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8997 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8998 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8999 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
9000 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
9001 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
9007 for (i = 0; i < NUM_ENVELOPES; i++)
9009 char *text = level->envelope[i].text;
9010 int text_len = strlen(text);
9011 boolean has_text = FALSE;
9013 for (j = 0; j < text_len; j++)
9014 if (text[j] != ' ' && text[j] != '\n')
9020 Print("Envelope %d:\n'%s'\n", i + 1, text);
9028 void DumpLevels(void)
9030 static LevelDirTree *dumplevel_leveldir = NULL;
9032 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9033 global.dumplevel_leveldir);
9035 if (dumplevel_leveldir == NULL)
9036 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
9038 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
9039 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
9040 Fail("no such level number: %d", global.dumplevel_level_nr);
9042 leveldir_current = dumplevel_leveldir;
9044 LoadLevel(global.dumplevel_level_nr);
9050 void DumpLevelsetFromFilename_BD(char *filename)
9052 if (leveldir_current == NULL) // no levelsets loaded yet
9055 if (!LoadNativeLevel_BD(filename, 0, FALSE))
9056 CloseAllAndExit(0); // function has already printed warning
9059 Print("Levelset '%s'\n", filename);
9069 void DumpLevelset(void)
9071 static LevelDirTree *dumplevelset_leveldir = NULL;
9073 dumplevelset_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9074 global.dumplevelset_leveldir);
9075 if (dumplevelset_leveldir == NULL)
9076 Fail("no such level identifier: '%s'", global.dumplevelset_leveldir);
9079 Print("Levelset '%s'\n", dumplevelset_leveldir->identifier);
9082 Print("Number of levels: %d\n", dumplevelset_leveldir->levels);
9083 Print("First level number: %d\n", dumplevelset_leveldir->first_level);
9091 // ============================================================================
9092 // tape file functions
9093 // ============================================================================
9095 static void setTapeInfoToDefaults(void)
9099 // always start with reliable default values (empty tape)
9102 // default values (also for pre-1.2 tapes) with only the first player
9103 tape.player_participates[0] = TRUE;
9104 for (i = 1; i < MAX_PLAYERS; i++)
9105 tape.player_participates[i] = FALSE;
9107 // at least one (default: the first) player participates in every tape
9108 tape.num_participating_players = 1;
9110 tape.property_bits = TAPE_PROPERTY_NONE;
9112 tape.level_nr = level_nr;
9114 tape.changed = FALSE;
9115 tape.solved = FALSE;
9117 tape.recording = FALSE;
9118 tape.playing = FALSE;
9119 tape.pausing = FALSE;
9121 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9122 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9124 tape.no_info_chunk = TRUE;
9125 tape.no_valid_file = FALSE;
9128 static int getTapePosSize(struct TapeInfo *tape)
9130 int tape_pos_size = 0;
9132 if (tape->use_key_actions)
9133 tape_pos_size += tape->num_participating_players;
9135 if (tape->use_mouse_actions)
9136 tape_pos_size += 3; // x and y position and mouse button mask
9138 tape_pos_size += 1; // tape action delay value
9140 return tape_pos_size;
9143 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9145 tape->use_key_actions = FALSE;
9146 tape->use_mouse_actions = FALSE;
9148 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9149 tape->use_key_actions = TRUE;
9151 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9152 tape->use_mouse_actions = TRUE;
9155 static int getTapeActionValue(struct TapeInfo *tape)
9157 return (tape->use_key_actions &&
9158 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9159 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
9160 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9161 TAPE_ACTIONS_DEFAULT);
9164 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9166 tape->file_version = getFileVersion(file);
9167 tape->game_version = getFileVersion(file);
9172 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9176 tape->random_seed = getFile32BitBE(file);
9177 tape->date = getFile32BitBE(file);
9178 tape->length = getFile32BitBE(file);
9180 // read header fields that are new since version 1.2
9181 if (tape->file_version >= FILE_VERSION_1_2)
9183 byte store_participating_players = getFile8Bit(file);
9186 // since version 1.2, tapes store which players participate in the tape
9187 tape->num_participating_players = 0;
9188 for (i = 0; i < MAX_PLAYERS; i++)
9190 tape->player_participates[i] = FALSE;
9192 if (store_participating_players & (1 << i))
9194 tape->player_participates[i] = TRUE;
9195 tape->num_participating_players++;
9199 setTapeActionFlags(tape, getFile8Bit(file));
9201 tape->property_bits = getFile8Bit(file);
9202 tape->solved = getFile8Bit(file);
9204 engine_version = getFileVersion(file);
9205 if (engine_version > 0)
9206 tape->engine_version = engine_version;
9208 tape->engine_version = tape->game_version;
9214 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9216 tape->scr_fieldx = getFile8Bit(file);
9217 tape->scr_fieldy = getFile8Bit(file);
9222 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9224 char *level_identifier = NULL;
9225 int level_identifier_size;
9228 tape->no_info_chunk = FALSE;
9230 level_identifier_size = getFile16BitBE(file);
9232 level_identifier = checked_malloc(level_identifier_size);
9234 for (i = 0; i < level_identifier_size; i++)
9235 level_identifier[i] = getFile8Bit(file);
9237 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9238 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9240 checked_free(level_identifier);
9242 tape->level_nr = getFile16BitBE(file);
9244 chunk_size = 2 + level_identifier_size + 2;
9249 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9252 int tape_pos_size = getTapePosSize(tape);
9253 int chunk_size_expected = tape_pos_size * tape->length;
9255 if (chunk_size_expected != chunk_size)
9257 ReadUnusedBytesFromFile(file, chunk_size);
9258 return chunk_size_expected;
9261 for (i = 0; i < tape->length; i++)
9263 if (i >= MAX_TAPE_LEN)
9265 Warn("tape truncated -- size exceeds maximum tape size %d",
9268 // tape too large; read and ignore remaining tape data from this chunk
9269 for (;i < tape->length; i++)
9270 ReadUnusedBytesFromFile(file, tape_pos_size);
9275 if (tape->use_key_actions)
9277 for (j = 0; j < MAX_PLAYERS; j++)
9279 tape->pos[i].action[j] = MV_NONE;
9281 if (tape->player_participates[j])
9282 tape->pos[i].action[j] = getFile8Bit(file);
9286 if (tape->use_mouse_actions)
9288 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
9289 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
9290 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9293 tape->pos[i].delay = getFile8Bit(file);
9295 if (tape->file_version == FILE_VERSION_1_0)
9297 // eliminate possible diagonal moves in old tapes
9298 // this is only for backward compatibility
9300 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9301 byte action = tape->pos[i].action[0];
9302 int k, num_moves = 0;
9304 for (k = 0; k < 4; k++)
9306 if (action & joy_dir[k])
9308 tape->pos[i + num_moves].action[0] = joy_dir[k];
9310 tape->pos[i + num_moves].delay = 0;
9319 tape->length += num_moves;
9322 else if (tape->file_version < FILE_VERSION_2_0)
9324 // convert pre-2.0 tapes to new tape format
9326 if (tape->pos[i].delay > 1)
9329 tape->pos[i + 1] = tape->pos[i];
9330 tape->pos[i + 1].delay = 1;
9333 for (j = 0; j < MAX_PLAYERS; j++)
9334 tape->pos[i].action[j] = MV_NONE;
9335 tape->pos[i].delay--;
9342 if (checkEndOfFile(file))
9346 if (i != tape->length)
9347 chunk_size = tape_pos_size * i;
9352 static void LoadTape_SokobanSolution(char *filename)
9355 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9357 if (!(file = openFile(filename, MODE_READ)))
9359 tape.no_valid_file = TRUE;
9364 while (!checkEndOfFile(file))
9366 unsigned char c = getByteFromFile(file);
9368 if (checkEndOfFile(file))
9375 tape.pos[tape.length].action[0] = MV_UP;
9376 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9382 tape.pos[tape.length].action[0] = MV_DOWN;
9383 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9389 tape.pos[tape.length].action[0] = MV_LEFT;
9390 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9396 tape.pos[tape.length].action[0] = MV_RIGHT;
9397 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9405 // ignore white-space characters
9409 tape.no_valid_file = TRUE;
9411 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9419 if (tape.no_valid_file)
9422 tape.length_frames = GetTapeLengthFrames();
9423 tape.length_seconds = GetTapeLengthSeconds();
9426 void LoadTapeFromFilename(char *filename)
9428 char cookie[MAX_LINE_LEN];
9429 char chunk_name[CHUNK_ID_LEN + 1];
9433 // always start with reliable default values
9434 setTapeInfoToDefaults();
9436 if (strSuffix(filename, ".sln"))
9438 LoadTape_SokobanSolution(filename);
9443 if (!(file = openFile(filename, MODE_READ)))
9445 tape.no_valid_file = TRUE;
9450 getFileChunkBE(file, chunk_name, NULL);
9451 if (strEqual(chunk_name, "RND1"))
9453 getFile32BitBE(file); // not used
9455 getFileChunkBE(file, chunk_name, NULL);
9456 if (!strEqual(chunk_name, "TAPE"))
9458 tape.no_valid_file = TRUE;
9460 Warn("unknown format of tape file '%s'", filename);
9467 else // check for pre-2.0 file format with cookie string
9469 strcpy(cookie, chunk_name);
9470 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9472 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9473 cookie[strlen(cookie) - 1] = '\0';
9475 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9477 tape.no_valid_file = TRUE;
9479 Warn("unknown format of tape file '%s'", filename);
9486 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9488 tape.no_valid_file = TRUE;
9490 Warn("unsupported version of tape file '%s'", filename);
9497 // pre-2.0 tape files have no game version, so use file version here
9498 tape.game_version = tape.file_version;
9501 if (tape.file_version < FILE_VERSION_1_2)
9503 // tape files from versions before 1.2.0 without chunk structure
9504 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9505 LoadTape_BODY(file, 2 * tape.length, &tape);
9513 int (*loader)(File *, int, struct TapeInfo *);
9517 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9518 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9519 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9520 { "INFO", -1, LoadTape_INFO },
9521 { "BODY", -1, LoadTape_BODY },
9525 while (getFileChunkBE(file, chunk_name, &chunk_size))
9529 while (chunk_info[i].name != NULL &&
9530 !strEqual(chunk_name, chunk_info[i].name))
9533 if (chunk_info[i].name == NULL)
9535 Warn("unknown chunk '%s' in tape file '%s'",
9536 chunk_name, filename);
9538 ReadUnusedBytesFromFile(file, chunk_size);
9540 else if (chunk_info[i].size != -1 &&
9541 chunk_info[i].size != chunk_size)
9543 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9544 chunk_size, chunk_name, filename);
9546 ReadUnusedBytesFromFile(file, chunk_size);
9550 // call function to load this tape chunk
9551 int chunk_size_expected =
9552 (chunk_info[i].loader)(file, chunk_size, &tape);
9554 // the size of some chunks cannot be checked before reading other
9555 // chunks first (like "HEAD" and "BODY") that contain some header
9556 // information, so check them here
9557 if (chunk_size_expected != chunk_size)
9559 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9560 chunk_size, chunk_name, filename);
9568 tape.length_frames = GetTapeLengthFrames();
9569 tape.length_seconds = GetTapeLengthSeconds();
9572 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9574 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9576 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9577 tape.engine_version);
9581 void LoadTape(int nr)
9583 char *filename = getTapeFilename(nr);
9585 LoadTapeFromFilename(filename);
9588 void LoadSolutionTape(int nr)
9590 char *filename = getSolutionTapeFilename(nr);
9592 LoadTapeFromFilename(filename);
9594 if (TAPE_IS_EMPTY(tape))
9596 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9597 level.native_bd_level->replay != NULL)
9598 CopyNativeTape_BD_to_RND(&level);
9599 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9600 level.native_sp_level->demo.is_available)
9601 CopyNativeTape_SP_to_RND(&level);
9605 void LoadScoreTape(char *score_tape_basename, int nr)
9607 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9609 LoadTapeFromFilename(filename);
9612 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9614 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9616 LoadTapeFromFilename(filename);
9619 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9621 // chunk required for team mode tapes with non-default screen size
9622 return (tape->num_participating_players > 1 &&
9623 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9624 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9627 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9629 putFileVersion(file, tape->file_version);
9630 putFileVersion(file, tape->game_version);
9633 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9636 byte store_participating_players = 0;
9638 // set bits for participating players for compact storage
9639 for (i = 0; i < MAX_PLAYERS; i++)
9640 if (tape->player_participates[i])
9641 store_participating_players |= (1 << i);
9643 putFile32BitBE(file, tape->random_seed);
9644 putFile32BitBE(file, tape->date);
9645 putFile32BitBE(file, tape->length);
9647 putFile8Bit(file, store_participating_players);
9649 putFile8Bit(file, getTapeActionValue(tape));
9651 putFile8Bit(file, tape->property_bits);
9652 putFile8Bit(file, tape->solved);
9654 putFileVersion(file, tape->engine_version);
9657 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9659 putFile8Bit(file, tape->scr_fieldx);
9660 putFile8Bit(file, tape->scr_fieldy);
9663 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9665 int level_identifier_size = strlen(tape->level_identifier) + 1;
9668 putFile16BitBE(file, level_identifier_size);
9670 for (i = 0; i < level_identifier_size; i++)
9671 putFile8Bit(file, tape->level_identifier[i]);
9673 putFile16BitBE(file, tape->level_nr);
9676 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9680 for (i = 0; i < tape->length; i++)
9682 if (tape->use_key_actions)
9684 for (j = 0; j < MAX_PLAYERS; j++)
9685 if (tape->player_participates[j])
9686 putFile8Bit(file, tape->pos[i].action[j]);
9689 if (tape->use_mouse_actions)
9691 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9692 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9693 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9696 putFile8Bit(file, tape->pos[i].delay);
9700 void SaveTapeToFilename(char *filename)
9704 int info_chunk_size;
9705 int body_chunk_size;
9707 if (!(file = fopen(filename, MODE_WRITE)))
9709 Warn("cannot save level recording file '%s'", filename);
9714 tape_pos_size = getTapePosSize(&tape);
9716 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9717 body_chunk_size = tape_pos_size * tape.length;
9719 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9720 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9722 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9723 SaveTape_VERS(file, &tape);
9725 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9726 SaveTape_HEAD(file, &tape);
9728 if (checkSaveTape_SCRN(&tape))
9730 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9731 SaveTape_SCRN(file, &tape);
9734 putFileChunkBE(file, "INFO", info_chunk_size);
9735 SaveTape_INFO(file, &tape);
9737 putFileChunkBE(file, "BODY", body_chunk_size);
9738 SaveTape_BODY(file, &tape);
9742 SetFilePermissions(filename, PERMS_PRIVATE);
9745 static void SaveTapeExt(char *filename)
9749 tape.file_version = FILE_VERSION_ACTUAL;
9750 tape.game_version = GAME_VERSION_ACTUAL;
9752 tape.num_participating_players = 0;
9754 // count number of participating players
9755 for (i = 0; i < MAX_PLAYERS; i++)
9756 if (tape.player_participates[i])
9757 tape.num_participating_players++;
9759 SaveTapeToFilename(filename);
9761 tape.changed = FALSE;
9764 void SaveTape(int nr)
9766 char *filename = getTapeFilename(nr);
9768 InitTapeDirectory(leveldir_current->subdir);
9770 SaveTapeExt(filename);
9773 void SaveScoreTape(int nr)
9775 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9777 // used instead of "leveldir_current->subdir" (for network games)
9778 InitScoreTapeDirectory(levelset.identifier, nr);
9780 SaveTapeExt(filename);
9783 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9784 unsigned int req_state_added)
9786 char *filename = getTapeFilename(nr);
9787 boolean new_tape = !fileExists(filename);
9788 boolean tape_saved = FALSE;
9790 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9795 Request(msg_saved, REQ_CONFIRM | req_state_added);
9803 boolean SaveTapeChecked(int nr)
9805 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9808 boolean SaveTapeChecked_LevelSolved(int nr)
9810 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9811 "Level solved! Tape saved!", REQ_STAY_OPEN);
9814 void DumpTape(struct TapeInfo *tape)
9816 int tape_frame_counter;
9819 if (tape->no_valid_file)
9821 Warn("cannot dump -- no valid tape file found");
9828 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9829 tape->level_nr, tape->file_version, tape->game_version);
9830 Print(" (effective engine version %08d)\n",
9831 tape->engine_version);
9832 Print("Level series identifier: '%s'\n", tape->level_identifier);
9834 Print("Solution tape: %s\n",
9835 tape->solved ? "yes" :
9836 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9838 Print("Special tape properties: ");
9839 if (tape->property_bits == TAPE_PROPERTY_NONE)
9841 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9842 Print("[em_random_bug]");
9843 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9844 Print("[game_speed]");
9845 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9847 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9848 Print("[single_step]");
9849 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9850 Print("[snapshot]");
9851 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9852 Print("[replayed]");
9853 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9854 Print("[tas_keys]");
9855 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9856 Print("[small_graphics]");
9859 int year2 = tape->date / 10000;
9860 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9861 int month_index_raw = (tape->date / 100) % 100;
9862 int month_index = month_index_raw % 12; // prevent invalid index
9863 int month = month_index + 1;
9864 int day = tape->date % 100;
9866 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9870 tape_frame_counter = 0;
9872 for (i = 0; i < tape->length; i++)
9874 if (i >= MAX_TAPE_LEN)
9879 for (j = 0; j < MAX_PLAYERS; j++)
9881 if (tape->player_participates[j])
9883 int action = tape->pos[i].action[j];
9885 Print("%d:%02x ", j, action);
9886 Print("[%c%c%c%c|%c%c] - ",
9887 (action & JOY_LEFT ? '<' : ' '),
9888 (action & JOY_RIGHT ? '>' : ' '),
9889 (action & JOY_UP ? '^' : ' '),
9890 (action & JOY_DOWN ? 'v' : ' '),
9891 (action & JOY_BUTTON_1 ? '1' : ' '),
9892 (action & JOY_BUTTON_2 ? '2' : ' '));
9896 Print("(%03d) ", tape->pos[i].delay);
9897 Print("[%05d]\n", tape_frame_counter);
9899 tape_frame_counter += tape->pos[i].delay;
9905 void DumpTapes(void)
9907 static LevelDirTree *dumptape_leveldir = NULL;
9909 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9910 global.dumptape_leveldir);
9912 if (dumptape_leveldir == NULL)
9913 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9915 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9916 global.dumptape_level_nr > dumptape_leveldir->last_level)
9917 Fail("no such level number: %d", global.dumptape_level_nr);
9919 leveldir_current = dumptape_leveldir;
9921 if (options.mytapes)
9922 LoadTape(global.dumptape_level_nr);
9924 LoadSolutionTape(global.dumptape_level_nr);
9932 // ============================================================================
9933 // score file functions
9934 // ============================================================================
9936 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9940 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9942 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9943 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9944 scores->entry[i].score = 0;
9945 scores->entry[i].time = 0;
9947 scores->entry[i].id = -1;
9948 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9949 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9950 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9951 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9952 strcpy(scores->entry[i].country_code, "??");
9955 scores->num_entries = 0;
9956 scores->last_added = -1;
9957 scores->last_added_local = -1;
9959 scores->updated = FALSE;
9960 scores->uploaded = FALSE;
9961 scores->tape_downloaded = FALSE;
9962 scores->force_last_added = FALSE;
9964 // The following values are intentionally not reset here:
9968 // - continue_playing
9969 // - continue_on_return
9972 static void setScoreInfoToDefaults(void)
9974 setScoreInfoToDefaultsExt(&scores);
9977 static void setServerScoreInfoToDefaults(void)
9979 setScoreInfoToDefaultsExt(&server_scores);
9982 static void LoadScore_OLD(int nr)
9985 char *filename = getScoreFilename(nr);
9986 char cookie[MAX_LINE_LEN];
9987 char line[MAX_LINE_LEN];
9991 if (!(file = fopen(filename, MODE_READ)))
9994 // check file identifier
9995 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9997 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9998 cookie[strlen(cookie) - 1] = '\0';
10000 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10002 Warn("unknown format of score file '%s'", filename);
10009 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10011 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
10012 Warn("fscanf() failed; %s", strerror(errno));
10014 if (fgets(line, MAX_LINE_LEN, file) == NULL)
10017 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
10018 line[strlen(line) - 1] = '\0';
10020 for (line_ptr = line; *line_ptr; line_ptr++)
10022 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
10024 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
10025 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10034 static void ConvertScore_OLD(void)
10036 // only convert score to time for levels that rate playing time over score
10037 if (!level.rate_time_over_score)
10040 // convert old score to playing time for score-less levels (like Supaplex)
10041 int time_final_max = 999;
10044 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10046 int score = scores.entry[i].score;
10048 if (score > 0 && score < time_final_max)
10049 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
10053 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
10055 scores->file_version = getFileVersion(file);
10056 scores->game_version = getFileVersion(file);
10061 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
10063 char *level_identifier = NULL;
10064 int level_identifier_size;
10067 level_identifier_size = getFile16BitBE(file);
10069 level_identifier = checked_malloc(level_identifier_size);
10071 for (i = 0; i < level_identifier_size; i++)
10072 level_identifier[i] = getFile8Bit(file);
10074 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
10075 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
10077 checked_free(level_identifier);
10079 scores->level_nr = getFile16BitBE(file);
10080 scores->num_entries = getFile16BitBE(file);
10082 chunk_size = 2 + level_identifier_size + 2 + 2;
10087 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10091 for (i = 0; i < scores->num_entries; i++)
10093 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10094 scores->entry[i].name[j] = getFile8Bit(file);
10096 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10099 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10104 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10108 for (i = 0; i < scores->num_entries; i++)
10109 scores->entry[i].score = getFile16BitBE(file);
10111 chunk_size = scores->num_entries * 2;
10116 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10120 for (i = 0; i < scores->num_entries; i++)
10121 scores->entry[i].score = getFile32BitBE(file);
10123 chunk_size = scores->num_entries * 4;
10128 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10132 for (i = 0; i < scores->num_entries; i++)
10133 scores->entry[i].time = getFile32BitBE(file);
10135 chunk_size = scores->num_entries * 4;
10140 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10144 for (i = 0; i < scores->num_entries; i++)
10146 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10147 scores->entry[i].tape_basename[j] = getFile8Bit(file);
10149 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10152 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10157 void LoadScore(int nr)
10159 char *filename = getScoreFilename(nr);
10160 char cookie[MAX_LINE_LEN];
10161 char chunk_name[CHUNK_ID_LEN + 1];
10163 boolean old_score_file_format = FALSE;
10166 // always start with reliable default values
10167 setScoreInfoToDefaults();
10169 if (!(file = openFile(filename, MODE_READ)))
10172 getFileChunkBE(file, chunk_name, NULL);
10173 if (strEqual(chunk_name, "RND1"))
10175 getFile32BitBE(file); // not used
10177 getFileChunkBE(file, chunk_name, NULL);
10178 if (!strEqual(chunk_name, "SCOR"))
10180 Warn("unknown format of score file '%s'", filename);
10187 else // check for old file format with cookie string
10189 strcpy(cookie, chunk_name);
10190 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10192 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10193 cookie[strlen(cookie) - 1] = '\0';
10195 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10197 Warn("unknown format of score file '%s'", filename);
10204 old_score_file_format = TRUE;
10207 if (old_score_file_format)
10209 // score files from versions before 4.2.4.0 without chunk structure
10212 // convert score to time, if possible (mainly for Supaplex levels)
10213 ConvertScore_OLD();
10221 int (*loader)(File *, int, struct ScoreInfo *);
10225 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
10226 { "INFO", -1, LoadScore_INFO },
10227 { "NAME", -1, LoadScore_NAME },
10228 { "SCOR", -1, LoadScore_SCOR },
10229 { "SC4R", -1, LoadScore_SC4R },
10230 { "TIME", -1, LoadScore_TIME },
10231 { "TAPE", -1, LoadScore_TAPE },
10236 while (getFileChunkBE(file, chunk_name, &chunk_size))
10240 while (chunk_info[i].name != NULL &&
10241 !strEqual(chunk_name, chunk_info[i].name))
10244 if (chunk_info[i].name == NULL)
10246 Warn("unknown chunk '%s' in score file '%s'",
10247 chunk_name, filename);
10249 ReadUnusedBytesFromFile(file, chunk_size);
10251 else if (chunk_info[i].size != -1 &&
10252 chunk_info[i].size != chunk_size)
10254 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10255 chunk_size, chunk_name, filename);
10257 ReadUnusedBytesFromFile(file, chunk_size);
10261 // call function to load this score chunk
10262 int chunk_size_expected =
10263 (chunk_info[i].loader)(file, chunk_size, &scores);
10265 // the size of some chunks cannot be checked before reading other
10266 // chunks first (like "HEAD" and "BODY") that contain some header
10267 // information, so check them here
10268 if (chunk_size_expected != chunk_size)
10270 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10271 chunk_size, chunk_name, filename);
10280 #if ENABLE_HISTORIC_CHUNKS
10281 void SaveScore_OLD(int nr)
10284 char *filename = getScoreFilename(nr);
10287 // used instead of "leveldir_current->subdir" (for network games)
10288 InitScoreDirectory(levelset.identifier);
10290 if (!(file = fopen(filename, MODE_WRITE)))
10292 Warn("cannot save score for level %d", nr);
10297 fprintf(file, "%s\n\n", SCORE_COOKIE);
10299 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10300 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10304 SetFilePermissions(filename, PERMS_PRIVATE);
10308 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10310 putFileVersion(file, scores->file_version);
10311 putFileVersion(file, scores->game_version);
10314 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10316 int level_identifier_size = strlen(scores->level_identifier) + 1;
10319 putFile16BitBE(file, level_identifier_size);
10321 for (i = 0; i < level_identifier_size; i++)
10322 putFile8Bit(file, scores->level_identifier[i]);
10324 putFile16BitBE(file, scores->level_nr);
10325 putFile16BitBE(file, scores->num_entries);
10328 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10332 for (i = 0; i < scores->num_entries; i++)
10334 int name_size = strlen(scores->entry[i].name);
10336 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10337 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10341 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10345 for (i = 0; i < scores->num_entries; i++)
10346 putFile16BitBE(file, scores->entry[i].score);
10349 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10353 for (i = 0; i < scores->num_entries; i++)
10354 putFile32BitBE(file, scores->entry[i].score);
10357 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10361 for (i = 0; i < scores->num_entries; i++)
10362 putFile32BitBE(file, scores->entry[i].time);
10365 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10369 for (i = 0; i < scores->num_entries; i++)
10371 int size = strlen(scores->entry[i].tape_basename);
10373 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10374 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10378 static void SaveScoreToFilename(char *filename)
10381 int info_chunk_size;
10382 int name_chunk_size;
10383 int scor_chunk_size;
10384 int sc4r_chunk_size;
10385 int time_chunk_size;
10386 int tape_chunk_size;
10387 boolean has_large_score_values;
10390 if (!(file = fopen(filename, MODE_WRITE)))
10392 Warn("cannot save score file '%s'", filename);
10397 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10398 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10399 scor_chunk_size = scores.num_entries * 2;
10400 sc4r_chunk_size = scores.num_entries * 4;
10401 time_chunk_size = scores.num_entries * 4;
10402 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10404 has_large_score_values = FALSE;
10405 for (i = 0; i < scores.num_entries; i++)
10406 if (scores.entry[i].score > 0xffff)
10407 has_large_score_values = TRUE;
10409 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10410 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10412 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10413 SaveScore_VERS(file, &scores);
10415 putFileChunkBE(file, "INFO", info_chunk_size);
10416 SaveScore_INFO(file, &scores);
10418 putFileChunkBE(file, "NAME", name_chunk_size);
10419 SaveScore_NAME(file, &scores);
10421 if (has_large_score_values)
10423 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10424 SaveScore_SC4R(file, &scores);
10428 putFileChunkBE(file, "SCOR", scor_chunk_size);
10429 SaveScore_SCOR(file, &scores);
10432 putFileChunkBE(file, "TIME", time_chunk_size);
10433 SaveScore_TIME(file, &scores);
10435 putFileChunkBE(file, "TAPE", tape_chunk_size);
10436 SaveScore_TAPE(file, &scores);
10440 SetFilePermissions(filename, PERMS_PRIVATE);
10443 void SaveScore(int nr)
10445 char *filename = getScoreFilename(nr);
10448 // used instead of "leveldir_current->subdir" (for network games)
10449 InitScoreDirectory(levelset.identifier);
10451 scores.file_version = FILE_VERSION_ACTUAL;
10452 scores.game_version = GAME_VERSION_ACTUAL;
10454 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10455 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10456 scores.level_nr = level_nr;
10458 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10459 if (scores.entry[i].score == 0 &&
10460 scores.entry[i].time == 0 &&
10461 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10464 scores.num_entries = i;
10466 if (scores.num_entries == 0)
10469 SaveScoreToFilename(filename);
10472 static void LoadServerScoreFromCache(int nr)
10474 struct ScoreEntry score_entry;
10483 { &score_entry.score, FALSE, 0 },
10484 { &score_entry.time, FALSE, 0 },
10485 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10486 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10487 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10488 { &score_entry.id, FALSE, 0 },
10489 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10490 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10491 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10492 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10496 char *filename = getScoreCacheFilename(nr);
10497 SetupFileHash *score_hash = loadSetupFileHash(filename);
10500 server_scores.num_entries = 0;
10502 if (score_hash == NULL)
10505 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10507 score_entry = server_scores.entry[i];
10509 for (j = 0; score_mapping[j].value != NULL; j++)
10513 sprintf(token, "%02d.%d", i, j);
10515 char *value = getHashEntry(score_hash, token);
10520 if (score_mapping[j].is_string)
10522 char *score_value = (char *)score_mapping[j].value;
10523 int value_size = score_mapping[j].string_size;
10525 strncpy(score_value, value, value_size);
10526 score_value[value_size] = '\0';
10530 int *score_value = (int *)score_mapping[j].value;
10532 *score_value = atoi(value);
10535 server_scores.num_entries = i + 1;
10538 server_scores.entry[i] = score_entry;
10541 freeSetupFileHash(score_hash);
10544 void LoadServerScore(int nr, boolean download_score)
10546 if (!setup.use_api_server)
10549 // always start with reliable default values
10550 setServerScoreInfoToDefaults();
10552 // 1st step: load server scores from cache file (which may not exist)
10553 // (this should prevent reading it while the thread is writing to it)
10554 LoadServerScoreFromCache(nr);
10556 if (download_score && runtime.use_api_server)
10558 // 2nd step: download server scores from score server to cache file
10559 // (as thread, as it might time out if the server is not reachable)
10560 ApiGetScoreAsThread(nr);
10564 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10566 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10568 // if score tape not uploaded, ask for uploading missing tapes later
10569 if (!setup.has_remaining_tapes)
10570 setup.ask_for_remaining_tapes = TRUE;
10572 setup.provide_uploading_tapes = TRUE;
10573 setup.has_remaining_tapes = TRUE;
10575 SaveSetup_ServerSetup();
10578 void SaveServerScore(int nr, boolean tape_saved)
10580 if (!runtime.use_api_server)
10582 PrepareScoreTapesForUpload(leveldir_current->subdir);
10587 ApiAddScoreAsThread(nr, tape_saved, NULL);
10590 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10591 char *score_tape_filename)
10593 if (!runtime.use_api_server)
10596 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10599 void LoadLocalAndServerScore(int nr, boolean download_score)
10601 int last_added_local = scores.last_added_local;
10602 boolean force_last_added = scores.force_last_added;
10604 // needed if only showing server scores
10605 setScoreInfoToDefaults();
10607 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10610 // restore last added local score entry (before merging server scores)
10611 scores.last_added = scores.last_added_local = last_added_local;
10613 if (setup.use_api_server &&
10614 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10616 // load server scores from cache file and trigger update from server
10617 LoadServerScore(nr, download_score);
10619 // merge local scores with scores from server
10620 MergeServerScore();
10623 if (force_last_added)
10624 scores.force_last_added = force_last_added;
10628 // ============================================================================
10629 // setup file functions
10630 // ============================================================================
10632 #define TOKEN_STR_PLAYER_PREFIX "player_"
10635 static struct TokenInfo global_setup_tokens[] =
10639 &setup.player_name, "player_name"
10643 &setup.multiple_users, "multiple_users"
10647 &setup.sound, "sound"
10651 &setup.sound_loops, "repeating_sound_loops"
10655 &setup.sound_music, "background_music"
10659 &setup.sound_simple, "simple_sound_effects"
10663 &setup.toons, "toons"
10667 &setup.global_animations, "global_animations"
10671 &setup.scroll_delay, "scroll_delay"
10675 &setup.forced_scroll_delay, "forced_scroll_delay"
10679 &setup.scroll_delay_value, "scroll_delay_value"
10683 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10687 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10691 &setup.fade_screens, "fade_screens"
10695 &setup.autorecord, "automatic_tape_recording"
10699 &setup.autorecord_after_replay, "autorecord_after_replay"
10703 &setup.auto_pause_on_start, "auto_pause_on_start"
10707 &setup.show_titlescreen, "show_titlescreen"
10711 &setup.quick_doors, "quick_doors"
10715 &setup.team_mode, "team_mode"
10719 &setup.handicap, "handicap"
10723 &setup.skip_levels, "skip_levels"
10726 TYPE_SWITCH_3_STATES,
10727 &setup.allow_skipping_levels, "allow_skipping_levels"
10731 &setup.increment_levels, "increment_levels"
10735 &setup.auto_play_next_level, "auto_play_next_level"
10739 &setup.count_score_after_game, "count_score_after_game"
10743 &setup.show_scores_after_game, "show_scores_after_game"
10747 &setup.time_limit, "time_limit"
10751 &setup.fullscreen, "fullscreen"
10755 &setup.window_scaling_percent, "window_scaling_percent"
10759 &setup.window_scaling_quality, "window_scaling_quality"
10763 &setup.screen_rendering_mode, "screen_rendering_mode"
10767 &setup.vsync_mode, "vsync_mode"
10771 &setup.ask_on_escape, "ask_on_escape"
10775 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10779 &setup.ask_on_game_over, "ask_on_game_over"
10783 &setup.ask_on_quit_game, "ask_on_quit_game"
10787 &setup.ask_on_quit_program, "ask_on_quit_program"
10791 &setup.quick_switch, "quick_player_switch"
10795 &setup.input_on_focus, "input_on_focus"
10799 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10803 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10807 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10811 &setup.game_speed_extended, "game_speed_extended"
10815 &setup.game_frame_delay, "game_frame_delay"
10819 &setup.default_game_engine_type, "default_game_engine_type"
10823 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10827 &setup.bd_skip_hatching, "bd_skip_hatching"
10831 &setup.bd_scroll_delay, "bd_scroll_delay"
10835 &setup.bd_show_invisible_outbox, "bd_show_invisible_outbox"
10838 TYPE_SWITCH_3_STATES,
10839 &setup.bd_smooth_movements, "bd_smooth_movements"
10842 TYPE_SWITCH_3_STATES,
10843 &setup.bd_pushing_graphics, "bd_pushing_graphics"
10846 TYPE_SWITCH_3_STATES,
10847 &setup.bd_up_down_graphics, "bd_up_down_graphics"
10850 TYPE_SWITCH_3_STATES,
10851 &setup.bd_skip_falling_sounds, "bd_skip_falling_sounds"
10855 &setup.bd_palette_c64, "bd_palette_c64"
10859 &setup.bd_palette_c64dtv, "bd_palette_c64dtv"
10863 &setup.bd_palette_atari, "bd_palette_atari"
10867 &setup.bd_default_color_type, "bd_default_color_type"
10871 &setup.bd_random_colors, "bd_random_colors"
10875 &setup.sp_show_border_elements, "sp_show_border_elements"
10879 &setup.small_game_graphics, "small_game_graphics"
10883 &setup.show_load_save_buttons, "show_load_save_buttons"
10887 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10891 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10895 &setup.graphics_set, "graphics_set"
10899 &setup.sounds_set, "sounds_set"
10903 &setup.music_set, "music_set"
10906 TYPE_SWITCH_3_STATES,
10907 &setup.override_level_graphics, "override_level_graphics"
10910 TYPE_SWITCH_3_STATES,
10911 &setup.override_level_sounds, "override_level_sounds"
10914 TYPE_SWITCH_3_STATES,
10915 &setup.override_level_music, "override_level_music"
10919 &setup.volume_simple, "volume_simple"
10923 &setup.volume_loops, "volume_loops"
10927 &setup.volume_music, "volume_music"
10931 &setup.audio_sample_rate_44100, "audio_sample_rate_44100"
10935 &setup.network_mode, "network_mode"
10939 &setup.network_player_nr, "network_player"
10943 &setup.network_server_hostname, "network_server_hostname"
10947 &setup.touch.control_type, "touch.control_type"
10951 &setup.touch.move_distance, "touch.move_distance"
10955 &setup.touch.drop_distance, "touch.drop_distance"
10959 &setup.touch.transparency, "touch.transparency"
10963 &setup.touch.draw_outlined, "touch.draw_outlined"
10967 &setup.touch.draw_pressed, "touch.draw_pressed"
10971 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10975 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10979 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10983 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10987 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10991 static struct TokenInfo auto_setup_tokens[] =
10995 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10999 static struct TokenInfo server_setup_tokens[] =
11003 &setup.player_uuid, "player_uuid"
11007 &setup.player_version, "player_version"
11011 &setup.use_api_server, TEST_PREFIX "use_api_server"
11015 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
11019 &setup.api_server_password, TEST_PREFIX "api_server_password"
11023 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
11027 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
11031 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
11035 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
11039 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
11043 static struct TokenInfo editor_setup_tokens[] =
11047 &setup.editor.el_classic, "editor.el_classic"
11051 &setup.editor.el_custom, "editor.el_custom"
11055 &setup.editor.el_user_defined, "editor.el_user_defined"
11059 &setup.editor.el_dynamic, "editor.el_dynamic"
11063 &setup.editor.el_headlines, "editor.el_headlines"
11067 &setup.editor.show_element_token, "editor.show_element_token"
11071 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
11075 static struct TokenInfo editor_cascade_setup_tokens[] =
11079 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
11083 &setup.editor_cascade.el_bdx, "editor.cascade.el_bdx"
11087 &setup.editor_cascade.el_bdx_effects, "editor.cascade.el_bdx_effects"
11091 &setup.editor_cascade.el_em, "editor.cascade.el_em"
11095 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
11099 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
11103 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
11107 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
11111 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
11115 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
11119 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
11123 &setup.editor_cascade.el_df, "editor.cascade.el_df"
11127 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
11131 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
11135 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
11139 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
11143 &setup.editor_cascade.el_es, "editor.cascade.el_es"
11147 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
11151 &setup.editor_cascade.el_user, "editor.cascade.el_user"
11155 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
11159 static struct TokenInfo shortcut_setup_tokens[] =
11163 &setup.shortcut.save_game, "shortcut.save_game"
11167 &setup.shortcut.load_game, "shortcut.load_game"
11171 &setup.shortcut.restart_game, "shortcut.restart_game"
11175 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
11179 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
11183 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
11187 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
11191 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
11195 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
11199 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
11203 &setup.shortcut.tape_eject, "shortcut.tape_eject"
11207 &setup.shortcut.tape_extra, "shortcut.tape_extra"
11211 &setup.shortcut.tape_stop, "shortcut.tape_stop"
11215 &setup.shortcut.tape_pause, "shortcut.tape_pause"
11219 &setup.shortcut.tape_record, "shortcut.tape_record"
11223 &setup.shortcut.tape_play, "shortcut.tape_play"
11227 &setup.shortcut.sound_simple, "shortcut.sound_simple"
11231 &setup.shortcut.sound_loops, "shortcut.sound_loops"
11235 &setup.shortcut.sound_music, "shortcut.sound_music"
11239 &setup.shortcut.snap_left, "shortcut.snap_left"
11243 &setup.shortcut.snap_right, "shortcut.snap_right"
11247 &setup.shortcut.snap_up, "shortcut.snap_up"
11251 &setup.shortcut.snap_down, "shortcut.snap_down"
11255 &setup.shortcut.speed_fast, "shortcut.speed_fast"
11259 &setup.shortcut.speed_slow, "shortcut.speed_slow"
11263 static struct SetupInputInfo setup_input;
11264 static struct TokenInfo player_setup_tokens[] =
11268 &setup_input.use_joystick, ".use_joystick"
11272 &setup_input.joy.device_name, ".joy.device_name"
11276 &setup_input.joy.xleft, ".joy.xleft"
11280 &setup_input.joy.xmiddle, ".joy.xmiddle"
11284 &setup_input.joy.xright, ".joy.xright"
11288 &setup_input.joy.yupper, ".joy.yupper"
11292 &setup_input.joy.ymiddle, ".joy.ymiddle"
11296 &setup_input.joy.ylower, ".joy.ylower"
11300 &setup_input.joy.snap, ".joy.snap_field"
11304 &setup_input.joy.drop, ".joy.place_bomb"
11308 &setup_input.key.left, ".key.move_left"
11312 &setup_input.key.right, ".key.move_right"
11316 &setup_input.key.up, ".key.move_up"
11320 &setup_input.key.down, ".key.move_down"
11324 &setup_input.key.snap, ".key.snap_field"
11328 &setup_input.key.drop, ".key.place_bomb"
11332 static struct TokenInfo system_setup_tokens[] =
11336 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
11340 &setup.system.sdl_videodriver, "system.sdl_videodriver"
11344 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
11348 &setup.system.audio_fragment_size, "system.audio_fragment_size"
11352 static struct TokenInfo internal_setup_tokens[] =
11356 &setup.internal.program_title, "program_title"
11360 &setup.internal.program_version, "program_version"
11364 &setup.internal.program_author, "program_author"
11368 &setup.internal.program_email, "program_email"
11372 &setup.internal.program_website, "program_website"
11376 &setup.internal.program_copyright, "program_copyright"
11380 &setup.internal.program_company, "program_company"
11384 &setup.internal.program_icon_file, "program_icon_file"
11388 &setup.internal.default_graphics_set, "default_graphics_set"
11392 &setup.internal.default_sounds_set, "default_sounds_set"
11396 &setup.internal.default_music_set, "default_music_set"
11400 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11404 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11408 &setup.internal.fallback_music_file, "fallback_music_file"
11412 &setup.internal.default_level_series, "default_level_series"
11416 &setup.internal.default_window_width, "default_window_width"
11420 &setup.internal.default_window_height, "default_window_height"
11424 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11428 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11432 &setup.internal.create_user_levelset, "create_user_levelset"
11436 &setup.internal.info_screens_from_main, "info_screens_from_main"
11440 &setup.internal.menu_game, "menu_game"
11444 &setup.internal.menu_engines, "menu_engines"
11448 &setup.internal.menu_editor, "menu_editor"
11452 &setup.internal.menu_graphics, "menu_graphics"
11456 &setup.internal.menu_sound, "menu_sound"
11460 &setup.internal.menu_artwork, "menu_artwork"
11464 &setup.internal.menu_input, "menu_input"
11468 &setup.internal.menu_touch, "menu_touch"
11472 &setup.internal.menu_shortcuts, "menu_shortcuts"
11476 &setup.internal.menu_exit, "menu_exit"
11480 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11484 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11488 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11492 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11496 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11500 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11504 &setup.internal.menu_shortcuts_speed, "menu_shortcuts_speed"
11508 &setup.internal.info_title, "info_title"
11512 &setup.internal.info_elements, "info_elements"
11516 &setup.internal.info_music, "info_music"
11520 &setup.internal.info_credits, "info_credits"
11524 &setup.internal.info_program, "info_program"
11528 &setup.internal.info_version, "info_version"
11532 &setup.internal.info_levelset, "info_levelset"
11536 &setup.internal.info_exit, "info_exit"
11540 static struct TokenInfo debug_setup_tokens[] =
11544 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11548 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11552 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11556 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11560 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11564 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11568 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11572 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11576 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11580 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11584 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11588 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11592 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11596 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11600 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11604 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11608 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11612 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11616 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11620 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11624 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11627 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11631 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11634 TYPE_SWITCH_3_STATES,
11635 &setup.debug.xsn_mode, "debug.xsn_mode"
11639 &setup.debug.xsn_percent, "debug.xsn_percent"
11643 static struct TokenInfo options_setup_tokens[] =
11647 &setup.options.verbose, "options.verbose"
11651 &setup.options.debug, "options.debug"
11655 &setup.options.debug_mode, "options.debug_mode"
11659 static void setSetupInfoToDefaults(struct SetupInfo *si)
11663 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11665 si->multiple_users = TRUE;
11668 si->sound_loops = TRUE;
11669 si->sound_music = TRUE;
11670 si->sound_simple = TRUE;
11672 si->global_animations = TRUE;
11673 si->scroll_delay = TRUE;
11674 si->forced_scroll_delay = FALSE;
11675 si->scroll_delay_value = STD_SCROLL_DELAY;
11676 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11677 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11678 si->fade_screens = TRUE;
11679 si->autorecord = TRUE;
11680 si->autorecord_after_replay = TRUE;
11681 si->auto_pause_on_start = FALSE;
11682 si->show_titlescreen = TRUE;
11683 si->quick_doors = FALSE;
11684 si->team_mode = FALSE;
11685 si->handicap = TRUE;
11686 si->skip_levels = TRUE;
11687 si->allow_skipping_levels = STATE_ASK;
11688 si->increment_levels = TRUE;
11689 si->auto_play_next_level = TRUE;
11690 si->count_score_after_game = TRUE;
11691 si->show_scores_after_game = TRUE;
11692 si->time_limit = TRUE;
11693 si->fullscreen = FALSE;
11694 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11695 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11696 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11697 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11698 si->ask_on_escape = TRUE;
11699 si->ask_on_escape_editor = TRUE;
11700 si->ask_on_game_over = TRUE;
11701 si->ask_on_quit_game = TRUE;
11702 si->ask_on_quit_program = TRUE;
11703 si->quick_switch = FALSE;
11704 si->input_on_focus = FALSE;
11705 si->prefer_aga_graphics = TRUE;
11706 si->prefer_lowpass_sounds = FALSE;
11707 si->prefer_extra_panel_items = TRUE;
11708 si->game_speed_extended = FALSE;
11709 si->game_frame_delay = GAME_FRAME_DELAY;
11710 si->default_game_engine_type = GAME_ENGINE_TYPE_RND;
11711 si->bd_skip_uncovering = FALSE;
11712 si->bd_skip_hatching = FALSE;
11713 si->bd_scroll_delay = TRUE;
11714 si->bd_show_invisible_outbox = FALSE;
11715 si->bd_smooth_movements = STATE_TRUE;
11716 si->bd_pushing_graphics = STATE_TRUE;
11717 si->bd_up_down_graphics = STATE_TRUE;
11718 si->bd_skip_falling_sounds = STATE_TRUE;
11719 si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11720 si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11721 si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11722 si->bd_default_color_type = GD_DEFAULT_COLOR_TYPE;
11723 si->bd_random_colors = FALSE;
11724 si->sp_show_border_elements = FALSE;
11725 si->small_game_graphics = FALSE;
11726 si->show_load_save_buttons = FALSE;
11727 si->show_undo_redo_buttons = FALSE;
11728 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11730 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11731 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11732 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11734 si->override_level_graphics = STATE_FALSE;
11735 si->override_level_sounds = STATE_FALSE;
11736 si->override_level_music = STATE_FALSE;
11738 si->volume_simple = 100; // percent
11739 si->volume_loops = 100; // percent
11740 si->volume_music = 100; // percent
11741 si->audio_sample_rate_44100 = FALSE;
11743 si->network_mode = FALSE;
11744 si->network_player_nr = 0; // first player
11745 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11747 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11748 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11749 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11750 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11751 si->touch.draw_outlined = TRUE;
11752 si->touch.draw_pressed = TRUE;
11754 for (i = 0; i < 2; i++)
11756 char *default_grid_button[6][2] =
11762 { "111222", " vv " },
11763 { "111222", " vv " }
11765 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11766 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11767 int min_xsize = MIN(6, grid_xsize);
11768 int min_ysize = MIN(6, grid_ysize);
11769 int startx = grid_xsize - min_xsize;
11770 int starty = grid_ysize - min_ysize;
11773 // virtual buttons grid can only be set to defaults if video is initialized
11774 // (this will be repeated if virtual buttons are not loaded from setup file)
11775 if (video.initialized)
11777 si->touch.grid_xsize[i] = grid_xsize;
11778 si->touch.grid_ysize[i] = grid_ysize;
11782 si->touch.grid_xsize[i] = -1;
11783 si->touch.grid_ysize[i] = -1;
11786 for (x = 0; x < MAX_GRID_XSIZE; x++)
11787 for (y = 0; y < MAX_GRID_YSIZE; y++)
11788 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11790 for (x = 0; x < min_xsize; x++)
11791 for (y = 0; y < min_ysize; y++)
11792 si->touch.grid_button[i][x][starty + y] =
11793 default_grid_button[y][0][x];
11795 for (x = 0; x < min_xsize; x++)
11796 for (y = 0; y < min_ysize; y++)
11797 si->touch.grid_button[i][startx + x][starty + y] =
11798 default_grid_button[y][1][x];
11801 si->touch.grid_initialized = video.initialized;
11803 si->touch.overlay_buttons = FALSE;
11805 si->editor.el_boulderdash = TRUE;
11806 si->editor.el_boulderdash_native = TRUE;
11807 si->editor.el_boulderdash_effects = TRUE;
11808 si->editor.el_emerald_mine = TRUE;
11809 si->editor.el_emerald_mine_club = TRUE;
11810 si->editor.el_more = TRUE;
11811 si->editor.el_sokoban = TRUE;
11812 si->editor.el_supaplex = TRUE;
11813 si->editor.el_diamond_caves = TRUE;
11814 si->editor.el_dx_boulderdash = TRUE;
11816 si->editor.el_mirror_magic = TRUE;
11817 si->editor.el_deflektor = TRUE;
11819 si->editor.el_chars = TRUE;
11820 si->editor.el_steel_chars = TRUE;
11822 si->editor.el_classic = TRUE;
11823 si->editor.el_custom = TRUE;
11825 si->editor.el_user_defined = FALSE;
11826 si->editor.el_dynamic = TRUE;
11828 si->editor.el_headlines = TRUE;
11830 si->editor.show_element_token = FALSE;
11832 si->editor.show_read_only_warning = TRUE;
11834 si->editor.use_template_for_new_levels = TRUE;
11836 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11837 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11838 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11839 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11840 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11842 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11843 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11844 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11845 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11846 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11848 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11849 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11850 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11851 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11852 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11853 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11855 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11856 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11857 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11859 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11860 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11861 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11862 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11864 si->shortcut.speed_fast = DEFAULT_KEY_SPEED_FAST;
11865 si->shortcut.speed_slow = DEFAULT_KEY_SPEED_SLOW;
11867 for (i = 0; i < MAX_PLAYERS; i++)
11869 si->input[i].use_joystick = FALSE;
11870 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11871 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11872 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11873 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11874 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11875 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11876 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11877 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11878 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11879 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11880 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11881 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11882 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11883 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11884 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11887 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11888 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11889 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11890 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11892 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11893 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11894 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11895 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11896 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11897 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11898 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11900 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11902 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11903 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11904 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11906 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11907 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11908 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11910 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11911 si->internal.choose_from_top_leveldir = FALSE;
11912 si->internal.show_scaling_in_title = TRUE;
11913 si->internal.create_user_levelset = TRUE;
11914 si->internal.info_screens_from_main = FALSE;
11916 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11917 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11919 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11920 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11921 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11922 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11923 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11924 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11925 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11926 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11927 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11928 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11930 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11931 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11932 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11933 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11934 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11935 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11936 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11937 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11938 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11939 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11941 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11942 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11944 si->debug.show_frames_per_second = FALSE;
11946 si->debug.xsn_mode = STATE_AUTO;
11947 si->debug.xsn_percent = 0;
11949 si->options.verbose = FALSE;
11950 si->options.debug = FALSE;
11951 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11953 #if defined(PLATFORM_ANDROID)
11954 si->fullscreen = TRUE;
11955 si->touch.overlay_buttons = TRUE;
11958 setHideSetupEntry(&setup.debug.xsn_mode);
11961 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11963 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11966 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11968 si->player_uuid = NULL; // (will be set later)
11969 si->player_version = 1; // (will be set later)
11971 si->use_api_server = TRUE;
11972 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11973 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11974 si->ask_for_uploading_tapes = TRUE;
11975 si->ask_for_remaining_tapes = FALSE;
11976 si->provide_uploading_tapes = TRUE;
11977 si->ask_for_using_api_server = TRUE;
11978 si->has_remaining_tapes = FALSE;
11981 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11983 si->editor_cascade.el_bd = TRUE;
11984 si->editor_cascade.el_bdx = TRUE;
11985 si->editor_cascade.el_bdx_effects = FALSE;
11986 si->editor_cascade.el_em = TRUE;
11987 si->editor_cascade.el_emc = TRUE;
11988 si->editor_cascade.el_rnd = TRUE;
11989 si->editor_cascade.el_sb = TRUE;
11990 si->editor_cascade.el_sp = TRUE;
11991 si->editor_cascade.el_dc = TRUE;
11992 si->editor_cascade.el_dx = TRUE;
11994 si->editor_cascade.el_mm = TRUE;
11995 si->editor_cascade.el_df = TRUE;
11997 si->editor_cascade.el_chars = FALSE;
11998 si->editor_cascade.el_steel_chars = FALSE;
11999 si->editor_cascade.el_ce = FALSE;
12000 si->editor_cascade.el_ge = FALSE;
12001 si->editor_cascade.el_es = FALSE;
12002 si->editor_cascade.el_ref = FALSE;
12003 si->editor_cascade.el_user = FALSE;
12004 si->editor_cascade.el_dynamic = FALSE;
12007 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
12009 static char *getHideSetupToken(void *setup_value)
12011 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
12013 if (setup_value != NULL)
12014 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
12016 return hide_setup_token;
12019 void setHideSetupEntry(void *setup_value)
12021 char *hide_setup_token = getHideSetupToken(setup_value);
12023 if (hide_setup_hash == NULL)
12024 hide_setup_hash = newSetupFileHash();
12026 if (setup_value != NULL)
12027 setHashEntry(hide_setup_hash, hide_setup_token, "");
12030 void removeHideSetupEntry(void *setup_value)
12032 char *hide_setup_token = getHideSetupToken(setup_value);
12034 if (setup_value != NULL)
12035 removeHashEntry(hide_setup_hash, hide_setup_token);
12038 boolean hideSetupEntry(void *setup_value)
12040 char *hide_setup_token = getHideSetupToken(setup_value);
12042 return (setup_value != NULL &&
12043 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
12046 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
12047 struct TokenInfo *token_info,
12048 int token_nr, char *token_text)
12050 char *token_hide_text = getStringCat2(token_text, ".hide");
12051 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
12053 // set the value of this setup option in the setup option structure
12054 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
12056 // check if this setup option should be hidden in the setup menu
12057 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
12058 setHideSetupEntry(token_info[token_nr].value);
12060 free(token_hide_text);
12063 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
12064 struct TokenInfo *token_info,
12067 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
12068 token_info[token_nr].text);
12071 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
12075 if (!setup_file_hash)
12078 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12079 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
12081 setup.touch.grid_initialized = TRUE;
12082 for (i = 0; i < 2; i++)
12084 int grid_xsize = setup.touch.grid_xsize[i];
12085 int grid_ysize = setup.touch.grid_ysize[i];
12088 // if virtual buttons are not loaded from setup file, repeat initializing
12089 // virtual buttons grid with default values later when video is initialized
12090 if (grid_xsize == -1 ||
12093 setup.touch.grid_initialized = FALSE;
12098 for (y = 0; y < grid_ysize; y++)
12100 char token_string[MAX_LINE_LEN];
12102 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12104 char *value_string = getHashEntry(setup_file_hash, token_string);
12106 if (value_string == NULL)
12109 for (x = 0; x < grid_xsize; x++)
12111 char c = value_string[x];
12113 setup.touch.grid_button[i][x][y] =
12114 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
12119 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12120 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
12122 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12123 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
12125 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12129 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12131 setup_input = setup.input[pnr];
12132 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12134 char full_token[100];
12136 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
12137 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12140 setup.input[pnr] = setup_input;
12143 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12144 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12146 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12147 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12149 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12150 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12152 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12153 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12155 setHideRelatedSetupEntries();
12158 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12162 if (!setup_file_hash)
12165 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12166 setSetupInfo(auto_setup_tokens, i,
12167 getHashEntry(setup_file_hash,
12168 auto_setup_tokens[i].text));
12171 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12175 if (!setup_file_hash)
12178 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12179 setSetupInfo(server_setup_tokens, i,
12180 getHashEntry(setup_file_hash,
12181 server_setup_tokens[i].text));
12184 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12188 if (!setup_file_hash)
12191 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12192 setSetupInfo(editor_cascade_setup_tokens, i,
12193 getHashEntry(setup_file_hash,
12194 editor_cascade_setup_tokens[i].text));
12197 void LoadUserNames(void)
12199 int last_user_nr = user.nr;
12202 if (global.user_names != NULL)
12204 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12205 checked_free(global.user_names[i]);
12207 checked_free(global.user_names);
12210 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12212 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12216 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12218 if (setup_file_hash)
12220 char *player_name = getHashEntry(setup_file_hash, "player_name");
12222 global.user_names[i] = getFixedUserName(player_name);
12224 freeSetupFileHash(setup_file_hash);
12227 if (global.user_names[i] == NULL)
12228 global.user_names[i] = getStringCopy(getDefaultUserName(i));
12231 user.nr = last_user_nr;
12234 void LoadSetupFromFilename(char *filename)
12236 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12238 if (setup_file_hash)
12240 decodeSetupFileHash_Default(setup_file_hash);
12242 freeSetupFileHash(setup_file_hash);
12246 Debug("setup", "using default setup values");
12250 static void LoadSetup_SpecialPostProcessing(void)
12252 char *player_name_new;
12254 // needed to work around problems with fixed length strings
12255 player_name_new = getFixedUserName(setup.player_name);
12256 free(setup.player_name);
12257 setup.player_name = player_name_new;
12259 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12260 if (setup.scroll_delay == FALSE)
12262 setup.scroll_delay_value = MIN_SCROLL_DELAY;
12263 setup.scroll_delay = TRUE; // now always "on"
12266 // make sure that scroll delay value stays inside valid range
12267 setup.scroll_delay_value =
12268 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12271 void LoadSetup_Default(void)
12275 // always start with reliable default values
12276 setSetupInfoToDefaults(&setup);
12278 // try to load setup values from default setup file
12279 filename = getDefaultSetupFilename();
12281 if (fileExists(filename))
12282 LoadSetupFromFilename(filename);
12284 // try to load setup values from platform setup file
12285 filename = getPlatformSetupFilename();
12287 if (fileExists(filename))
12288 LoadSetupFromFilename(filename);
12290 // try to load setup values from user setup file
12291 filename = getSetupFilename();
12293 LoadSetupFromFilename(filename);
12295 LoadSetup_SpecialPostProcessing();
12298 void LoadSetup_AutoSetup(void)
12300 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12301 SetupFileHash *setup_file_hash = NULL;
12303 // always start with reliable default values
12304 setSetupInfoToDefaults_AutoSetup(&setup);
12306 setup_file_hash = loadSetupFileHash(filename);
12308 if (setup_file_hash)
12310 decodeSetupFileHash_AutoSetup(setup_file_hash);
12312 freeSetupFileHash(setup_file_hash);
12318 void LoadSetup_ServerSetup(void)
12320 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12321 SetupFileHash *setup_file_hash = NULL;
12323 // always start with reliable default values
12324 setSetupInfoToDefaults_ServerSetup(&setup);
12326 setup_file_hash = loadSetupFileHash(filename);
12328 if (setup_file_hash)
12330 decodeSetupFileHash_ServerSetup(setup_file_hash);
12332 freeSetupFileHash(setup_file_hash);
12337 if (setup.player_uuid == NULL)
12339 // player UUID does not yet exist in setup file
12340 setup.player_uuid = getStringCopy(getUUID());
12341 setup.player_version = 2;
12343 SaveSetup_ServerSetup();
12347 void LoadSetup_EditorCascade(void)
12349 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12350 SetupFileHash *setup_file_hash = NULL;
12352 // always start with reliable default values
12353 setSetupInfoToDefaults_EditorCascade(&setup);
12355 setup_file_hash = loadSetupFileHash(filename);
12357 if (setup_file_hash)
12359 decodeSetupFileHash_EditorCascade(setup_file_hash);
12361 freeSetupFileHash(setup_file_hash);
12367 void LoadSetup(void)
12369 LoadSetup_Default();
12370 LoadSetup_AutoSetup();
12371 LoadSetup_ServerSetup();
12372 LoadSetup_EditorCascade();
12375 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12376 char *mapping_line)
12378 char mapping_guid[MAX_LINE_LEN];
12379 char *mapping_start, *mapping_end;
12381 // get GUID from game controller mapping line: copy complete line
12382 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12383 mapping_guid[MAX_LINE_LEN - 1] = '\0';
12385 // get GUID from game controller mapping line: cut after GUID part
12386 mapping_start = strchr(mapping_guid, ',');
12387 if (mapping_start != NULL)
12388 *mapping_start = '\0';
12390 // cut newline from game controller mapping line
12391 mapping_end = strchr(mapping_line, '\n');
12392 if (mapping_end != NULL)
12393 *mapping_end = '\0';
12395 // add mapping entry to game controller mappings hash
12396 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12399 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12404 if (!(file = fopen(filename, MODE_READ)))
12406 Warn("cannot read game controller mappings file '%s'", filename);
12411 while (!feof(file))
12413 char line[MAX_LINE_LEN];
12415 if (!fgets(line, MAX_LINE_LEN, file))
12418 addGameControllerMappingToHash(mappings_hash, line);
12424 void SaveSetup_Default(void)
12426 char *filename = getSetupFilename();
12430 InitUserDataDirectory();
12432 if (!(file = fopen(filename, MODE_WRITE)))
12434 Warn("cannot write setup file '%s'", filename);
12439 fprintFileHeader(file, SETUP_FILENAME);
12441 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12443 // just to make things nicer :)
12444 if (global_setup_tokens[i].value == &setup.multiple_users ||
12445 global_setup_tokens[i].value == &setup.sound ||
12446 global_setup_tokens[i].value == &setup.graphics_set ||
12447 global_setup_tokens[i].value == &setup.volume_simple ||
12448 global_setup_tokens[i].value == &setup.network_mode ||
12449 global_setup_tokens[i].value == &setup.touch.control_type ||
12450 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12451 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12452 fprintf(file, "\n");
12454 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12457 for (i = 0; i < 2; i++)
12459 int grid_xsize = setup.touch.grid_xsize[i];
12460 int grid_ysize = setup.touch.grid_ysize[i];
12463 fprintf(file, "\n");
12465 for (y = 0; y < grid_ysize; y++)
12467 char token_string[MAX_LINE_LEN];
12468 char value_string[MAX_LINE_LEN];
12470 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12472 for (x = 0; x < grid_xsize; x++)
12474 char c = setup.touch.grid_button[i][x][y];
12476 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12479 value_string[grid_xsize] = '\0';
12481 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12485 fprintf(file, "\n");
12486 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12487 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12489 fprintf(file, "\n");
12490 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12491 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12493 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12497 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12498 fprintf(file, "\n");
12500 setup_input = setup.input[pnr];
12501 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12502 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12505 fprintf(file, "\n");
12506 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12507 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12509 // (internal setup values not saved to user setup file)
12511 fprintf(file, "\n");
12512 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12513 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12514 setup.debug.xsn_mode != STATE_AUTO)
12515 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12517 fprintf(file, "\n");
12518 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12519 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12523 SetFilePermissions(filename, PERMS_PRIVATE);
12526 void SaveSetup_AutoSetup(void)
12528 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12532 InitUserDataDirectory();
12534 if (!(file = fopen(filename, MODE_WRITE)))
12536 Warn("cannot write auto setup file '%s'", filename);
12543 fprintFileHeader(file, AUTOSETUP_FILENAME);
12545 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12546 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12550 SetFilePermissions(filename, PERMS_PRIVATE);
12555 void SaveSetup_ServerSetup(void)
12557 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12561 InitUserDataDirectory();
12563 if (!(file = fopen(filename, MODE_WRITE)))
12565 Warn("cannot write server setup file '%s'", filename);
12572 fprintFileHeader(file, SERVERSETUP_FILENAME);
12574 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12576 // just to make things nicer :)
12577 if (server_setup_tokens[i].value == &setup.use_api_server)
12578 fprintf(file, "\n");
12580 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12585 SetFilePermissions(filename, PERMS_PRIVATE);
12590 void SaveSetup_EditorCascade(void)
12592 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12596 InitUserDataDirectory();
12598 if (!(file = fopen(filename, MODE_WRITE)))
12600 Warn("cannot write editor cascade state file '%s'", filename);
12607 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12609 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12610 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12614 SetFilePermissions(filename, PERMS_PRIVATE);
12619 void SaveSetup(void)
12621 SaveSetup_Default();
12622 SaveSetup_AutoSetup();
12623 SaveSetup_ServerSetup();
12624 SaveSetup_EditorCascade();
12627 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12632 if (!(file = fopen(filename, MODE_WRITE)))
12634 Warn("cannot write game controller mappings file '%s'", filename);
12639 BEGIN_HASH_ITERATION(mappings_hash, itr)
12641 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12643 END_HASH_ITERATION(mappings_hash, itr)
12648 void SaveSetup_AddGameControllerMapping(char *mapping)
12650 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12651 SetupFileHash *mappings_hash = newSetupFileHash();
12653 InitUserDataDirectory();
12655 // load existing personal game controller mappings
12656 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12658 // add new mapping to personal game controller mappings
12659 addGameControllerMappingToHash(mappings_hash, mapping);
12661 // save updated personal game controller mappings
12662 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12664 freeSetupFileHash(mappings_hash);
12668 void LoadCustomElementDescriptions(void)
12670 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12671 SetupFileHash *setup_file_hash;
12674 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12676 if (element_info[i].custom_description != NULL)
12678 free(element_info[i].custom_description);
12679 element_info[i].custom_description = NULL;
12683 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12686 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12688 char *token = getStringCat2(element_info[i].token_name, ".name");
12689 char *value = getHashEntry(setup_file_hash, token);
12692 element_info[i].custom_description = getStringCopy(value);
12697 freeSetupFileHash(setup_file_hash);
12700 static int getElementFromToken(char *token)
12702 char *value = getHashEntry(element_token_hash, token);
12705 return atoi(value);
12707 Warn("unknown element token '%s'", token);
12709 return EL_UNDEFINED;
12712 void FreeGlobalAnimEventInfo(void)
12714 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12716 if (gaei->event_list == NULL)
12721 for (i = 0; i < gaei->num_event_lists; i++)
12723 checked_free(gaei->event_list[i]->event_value);
12724 checked_free(gaei->event_list[i]);
12727 checked_free(gaei->event_list);
12729 gaei->event_list = NULL;
12730 gaei->num_event_lists = 0;
12733 static int AddGlobalAnimEventList(void)
12735 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12736 int list_pos = gaei->num_event_lists++;
12738 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12739 sizeof(struct GlobalAnimEventListInfo *));
12741 gaei->event_list[list_pos] =
12742 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12744 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12746 gaeli->event_value = NULL;
12747 gaeli->num_event_values = 0;
12752 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12754 // do not add empty global animation events
12755 if (event_value == ANIM_EVENT_NONE)
12758 // if list position is undefined, create new list
12759 if (list_pos == ANIM_EVENT_UNDEFINED)
12760 list_pos = AddGlobalAnimEventList();
12762 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12763 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12764 int value_pos = gaeli->num_event_values++;
12766 gaeli->event_value = checked_realloc(gaeli->event_value,
12767 gaeli->num_event_values * sizeof(int *));
12769 gaeli->event_value[value_pos] = event_value;
12774 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12776 if (list_pos == ANIM_EVENT_UNDEFINED)
12777 return ANIM_EVENT_NONE;
12779 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12780 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12782 return gaeli->event_value[value_pos];
12785 int GetGlobalAnimEventValueCount(int list_pos)
12787 if (list_pos == ANIM_EVENT_UNDEFINED)
12790 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12791 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12793 return gaeli->num_event_values;
12796 // This function checks if a string <s> of the format "string1, string2, ..."
12797 // exactly contains a string <s_contained>.
12799 static boolean string_has_parameter(char *s, char *s_contained)
12803 if (s == NULL || s_contained == NULL)
12806 if (strlen(s_contained) > strlen(s))
12809 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12811 char next_char = s[strlen(s_contained)];
12813 // check if next character is delimiter or whitespace
12814 if (next_char == ',' || next_char == '\0' ||
12815 next_char == ' ' || next_char == '\t')
12819 // check if string contains another parameter string after a comma
12820 substring = strchr(s, ',');
12821 if (substring == NULL) // string does not contain a comma
12824 // advance string pointer to next character after the comma
12827 // skip potential whitespaces after the comma
12828 while (*substring == ' ' || *substring == '\t')
12831 return string_has_parameter(substring, s_contained);
12834 static int get_anim_parameter_value_ce(char *s)
12837 char *pattern_1 = "ce_change:custom_";
12838 char *pattern_2 = ".page_";
12839 int pattern_1_len = strlen(pattern_1);
12840 char *matching_char = strstr(s_ptr, pattern_1);
12841 int result = ANIM_EVENT_NONE;
12843 if (matching_char == NULL)
12844 return ANIM_EVENT_NONE;
12846 result = ANIM_EVENT_CE_CHANGE;
12848 s_ptr = matching_char + pattern_1_len;
12850 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12851 if (*s_ptr >= '0' && *s_ptr <= '9')
12853 int gic_ce_nr = (*s_ptr++ - '0');
12855 if (*s_ptr >= '0' && *s_ptr <= '9')
12857 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12859 if (*s_ptr >= '0' && *s_ptr <= '9')
12860 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12863 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12864 return ANIM_EVENT_NONE;
12866 // custom element stored as 0 to 255
12869 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12873 // invalid custom element number specified
12875 return ANIM_EVENT_NONE;
12878 // check for change page number ("page_X" or "page_XX") (optional)
12879 if (strPrefix(s_ptr, pattern_2))
12881 s_ptr += strlen(pattern_2);
12883 if (*s_ptr >= '0' && *s_ptr <= '9')
12885 int gic_page_nr = (*s_ptr++ - '0');
12887 if (*s_ptr >= '0' && *s_ptr <= '9')
12888 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12890 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12891 return ANIM_EVENT_NONE;
12893 // change page stored as 1 to 32 (0 means "all change pages")
12895 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12899 // invalid animation part number specified
12901 return ANIM_EVENT_NONE;
12905 // discard result if next character is neither delimiter nor whitespace
12906 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12907 *s_ptr == ' ' || *s_ptr == '\t'))
12908 return ANIM_EVENT_NONE;
12913 static int get_anim_parameter_value(char *s)
12915 int event_value[] =
12923 char *pattern_1[] =
12931 char *pattern_2 = ".part_";
12932 char *matching_char = NULL;
12934 int pattern_1_len = 0;
12935 int result = ANIM_EVENT_NONE;
12938 result = get_anim_parameter_value_ce(s);
12940 if (result != ANIM_EVENT_NONE)
12943 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12945 matching_char = strstr(s_ptr, pattern_1[i]);
12946 pattern_1_len = strlen(pattern_1[i]);
12947 result = event_value[i];
12949 if (matching_char != NULL)
12953 if (matching_char == NULL)
12954 return ANIM_EVENT_NONE;
12956 s_ptr = matching_char + pattern_1_len;
12958 // check for main animation number ("anim_X" or "anim_XX")
12959 if (*s_ptr >= '0' && *s_ptr <= '9')
12961 int gic_anim_nr = (*s_ptr++ - '0');
12963 if (*s_ptr >= '0' && *s_ptr <= '9')
12964 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12966 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12967 return ANIM_EVENT_NONE;
12969 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12973 // invalid main animation number specified
12975 return ANIM_EVENT_NONE;
12978 // check for animation part number ("part_X" or "part_XX") (optional)
12979 if (strPrefix(s_ptr, pattern_2))
12981 s_ptr += strlen(pattern_2);
12983 if (*s_ptr >= '0' && *s_ptr <= '9')
12985 int gic_part_nr = (*s_ptr++ - '0');
12987 if (*s_ptr >= '0' && *s_ptr <= '9')
12988 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12990 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12991 return ANIM_EVENT_NONE;
12993 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12997 // invalid animation part number specified
12999 return ANIM_EVENT_NONE;
13003 // discard result if next character is neither delimiter nor whitespace
13004 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
13005 *s_ptr == ' ' || *s_ptr == '\t'))
13006 return ANIM_EVENT_NONE;
13011 static int get_anim_parameter_values(char *s)
13013 int list_pos = ANIM_EVENT_UNDEFINED;
13014 int event_value = ANIM_EVENT_DEFAULT;
13016 if (string_has_parameter(s, "any"))
13017 event_value |= ANIM_EVENT_ANY;
13019 if (string_has_parameter(s, "click:self") ||
13020 string_has_parameter(s, "click") ||
13021 string_has_parameter(s, "self"))
13022 event_value |= ANIM_EVENT_SELF;
13024 if (string_has_parameter(s, "unclick:any"))
13025 event_value |= ANIM_EVENT_UNCLICK_ANY;
13027 // if animation event found, add it to global animation event list
13028 if (event_value != ANIM_EVENT_NONE)
13029 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
13033 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
13034 event_value = get_anim_parameter_value(s);
13036 // if animation event found, add it to global animation event list
13037 if (event_value != ANIM_EVENT_NONE)
13038 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
13040 // continue with next part of the string, starting with next comma
13041 s = strchr(s + 1, ',');
13047 static int get_anim_action_parameter_value(char *token)
13049 // check most common default case first to massively speed things up
13050 if (strEqual(token, ARG_UNDEFINED))
13051 return ANIM_EVENT_ACTION_NONE;
13053 int result = getImageIDFromToken(token);
13057 char *gfx_token = getStringCat2("gfx.", token);
13059 result = getImageIDFromToken(gfx_token);
13061 checked_free(gfx_token);
13066 Key key = getKeyFromX11KeyName(token);
13068 if (key != KSYM_UNDEFINED)
13069 result = -(int)key;
13076 result = get_hash_from_string(token); // unsigned int => int
13077 result = ABS(result); // may be negative now
13078 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
13080 setHashEntry(anim_url_hash, int2str(result, 0), token);
13085 result = ANIM_EVENT_ACTION_NONE;
13090 int get_parameter_value(char *value_raw, char *suffix, int type)
13092 char *value = getStringToLower(value_raw);
13093 int result = 0; // probably a save default value
13095 if (strEqual(suffix, ".direction"))
13097 result = (strEqual(value, "left") ? MV_LEFT :
13098 strEqual(value, "right") ? MV_RIGHT :
13099 strEqual(value, "up") ? MV_UP :
13100 strEqual(value, "down") ? MV_DOWN : MV_NONE);
13102 else if (strEqual(suffix, ".position"))
13104 result = (strEqual(value, "left") ? POS_LEFT :
13105 strEqual(value, "right") ? POS_RIGHT :
13106 strEqual(value, "top") ? POS_TOP :
13107 strEqual(value, "upper") ? POS_UPPER :
13108 strEqual(value, "middle") ? POS_MIDDLE :
13109 strEqual(value, "lower") ? POS_LOWER :
13110 strEqual(value, "bottom") ? POS_BOTTOM :
13111 strEqual(value, "any") ? POS_ANY :
13112 strEqual(value, "ce") ? POS_CE :
13113 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
13114 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
13116 else if (strEqual(suffix, ".align"))
13118 result = (strEqual(value, "left") ? ALIGN_LEFT :
13119 strEqual(value, "right") ? ALIGN_RIGHT :
13120 strEqual(value, "center") ? ALIGN_CENTER :
13121 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
13123 else if (strEqual(suffix, ".valign"))
13125 result = (strEqual(value, "top") ? VALIGN_TOP :
13126 strEqual(value, "bottom") ? VALIGN_BOTTOM :
13127 strEqual(value, "middle") ? VALIGN_MIDDLE :
13128 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
13130 else if (strEqual(suffix, ".anim_mode"))
13132 result = (string_has_parameter(value, "none") ? ANIM_NONE :
13133 string_has_parameter(value, "loop") ? ANIM_LOOP :
13134 string_has_parameter(value, "linear") ? ANIM_LINEAR :
13135 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
13136 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
13137 string_has_parameter(value, "random") ? ANIM_RANDOM :
13138 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13139 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
13140 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
13141 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
13142 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13143 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
13144 string_has_parameter(value, "centered") ? ANIM_CENTERED :
13145 string_has_parameter(value, "all") ? ANIM_ALL :
13146 string_has_parameter(value, "tiled") ? ANIM_TILED :
13147 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
13150 if (string_has_parameter(value, "once"))
13151 result |= ANIM_ONCE;
13153 if (string_has_parameter(value, "reverse"))
13154 result |= ANIM_REVERSE;
13156 if (string_has_parameter(value, "opaque_player"))
13157 result |= ANIM_OPAQUE_PLAYER;
13159 if (string_has_parameter(value, "static_panel"))
13160 result |= ANIM_STATIC_PANEL;
13162 else if (strEqual(suffix, ".init_event") ||
13163 strEqual(suffix, ".anim_event"))
13165 result = get_anim_parameter_values(value);
13167 else if (strEqual(suffix, ".init_delay_action") ||
13168 strEqual(suffix, ".anim_delay_action") ||
13169 strEqual(suffix, ".post_delay_action") ||
13170 strEqual(suffix, ".init_event_action") ||
13171 strEqual(suffix, ".anim_event_action"))
13173 result = get_anim_action_parameter_value(value_raw);
13175 else if (strEqual(suffix, ".class"))
13177 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13178 get_hash_from_string(value));
13180 else if (strEqual(suffix, ".style"))
13182 result = STYLE_DEFAULT;
13184 if (string_has_parameter(value, "accurate_borders"))
13185 result |= STYLE_ACCURATE_BORDERS;
13187 if (string_has_parameter(value, "inner_corners"))
13188 result |= STYLE_INNER_CORNERS;
13190 if (string_has_parameter(value, "reverse"))
13191 result |= STYLE_REVERSE;
13193 if (string_has_parameter(value, "leftmost_position"))
13194 result |= STYLE_LEFTMOST_POSITION;
13196 if (string_has_parameter(value, "block_clicks"))
13197 result |= STYLE_BLOCK;
13199 if (string_has_parameter(value, "passthrough_clicks"))
13200 result |= STYLE_PASSTHROUGH;
13202 if (string_has_parameter(value, "multiple_actions"))
13203 result |= STYLE_MULTIPLE_ACTIONS;
13205 if (string_has_parameter(value, "consume_ce_event"))
13206 result |= STYLE_CONSUME_CE_EVENT;
13208 else if (strEqual(suffix, ".fade_mode"))
13210 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
13211 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
13212 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
13213 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
13214 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
13215 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
13216 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
13217 FADE_MODE_DEFAULT);
13219 else if (strEqual(suffix, ".auto_delay_unit"))
13221 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
13222 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13223 AUTO_DELAY_UNIT_DEFAULT);
13225 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
13227 result = gfx.get_font_from_token_function(value);
13229 else // generic parameter of type integer or boolean
13231 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13232 type == TYPE_INTEGER ? get_integer_from_string(value) :
13233 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13234 ARG_UNDEFINED_VALUE);
13242 static int get_token_parameter_value(char *token, char *value_raw)
13246 if (token == NULL || value_raw == NULL)
13247 return ARG_UNDEFINED_VALUE;
13249 suffix = strrchr(token, '.');
13250 if (suffix == NULL)
13253 if (strEqual(suffix, ".element"))
13254 return getElementFromToken(value_raw);
13256 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13257 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13260 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13261 boolean ignore_defaults)
13265 for (i = 0; image_config_vars[i].token != NULL; i++)
13267 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13269 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13270 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13274 *image_config_vars[i].value =
13275 get_token_parameter_value(image_config_vars[i].token, value);
13279 void InitMenuDesignSettings_Static(void)
13281 // always start with reliable default values from static default config
13282 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13285 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13289 // the following initializes hierarchical values from static configuration
13291 // special case: initialize "ARG_DEFAULT" values in static default config
13292 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13293 titlescreen_initial_first_default.fade_mode =
13294 title_initial_first_default.fade_mode;
13295 titlescreen_initial_first_default.fade_delay =
13296 title_initial_first_default.fade_delay;
13297 titlescreen_initial_first_default.post_delay =
13298 title_initial_first_default.post_delay;
13299 titlescreen_initial_first_default.auto_delay =
13300 title_initial_first_default.auto_delay;
13301 titlescreen_initial_first_default.auto_delay_unit =
13302 title_initial_first_default.auto_delay_unit;
13303 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
13304 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13305 titlescreen_first_default.post_delay = title_first_default.post_delay;
13306 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13307 titlescreen_first_default.auto_delay_unit =
13308 title_first_default.auto_delay_unit;
13309 titlemessage_initial_first_default.fade_mode =
13310 title_initial_first_default.fade_mode;
13311 titlemessage_initial_first_default.fade_delay =
13312 title_initial_first_default.fade_delay;
13313 titlemessage_initial_first_default.post_delay =
13314 title_initial_first_default.post_delay;
13315 titlemessage_initial_first_default.auto_delay =
13316 title_initial_first_default.auto_delay;
13317 titlemessage_initial_first_default.auto_delay_unit =
13318 title_initial_first_default.auto_delay_unit;
13319 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
13320 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13321 titlemessage_first_default.post_delay = title_first_default.post_delay;
13322 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13323 titlemessage_first_default.auto_delay_unit =
13324 title_first_default.auto_delay_unit;
13326 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
13327 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13328 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13329 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13330 titlescreen_initial_default.auto_delay_unit =
13331 title_initial_default.auto_delay_unit;
13332 titlescreen_default.fade_mode = title_default.fade_mode;
13333 titlescreen_default.fade_delay = title_default.fade_delay;
13334 titlescreen_default.post_delay = title_default.post_delay;
13335 titlescreen_default.auto_delay = title_default.auto_delay;
13336 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13337 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
13338 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13339 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13340 titlemessage_initial_default.auto_delay_unit =
13341 title_initial_default.auto_delay_unit;
13342 titlemessage_default.fade_mode = title_default.fade_mode;
13343 titlemessage_default.fade_delay = title_default.fade_delay;
13344 titlemessage_default.post_delay = title_default.post_delay;
13345 titlemessage_default.auto_delay = title_default.auto_delay;
13346 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13348 // special case: initialize "ARG_DEFAULT" values in static default config
13349 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13350 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13352 titlescreen_initial_first[i] = titlescreen_initial_first_default;
13353 titlescreen_first[i] = titlescreen_first_default;
13354 titlemessage_initial_first[i] = titlemessage_initial_first_default;
13355 titlemessage_first[i] = titlemessage_first_default;
13357 titlescreen_initial[i] = titlescreen_initial_default;
13358 titlescreen[i] = titlescreen_default;
13359 titlemessage_initial[i] = titlemessage_initial_default;
13360 titlemessage[i] = titlemessage_default;
13363 // special case: initialize "ARG_DEFAULT" values in static default config
13364 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13365 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13367 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
13370 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13371 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13372 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13375 // special case: initialize "ARG_DEFAULT" values in static default config
13376 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13377 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13379 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13380 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13381 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13383 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
13386 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13390 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13394 struct XY *dst, *src;
13396 game_buttons_xy[] =
13398 { &game.button.save, &game.button.stop },
13399 { &game.button.pause2, &game.button.pause },
13400 { &game.button.load, &game.button.play },
13401 { &game.button.undo, &game.button.stop },
13402 { &game.button.redo, &game.button.play },
13408 // special case: initialize later added SETUP list size from LEVELS value
13409 if (menu.list_size[GAME_MODE_SETUP] == -1)
13410 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13412 // set default position for snapshot buttons to stop/pause/play buttons
13413 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13414 if ((*game_buttons_xy[i].dst).x == -1 &&
13415 (*game_buttons_xy[i].dst).y == -1)
13416 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13418 // --------------------------------------------------------------------------
13419 // dynamic viewports (including playfield margins, borders and alignments)
13420 // --------------------------------------------------------------------------
13422 // dynamic viewports currently only supported for landscape mode
13423 int display_width = MAX(video.display_width, video.display_height);
13424 int display_height = MIN(video.display_width, video.display_height);
13426 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13428 struct RectWithBorder *vp_window = &viewport.window[i];
13429 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13430 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13431 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13432 boolean dynamic_window_width = (vp_window->min_width != -1);
13433 boolean dynamic_window_height = (vp_window->min_height != -1);
13434 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13435 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13437 // adjust window size if min/max width/height is specified
13439 if (vp_window->min_width != -1)
13441 int window_width = display_width;
13443 // when using static window height, use aspect ratio of display
13444 if (vp_window->min_height == -1)
13445 window_width = vp_window->height * display_width / display_height;
13447 vp_window->width = MAX(vp_window->min_width, window_width);
13450 if (vp_window->min_height != -1)
13452 int window_height = display_height;
13454 // when using static window width, use aspect ratio of display
13455 if (vp_window->min_width == -1)
13456 window_height = vp_window->width * display_height / display_width;
13458 vp_window->height = MAX(vp_window->min_height, window_height);
13461 if (vp_window->max_width != -1)
13462 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13464 if (vp_window->max_height != -1)
13465 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13467 int playfield_width = vp_window->width;
13468 int playfield_height = vp_window->height;
13470 // adjust playfield size and position according to specified margins
13472 playfield_width -= vp_playfield->margin_left;
13473 playfield_width -= vp_playfield->margin_right;
13475 playfield_height -= vp_playfield->margin_top;
13476 playfield_height -= vp_playfield->margin_bottom;
13478 // adjust playfield size if min/max width/height is specified
13480 if (vp_playfield->min_width != -1)
13481 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13483 if (vp_playfield->min_height != -1)
13484 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13486 if (vp_playfield->max_width != -1)
13487 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13489 if (vp_playfield->max_height != -1)
13490 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13492 // adjust playfield position according to specified alignment
13494 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13495 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13496 else if (vp_playfield->align == ALIGN_CENTER)
13497 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13498 else if (vp_playfield->align == ALIGN_RIGHT)
13499 vp_playfield->x += playfield_width - vp_playfield->width;
13501 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13502 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13503 else if (vp_playfield->valign == VALIGN_MIDDLE)
13504 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13505 else if (vp_playfield->valign == VALIGN_BOTTOM)
13506 vp_playfield->y += playfield_height - vp_playfield->height;
13508 vp_playfield->x += vp_playfield->margin_left;
13509 vp_playfield->y += vp_playfield->margin_top;
13511 // adjust individual playfield borders if only default border is specified
13513 if (vp_playfield->border_left == -1)
13514 vp_playfield->border_left = vp_playfield->border_size;
13515 if (vp_playfield->border_right == -1)
13516 vp_playfield->border_right = vp_playfield->border_size;
13517 if (vp_playfield->border_top == -1)
13518 vp_playfield->border_top = vp_playfield->border_size;
13519 if (vp_playfield->border_bottom == -1)
13520 vp_playfield->border_bottom = vp_playfield->border_size;
13522 // set dynamic playfield borders if borders are specified as undefined
13523 // (but only if window size was dynamic and playfield size was static)
13525 if (dynamic_window_width && !dynamic_playfield_width)
13527 if (vp_playfield->border_left == -1)
13529 vp_playfield->border_left = (vp_playfield->x -
13530 vp_playfield->margin_left);
13531 vp_playfield->x -= vp_playfield->border_left;
13532 vp_playfield->width += vp_playfield->border_left;
13535 if (vp_playfield->border_right == -1)
13537 vp_playfield->border_right = (vp_window->width -
13539 vp_playfield->width -
13540 vp_playfield->margin_right);
13541 vp_playfield->width += vp_playfield->border_right;
13545 if (dynamic_window_height && !dynamic_playfield_height)
13547 if (vp_playfield->border_top == -1)
13549 vp_playfield->border_top = (vp_playfield->y -
13550 vp_playfield->margin_top);
13551 vp_playfield->y -= vp_playfield->border_top;
13552 vp_playfield->height += vp_playfield->border_top;
13555 if (vp_playfield->border_bottom == -1)
13557 vp_playfield->border_bottom = (vp_window->height -
13559 vp_playfield->height -
13560 vp_playfield->margin_bottom);
13561 vp_playfield->height += vp_playfield->border_bottom;
13565 // adjust playfield size to be a multiple of a defined alignment tile size
13567 int align_size = vp_playfield->align_size;
13568 int playfield_xtiles = vp_playfield->width / align_size;
13569 int playfield_ytiles = vp_playfield->height / align_size;
13570 int playfield_width_corrected = playfield_xtiles * align_size;
13571 int playfield_height_corrected = playfield_ytiles * align_size;
13572 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13573 i == GFX_SPECIAL_ARG_EDITOR);
13575 if (is_playfield_mode &&
13576 dynamic_playfield_width &&
13577 vp_playfield->width != playfield_width_corrected)
13579 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13581 vp_playfield->width = playfield_width_corrected;
13583 if (vp_playfield->align == ALIGN_LEFT)
13585 vp_playfield->border_left += playfield_xdiff;
13587 else if (vp_playfield->align == ALIGN_RIGHT)
13589 vp_playfield->border_right += playfield_xdiff;
13591 else if (vp_playfield->align == ALIGN_CENTER)
13593 int border_left_diff = playfield_xdiff / 2;
13594 int border_right_diff = playfield_xdiff - border_left_diff;
13596 vp_playfield->border_left += border_left_diff;
13597 vp_playfield->border_right += border_right_diff;
13601 if (is_playfield_mode &&
13602 dynamic_playfield_height &&
13603 vp_playfield->height != playfield_height_corrected)
13605 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13607 vp_playfield->height = playfield_height_corrected;
13609 if (vp_playfield->valign == VALIGN_TOP)
13611 vp_playfield->border_top += playfield_ydiff;
13613 else if (vp_playfield->align == VALIGN_BOTTOM)
13615 vp_playfield->border_right += playfield_ydiff;
13617 else if (vp_playfield->align == VALIGN_MIDDLE)
13619 int border_top_diff = playfield_ydiff / 2;
13620 int border_bottom_diff = playfield_ydiff - border_top_diff;
13622 vp_playfield->border_top += border_top_diff;
13623 vp_playfield->border_bottom += border_bottom_diff;
13627 // adjust door positions according to specified alignment
13629 for (j = 0; j < 2; j++)
13631 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13633 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13634 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13635 else if (vp_door->align == ALIGN_CENTER)
13636 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13637 else if (vp_door->align == ALIGN_RIGHT)
13638 vp_door->x += vp_window->width - vp_door->width;
13640 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13641 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13642 else if (vp_door->valign == VALIGN_MIDDLE)
13643 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13644 else if (vp_door->valign == VALIGN_BOTTOM)
13645 vp_door->y += vp_window->height - vp_door->height;
13650 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13654 struct XYTileSize *dst, *src;
13657 editor_buttons_xy[] =
13660 &editor.button.element_left, &editor.palette.element_left,
13661 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13664 &editor.button.element_middle, &editor.palette.element_middle,
13665 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13668 &editor.button.element_right, &editor.palette.element_right,
13669 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13676 // set default position for element buttons to element graphics
13677 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13679 if ((*editor_buttons_xy[i].dst).x == -1 &&
13680 (*editor_buttons_xy[i].dst).y == -1)
13682 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13684 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13686 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13690 // adjust editor palette rows and columns if specified to be dynamic
13692 if (editor.palette.cols == -1)
13694 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13695 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13696 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13698 editor.palette.cols = (vp_width - sc_width) / bt_width;
13700 if (editor.palette.x == -1)
13702 int palette_width = editor.palette.cols * bt_width + sc_width;
13704 editor.palette.x = (vp_width - palette_width) / 2;
13708 if (editor.palette.rows == -1)
13710 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13711 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13712 int tx_height = getFontHeight(FONT_TEXT_2);
13714 editor.palette.rows = (vp_height - tx_height) / bt_height;
13716 if (editor.palette.y == -1)
13718 int palette_height = editor.palette.rows * bt_height + tx_height;
13720 editor.palette.y = (vp_height - palette_height) / 2;
13725 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13726 boolean initialize)
13728 // special case: check if network and preview player positions are redefined,
13729 // to compare this later against the main menu level preview being redefined
13730 struct TokenIntPtrInfo menu_config_players[] =
13732 { "main.network_players.x", &menu.main.network_players.redefined },
13733 { "main.network_players.y", &menu.main.network_players.redefined },
13734 { "main.preview_players.x", &menu.main.preview_players.redefined },
13735 { "main.preview_players.y", &menu.main.preview_players.redefined },
13736 { "preview.x", &preview.redefined },
13737 { "preview.y", &preview.redefined }
13743 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13744 *menu_config_players[i].value = FALSE;
13748 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13749 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13750 *menu_config_players[i].value = TRUE;
13754 static void InitMenuDesignSettings_PreviewPlayers(void)
13756 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13759 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13761 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13764 static void LoadMenuDesignSettingsFromFilename(char *filename)
13766 static struct TitleFadingInfo tfi;
13767 static struct TitleMessageInfo tmi;
13768 static struct TokenInfo title_tokens[] =
13770 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13771 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13772 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13773 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13774 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13778 static struct TokenInfo titlemessage_tokens[] =
13780 { TYPE_INTEGER, &tmi.x, ".x" },
13781 { TYPE_INTEGER, &tmi.y, ".y" },
13782 { TYPE_INTEGER, &tmi.width, ".width" },
13783 { TYPE_INTEGER, &tmi.height, ".height" },
13784 { TYPE_INTEGER, &tmi.chars, ".chars" },
13785 { TYPE_INTEGER, &tmi.lines, ".lines" },
13786 { TYPE_INTEGER, &tmi.align, ".align" },
13787 { TYPE_INTEGER, &tmi.valign, ".valign" },
13788 { TYPE_INTEGER, &tmi.font, ".font" },
13789 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13790 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13791 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13792 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13793 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13794 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13795 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13796 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13797 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13803 struct TitleFadingInfo *info;
13808 // initialize first titles from "enter screen" definitions, if defined
13809 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13810 { &title_first_default, "menu.enter_screen.TITLE" },
13812 // initialize title screens from "next screen" definitions, if defined
13813 { &title_initial_default, "menu.next_screen.TITLE" },
13814 { &title_default, "menu.next_screen.TITLE" },
13820 struct TitleMessageInfo *array;
13823 titlemessage_arrays[] =
13825 // initialize first titles from "enter screen" definitions, if defined
13826 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13827 { titlescreen_first, "menu.enter_screen.TITLE" },
13828 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13829 { titlemessage_first, "menu.enter_screen.TITLE" },
13831 // initialize titles from "next screen" definitions, if defined
13832 { titlescreen_initial, "menu.next_screen.TITLE" },
13833 { titlescreen, "menu.next_screen.TITLE" },
13834 { titlemessage_initial, "menu.next_screen.TITLE" },
13835 { titlemessage, "menu.next_screen.TITLE" },
13837 // overwrite titles with title definitions, if defined
13838 { titlescreen_initial_first, "[title_initial]" },
13839 { titlescreen_first, "[title]" },
13840 { titlemessage_initial_first, "[title_initial]" },
13841 { titlemessage_first, "[title]" },
13843 { titlescreen_initial, "[title_initial]" },
13844 { titlescreen, "[title]" },
13845 { titlemessage_initial, "[title_initial]" },
13846 { titlemessage, "[title]" },
13848 // overwrite titles with title screen/message definitions, if defined
13849 { titlescreen_initial_first, "[titlescreen_initial]" },
13850 { titlescreen_first, "[titlescreen]" },
13851 { titlemessage_initial_first, "[titlemessage_initial]" },
13852 { titlemessage_first, "[titlemessage]" },
13854 { titlescreen_initial, "[titlescreen_initial]" },
13855 { titlescreen, "[titlescreen]" },
13856 { titlemessage_initial, "[titlemessage_initial]" },
13857 { titlemessage, "[titlemessage]" },
13861 SetupFileHash *setup_file_hash;
13864 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13867 // the following initializes hierarchical values from dynamic configuration
13869 // special case: initialize with default values that may be overwritten
13870 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13871 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13873 struct TokenIntPtrInfo menu_config[] =
13875 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13876 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13877 { "menu.list_size", &menu.list_size[i] }
13880 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13882 char *token = menu_config[j].token;
13883 char *value = getHashEntry(setup_file_hash, token);
13886 *menu_config[j].value = get_integer_from_string(value);
13890 // special case: initialize with default values that may be overwritten
13891 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13892 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13894 struct TokenIntPtrInfo menu_config[] =
13896 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13897 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13898 { "menu.list_size.INFO", &menu.list_size_info[i] },
13899 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13900 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13903 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13905 char *token = menu_config[j].token;
13906 char *value = getHashEntry(setup_file_hash, token);
13909 *menu_config[j].value = get_integer_from_string(value);
13913 // special case: initialize with default values that may be overwritten
13914 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13915 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13917 struct TokenIntPtrInfo menu_config[] =
13919 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13920 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13923 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13925 char *token = menu_config[j].token;
13926 char *value = getHashEntry(setup_file_hash, token);
13929 *menu_config[j].value = get_integer_from_string(value);
13933 // special case: initialize with default values that may be overwritten
13934 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13935 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13937 struct TokenIntPtrInfo menu_config[] =
13939 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13940 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13941 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13942 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13943 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13944 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13945 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13946 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13947 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13948 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13951 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13953 char *token = menu_config[j].token;
13954 char *value = getHashEntry(setup_file_hash, token);
13957 *menu_config[j].value = get_integer_from_string(value);
13961 // special case: initialize with default values that may be overwritten
13962 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13963 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13965 struct TokenIntPtrInfo menu_config[] =
13967 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13968 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13969 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13970 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13971 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13972 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13973 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13974 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13975 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13978 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13980 char *token = menu_config[j].token;
13981 char *value = getHashEntry(setup_file_hash, token);
13984 *menu_config[j].value = get_token_parameter_value(token, value);
13988 // special case: initialize with default values that may be overwritten
13989 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13990 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13994 char *token_prefix;
13995 struct RectWithBorder *struct_ptr;
13999 { "viewport.window", &viewport.window[i] },
14000 { "viewport.playfield", &viewport.playfield[i] },
14001 { "viewport.door_1", &viewport.door_1[i] },
14002 { "viewport.door_2", &viewport.door_2[i] }
14005 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
14007 struct TokenIntPtrInfo vp_config[] =
14009 { ".x", &vp_struct[j].struct_ptr->x },
14010 { ".y", &vp_struct[j].struct_ptr->y },
14011 { ".width", &vp_struct[j].struct_ptr->width },
14012 { ".height", &vp_struct[j].struct_ptr->height },
14013 { ".min_width", &vp_struct[j].struct_ptr->min_width },
14014 { ".min_height", &vp_struct[j].struct_ptr->min_height },
14015 { ".max_width", &vp_struct[j].struct_ptr->max_width },
14016 { ".max_height", &vp_struct[j].struct_ptr->max_height },
14017 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
14018 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
14019 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
14020 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
14021 { ".border_left", &vp_struct[j].struct_ptr->border_left },
14022 { ".border_right", &vp_struct[j].struct_ptr->border_right },
14023 { ".border_top", &vp_struct[j].struct_ptr->border_top },
14024 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
14025 { ".border_size", &vp_struct[j].struct_ptr->border_size },
14026 { ".align_size", &vp_struct[j].struct_ptr->align_size },
14027 { ".align", &vp_struct[j].struct_ptr->align },
14028 { ".valign", &vp_struct[j].struct_ptr->valign }
14031 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
14033 char *token = getStringCat2(vp_struct[j].token_prefix,
14034 vp_config[k].token);
14035 char *value = getHashEntry(setup_file_hash, token);
14038 *vp_config[k].value = get_token_parameter_value(token, value);
14045 // special case: initialize with default values that may be overwritten
14046 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
14047 for (i = 0; title_info[i].info != NULL; i++)
14049 struct TitleFadingInfo *info = title_info[i].info;
14050 char *base_token = title_info[i].text;
14052 for (j = 0; title_tokens[j].type != -1; j++)
14054 char *token = getStringCat2(base_token, title_tokens[j].text);
14055 char *value = getHashEntry(setup_file_hash, token);
14059 int parameter_value = get_token_parameter_value(token, value);
14063 *(int *)title_tokens[j].value = (int)parameter_value;
14072 // special case: initialize with default values that may be overwritten
14073 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
14074 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
14076 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
14077 char *base_token = titlemessage_arrays[i].text;
14079 for (j = 0; titlemessage_tokens[j].type != -1; j++)
14081 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
14082 char *value = getHashEntry(setup_file_hash, token);
14086 int parameter_value = get_token_parameter_value(token, value);
14088 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
14092 if (titlemessage_tokens[j].type == TYPE_INTEGER)
14093 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
14095 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
14105 // read (and overwrite with) values that may be specified in config file
14106 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
14108 // special case: check if network and preview player positions are redefined
14109 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
14111 freeSetupFileHash(setup_file_hash);
14114 void LoadMenuDesignSettings(void)
14116 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14118 InitMenuDesignSettings_Static();
14119 InitMenuDesignSettings_SpecialPreProcessing();
14120 InitMenuDesignSettings_PreviewPlayers();
14122 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
14124 // first look for special settings configured in level series config
14125 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
14127 if (fileExists(filename_base))
14128 LoadMenuDesignSettingsFromFilename(filename_base);
14131 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
14133 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14134 LoadMenuDesignSettingsFromFilename(filename_local);
14136 InitMenuDesignSettings_SpecialPostProcessing();
14139 void LoadMenuDesignSettings_AfterGraphics(void)
14141 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14144 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14145 boolean ignore_defaults)
14149 for (i = 0; sound_config_vars[i].token != NULL; i++)
14151 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14153 // (ignore definitions set to "[DEFAULT]" which are already initialized)
14154 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14158 *sound_config_vars[i].value =
14159 get_token_parameter_value(sound_config_vars[i].token, value);
14163 void InitSoundSettings_Static(void)
14165 // always start with reliable default values from static default config
14166 InitSoundSettings_FromHash(sound_config_hash, FALSE);
14169 static void LoadSoundSettingsFromFilename(char *filename)
14171 SetupFileHash *setup_file_hash;
14173 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14176 // read (and overwrite with) values that may be specified in config file
14177 InitSoundSettings_FromHash(setup_file_hash, TRUE);
14179 freeSetupFileHash(setup_file_hash);
14182 void LoadSoundSettings(void)
14184 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14186 InitSoundSettings_Static();
14188 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14190 // first look for special settings configured in level series config
14191 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14193 if (fileExists(filename_base))
14194 LoadSoundSettingsFromFilename(filename_base);
14197 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14199 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14200 LoadSoundSettingsFromFilename(filename_local);
14203 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14205 char *filename = getEditorSetupFilename();
14206 SetupFileList *setup_file_list, *list;
14207 SetupFileHash *element_hash;
14208 int num_unknown_tokens = 0;
14211 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14214 element_hash = newSetupFileHash();
14216 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14217 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14219 // determined size may be larger than needed (due to unknown elements)
14221 for (list = setup_file_list; list != NULL; list = list->next)
14224 // add space for up to 3 more elements for padding that may be needed
14225 *num_elements += 3;
14227 // free memory for old list of elements, if needed
14228 checked_free(*elements);
14230 // allocate memory for new list of elements
14231 *elements = checked_malloc(*num_elements * sizeof(int));
14234 for (list = setup_file_list; list != NULL; list = list->next)
14236 char *value = getHashEntry(element_hash, list->token);
14238 if (value == NULL) // try to find obsolete token mapping
14240 char *mapped_token = get_mapped_token(list->token);
14242 if (mapped_token != NULL)
14244 value = getHashEntry(element_hash, mapped_token);
14246 free(mapped_token);
14252 (*elements)[(*num_elements)++] = atoi(value);
14256 if (num_unknown_tokens == 0)
14259 Warn("unknown token(s) found in config file:");
14260 Warn("- config file: '%s'", filename);
14262 num_unknown_tokens++;
14265 Warn("- token: '%s'", list->token);
14269 if (num_unknown_tokens > 0)
14272 while (*num_elements % 4) // pad with empty elements, if needed
14273 (*elements)[(*num_elements)++] = EL_EMPTY;
14275 freeSetupFileList(setup_file_list);
14276 freeSetupFileHash(element_hash);
14279 for (i = 0; i < *num_elements; i++)
14280 Debug("editor", "element '%s' [%d]\n",
14281 element_info[(*elements)[i]].token_name, (*elements)[i]);
14285 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14288 SetupFileHash *setup_file_hash = NULL;
14289 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14290 char *filename_music, *filename_prefix, *filename_info;
14296 token_to_value_ptr[] =
14298 { "title_header", &tmp_music_file_info.title_header },
14299 { "artist_header", &tmp_music_file_info.artist_header },
14300 { "album_header", &tmp_music_file_info.album_header },
14301 { "year_header", &tmp_music_file_info.year_header },
14302 { "played_header", &tmp_music_file_info.played_header },
14304 { "title", &tmp_music_file_info.title },
14305 { "artist", &tmp_music_file_info.artist },
14306 { "album", &tmp_music_file_info.album },
14307 { "year", &tmp_music_file_info.year },
14308 { "played", &tmp_music_file_info.played },
14314 filename_music = (is_sound ? getCustomSoundFilename(basename) :
14315 getCustomMusicFilename(basename));
14317 if (filename_music == NULL)
14320 // ---------- try to replace file extension ----------
14322 filename_prefix = getStringCopy(filename_music);
14323 if (strrchr(filename_prefix, '.') != NULL)
14324 *strrchr(filename_prefix, '.') = '\0';
14325 filename_info = getStringCat2(filename_prefix, ".txt");
14327 if (fileExists(filename_info))
14328 setup_file_hash = loadSetupFileHash(filename_info);
14330 free(filename_prefix);
14331 free(filename_info);
14333 if (setup_file_hash == NULL)
14335 // ---------- try to add file extension ----------
14337 filename_prefix = getStringCopy(filename_music);
14338 filename_info = getStringCat2(filename_prefix, ".txt");
14340 if (fileExists(filename_info))
14341 setup_file_hash = loadSetupFileHash(filename_info);
14343 free(filename_prefix);
14344 free(filename_info);
14347 if (setup_file_hash == NULL)
14350 // ---------- music file info found ----------
14352 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14354 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14356 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14358 *token_to_value_ptr[i].value_ptr =
14359 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14362 tmp_music_file_info.basename = getStringCopy(basename);
14363 tmp_music_file_info.music = music;
14364 tmp_music_file_info.is_sound = is_sound;
14366 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14367 *new_music_file_info = tmp_music_file_info;
14369 return new_music_file_info;
14372 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14374 return get_music_file_info_ext(basename, music, FALSE);
14377 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14379 return get_music_file_info_ext(basename, sound, TRUE);
14382 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14383 char *basename, boolean is_sound)
14385 for (; list != NULL; list = list->next)
14386 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14392 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14394 return music_info_listed_ext(list, basename, FALSE);
14397 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14399 return music_info_listed_ext(list, basename, TRUE);
14402 void LoadMusicInfo(void)
14404 int num_music_noconf = getMusicListSize_NoConf();
14405 int num_music = getMusicListSize();
14406 int num_sounds = getSoundListSize();
14407 struct FileInfo *music, *sound;
14408 struct MusicFileInfo *next, **new;
14412 while (music_file_info != NULL)
14414 next = music_file_info->next;
14416 checked_free(music_file_info->basename);
14418 checked_free(music_file_info->title_header);
14419 checked_free(music_file_info->artist_header);
14420 checked_free(music_file_info->album_header);
14421 checked_free(music_file_info->year_header);
14422 checked_free(music_file_info->played_header);
14424 checked_free(music_file_info->title);
14425 checked_free(music_file_info->artist);
14426 checked_free(music_file_info->album);
14427 checked_free(music_file_info->year);
14428 checked_free(music_file_info->played);
14430 free(music_file_info);
14432 music_file_info = next;
14435 new = &music_file_info;
14437 // get (configured or unconfigured) music file info for all levels
14438 for (i = leveldir_current->first_level;
14439 i <= leveldir_current->last_level; i++)
14443 if (levelset.music[i] != MUS_UNDEFINED)
14445 // get music file info for configured level music
14446 music_nr = levelset.music[i];
14448 else if (num_music_noconf > 0)
14450 // get music file info for unconfigured level music
14451 int level_pos = i - leveldir_current->first_level;
14453 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14460 char *basename = getMusicInfoEntryFilename(music_nr);
14462 if (basename == NULL)
14465 if (!music_info_listed(music_file_info, basename))
14467 *new = get_music_file_info(basename, music_nr);
14470 new = &(*new)->next;
14474 // get music file info for all remaining configured music files
14475 for (i = 0; i < num_music; i++)
14477 music = getMusicListEntry(i);
14479 if (music->filename == NULL)
14482 if (strEqual(music->filename, UNDEFINED_FILENAME))
14485 // a configured file may be not recognized as music
14486 if (!FileIsMusic(music->filename))
14489 if (!music_info_listed(music_file_info, music->filename))
14491 *new = get_music_file_info(music->filename, i);
14494 new = &(*new)->next;
14498 // get sound file info for all configured sound files
14499 for (i = 0; i < num_sounds; i++)
14501 sound = getSoundListEntry(i);
14503 if (sound->filename == NULL)
14506 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14509 // a configured file may be not recognized as sound
14510 if (!FileIsSound(sound->filename))
14513 if (!sound_info_listed(music_file_info, sound->filename))
14515 *new = get_sound_file_info(sound->filename, i);
14517 new = &(*new)->next;
14521 // add pointers to previous list nodes
14523 struct MusicFileInfo *node = music_file_info;
14525 while (node != NULL)
14528 node->next->prev = node;
14534 static void add_helpanim_entry(int element, int action, int direction,
14535 int delay, int *num_list_entries)
14537 struct HelpAnimInfo *new_list_entry;
14538 (*num_list_entries)++;
14541 checked_realloc(helpanim_info,
14542 *num_list_entries * sizeof(struct HelpAnimInfo));
14543 new_list_entry = &helpanim_info[*num_list_entries - 1];
14545 new_list_entry->element = element;
14546 new_list_entry->action = action;
14547 new_list_entry->direction = direction;
14548 new_list_entry->delay = delay;
14551 static void print_unknown_token(char *filename, char *token, int token_nr)
14556 Warn("unknown token(s) found in config file:");
14557 Warn("- config file: '%s'", filename);
14560 Warn("- token: '%s'", token);
14563 static void print_unknown_token_end(int token_nr)
14569 void LoadHelpAnimInfo(void)
14571 char *filename = getHelpAnimFilename();
14572 SetupFileList *setup_file_list = NULL, *list;
14573 SetupFileHash *element_hash, *action_hash, *direction_hash;
14574 int num_list_entries = 0;
14575 int num_unknown_tokens = 0;
14578 if (fileExists(filename))
14579 setup_file_list = loadSetupFileList(filename);
14581 if (setup_file_list == NULL)
14583 // use reliable default values from static configuration
14584 SetupFileList *insert_ptr;
14586 insert_ptr = setup_file_list =
14587 newSetupFileList(helpanim_config[0].token,
14588 helpanim_config[0].value);
14590 for (i = 1; helpanim_config[i].token; i++)
14591 insert_ptr = addListEntry(insert_ptr,
14592 helpanim_config[i].token,
14593 helpanim_config[i].value);
14596 element_hash = newSetupFileHash();
14597 action_hash = newSetupFileHash();
14598 direction_hash = newSetupFileHash();
14600 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14601 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14603 for (i = 0; i < NUM_ACTIONS; i++)
14604 setHashEntry(action_hash, element_action_info[i].suffix,
14605 i_to_a(element_action_info[i].value));
14607 // do not store direction index (bit) here, but direction value!
14608 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14609 setHashEntry(direction_hash, element_direction_info[i].suffix,
14610 i_to_a(1 << element_direction_info[i].value));
14612 for (list = setup_file_list; list != NULL; list = list->next)
14614 char *element_token, *action_token, *direction_token;
14615 char *element_value, *action_value, *direction_value;
14616 int delay = atoi(list->value);
14618 if (strEqual(list->token, "end"))
14620 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14625 /* first try to break element into element/action/direction parts;
14626 if this does not work, also accept combined "element[.act][.dir]"
14627 elements (like "dynamite.active"), which are unique elements */
14629 if (strchr(list->token, '.') == NULL) // token contains no '.'
14631 element_value = getHashEntry(element_hash, list->token);
14632 if (element_value != NULL) // element found
14633 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14634 &num_list_entries);
14637 // no further suffixes found -- this is not an element
14638 print_unknown_token(filename, list->token, num_unknown_tokens++);
14644 // token has format "<prefix>.<something>"
14646 action_token = strchr(list->token, '.'); // suffix may be action ...
14647 direction_token = action_token; // ... or direction
14649 element_token = getStringCopy(list->token);
14650 *strchr(element_token, '.') = '\0';
14652 element_value = getHashEntry(element_hash, element_token);
14654 if (element_value == NULL) // this is no element
14656 element_value = getHashEntry(element_hash, list->token);
14657 if (element_value != NULL) // combined element found
14658 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14659 &num_list_entries);
14661 print_unknown_token(filename, list->token, num_unknown_tokens++);
14663 free(element_token);
14668 action_value = getHashEntry(action_hash, action_token);
14670 if (action_value != NULL) // action found
14672 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14673 &num_list_entries);
14675 free(element_token);
14680 direction_value = getHashEntry(direction_hash, direction_token);
14682 if (direction_value != NULL) // direction found
14684 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14685 &num_list_entries);
14687 free(element_token);
14692 if (strchr(action_token + 1, '.') == NULL)
14694 // no further suffixes found -- this is not an action nor direction
14696 element_value = getHashEntry(element_hash, list->token);
14697 if (element_value != NULL) // combined element found
14698 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14699 &num_list_entries);
14701 print_unknown_token(filename, list->token, num_unknown_tokens++);
14703 free(element_token);
14708 // token has format "<prefix>.<suffix>.<something>"
14710 direction_token = strchr(action_token + 1, '.');
14712 action_token = getStringCopy(action_token);
14713 *strchr(action_token + 1, '.') = '\0';
14715 action_value = getHashEntry(action_hash, action_token);
14717 if (action_value == NULL) // this is no action
14719 element_value = getHashEntry(element_hash, list->token);
14720 if (element_value != NULL) // combined element found
14721 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14722 &num_list_entries);
14724 print_unknown_token(filename, list->token, num_unknown_tokens++);
14726 free(element_token);
14727 free(action_token);
14732 direction_value = getHashEntry(direction_hash, direction_token);
14734 if (direction_value != NULL) // direction found
14736 add_helpanim_entry(atoi(element_value), atoi(action_value),
14737 atoi(direction_value), delay, &num_list_entries);
14739 free(element_token);
14740 free(action_token);
14745 // this is no direction
14747 element_value = getHashEntry(element_hash, list->token);
14748 if (element_value != NULL) // combined element found
14749 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14750 &num_list_entries);
14752 print_unknown_token(filename, list->token, num_unknown_tokens++);
14754 free(element_token);
14755 free(action_token);
14758 print_unknown_token_end(num_unknown_tokens);
14760 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14761 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14763 freeSetupFileList(setup_file_list);
14764 freeSetupFileHash(element_hash);
14765 freeSetupFileHash(action_hash);
14766 freeSetupFileHash(direction_hash);
14769 for (i = 0; i < num_list_entries; i++)
14770 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14771 EL_NAME(helpanim_info[i].element),
14772 helpanim_info[i].element,
14773 helpanim_info[i].action,
14774 helpanim_info[i].direction,
14775 helpanim_info[i].delay);
14779 void LoadHelpTextInfo(void)
14781 char *filename = getHelpTextFilename();
14784 if (helptext_info != NULL)
14786 freeSetupFileHash(helptext_info);
14787 helptext_info = NULL;
14790 if (fileExists(filename))
14791 helptext_info = loadSetupFileHash(filename);
14793 if (helptext_info == NULL)
14795 // use reliable default values from static configuration
14796 helptext_info = newSetupFileHash();
14798 for (i = 0; helptext_config[i].token; i++)
14799 setHashEntry(helptext_info,
14800 helptext_config[i].token,
14801 helptext_config[i].value);
14805 BEGIN_HASH_ITERATION(helptext_info, itr)
14807 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14808 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14810 END_HASH_ITERATION(hash, itr)
14815 // ----------------------------------------------------------------------------
14817 // ----------------------------------------------------------------------------
14819 #define MAX_NUM_CONVERT_LEVELS 1000
14821 void ConvertLevels(void)
14823 static LevelDirTree *convert_leveldir = NULL;
14824 static int convert_level_nr = -1;
14825 static int num_levels_handled = 0;
14826 static int num_levels_converted = 0;
14827 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14830 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14831 global.convert_leveldir);
14833 if (convert_leveldir == NULL)
14834 Fail("no such level identifier: '%s'", global.convert_leveldir);
14836 leveldir_current = convert_leveldir;
14838 if (global.convert_level_nr != -1)
14840 convert_leveldir->first_level = global.convert_level_nr;
14841 convert_leveldir->last_level = global.convert_level_nr;
14844 convert_level_nr = convert_leveldir->first_level;
14846 PrintLine("=", 79);
14847 Print("Converting levels\n");
14848 PrintLine("-", 79);
14849 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14850 Print("Level series name: '%s'\n", convert_leveldir->name);
14851 Print("Level series author: '%s'\n", convert_leveldir->author);
14852 Print("Number of levels: %d\n", convert_leveldir->levels);
14853 PrintLine("=", 79);
14856 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14857 levels_failed[i] = FALSE;
14859 while (convert_level_nr <= convert_leveldir->last_level)
14861 char *level_filename;
14864 level_nr = convert_level_nr++;
14866 Print("Level %03d: ", level_nr);
14868 LoadLevel(level_nr);
14869 if (level.no_level_file || level.no_valid_file)
14871 Print("(no level)\n");
14875 Print("converting level ... ");
14878 // special case: conversion of some EMC levels as requested by ACME
14879 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14882 level_filename = getDefaultLevelFilename(level_nr);
14883 new_level = !fileExists(level_filename);
14887 SaveLevel(level_nr);
14889 num_levels_converted++;
14891 Print("converted.\n");
14895 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14896 levels_failed[level_nr] = TRUE;
14898 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14901 num_levels_handled++;
14905 PrintLine("=", 79);
14906 Print("Number of levels handled: %d\n", num_levels_handled);
14907 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14908 (num_levels_handled ?
14909 num_levels_converted * 100 / num_levels_handled : 0));
14910 PrintLine("-", 79);
14911 Print("Summary (for automatic parsing by scripts):\n");
14912 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14913 convert_leveldir->identifier, num_levels_converted,
14914 num_levels_handled,
14915 (num_levels_handled ?
14916 num_levels_converted * 100 / num_levels_handled : 0));
14918 if (num_levels_handled != num_levels_converted)
14920 Print(", FAILED:");
14921 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14922 if (levels_failed[i])
14927 PrintLine("=", 79);
14929 CloseAllAndExit(0);
14933 // ----------------------------------------------------------------------------
14934 // create and save images for use in level sketches (raw BMP format)
14935 // ----------------------------------------------------------------------------
14937 void CreateLevelSketchImages(void)
14943 InitElementPropertiesGfxElement();
14945 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14946 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14948 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14950 int element = getMappedElement(i);
14951 char basename1[16];
14952 char basename2[16];
14956 sprintf(basename1, "%04d.bmp", i);
14957 sprintf(basename2, "%04ds.bmp", i);
14959 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14960 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14962 DrawSizedElement(0, 0, element, TILESIZE);
14963 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14965 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14966 Fail("cannot save level sketch image file '%s'", filename1);
14968 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14969 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14971 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14972 Fail("cannot save level sketch image file '%s'", filename2);
14977 // create corresponding SQL statements (for normal and small images)
14980 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14981 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14984 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14985 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14987 // optional: create content for forum level sketch demonstration post
14989 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14992 FreeBitmap(bitmap1);
14993 FreeBitmap(bitmap2);
14996 fprintf(stderr, "\n");
14998 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
15000 CloseAllAndExit(0);
15004 // ----------------------------------------------------------------------------
15005 // create and save images for element collecting animations (raw BMP format)
15006 // ----------------------------------------------------------------------------
15008 static boolean createCollectImage(int element)
15010 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
15013 void CreateCollectElementImages(void)
15017 int anim_frames = num_steps - 1;
15018 int tile_size = TILESIZE;
15019 int anim_width = tile_size * anim_frames;
15020 int anim_height = tile_size;
15021 int num_collect_images = 0;
15022 int pos_collect_images = 0;
15024 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15025 if (createCollectImage(i))
15026 num_collect_images++;
15028 Info("Creating %d element collecting animation images ...",
15029 num_collect_images);
15031 int dst_width = anim_width * 2;
15032 int dst_height = anim_height * num_collect_images / 2;
15033 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
15034 char *basename_bmp = "RocksCollect.bmp";
15035 char *basename_png = "RocksCollect.png";
15036 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
15037 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
15038 int len_filename_bmp = strlen(filename_bmp);
15039 int len_filename_png = strlen(filename_png);
15040 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
15041 char cmd_convert[max_command_len];
15043 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
15047 // force using RGBA surface for destination bitmap
15048 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
15049 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
15051 dst_bitmap->surface =
15052 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15054 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15056 if (!createCollectImage(i))
15059 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
15060 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
15061 int graphic = el2img(i);
15062 char *token_name = element_info[i].token_name;
15063 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
15064 Bitmap *src_bitmap;
15067 Info("- creating collecting image for '%s' ...", token_name);
15069 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
15071 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
15072 tile_size, tile_size, 0, 0);
15074 // force using RGBA surface for temporary bitmap (using transparent black)
15075 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
15076 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
15078 tmp_bitmap->surface =
15079 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15081 tmp_bitmap->surface_masked = tmp_bitmap->surface;
15083 for (j = 0; j < anim_frames; j++)
15085 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
15086 int frame_size = frame_size_final * num_steps;
15087 int offset = (tile_size - frame_size_final) / 2;
15088 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
15090 while (frame_size > frame_size_final)
15094 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
15096 FreeBitmap(frame_bitmap);
15098 frame_bitmap = half_bitmap;
15101 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
15102 frame_size_final, frame_size_final,
15103 dst_x + j * tile_size + offset, dst_y + offset);
15105 FreeBitmap(frame_bitmap);
15108 tmp_bitmap->surface_masked = NULL;
15110 FreeBitmap(tmp_bitmap);
15112 pos_collect_images++;
15115 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
15116 Fail("cannot save element collecting image file '%s'", filename_bmp);
15118 FreeBitmap(dst_bitmap);
15120 Info("Converting image file from BMP to PNG ...");
15122 if (system(cmd_convert) != 0)
15123 Fail("converting image file failed");
15125 unlink(filename_bmp);
15129 CloseAllAndExit(0);
15133 // ----------------------------------------------------------------------------
15134 // create and save images for custom and group elements (raw BMP format)
15135 // ----------------------------------------------------------------------------
15137 void CreateCustomElementImages(char *directory)
15139 char *src_basename = "RocksCE-template.ilbm";
15140 char *dst_basename = "RocksCE.bmp";
15141 char *src_filename = getPath2(directory, src_basename);
15142 char *dst_filename = getPath2(directory, dst_basename);
15143 Bitmap *src_bitmap;
15145 int yoffset_ce = 0;
15146 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15149 InitVideoDefaults();
15151 ReCreateBitmap(&backbuffer, video.width, video.height);
15153 src_bitmap = LoadImage(src_filename);
15155 bitmap = CreateBitmap(TILEX * 16 * 2,
15156 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15159 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15166 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15167 TILEX * x, TILEY * y + yoffset_ce);
15169 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15171 TILEX * x + TILEX * 16,
15172 TILEY * y + yoffset_ce);
15174 for (j = 2; j >= 0; j--)
15178 BlitBitmap(src_bitmap, bitmap,
15179 TILEX + c * 7, 0, 6, 10,
15180 TILEX * x + 6 + j * 7,
15181 TILEY * y + 11 + yoffset_ce);
15183 BlitBitmap(src_bitmap, bitmap,
15184 TILEX + c * 8, TILEY, 6, 10,
15185 TILEX * 16 + TILEX * x + 6 + j * 8,
15186 TILEY * y + 10 + yoffset_ce);
15192 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15199 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15200 TILEX * x, TILEY * y + yoffset_ge);
15202 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15204 TILEX * x + TILEX * 16,
15205 TILEY * y + yoffset_ge);
15207 for (j = 1; j >= 0; j--)
15211 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15212 TILEX * x + 6 + j * 10,
15213 TILEY * y + 11 + yoffset_ge);
15215 BlitBitmap(src_bitmap, bitmap,
15216 TILEX + c * 8, TILEY + 12, 6, 10,
15217 TILEX * 16 + TILEX * x + 10 + j * 8,
15218 TILEY * y + 10 + yoffset_ge);
15224 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15225 Fail("cannot save CE graphics file '%s'", dst_filename);
15227 FreeBitmap(bitmap);
15229 CloseAllAndExit(0);