1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
28 #define ENABLE_UNUSED_CODE 0 // currently unused functions
29 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
30 #define ENABLE_RESERVED_CODE 0 // reserved for later use
32 #define CHUNK_ID_LEN 4 // IFF style chunk id length
33 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
34 #define CHUNK_SIZE_NONE -1 // do not write chunk size
36 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
37 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
39 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
40 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
41 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
42 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
43 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
44 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
45 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
46 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
47 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
48 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
49 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
51 // (element number, number of change pages, change page number)
52 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
54 // (element number only)
55 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
56 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
57 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
59 // (nothing at all if unchanged)
60 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
62 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
63 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
64 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
66 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
68 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
69 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
70 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
72 // file identifier strings
73 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
74 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
75 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
77 // values for deciding when (not) to save configuration data
78 #define SAVE_CONF_NEVER 0
79 #define SAVE_CONF_ALWAYS 1
80 #define SAVE_CONF_WHEN_CHANGED -1
82 // values for chunks using micro chunks
83 #define CONF_MASK_1_BYTE 0x00
84 #define CONF_MASK_2_BYTE 0x40
85 #define CONF_MASK_4_BYTE 0x80
86 #define CONF_MASK_MULTI_BYTES 0xc0
88 #define CONF_MASK_BYTES 0xc0
89 #define CONF_MASK_TOKEN 0x3f
91 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
92 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
93 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
94 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
96 // these definitions are just for convenience of use and readability
97 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
98 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
99 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
100 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
102 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
103 (x) == CONF_MASK_2_BYTE ? 2 : \
104 (x) == CONF_MASK_4_BYTE ? 4 : 0)
106 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
107 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
108 #define CONF_ELEMENT_NUM_BYTES (2)
110 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
111 (t) == TYPE_ELEMENT_LIST ? \
112 CONF_ELEMENT_NUM_BYTES : \
113 (t) == TYPE_CONTENT || \
114 (t) == TYPE_CONTENT_LIST ? \
115 CONF_CONTENT_NUM_BYTES : 1)
117 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
118 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
119 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
121 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
123 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
124 CONF_ELEMENT_NUM_BYTES)
125 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
126 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
128 // temporary variables used to store pointers to structure members
129 static struct LevelInfo li;
130 static struct ElementInfo xx_ei, yy_ei;
131 static struct ElementChangeInfo xx_change;
132 static struct ElementGroupInfo xx_group;
133 static struct EnvelopeInfo xx_envelope;
134 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
135 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
136 static int xx_num_contents;
137 static int xx_current_change_page;
138 static char xx_default_string_empty[1] = "";
139 static int xx_string_length_unused;
141 struct LevelFileConfigInfo
143 int element; // element for which data is to be stored
144 int save_type; // save data always, never or when changed
145 int data_type; // data type (used internally, not stored)
146 int conf_type; // micro chunk identifier (stored in file)
149 void *value; // variable that holds the data to be stored
150 int default_value; // initial default value for this variable
153 void *value_copy; // variable that holds the data to be copied
154 void *num_entities; // number of entities for multi-byte data
155 int default_num_entities; // default number of entities for this data
156 int max_num_entities; // maximal number of entities for this data
157 char *default_string; // optional default string for string data
160 static struct LevelFileConfigInfo chunk_config_INFO[] =
162 // ---------- values not related to single elements -------------------------
165 -1, SAVE_CONF_ALWAYS,
166 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
167 &li.game_engine_type, GAME_ENGINE_TYPE_RND
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
172 &li.fieldx, STD_LEV_FIELDX
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
177 &li.fieldy, STD_LEV_FIELDY
180 -1, SAVE_CONF_ALWAYS,
181 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
185 -1, SAVE_CONF_ALWAYS,
186 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
191 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
196 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
197 &li.use_step_counter, FALSE
201 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
202 &li.wind_direction_initial, MV_NONE
206 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
207 &li.em_slippery_gems, FALSE
211 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
212 &li.use_custom_template, FALSE
216 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
217 &li.can_move_into_acid_bits, ~0 // default: everything can
221 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
222 &li.dont_collide_with_bits, ~0 // default: always deadly
226 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
227 &li.em_explodes_by_fire, FALSE
231 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
232 &li.score[SC_TIME_BONUS], 1
236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
237 &li.auto_exit_sokoban, FALSE
241 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
242 &li.auto_count_gems, FALSE
246 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
247 &li.solved_by_one_player, FALSE
251 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
252 &li.time_score_base, 1
256 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
257 &li.rate_time_over_score, FALSE
261 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
262 &li.bd_intermission, FALSE
266 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
267 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
271 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
272 &li.bd_pal_timing, FALSE
276 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
277 &li.bd_cycle_delay_ms, 160
281 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
282 &li.bd_cycle_delay_c64, 0
286 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
287 &li.bd_hatching_delay_cycles, 21
291 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
292 &li.bd_hatching_delay_seconds, 2
296 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
297 &li.bd_line_shifting_borders, FALSE
301 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
302 &li.bd_scan_first_and_last_row, TRUE
306 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
307 &li.bd_short_explosions, TRUE
311 TYPE_INTEGER, CONF_VALUE_8_BIT(23),
312 &li.bd_cave_random_seed_c64, 0
316 TYPE_INTEGER, CONF_VALUE_32_BIT(3),
317 &li.bd_color_b, GD_C64_COLOR(0)
321 TYPE_INTEGER, CONF_VALUE_32_BIT(4),
322 &li.bd_color_0, GD_C64_COLOR(0)
326 TYPE_INTEGER, CONF_VALUE_32_BIT(5),
327 &li.bd_color_1, GD_C64_COLOR(8)
331 TYPE_INTEGER, CONF_VALUE_32_BIT(6),
332 &li.bd_color_2, GD_C64_COLOR(11)
336 TYPE_INTEGER, CONF_VALUE_32_BIT(7),
337 &li.bd_color_3, GD_C64_COLOR(1)
341 TYPE_INTEGER, CONF_VALUE_32_BIT(8),
342 &li.bd_color_4, GD_C64_COLOR(5)
346 TYPE_INTEGER, CONF_VALUE_32_BIT(9),
347 &li.bd_color_5, GD_C64_COLOR(6)
357 static struct LevelFileConfigInfo chunk_config_ELEM[] =
359 // (these values are the same for each player)
362 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
363 &li.block_last_field, FALSE // default case for EM levels
367 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
368 &li.sp_block_last_field, TRUE // default case for SP levels
372 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
373 &li.instant_relocation, FALSE
377 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
378 &li.can_pass_to_walkable, FALSE
382 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
383 &li.block_snap_field, TRUE
387 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
388 &li.continuous_snapping, TRUE
392 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
393 &li.shifted_relocation, FALSE
397 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
398 &li.lazy_relocation, FALSE
402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
403 &li.finish_dig_collect, TRUE
407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
408 &li.keep_walkable_ce, FALSE
411 // (these values are different for each player)
414 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
415 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
419 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
420 &li.initial_player_gravity[0], FALSE
424 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
425 &li.use_start_element[0], FALSE
429 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
430 &li.start_element[0], EL_PLAYER_1
434 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
435 &li.use_artwork_element[0], FALSE
439 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
440 &li.artwork_element[0], EL_PLAYER_1
444 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
445 &li.use_explosion_element[0], FALSE
449 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
450 &li.explosion_element[0], EL_PLAYER_1
454 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
455 &li.use_initial_inventory[0], FALSE
459 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
460 &li.initial_inventory_size[0], 1
464 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
465 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
466 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
471 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
472 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
476 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
477 &li.initial_player_gravity[1], FALSE
481 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
482 &li.use_start_element[1], FALSE
486 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
487 &li.start_element[1], EL_PLAYER_2
491 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
492 &li.use_artwork_element[1], FALSE
496 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
497 &li.artwork_element[1], EL_PLAYER_2
501 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
502 &li.use_explosion_element[1], FALSE
506 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
507 &li.explosion_element[1], EL_PLAYER_2
511 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
512 &li.use_initial_inventory[1], FALSE
516 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
517 &li.initial_inventory_size[1], 1
521 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
522 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
523 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
528 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
529 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
533 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
534 &li.initial_player_gravity[2], FALSE
538 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
539 &li.use_start_element[2], FALSE
543 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
544 &li.start_element[2], EL_PLAYER_3
548 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
549 &li.use_artwork_element[2], FALSE
553 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
554 &li.artwork_element[2], EL_PLAYER_3
558 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
559 &li.use_explosion_element[2], FALSE
563 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
564 &li.explosion_element[2], EL_PLAYER_3
568 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
569 &li.use_initial_inventory[2], FALSE
573 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
574 &li.initial_inventory_size[2], 1
578 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
579 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
580 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
585 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
586 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
590 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
591 &li.initial_player_gravity[3], FALSE
595 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
596 &li.use_start_element[3], FALSE
600 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
601 &li.start_element[3], EL_PLAYER_4
605 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
606 &li.use_artwork_element[3], FALSE
610 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
611 &li.artwork_element[3], EL_PLAYER_4
615 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
616 &li.use_explosion_element[3], FALSE
620 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
621 &li.explosion_element[3], EL_PLAYER_4
625 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
626 &li.use_initial_inventory[3], FALSE
630 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
631 &li.initial_inventory_size[3], 1
635 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
636 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
637 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
640 // (these values are only valid for BD style levels)
641 // (some values for BD style amoeba following below)
644 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
645 &li.bd_diagonal_movements, FALSE
649 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
650 &li.bd_topmost_player_active, TRUE
654 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
655 &li.bd_pushing_prob, 25
659 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
660 &li.bd_pushing_prob_with_sweet, 100
664 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
665 &li.bd_push_mega_rock_with_sweet, FALSE
669 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
670 &li.bd_snap_element, EL_EMPTY
675 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
676 &li.bd_sand_looks_like, EL_BDX_SAND_1
681 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
682 &li.bd_rock_turns_to_on_falling, EL_BDX_ROCK_FALLING
686 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
687 &li.bd_rock_turns_to_on_impact, EL_BDX_ROCK
692 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
693 &li.score[SC_DIAMOND_EXTRA], 20
697 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
698 &li.bd_diamond_turns_to_on_falling, EL_BDX_DIAMOND_FALLING
702 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
703 &li.bd_diamond_turns_to_on_impact, EL_BDX_DIAMOND
707 EL_BDX_FIREFLY_1, -1,
708 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
709 &li.bd_firefly_1_explodes_to, EL_BDX_EXPLODING_1
713 EL_BDX_FIREFLY_2, -1,
714 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
715 &li.bd_firefly_2_explodes_to, EL_BDX_EXPLODING_1
719 EL_BDX_BUTTERFLY_1, -1,
720 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
721 &li.bd_butterfly_1_explodes_to, EL_BDX_DIAMOND_GROWING_1
725 EL_BDX_BUTTERFLY_2, -1,
726 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
727 &li.bd_butterfly_2_explodes_to, EL_BDX_DIAMOND_GROWING_1
732 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
733 &li.bd_stonefly_explodes_to, EL_BDX_ROCK_GROWING_1
737 EL_BDX_DRAGONFLY, -1,
738 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
739 &li.bd_dragonfly_explodes_to, EL_BDX_EXPLODING_1
743 EL_BDX_DIAMOND_GROWING_5, -1,
744 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
745 &li.bd_diamond_birth_turns_to, EL_BDX_DIAMOND
749 EL_BDX_BOMB_EXPLODING_4, -1,
750 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
751 &li.bd_bomb_explosion_turns_to, EL_BDX_WALL
755 EL_BDX_NITRO_PACK_EXPLODING_4, -1,
756 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
757 &li.bd_nitro_explosion_turns_to, EL_EMPTY
761 EL_BDX_EXPLODING_5, -1,
762 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
763 &li.bd_explosion_turns_to, EL_EMPTY
767 EL_BDX_MAGIC_WALL, -1,
768 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
769 &li.bd_magic_wall_wait_hatching, FALSE
772 EL_BDX_MAGIC_WALL, -1,
773 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
774 &li.bd_magic_wall_stops_amoeba, TRUE
777 EL_BDX_MAGIC_WALL, -1,
778 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
779 &li.bd_magic_wall_zero_infinite, TRUE
782 EL_BDX_MAGIC_WALL, -1,
783 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
784 &li.bd_magic_wall_break_scan, FALSE
787 EL_BDX_MAGIC_WALL, -1,
788 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
789 &li.bd_magic_wall_time, 999
792 EL_BDX_MAGIC_WALL, -1,
793 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
794 &li.bd_magic_wall_diamond_to, EL_BDX_ROCK_FALLING
797 EL_BDX_MAGIC_WALL, -1,
798 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
799 &li.bd_magic_wall_rock_to, EL_BDX_DIAMOND_FALLING
802 EL_BDX_MAGIC_WALL, -1,
803 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
804 &li.bd_magic_wall_mega_rock_to, EL_BDX_NITRO_PACK_FALLING
807 EL_BDX_MAGIC_WALL, -1,
808 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
809 &li.bd_magic_wall_nut_to, EL_BDX_NUT_FALLING
812 EL_BDX_MAGIC_WALL, -1,
813 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
814 &li.bd_magic_wall_nitro_pack_to, EL_BDX_MEGA_ROCK_FALLING
817 EL_BDX_MAGIC_WALL, -1,
818 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
819 &li.bd_magic_wall_flying_diamond_to, EL_BDX_FLYING_ROCK_FLYING
822 EL_BDX_MAGIC_WALL, -1,
823 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
824 &li.bd_magic_wall_flying_rock_to, EL_BDX_FLYING_DIAMOND_FLYING
829 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
830 &li.bd_clock_extra_time, 30
834 EL_BDX_VOODOO_DOLL, -1,
835 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
836 &li.bd_voodoo_collects_diamonds, FALSE
839 EL_BDX_VOODOO_DOLL, -1,
840 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
841 &li.bd_voodoo_hurt_kills_player, FALSE
844 EL_BDX_VOODOO_DOLL, -1,
845 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
846 &li.bd_voodoo_dies_by_rock, FALSE
849 EL_BDX_VOODOO_DOLL, -1,
850 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
851 &li.bd_voodoo_vanish_by_explosion, TRUE
854 EL_BDX_VOODOO_DOLL, -1,
855 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
856 &li.bd_voodoo_penalty_time, 30
861 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
862 &li.bd_slime_is_predictable, TRUE
866 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
867 &li.bd_slime_permeability_rate, 100
871 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
872 &li.bd_slime_permeability_bits_c64, 0
876 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
877 &li.bd_slime_random_seed_c64, -1
881 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
882 &li.bd_slime_eats_element_1, EL_BDX_DIAMOND
886 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
887 &li.bd_slime_converts_to_element_1, EL_BDX_DIAMOND_FALLING
891 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
892 &li.bd_slime_eats_element_2, EL_BDX_ROCK
896 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
897 &li.bd_slime_converts_to_element_2, EL_BDX_ROCK_FALLING
901 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
902 &li.bd_slime_eats_element_3, EL_BDX_NUT
906 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
907 &li.bd_slime_converts_to_element_3, EL_BDX_NUT_FALLING
912 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
913 &li.bd_acid_eats_element, EL_BDX_SAND_1
917 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
918 &li.bd_acid_spread_rate, 3
922 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
923 &li.bd_acid_turns_to_element, EL_BDX_EXPLODING_3
928 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
929 &li.bd_biter_move_delay, 0
933 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
934 &li.bd_biter_eats_element, EL_BDX_DIAMOND
939 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
940 &li.bd_bladder_converts_by_element, EL_BDX_VOODOO_DOLL
944 EL_BDX_EXPANDABLE_WALL_ANY, -1,
945 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
946 &li.bd_change_expanding_wall, FALSE
949 EL_BDX_EXPANDABLE_WALL_ANY, -1,
950 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
951 &li.bd_expanding_wall_looks_like, EL_BDX_WALL
955 EL_BDX_REPLICATOR, -1,
956 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
957 &li.bd_replicators_active, TRUE
960 EL_BDX_REPLICATOR, -1,
961 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
962 &li.bd_replicator_create_delay, 4
966 EL_BDX_CONVEYOR_LEFT, -1,
967 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
968 &li.bd_conveyor_belts_active, TRUE
971 EL_BDX_CONVEYOR_LEFT, -1,
972 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
973 &li.bd_conveyor_belts_changed, FALSE
978 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
979 &li.bd_water_cannot_flow_down, FALSE
984 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
985 &li.bd_nut_content, EL_BDX_NUT_BREAKING_1
989 EL_BDX_PNEUMATIC_HAMMER, -1,
990 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
991 &li.bd_hammer_walls_break_delay, 5
994 EL_BDX_PNEUMATIC_HAMMER, -1,
995 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
996 &li.bd_hammer_walls_reappear, FALSE
999 EL_BDX_PNEUMATIC_HAMMER, -1,
1000 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1001 &li.bd_hammer_walls_reappear_delay, 100
1005 EL_BDX_ROCKET_LAUNCHER, -1,
1006 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1007 &li.bd_infinite_rockets, FALSE
1011 EL_BDX_SKELETON, -1,
1012 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1013 &li.bd_num_skeletons_needed_for_pot, 5
1016 EL_BDX_SKELETON, -1,
1017 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1018 &li.bd_skeleton_worth_num_diamonds, 0
1022 EL_BDX_CREATURE_SWITCH, -1,
1023 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1024 &li.bd_creatures_start_backwards, FALSE
1027 EL_BDX_CREATURE_SWITCH, -1,
1028 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1029 &li.bd_creatures_turn_on_hatching, FALSE
1032 EL_BDX_CREATURE_SWITCH, -1,
1033 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1034 &li.bd_creatures_auto_turn_delay, 0
1038 EL_BDX_GRAVITY_SWITCH, -1,
1039 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1040 &li.bd_gravity_direction, GD_MV_DOWN
1043 EL_BDX_GRAVITY_SWITCH, -1,
1044 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1045 &li.bd_gravity_switch_active, FALSE
1048 EL_BDX_GRAVITY_SWITCH, -1,
1049 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1050 &li.bd_gravity_switch_delay, 10
1053 EL_BDX_GRAVITY_SWITCH, -1,
1054 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1055 &li.bd_gravity_affects_all, TRUE
1058 // (the following values are related to various game elements)
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1063 &li.score[SC_EMERALD], 10
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1069 &li.score[SC_DIAMOND], 10
1074 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1075 &li.score[SC_BUG], 10
1080 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1081 &li.score[SC_SPACESHIP], 10
1086 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1087 &li.score[SC_PACMAN], 10
1092 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1093 &li.score[SC_NUT], 10
1098 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1099 &li.score[SC_DYNAMITE], 10
1104 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1105 &li.score[SC_KEY], 10
1110 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1111 &li.score[SC_PEARL], 10
1116 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1117 &li.score[SC_CRYSTAL], 10
1122 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1123 &li.amoeba_content, EL_DIAMOND
1127 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1128 &li.amoeba_speed, 10
1132 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1133 &li.grow_into_diggable, TRUE
1137 EL_BDX_AMOEBA_1, -1,
1138 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1139 &li.bd_amoeba_1_threshold_too_big, 200
1142 EL_BDX_AMOEBA_1, -1,
1143 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1144 &li.bd_amoeba_1_slow_growth_time, 200
1147 EL_BDX_AMOEBA_1, -1,
1148 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1149 &li.bd_amoeba_1_content_too_big, EL_BDX_ROCK
1152 EL_BDX_AMOEBA_1, -1,
1153 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
1154 &li.bd_amoeba_1_content_enclosed, EL_BDX_DIAMOND
1157 EL_BDX_AMOEBA_1, -1,
1158 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1159 &li.bd_amoeba_1_slow_growth_rate, 3
1162 EL_BDX_AMOEBA_1, -1,
1163 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1164 &li.bd_amoeba_1_fast_growth_rate, 25
1167 EL_BDX_AMOEBA_1, -1,
1168 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1169 &li.bd_amoeba_wait_for_hatching, FALSE
1172 EL_BDX_AMOEBA_1, -1,
1173 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1174 &li.bd_amoeba_start_immediately, TRUE
1178 EL_BDX_AMOEBA_2, -1,
1179 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1180 &li.bd_amoeba_2_threshold_too_big, 200
1183 EL_BDX_AMOEBA_2, -1,
1184 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1185 &li.bd_amoeba_2_slow_growth_time, 200
1188 EL_BDX_AMOEBA_2, -1,
1189 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1190 &li.bd_amoeba_2_content_too_big, EL_BDX_ROCK
1193 EL_BDX_AMOEBA_2, -1,
1194 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
1195 &li.bd_amoeba_2_content_enclosed, EL_BDX_DIAMOND
1198 EL_BDX_AMOEBA_2, -1,
1199 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1200 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1203 EL_BDX_AMOEBA_2, -1,
1204 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1205 &li.bd_amoeba_2_content_looks_like, EL_BDX_AMOEBA_2
1208 EL_BDX_AMOEBA_2, -1,
1209 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1210 &li.bd_amoeba_2_slow_growth_rate, 3
1213 EL_BDX_AMOEBA_2, -1,
1214 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1215 &li.bd_amoeba_2_fast_growth_rate, 25
1218 EL_BDX_AMOEBA_2, -1,
1219 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1220 &li.bd_amoeba_2_explode_by_amoeba, TRUE
1225 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1226 &li.yamyam_content, EL_ROCK, NULL,
1227 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1231 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1232 &li.score[SC_YAMYAM], 10
1237 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1238 &li.score[SC_ROBOT], 10
1242 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1248 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1254 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1255 &li.time_magic_wall, 10
1259 EL_GAME_OF_LIFE, -1,
1260 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1261 &li.game_of_life[0], 2
1264 EL_GAME_OF_LIFE, -1,
1265 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1266 &li.game_of_life[1], 3
1269 EL_GAME_OF_LIFE, -1,
1270 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1271 &li.game_of_life[2], 3
1274 EL_GAME_OF_LIFE, -1,
1275 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1276 &li.game_of_life[3], 3
1279 EL_GAME_OF_LIFE, -1,
1280 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1281 &li.use_life_bugs, FALSE
1286 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1291 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1296 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1301 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1306 EL_TIMEGATE_SWITCH, -1,
1307 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1308 &li.time_timegate, 10
1312 EL_LIGHT_SWITCH_ACTIVE, -1,
1313 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1318 EL_SHIELD_NORMAL, -1,
1319 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1320 &li.shield_normal_time, 10
1323 EL_SHIELD_NORMAL, -1,
1324 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1325 &li.score[SC_SHIELD], 10
1329 EL_SHIELD_DEADLY, -1,
1330 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1331 &li.shield_deadly_time, 10
1334 EL_SHIELD_DEADLY, -1,
1335 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1336 &li.score[SC_SHIELD], 10
1341 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1346 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1347 &li.extra_time_score, 10
1351 EL_TIME_ORB_FULL, -1,
1352 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1353 &li.time_orb_time, 10
1356 EL_TIME_ORB_FULL, -1,
1357 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1358 &li.use_time_orb_bug, FALSE
1363 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1364 &li.use_spring_bug, FALSE
1369 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1370 &li.android_move_time, 10
1374 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1375 &li.android_clone_time, 10
1378 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1379 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1380 &li.android_clone_element[0], EL_EMPTY, NULL,
1381 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1385 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1386 &li.android_clone_element[0], EL_EMPTY, NULL,
1387 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1392 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1393 &li.lenses_score, 10
1397 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1402 EL_EMC_MAGNIFIER, -1,
1403 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1404 &li.magnify_score, 10
1407 EL_EMC_MAGNIFIER, -1,
1408 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1409 &li.magnify_time, 10
1413 EL_EMC_MAGIC_BALL, -1,
1414 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1418 EL_EMC_MAGIC_BALL, -1,
1419 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1420 &li.ball_random, FALSE
1423 EL_EMC_MAGIC_BALL, -1,
1424 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1425 &li.ball_active_initial, FALSE
1428 EL_EMC_MAGIC_BALL, -1,
1429 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1430 &li.ball_content, EL_EMPTY, NULL,
1431 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1435 EL_SOKOBAN_FIELD_EMPTY, -1,
1436 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1437 &li.sb_fields_needed, TRUE
1441 EL_SOKOBAN_OBJECT, -1,
1442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1443 &li.sb_objects_needed, TRUE
1448 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1449 &li.mm_laser_red, FALSE
1453 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1454 &li.mm_laser_green, FALSE
1458 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1459 &li.mm_laser_blue, TRUE
1464 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1465 &li.df_laser_red, TRUE
1469 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1470 &li.df_laser_green, TRUE
1474 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1475 &li.df_laser_blue, FALSE
1479 EL_MM_FUSE_ACTIVE, -1,
1480 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1481 &li.mm_time_fuse, 25
1485 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1486 &li.mm_time_bomb, 75
1490 EL_MM_GRAY_BALL, -1,
1491 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1492 &li.mm_time_ball, 75
1495 EL_MM_GRAY_BALL, -1,
1496 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1497 &li.mm_ball_choice_mode, ANIM_RANDOM
1500 EL_MM_GRAY_BALL, -1,
1501 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1502 &li.mm_ball_content, EL_EMPTY, NULL,
1503 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1506 EL_MM_GRAY_BALL, -1,
1507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1508 &li.rotate_mm_ball_content, TRUE
1511 EL_MM_GRAY_BALL, -1,
1512 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1513 &li.explode_mm_ball, FALSE
1517 EL_MM_STEEL_BLOCK, -1,
1518 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1519 &li.mm_time_block, 75
1522 EL_MM_LIGHTBALL, -1,
1523 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1524 &li.score[SC_ELEM_BONUS], 10
1534 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1538 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1539 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1543 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1544 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1549 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1550 &xx_envelope.autowrap, FALSE
1554 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1555 &xx_envelope.centered, FALSE
1560 TYPE_STRING, CONF_VALUE_BYTES(1),
1561 &xx_envelope.text, -1, NULL,
1562 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1563 &xx_default_string_empty[0]
1573 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1577 TYPE_STRING, CONF_VALUE_BYTES(1),
1578 &xx_ei.description[0], -1,
1579 &yy_ei.description[0],
1580 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1581 &xx_default_description[0]
1586 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1587 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1588 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1590 #if ENABLE_RESERVED_CODE
1591 // (reserved for later use)
1594 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1595 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1596 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1602 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1603 &xx_ei.use_gfx_element, FALSE,
1604 &yy_ei.use_gfx_element
1608 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1609 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1610 &yy_ei.gfx_element_initial
1615 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1616 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1617 &yy_ei.access_direction
1622 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1623 &xx_ei.collect_score_initial, 10,
1624 &yy_ei.collect_score_initial
1628 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1629 &xx_ei.collect_count_initial, 1,
1630 &yy_ei.collect_count_initial
1635 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1636 &xx_ei.ce_value_fixed_initial, 0,
1637 &yy_ei.ce_value_fixed_initial
1641 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1642 &xx_ei.ce_value_random_initial, 0,
1643 &yy_ei.ce_value_random_initial
1647 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1648 &xx_ei.use_last_ce_value, FALSE,
1649 &yy_ei.use_last_ce_value
1654 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1655 &xx_ei.push_delay_fixed, 8,
1656 &yy_ei.push_delay_fixed
1660 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1661 &xx_ei.push_delay_random, 8,
1662 &yy_ei.push_delay_random
1666 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1667 &xx_ei.drop_delay_fixed, 0,
1668 &yy_ei.drop_delay_fixed
1672 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1673 &xx_ei.drop_delay_random, 0,
1674 &yy_ei.drop_delay_random
1678 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1679 &xx_ei.move_delay_fixed, 0,
1680 &yy_ei.move_delay_fixed
1684 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1685 &xx_ei.move_delay_random, 0,
1686 &yy_ei.move_delay_random
1690 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1691 &xx_ei.step_delay_fixed, 0,
1692 &yy_ei.step_delay_fixed
1696 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1697 &xx_ei.step_delay_random, 0,
1698 &yy_ei.step_delay_random
1703 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1704 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1709 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1710 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1711 &yy_ei.move_direction_initial
1715 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1716 &xx_ei.move_stepsize, TILEX / 8,
1717 &yy_ei.move_stepsize
1722 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1723 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1724 &yy_ei.move_enter_element
1728 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1729 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1730 &yy_ei.move_leave_element
1734 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1735 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1736 &yy_ei.move_leave_type
1741 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1742 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1743 &yy_ei.slippery_type
1748 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1749 &xx_ei.explosion_type, EXPLODES_3X3,
1750 &yy_ei.explosion_type
1754 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1755 &xx_ei.explosion_delay, 16,
1756 &yy_ei.explosion_delay
1760 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1761 &xx_ei.ignition_delay, 8,
1762 &yy_ei.ignition_delay
1767 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1768 &xx_ei.content, EL_EMPTY_SPACE,
1770 &xx_num_contents, 1, 1
1773 // ---------- "num_change_pages" must be the last entry ---------------------
1776 -1, SAVE_CONF_ALWAYS,
1777 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1778 &xx_ei.num_change_pages, 1,
1779 &yy_ei.num_change_pages
1790 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1792 // ---------- "current_change_page" must be the first entry -----------------
1795 -1, SAVE_CONF_ALWAYS,
1796 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1797 &xx_current_change_page, -1
1800 // ---------- (the remaining entries can be in any order) -------------------
1804 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1805 &xx_change.can_change, FALSE
1810 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1811 &xx_event_bits[0], 0
1815 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1816 &xx_event_bits[1], 0
1821 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1822 &xx_change.trigger_player, CH_PLAYER_ANY
1826 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1827 &xx_change.trigger_side, CH_SIDE_ANY
1831 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1832 &xx_change.trigger_page, CH_PAGE_ANY
1837 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1838 &xx_change.target_element, EL_EMPTY_SPACE
1843 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1844 &xx_change.delay_fixed, 0
1848 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1849 &xx_change.delay_random, 0
1853 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1854 &xx_change.delay_frames, FRAMES_PER_SECOND
1859 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1860 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1865 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1866 &xx_change.explode, FALSE
1870 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1871 &xx_change.use_target_content, FALSE
1875 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1876 &xx_change.only_if_complete, FALSE
1880 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1881 &xx_change.use_random_replace, FALSE
1885 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1886 &xx_change.random_percentage, 100
1890 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1891 &xx_change.replace_when, CP_WHEN_EMPTY
1896 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1897 &xx_change.has_action, FALSE
1901 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1902 &xx_change.action_type, CA_NO_ACTION
1906 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1907 &xx_change.action_mode, CA_MODE_UNDEFINED
1911 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1912 &xx_change.action_arg, CA_ARG_UNDEFINED
1917 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1918 &xx_change.action_element, EL_EMPTY_SPACE
1923 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1924 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1925 &xx_num_contents, 1, 1
1935 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1939 TYPE_STRING, CONF_VALUE_BYTES(1),
1940 &xx_ei.description[0], -1, NULL,
1941 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1942 &xx_default_description[0]
1947 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1948 &xx_ei.use_gfx_element, FALSE
1952 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1953 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1958 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1959 &xx_group.choice_mode, ANIM_RANDOM
1964 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1965 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1966 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1976 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1980 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1981 &xx_ei.use_gfx_element, FALSE
1985 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1986 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1996 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
2000 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
2001 &li.block_snap_field, TRUE
2005 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
2006 &li.continuous_snapping, TRUE
2010 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
2011 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
2015 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
2016 &li.use_start_element[0], FALSE
2020 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
2021 &li.start_element[0], EL_PLAYER_1
2025 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
2026 &li.use_artwork_element[0], FALSE
2030 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
2031 &li.artwork_element[0], EL_PLAYER_1
2035 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
2036 &li.use_explosion_element[0], FALSE
2040 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
2041 &li.explosion_element[0], EL_PLAYER_1
2056 filetype_id_list[] =
2058 { LEVEL_FILE_TYPE_RND, "RND" },
2059 { LEVEL_FILE_TYPE_BD, "BD" },
2060 { LEVEL_FILE_TYPE_EM, "EM" },
2061 { LEVEL_FILE_TYPE_SP, "SP" },
2062 { LEVEL_FILE_TYPE_DX, "DX" },
2063 { LEVEL_FILE_TYPE_SB, "SB" },
2064 { LEVEL_FILE_TYPE_DC, "DC" },
2065 { LEVEL_FILE_TYPE_MM, "MM" },
2066 { LEVEL_FILE_TYPE_MM, "DF" },
2071 // ============================================================================
2072 // level file functions
2073 // ============================================================================
2075 static boolean check_special_flags(char *flag)
2077 if (strEqual(options.special_flags, flag) ||
2078 strEqual(leveldir_current->special_flags, flag))
2084 static struct DateInfo getCurrentDate(void)
2086 time_t epoch_seconds = time(NULL);
2087 struct tm *now = localtime(&epoch_seconds);
2088 struct DateInfo date;
2090 date.year = now->tm_year + 1900;
2091 date.month = now->tm_mon + 1;
2092 date.day = now->tm_mday;
2094 date.src = DATE_SRC_CLOCK;
2099 static void resetEventFlags(struct ElementChangeInfo *change)
2103 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2104 change->has_event[i] = FALSE;
2107 static void resetEventBits(void)
2111 for (i = 0; i < NUM_CE_BITFIELDS; i++)
2112 xx_event_bits[i] = 0;
2115 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2119 /* important: only change event flag if corresponding event bit is set
2120 (this is because all xx_event_bits[] values are loaded separately,
2121 and all xx_event_bits[] values are set back to zero before loading
2122 another value xx_event_bits[x] (each value representing 32 flags)) */
2124 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2125 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2126 change->has_event[i] = TRUE;
2129 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2133 /* in contrast to the above function setEventFlagsFromEventBits(), it
2134 would also be possible to set all bits in xx_event_bits[] to 0 or 1
2135 depending on the corresponding change->has_event[i] values here, as
2136 all xx_event_bits[] values are reset in resetEventBits() before */
2138 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2139 if (change->has_event[i])
2140 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2143 static char *getDefaultElementDescription(struct ElementInfo *ei)
2145 static char description[MAX_ELEMENT_NAME_LEN + 1];
2146 char *default_description = (ei->custom_description != NULL ?
2147 ei->custom_description :
2148 ei->editor_description);
2151 // always start with reliable default values
2152 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2153 description[i] = '\0';
2155 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2156 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2158 return &description[0];
2161 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2163 char *default_description = getDefaultElementDescription(ei);
2166 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2167 ei->description[i] = default_description[i];
2170 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2174 for (i = 0; conf[i].data_type != -1; i++)
2176 int default_value = conf[i].default_value;
2177 int data_type = conf[i].data_type;
2178 int conf_type = conf[i].conf_type;
2179 int byte_mask = conf_type & CONF_MASK_BYTES;
2181 if (byte_mask == CONF_MASK_MULTI_BYTES)
2183 int default_num_entities = conf[i].default_num_entities;
2184 int max_num_entities = conf[i].max_num_entities;
2186 *(int *)(conf[i].num_entities) = default_num_entities;
2188 if (data_type == TYPE_STRING)
2190 char *default_string = conf[i].default_string;
2191 char *string = (char *)(conf[i].value);
2193 strncpy(string, default_string, max_num_entities);
2195 else if (data_type == TYPE_ELEMENT_LIST)
2197 int *element_array = (int *)(conf[i].value);
2200 for (j = 0; j < max_num_entities; j++)
2201 element_array[j] = default_value;
2203 else if (data_type == TYPE_CONTENT_LIST)
2205 struct Content *content = (struct Content *)(conf[i].value);
2208 for (c = 0; c < max_num_entities; c++)
2209 for (y = 0; y < 3; y++)
2210 for (x = 0; x < 3; x++)
2211 content[c].e[x][y] = default_value;
2214 else // constant size configuration data (1, 2 or 4 bytes)
2216 if (data_type == TYPE_BOOLEAN)
2217 *(boolean *)(conf[i].value) = default_value;
2219 *(int *) (conf[i].value) = default_value;
2224 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2228 for (i = 0; conf[i].data_type != -1; i++)
2230 int data_type = conf[i].data_type;
2231 int conf_type = conf[i].conf_type;
2232 int byte_mask = conf_type & CONF_MASK_BYTES;
2234 if (byte_mask == CONF_MASK_MULTI_BYTES)
2236 int max_num_entities = conf[i].max_num_entities;
2238 if (data_type == TYPE_STRING)
2240 char *string = (char *)(conf[i].value);
2241 char *string_copy = (char *)(conf[i].value_copy);
2243 strncpy(string_copy, string, max_num_entities);
2245 else if (data_type == TYPE_ELEMENT_LIST)
2247 int *element_array = (int *)(conf[i].value);
2248 int *element_array_copy = (int *)(conf[i].value_copy);
2251 for (j = 0; j < max_num_entities; j++)
2252 element_array_copy[j] = element_array[j];
2254 else if (data_type == TYPE_CONTENT_LIST)
2256 struct Content *content = (struct Content *)(conf[i].value);
2257 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2260 for (c = 0; c < max_num_entities; c++)
2261 for (y = 0; y < 3; y++)
2262 for (x = 0; x < 3; x++)
2263 content_copy[c].e[x][y] = content[c].e[x][y];
2266 else // constant size configuration data (1, 2 or 4 bytes)
2268 if (data_type == TYPE_BOOLEAN)
2269 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2271 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2276 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2280 xx_ei = *ei_from; // copy element data into temporary buffer
2281 yy_ei = *ei_to; // copy element data into temporary buffer
2283 copyConfigFromConfigList(chunk_config_CUSX_base);
2288 // ---------- reinitialize and copy change pages ----------
2290 ei_to->num_change_pages = ei_from->num_change_pages;
2291 ei_to->current_change_page = ei_from->current_change_page;
2293 setElementChangePages(ei_to, ei_to->num_change_pages);
2295 for (i = 0; i < ei_to->num_change_pages; i++)
2296 ei_to->change_page[i] = ei_from->change_page[i];
2298 // ---------- copy group element info ----------
2299 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2300 *ei_to->group = *ei_from->group;
2302 // mark this custom element as modified
2303 ei_to->modified_settings = TRUE;
2306 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2308 int change_page_size = sizeof(struct ElementChangeInfo);
2310 ei->num_change_pages = MAX(1, change_pages);
2313 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2315 if (ei->current_change_page >= ei->num_change_pages)
2316 ei->current_change_page = ei->num_change_pages - 1;
2318 ei->change = &ei->change_page[ei->current_change_page];
2321 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2323 xx_change = *change; // copy change data into temporary buffer
2325 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2327 *change = xx_change;
2329 resetEventFlags(change);
2331 change->direct_action = 0;
2332 change->other_action = 0;
2334 change->pre_change_function = NULL;
2335 change->change_function = NULL;
2336 change->post_change_function = NULL;
2339 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2341 boolean add_border = FALSE;
2344 int x2 = STD_LEV_FIELDX - 1;
2345 int y2 = STD_LEV_FIELDY - 1;
2348 li = *level; // copy level data into temporary buffer
2349 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2350 *level = li; // copy temporary buffer back to level data
2352 setLevelInfoToDefaults_BD();
2353 setLevelInfoToDefaults_EM();
2354 setLevelInfoToDefaults_SP();
2355 setLevelInfoToDefaults_MM();
2357 level->native_bd_level = &native_bd_level;
2358 level->native_em_level = &native_em_level;
2359 level->native_sp_level = &native_sp_level;
2360 level->native_mm_level = &native_mm_level;
2362 level->file_version = FILE_VERSION_ACTUAL;
2363 level->game_version = GAME_VERSION_ACTUAL;
2365 level->creation_date = getCurrentDate();
2367 level->encoding_16bit_field = TRUE;
2368 level->encoding_16bit_yamyam = TRUE;
2369 level->encoding_16bit_amoeba = TRUE;
2371 // clear level name and level author string buffers
2372 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2373 level->name[i] = '\0';
2374 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2375 level->author[i] = '\0';
2377 // set level name and level author to default values
2378 strcpy(level->name, NAMELESS_LEVEL_NAME);
2379 strcpy(level->author, ANONYMOUS_NAME);
2381 // set default game engine type
2382 level->game_engine_type = setup.default_game_engine_type;
2384 // some game engines should have a default playfield with border elements
2385 if (level->game_engine_type == GAME_ENGINE_TYPE_BD ||
2386 level->game_engine_type == GAME_ENGINE_TYPE_EM ||
2387 level->game_engine_type == GAME_ENGINE_TYPE_SP)
2396 // set level playfield to playable default level with player and exit
2397 for (x = 0; x < MAX_LEV_FIELDX; x++)
2399 for (y = 0; y < MAX_LEV_FIELDY; y++)
2401 if (add_border && (x == 0 || x == STD_LEV_FIELDX - 1 ||
2402 y == 0 || y == STD_LEV_FIELDY - 1))
2403 level->field[x][y] = getEngineElement(EL_STEELWALL);
2405 level->field[x][y] = getEngineElement(EL_SAND);
2409 level->field[x1][y1] = getEngineElement(EL_PLAYER_1);
2410 level->field[x2][y2] = getEngineElement(EL_EXIT_CLOSED);
2412 BorderElement = getEngineElement(EL_STEELWALL);
2414 // detect custom elements when loading them
2415 level->file_has_custom_elements = FALSE;
2417 // set random colors for BD style levels according to preferred color type
2418 SetRandomLevelColors_BD(setup.bd_default_color_type);
2420 // set default color type and colors for BD style level colors
2421 SetDefaultLevelColorType_BD();
2422 SetDefaultLevelColors_BD();
2424 // set all bug compatibility flags to "false" => do not emulate this bug
2425 level->use_action_after_change_bug = FALSE;
2427 if (leveldir_current)
2429 // try to determine better author name than 'anonymous'
2430 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2432 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2433 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2437 switch (LEVELCLASS(leveldir_current))
2439 case LEVELCLASS_TUTORIAL:
2440 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2443 case LEVELCLASS_CONTRIB:
2444 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2445 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2448 case LEVELCLASS_PRIVATE:
2449 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2450 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2454 // keep default value
2461 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2463 static boolean clipboard_elements_initialized = FALSE;
2466 InitElementPropertiesStatic();
2468 li = *level; // copy level data into temporary buffer
2469 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2470 *level = li; // copy temporary buffer back to level data
2472 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2475 struct ElementInfo *ei = &element_info[element];
2477 if (element == EL_MM_GRAY_BALL)
2479 struct LevelInfo_MM *level_mm = level->native_mm_level;
2482 for (j = 0; j < level->num_mm_ball_contents; j++)
2483 level->mm_ball_content[j] =
2484 map_element_MM_to_RND(level_mm->ball_content[j]);
2487 // never initialize clipboard elements after the very first time
2488 // (to be able to use clipboard elements between several levels)
2489 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2492 if (IS_ENVELOPE(element))
2494 int envelope_nr = element - EL_ENVELOPE_1;
2496 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2498 level->envelope[envelope_nr] = xx_envelope;
2501 if (IS_CUSTOM_ELEMENT(element) ||
2502 IS_GROUP_ELEMENT(element) ||
2503 IS_INTERNAL_ELEMENT(element))
2505 xx_ei = *ei; // copy element data into temporary buffer
2507 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2512 setElementChangePages(ei, 1);
2513 setElementChangeInfoToDefaults(ei->change);
2515 if (IS_CUSTOM_ELEMENT(element) ||
2516 IS_GROUP_ELEMENT(element))
2518 setElementDescriptionToDefault(ei);
2520 ei->modified_settings = FALSE;
2523 if (IS_CUSTOM_ELEMENT(element) ||
2524 IS_INTERNAL_ELEMENT(element))
2526 // internal values used in level editor
2528 ei->access_type = 0;
2529 ei->access_layer = 0;
2530 ei->access_protected = 0;
2531 ei->walk_to_action = 0;
2532 ei->smash_targets = 0;
2535 ei->can_explode_by_fire = FALSE;
2536 ei->can_explode_smashed = FALSE;
2537 ei->can_explode_impact = FALSE;
2539 ei->current_change_page = 0;
2542 if (IS_GROUP_ELEMENT(element) ||
2543 IS_INTERNAL_ELEMENT(element))
2545 struct ElementGroupInfo *group;
2547 // initialize memory for list of elements in group
2548 if (ei->group == NULL)
2549 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2553 xx_group = *group; // copy group data into temporary buffer
2555 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2560 if (IS_EMPTY_ELEMENT(element) ||
2561 IS_INTERNAL_ELEMENT(element))
2563 xx_ei = *ei; // copy element data into temporary buffer
2565 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2571 clipboard_elements_initialized = TRUE;
2574 static void setLevelInfoToDefaults(struct LevelInfo *level,
2575 boolean level_info_only,
2576 boolean reset_file_status)
2578 setLevelInfoToDefaults_Level(level);
2580 if (!level_info_only)
2581 setLevelInfoToDefaults_Elements(level);
2583 if (reset_file_status)
2585 level->no_valid_file = FALSE;
2586 level->no_level_file = FALSE;
2589 level->changed = FALSE;
2592 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2594 level_file_info->nr = 0;
2595 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2596 level_file_info->packed = FALSE;
2598 setString(&level_file_info->basename, NULL);
2599 setString(&level_file_info->filename, NULL);
2602 int getMappedElement_SB(int, boolean);
2604 static void ActivateLevelTemplate(void)
2608 if (check_special_flags("load_xsb_to_ces"))
2610 // fill smaller playfields with padding "beyond border wall" elements
2611 if (level.fieldx < level_template.fieldx ||
2612 level.fieldy < level_template.fieldy)
2614 short field[level.fieldx][level.fieldy];
2615 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2616 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2617 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2618 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2620 // copy old playfield (which is smaller than the visible area)
2621 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2622 field[x][y] = level.field[x][y];
2624 // fill new, larger playfield with "beyond border wall" elements
2625 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2626 level.field[x][y] = getMappedElement_SB('_', TRUE);
2628 // copy the old playfield to the middle of the new playfield
2629 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2630 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2632 level.fieldx = new_fieldx;
2633 level.fieldy = new_fieldy;
2637 // Currently there is no special action needed to activate the template
2638 // data, because 'element_info' property settings overwrite the original
2639 // level data, while all other variables do not change.
2641 // Exception: 'from_level_template' elements in the original level playfield
2642 // are overwritten with the corresponding elements at the same position in
2643 // playfield from the level template.
2645 for (x = 0; x < level.fieldx; x++)
2646 for (y = 0; y < level.fieldy; y++)
2647 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2648 level.field[x][y] = level_template.field[x][y];
2650 if (check_special_flags("load_xsb_to_ces"))
2652 struct LevelInfo level_backup = level;
2654 // overwrite all individual level settings from template level settings
2655 level = level_template;
2657 // restore level file info
2658 level.file_info = level_backup.file_info;
2660 // restore playfield size
2661 level.fieldx = level_backup.fieldx;
2662 level.fieldy = level_backup.fieldy;
2664 // restore playfield content
2665 for (x = 0; x < level.fieldx; x++)
2666 for (y = 0; y < level.fieldy; y++)
2667 level.field[x][y] = level_backup.field[x][y];
2669 // restore name and author from individual level
2670 strcpy(level.name, level_backup.name);
2671 strcpy(level.author, level_backup.author);
2673 // restore flag "use_custom_template"
2674 level.use_custom_template = level_backup.use_custom_template;
2678 boolean isLevelsetFilename_BD(char *filename)
2680 return (strSuffixLower(filename, ".bd") ||
2681 strSuffixLower(filename, ".bdr") ||
2682 strSuffixLower(filename, ".brc") ||
2683 strSuffixLower(filename, ".gds"));
2686 static boolean checkForPackageFromBasename_BD(char *basename)
2688 // check for native BD level file extensions
2689 if (!isLevelsetFilename_BD(basename))
2692 // check for standard single-level BD files (like "001.bd")
2693 if (strSuffixLower(basename, ".bd") &&
2694 strlen(basename) == 6 &&
2695 basename[0] >= '0' && basename[0] <= '9' &&
2696 basename[1] >= '0' && basename[1] <= '9' &&
2697 basename[2] >= '0' && basename[2] <= '9')
2700 // this is a level package in native BD file format
2704 static char *getLevelFilenameFromBasename(char *basename)
2706 static char *filename = NULL;
2708 checked_free(filename);
2710 filename = getPath2(getCurrentLevelDir(), basename);
2715 static int getFileTypeFromBasename(char *basename)
2717 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2719 static char *filename = NULL;
2720 struct stat file_status;
2722 // ---------- try to determine file type from filename ----------
2724 // check for typical filename of a Supaplex level package file
2725 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2726 return LEVEL_FILE_TYPE_SP;
2728 // check for typical filename of a Diamond Caves II level package file
2729 if (strSuffixLower(basename, ".dc") ||
2730 strSuffixLower(basename, ".dc2"))
2731 return LEVEL_FILE_TYPE_DC;
2733 // check for typical filename of a Sokoban level package file
2734 if (strSuffixLower(basename, ".xsb") &&
2735 strchr(basename, '%') == NULL)
2736 return LEVEL_FILE_TYPE_SB;
2738 // check for typical filename of a Boulder Dash (GDash) level package file
2739 if (checkForPackageFromBasename_BD(basename))
2740 return LEVEL_FILE_TYPE_BD;
2742 // ---------- try to determine file type from filesize ----------
2744 checked_free(filename);
2745 filename = getPath2(getCurrentLevelDir(), basename);
2747 if (stat(filename, &file_status) == 0)
2749 // check for typical filesize of a Supaplex level package file
2750 if (file_status.st_size == 170496)
2751 return LEVEL_FILE_TYPE_SP;
2754 return LEVEL_FILE_TYPE_UNKNOWN;
2757 static int getFileTypeFromMagicBytes(char *filename, int type)
2761 if ((file = openFile(filename, MODE_READ)))
2763 char chunk_name[CHUNK_ID_LEN + 1];
2765 getFileChunkBE(file, chunk_name, NULL);
2767 if (strEqual(chunk_name, "MMII") ||
2768 strEqual(chunk_name, "MIRR"))
2769 type = LEVEL_FILE_TYPE_MM;
2777 static boolean checkForPackageFromBasename(char *basename)
2779 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2780 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2782 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2785 static char *getSingleLevelBasenameExt(int nr, char *extension)
2787 static char basename[MAX_FILENAME_LEN];
2790 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2792 sprintf(basename, "%03d.%s", nr, extension);
2797 static char *getSingleLevelBasename(int nr)
2799 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2802 static char *getPackedLevelBasename(int type)
2804 static char basename[MAX_FILENAME_LEN];
2805 char *directory = getCurrentLevelDir();
2807 DirectoryEntry *dir_entry;
2809 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2811 if ((dir = openDirectory(directory)) == NULL)
2813 Warn("cannot read current level directory '%s'", directory);
2818 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2820 char *entry_basename = dir_entry->basename;
2821 int entry_type = getFileTypeFromBasename(entry_basename);
2823 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2825 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2828 strcpy(basename, entry_basename);
2835 closeDirectory(dir);
2840 static char *getSingleLevelFilename(int nr)
2842 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2845 #if ENABLE_UNUSED_CODE
2846 static char *getPackedLevelFilename(int type)
2848 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2852 char *getDefaultLevelFilename(int nr)
2854 return getSingleLevelFilename(nr);
2857 #if ENABLE_UNUSED_CODE
2858 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2862 lfi->packed = FALSE;
2864 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2865 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2869 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2870 int type, char *format, ...)
2872 static char basename[MAX_FILENAME_LEN];
2875 va_start(ap, format);
2876 vsprintf(basename, format, ap);
2880 lfi->packed = FALSE;
2882 setString(&lfi->basename, basename);
2883 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2886 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2892 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2893 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2896 static int getFiletypeFromID(char *filetype_id)
2898 char *filetype_id_lower;
2899 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2902 if (filetype_id == NULL)
2903 return LEVEL_FILE_TYPE_UNKNOWN;
2905 filetype_id_lower = getStringToLower(filetype_id);
2907 for (i = 0; filetype_id_list[i].id != NULL; i++)
2909 char *id_lower = getStringToLower(filetype_id_list[i].id);
2911 if (strEqual(filetype_id_lower, id_lower))
2912 filetype = filetype_id_list[i].filetype;
2916 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2920 free(filetype_id_lower);
2925 char *getLocalLevelTemplateFilename(void)
2927 return getDefaultLevelFilename(-1);
2930 char *getGlobalLevelTemplateFilename(void)
2932 // global variable "leveldir_current" must be modified in the loop below
2933 LevelDirTree *leveldir_current_last = leveldir_current;
2934 char *filename = NULL;
2936 // check for template level in path from current to topmost tree node
2938 while (leveldir_current != NULL)
2940 filename = getDefaultLevelFilename(-1);
2942 if (fileExists(filename))
2945 leveldir_current = leveldir_current->node_parent;
2948 // restore global variable "leveldir_current" modified in above loop
2949 leveldir_current = leveldir_current_last;
2954 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2958 // special case: level number is negative => check for level template file
2961 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2962 getSingleLevelBasename(-1));
2964 // replace local level template filename with global template filename
2965 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2967 // no fallback if template file not existing
2971 // special case: check for file name/pattern specified in "levelinfo.conf"
2972 if (leveldir_current->level_filename != NULL)
2974 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2976 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2977 leveldir_current->level_filename, nr);
2979 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2981 if (fileExists(lfi->filename))
2984 else if (leveldir_current->level_filetype != NULL)
2986 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2988 // check for specified native level file with standard file name
2989 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2990 "%03d.%s", nr, LEVELFILE_EXTENSION);
2991 if (fileExists(lfi->filename))
2995 // check for native Rocks'n'Diamonds level file
2996 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2997 "%03d.%s", nr, LEVELFILE_EXTENSION);
2998 if (fileExists(lfi->filename))
3001 // check for native Boulder Dash level file
3002 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
3003 if (fileExists(lfi->filename))
3006 // check for Emerald Mine level file (V1)
3007 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
3008 'a' + (nr / 10) % 26, '0' + nr % 10);
3009 if (fileExists(lfi->filename))
3011 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
3012 'A' + (nr / 10) % 26, '0' + nr % 10);
3013 if (fileExists(lfi->filename))
3016 // check for Emerald Mine level file (V2 to V5)
3017 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
3018 if (fileExists(lfi->filename))
3021 // check for Emerald Mine level file (V6 / single mode)
3022 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
3023 if (fileExists(lfi->filename))
3025 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
3026 if (fileExists(lfi->filename))
3029 // check for Emerald Mine level file (V6 / teamwork mode)
3030 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
3031 if (fileExists(lfi->filename))
3033 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
3034 if (fileExists(lfi->filename))
3037 // check for various packed level file formats
3038 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
3039 if (fileExists(lfi->filename))
3042 // no known level file found -- use default values (and fail later)
3043 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3044 "%03d.%s", nr, LEVELFILE_EXTENSION);
3047 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3049 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3050 lfi->type = getFileTypeFromBasename(lfi->basename);
3052 if (lfi->type == LEVEL_FILE_TYPE_RND)
3053 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3056 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3058 // always start with reliable default values
3059 setFileInfoToDefaults(level_file_info);
3061 level_file_info->nr = nr; // set requested level number
3063 determineLevelFileInfo_Filename(level_file_info);
3064 determineLevelFileInfo_Filetype(level_file_info);
3067 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3068 struct LevelFileInfo *lfi_to)
3070 lfi_to->nr = lfi_from->nr;
3071 lfi_to->type = lfi_from->type;
3072 lfi_to->packed = lfi_from->packed;
3074 setString(&lfi_to->basename, lfi_from->basename);
3075 setString(&lfi_to->filename, lfi_from->filename);
3078 // ----------------------------------------------------------------------------
3079 // functions for loading R'n'D level
3080 // ----------------------------------------------------------------------------
3082 int getMappedElement(int element)
3084 // remap some (historic, now obsolete) elements
3088 case EL_PLAYER_OBSOLETE:
3089 element = EL_PLAYER_1;
3092 case EL_KEY_OBSOLETE:
3096 case EL_EM_KEY_1_FILE_OBSOLETE:
3097 element = EL_EM_KEY_1;
3100 case EL_EM_KEY_2_FILE_OBSOLETE:
3101 element = EL_EM_KEY_2;
3104 case EL_EM_KEY_3_FILE_OBSOLETE:
3105 element = EL_EM_KEY_3;
3108 case EL_EM_KEY_4_FILE_OBSOLETE:
3109 element = EL_EM_KEY_4;
3112 case EL_ENVELOPE_OBSOLETE:
3113 element = EL_ENVELOPE_1;
3121 if (element >= NUM_FILE_ELEMENTS)
3123 Warn("invalid level element %d", element);
3125 element = EL_UNKNOWN;
3133 static int getMappedElementByVersion(int element, int game_version)
3135 // remap some elements due to certain game version
3137 if (game_version <= VERSION_IDENT(2,2,0,0))
3139 // map game font elements
3140 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
3141 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3142 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
3143 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
3146 if (game_version < VERSION_IDENT(3,0,0,0))
3148 // map Supaplex gravity tube elements
3149 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
3150 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3151 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
3152 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
3159 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3161 level->file_version = getFileVersion(file);
3162 level->game_version = getFileVersion(file);
3167 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3169 level->creation_date.year = getFile16BitBE(file);
3170 level->creation_date.month = getFile8Bit(file);
3171 level->creation_date.day = getFile8Bit(file);
3173 level->creation_date.src = DATE_SRC_LEVELFILE;
3178 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3180 int initial_player_stepsize;
3181 int initial_player_gravity;
3184 level->fieldx = getFile8Bit(file);
3185 level->fieldy = getFile8Bit(file);
3187 level->time = getFile16BitBE(file);
3188 level->gems_needed = getFile16BitBE(file);
3190 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3191 level->name[i] = getFile8Bit(file);
3192 level->name[MAX_LEVEL_NAME_LEN] = 0;
3194 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3195 level->score[i] = getFile8Bit(file);
3197 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3198 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3199 for (y = 0; y < 3; y++)
3200 for (x = 0; x < 3; x++)
3201 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3203 level->amoeba_speed = getFile8Bit(file);
3204 level->time_magic_wall = getFile8Bit(file);
3205 level->time_wheel = getFile8Bit(file);
3206 level->amoeba_content = getMappedElement(getFile8Bit(file));
3208 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3211 for (i = 0; i < MAX_PLAYERS; i++)
3212 level->initial_player_stepsize[i] = initial_player_stepsize;
3214 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3216 for (i = 0; i < MAX_PLAYERS; i++)
3217 level->initial_player_gravity[i] = initial_player_gravity;
3219 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3220 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3222 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3224 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3225 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3226 level->can_move_into_acid_bits = getFile32BitBE(file);
3227 level->dont_collide_with_bits = getFile8Bit(file);
3229 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3230 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3232 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3233 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3234 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3236 level->game_engine_type = getFile8Bit(file);
3238 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3243 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3247 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3248 level->name[i] = getFile8Bit(file);
3249 level->name[MAX_LEVEL_NAME_LEN] = 0;
3254 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3258 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3259 level->author[i] = getFile8Bit(file);
3260 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3265 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3268 int chunk_size_expected = level->fieldx * level->fieldy;
3270 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3271 stored with 16-bit encoding (and should be twice as big then).
3272 Even worse, playfield data was stored 16-bit when only yamyam content
3273 contained 16-bit elements and vice versa. */
3275 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3276 chunk_size_expected *= 2;
3278 if (chunk_size_expected != chunk_size)
3280 ReadUnusedBytesFromFile(file, chunk_size);
3281 return chunk_size_expected;
3284 for (y = 0; y < level->fieldy; y++)
3285 for (x = 0; x < level->fieldx; x++)
3286 level->field[x][y] =
3287 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3292 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3295 int header_size = 4;
3296 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3297 int chunk_size_expected = header_size + content_size;
3299 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3300 stored with 16-bit encoding (and should be twice as big then).
3301 Even worse, playfield data was stored 16-bit when only yamyam content
3302 contained 16-bit elements and vice versa. */
3304 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3305 chunk_size_expected += content_size;
3307 if (chunk_size_expected != chunk_size)
3309 ReadUnusedBytesFromFile(file, chunk_size);
3310 return chunk_size_expected;
3314 level->num_yamyam_contents = getFile8Bit(file);
3318 // correct invalid number of content fields -- should never happen
3319 if (level->num_yamyam_contents < 1 ||
3320 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3321 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3323 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3324 for (y = 0; y < 3; y++)
3325 for (x = 0; x < 3; x++)
3326 level->yamyam_content[i].e[x][y] =
3327 getMappedElement(level->encoding_16bit_field ?
3328 getFile16BitBE(file) : getFile8Bit(file));
3332 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3337 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3339 element = getMappedElement(getFile16BitBE(file));
3340 num_contents = getFile8Bit(file);
3342 getFile8Bit(file); // content x size (unused)
3343 getFile8Bit(file); // content y size (unused)
3345 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3347 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3348 for (y = 0; y < 3; y++)
3349 for (x = 0; x < 3; x++)
3350 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3352 // correct invalid number of content fields -- should never happen
3353 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3354 num_contents = STD_ELEMENT_CONTENTS;
3356 if (element == EL_YAMYAM)
3358 level->num_yamyam_contents = num_contents;
3360 for (i = 0; i < num_contents; i++)
3361 for (y = 0; y < 3; y++)
3362 for (x = 0; x < 3; x++)
3363 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3365 else if (element == EL_BD_AMOEBA)
3367 level->amoeba_content = content_array[0][0][0];
3371 Warn("cannot load content for element '%d'", element);
3377 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3383 int chunk_size_expected;
3385 element = getMappedElement(getFile16BitBE(file));
3386 if (!IS_ENVELOPE(element))
3387 element = EL_ENVELOPE_1;
3389 envelope_nr = element - EL_ENVELOPE_1;
3391 envelope_len = getFile16BitBE(file);
3393 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3394 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3396 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3398 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3399 if (chunk_size_expected != chunk_size)
3401 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3402 return chunk_size_expected;
3405 for (i = 0; i < envelope_len; i++)
3406 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3411 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3413 int num_changed_custom_elements = getFile16BitBE(file);
3414 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3417 if (chunk_size_expected != chunk_size)
3419 ReadUnusedBytesFromFile(file, chunk_size - 2);
3420 return chunk_size_expected;
3423 for (i = 0; i < num_changed_custom_elements; i++)
3425 int element = getMappedElement(getFile16BitBE(file));
3426 int properties = getFile32BitBE(file);
3428 if (IS_CUSTOM_ELEMENT(element))
3429 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3431 Warn("invalid custom element number %d", element);
3433 // older game versions that wrote level files with CUS1 chunks used
3434 // different default push delay values (not yet stored in level file)
3435 element_info[element].push_delay_fixed = 2;
3436 element_info[element].push_delay_random = 8;
3439 level->file_has_custom_elements = TRUE;
3444 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3446 int num_changed_custom_elements = getFile16BitBE(file);
3447 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3450 if (chunk_size_expected != chunk_size)
3452 ReadUnusedBytesFromFile(file, chunk_size - 2);
3453 return chunk_size_expected;
3456 for (i = 0; i < num_changed_custom_elements; i++)
3458 int element = getMappedElement(getFile16BitBE(file));
3459 int custom_target_element = getMappedElement(getFile16BitBE(file));
3461 if (IS_CUSTOM_ELEMENT(element))
3462 element_info[element].change->target_element = custom_target_element;
3464 Warn("invalid custom element number %d", element);
3467 level->file_has_custom_elements = TRUE;
3472 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3474 int num_changed_custom_elements = getFile16BitBE(file);
3475 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3478 if (chunk_size_expected != chunk_size)
3480 ReadUnusedBytesFromFile(file, chunk_size - 2);
3481 return chunk_size_expected;
3484 for (i = 0; i < num_changed_custom_elements; i++)
3486 int element = getMappedElement(getFile16BitBE(file));
3487 struct ElementInfo *ei = &element_info[element];
3488 unsigned int event_bits;
3490 if (!IS_CUSTOM_ELEMENT(element))
3492 Warn("invalid custom element number %d", element);
3494 element = EL_INTERNAL_DUMMY;
3497 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3498 ei->description[j] = getFile8Bit(file);
3499 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3501 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3503 // some free bytes for future properties and padding
3504 ReadUnusedBytesFromFile(file, 7);
3506 ei->use_gfx_element = getFile8Bit(file);
3507 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3509 ei->collect_score_initial = getFile8Bit(file);
3510 ei->collect_count_initial = getFile8Bit(file);
3512 ei->push_delay_fixed = getFile16BitBE(file);
3513 ei->push_delay_random = getFile16BitBE(file);
3514 ei->move_delay_fixed = getFile16BitBE(file);
3515 ei->move_delay_random = getFile16BitBE(file);
3517 ei->move_pattern = getFile16BitBE(file);
3518 ei->move_direction_initial = getFile8Bit(file);
3519 ei->move_stepsize = getFile8Bit(file);
3521 for (y = 0; y < 3; y++)
3522 for (x = 0; x < 3; x++)
3523 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3525 // bits 0 - 31 of "has_event[]"
3526 event_bits = getFile32BitBE(file);
3527 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3528 if (event_bits & (1u << j))
3529 ei->change->has_event[j] = TRUE;
3531 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3533 ei->change->delay_fixed = getFile16BitBE(file);
3534 ei->change->delay_random = getFile16BitBE(file);
3535 ei->change->delay_frames = getFile16BitBE(file);
3537 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3539 ei->change->explode = getFile8Bit(file);
3540 ei->change->use_target_content = getFile8Bit(file);
3541 ei->change->only_if_complete = getFile8Bit(file);
3542 ei->change->use_random_replace = getFile8Bit(file);
3544 ei->change->random_percentage = getFile8Bit(file);
3545 ei->change->replace_when = getFile8Bit(file);
3547 for (y = 0; y < 3; y++)
3548 for (x = 0; x < 3; x++)
3549 ei->change->target_content.e[x][y] =
3550 getMappedElement(getFile16BitBE(file));
3552 ei->slippery_type = getFile8Bit(file);
3554 // some free bytes for future properties and padding
3555 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3557 // mark that this custom element has been modified
3558 ei->modified_settings = TRUE;
3561 level->file_has_custom_elements = TRUE;
3566 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3568 struct ElementInfo *ei;
3569 int chunk_size_expected;
3573 // ---------- custom element base property values (96 bytes) ----------------
3575 element = getMappedElement(getFile16BitBE(file));
3577 if (!IS_CUSTOM_ELEMENT(element))
3579 Warn("invalid custom element number %d", element);
3581 ReadUnusedBytesFromFile(file, chunk_size - 2);
3586 ei = &element_info[element];
3588 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3589 ei->description[i] = getFile8Bit(file);
3590 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3592 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3594 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3596 ei->num_change_pages = getFile8Bit(file);
3598 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3599 if (chunk_size_expected != chunk_size)
3601 ReadUnusedBytesFromFile(file, chunk_size - 43);
3602 return chunk_size_expected;
3605 ei->ce_value_fixed_initial = getFile16BitBE(file);
3606 ei->ce_value_random_initial = getFile16BitBE(file);
3607 ei->use_last_ce_value = getFile8Bit(file);
3609 ei->use_gfx_element = getFile8Bit(file);
3610 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3612 ei->collect_score_initial = getFile8Bit(file);
3613 ei->collect_count_initial = getFile8Bit(file);
3615 ei->drop_delay_fixed = getFile8Bit(file);
3616 ei->push_delay_fixed = getFile8Bit(file);
3617 ei->drop_delay_random = getFile8Bit(file);
3618 ei->push_delay_random = getFile8Bit(file);
3619 ei->move_delay_fixed = getFile16BitBE(file);
3620 ei->move_delay_random = getFile16BitBE(file);
3622 // bits 0 - 15 of "move_pattern" ...
3623 ei->move_pattern = getFile16BitBE(file);
3624 ei->move_direction_initial = getFile8Bit(file);
3625 ei->move_stepsize = getFile8Bit(file);
3627 ei->slippery_type = getFile8Bit(file);
3629 for (y = 0; y < 3; y++)
3630 for (x = 0; x < 3; x++)
3631 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3633 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3634 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3635 ei->move_leave_type = getFile8Bit(file);
3637 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3638 ei->move_pattern |= (getFile16BitBE(file) << 16);
3640 ei->access_direction = getFile8Bit(file);
3642 ei->explosion_delay = getFile8Bit(file);
3643 ei->ignition_delay = getFile8Bit(file);
3644 ei->explosion_type = getFile8Bit(file);
3646 // some free bytes for future custom property values and padding
3647 ReadUnusedBytesFromFile(file, 1);
3649 // ---------- change page property values (48 bytes) ------------------------
3651 setElementChangePages(ei, ei->num_change_pages);
3653 for (i = 0; i < ei->num_change_pages; i++)
3655 struct ElementChangeInfo *change = &ei->change_page[i];
3656 unsigned int event_bits;
3658 // always start with reliable default values
3659 setElementChangeInfoToDefaults(change);
3661 // bits 0 - 31 of "has_event[]" ...
3662 event_bits = getFile32BitBE(file);
3663 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3664 if (event_bits & (1u << j))
3665 change->has_event[j] = TRUE;
3667 change->target_element = getMappedElement(getFile16BitBE(file));
3669 change->delay_fixed = getFile16BitBE(file);
3670 change->delay_random = getFile16BitBE(file);
3671 change->delay_frames = getFile16BitBE(file);
3673 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3675 change->explode = getFile8Bit(file);
3676 change->use_target_content = getFile8Bit(file);
3677 change->only_if_complete = getFile8Bit(file);
3678 change->use_random_replace = getFile8Bit(file);
3680 change->random_percentage = getFile8Bit(file);
3681 change->replace_when = getFile8Bit(file);
3683 for (y = 0; y < 3; y++)
3684 for (x = 0; x < 3; x++)
3685 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3687 change->can_change = getFile8Bit(file);
3689 change->trigger_side = getFile8Bit(file);
3691 change->trigger_player = getFile8Bit(file);
3692 change->trigger_page = getFile8Bit(file);
3694 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3695 CH_PAGE_ANY : (1 << change->trigger_page));
3697 change->has_action = getFile8Bit(file);
3698 change->action_type = getFile8Bit(file);
3699 change->action_mode = getFile8Bit(file);
3700 change->action_arg = getFile16BitBE(file);
3702 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3703 event_bits = getFile8Bit(file);
3704 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3705 if (event_bits & (1u << (j - 32)))
3706 change->has_event[j] = TRUE;
3709 // mark this custom element as modified
3710 ei->modified_settings = TRUE;
3712 level->file_has_custom_elements = TRUE;
3717 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3719 struct ElementInfo *ei;
3720 struct ElementGroupInfo *group;
3724 element = getMappedElement(getFile16BitBE(file));
3726 if (!IS_GROUP_ELEMENT(element))
3728 Warn("invalid group element number %d", element);
3730 ReadUnusedBytesFromFile(file, chunk_size - 2);
3735 ei = &element_info[element];
3737 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3738 ei->description[i] = getFile8Bit(file);
3739 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3741 group = element_info[element].group;
3743 group->num_elements = getFile8Bit(file);
3745 ei->use_gfx_element = getFile8Bit(file);
3746 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3748 group->choice_mode = getFile8Bit(file);
3750 // some free bytes for future values and padding
3751 ReadUnusedBytesFromFile(file, 3);
3753 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3754 group->element[i] = getMappedElement(getFile16BitBE(file));
3756 // mark this group element as modified
3757 element_info[element].modified_settings = TRUE;
3759 level->file_has_custom_elements = TRUE;
3764 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3765 int element, int real_element)
3767 int micro_chunk_size = 0;
3768 int conf_type = getFile8Bit(file);
3769 int byte_mask = conf_type & CONF_MASK_BYTES;
3770 boolean element_found = FALSE;
3773 micro_chunk_size += 1;
3775 if (byte_mask == CONF_MASK_MULTI_BYTES)
3777 int num_bytes = getFile16BitBE(file);
3778 byte *buffer = checked_malloc(num_bytes);
3780 ReadBytesFromFile(file, buffer, num_bytes);
3782 for (i = 0; conf[i].data_type != -1; i++)
3784 if (conf[i].element == element &&
3785 conf[i].conf_type == conf_type)
3787 int data_type = conf[i].data_type;
3788 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3789 int max_num_entities = conf[i].max_num_entities;
3791 if (num_entities > max_num_entities)
3793 Warn("truncating number of entities for element %d from %d to %d",
3794 element, num_entities, max_num_entities);
3796 num_entities = max_num_entities;
3799 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3800 data_type == TYPE_CONTENT_LIST))
3802 // for element and content lists, zero entities are not allowed
3803 Warn("found empty list of entities for element %d", element);
3805 // do not set "num_entities" here to prevent reading behind buffer
3807 *(int *)(conf[i].num_entities) = 1; // at least one is required
3811 *(int *)(conf[i].num_entities) = num_entities;
3814 element_found = TRUE;
3816 if (data_type == TYPE_STRING)
3818 char *string = (char *)(conf[i].value);
3821 for (j = 0; j < max_num_entities; j++)
3822 string[j] = (j < num_entities ? buffer[j] : '\0');
3824 else if (data_type == TYPE_ELEMENT_LIST)
3826 int *element_array = (int *)(conf[i].value);
3829 for (j = 0; j < num_entities; j++)
3831 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3833 else if (data_type == TYPE_CONTENT_LIST)
3835 struct Content *content= (struct Content *)(conf[i].value);
3838 for (c = 0; c < num_entities; c++)
3839 for (y = 0; y < 3; y++)
3840 for (x = 0; x < 3; x++)
3841 content[c].e[x][y] =
3842 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3845 element_found = FALSE;
3851 checked_free(buffer);
3853 micro_chunk_size += 2 + num_bytes;
3855 else // constant size configuration data (1, 2 or 4 bytes)
3857 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3858 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3859 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3861 for (i = 0; conf[i].data_type != -1; i++)
3863 if (conf[i].element == element &&
3864 conf[i].conf_type == conf_type)
3866 int data_type = conf[i].data_type;
3868 if (data_type == TYPE_ELEMENT)
3869 value = getMappedElement(value);
3871 if (data_type == TYPE_BOOLEAN)
3872 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3874 *(int *) (conf[i].value) = value;
3876 element_found = TRUE;
3882 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3887 char *error_conf_chunk_bytes =
3888 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3889 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3890 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3891 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3892 int error_element = real_element;
3894 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3895 error_conf_chunk_bytes, error_conf_chunk_token,
3896 error_element, EL_NAME(error_element));
3899 return micro_chunk_size;
3902 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3904 int real_chunk_size = 0;
3906 li = *level; // copy level data into temporary buffer
3908 while (!checkEndOfFile(file))
3910 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3912 if (real_chunk_size >= chunk_size)
3916 *level = li; // copy temporary buffer back to level data
3918 return real_chunk_size;
3921 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3923 int real_chunk_size = 0;
3925 li = *level; // copy level data into temporary buffer
3927 while (!checkEndOfFile(file))
3929 int element = getMappedElement(getFile16BitBE(file));
3931 real_chunk_size += 2;
3932 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3934 if (real_chunk_size >= chunk_size)
3938 *level = li; // copy temporary buffer back to level data
3940 return real_chunk_size;
3943 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3945 int real_chunk_size = 0;
3947 li = *level; // copy level data into temporary buffer
3949 while (!checkEndOfFile(file))
3951 int element = getMappedElement(getFile16BitBE(file));
3953 real_chunk_size += 2;
3954 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3956 if (real_chunk_size >= chunk_size)
3960 *level = li; // copy temporary buffer back to level data
3962 return real_chunk_size;
3965 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3967 int element = getMappedElement(getFile16BitBE(file));
3968 int envelope_nr = element - EL_ENVELOPE_1;
3969 int real_chunk_size = 2;
3971 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3973 while (!checkEndOfFile(file))
3975 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3978 if (real_chunk_size >= chunk_size)
3982 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3984 return real_chunk_size;
3987 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3989 int element = getMappedElement(getFile16BitBE(file));
3990 int real_chunk_size = 2;
3991 struct ElementInfo *ei = &element_info[element];
3994 xx_ei = *ei; // copy element data into temporary buffer
3996 xx_ei.num_change_pages = -1;
3998 while (!checkEndOfFile(file))
4000 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
4002 if (xx_ei.num_change_pages != -1)
4005 if (real_chunk_size >= chunk_size)
4011 if (ei->num_change_pages == -1)
4013 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
4016 ei->num_change_pages = 1;
4018 setElementChangePages(ei, 1);
4019 setElementChangeInfoToDefaults(ei->change);
4021 return real_chunk_size;
4024 // initialize number of change pages stored for this custom element
4025 setElementChangePages(ei, ei->num_change_pages);
4026 for (i = 0; i < ei->num_change_pages; i++)
4027 setElementChangeInfoToDefaults(&ei->change_page[i]);
4029 // start with reading properties for the first change page
4030 xx_current_change_page = 0;
4032 while (!checkEndOfFile(file))
4034 // level file might contain invalid change page number
4035 if (xx_current_change_page >= ei->num_change_pages)
4038 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4040 xx_change = *change; // copy change data into temporary buffer
4042 resetEventBits(); // reset bits; change page might have changed
4044 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4047 *change = xx_change;
4049 setEventFlagsFromEventBits(change);
4051 if (real_chunk_size >= chunk_size)
4055 level->file_has_custom_elements = TRUE;
4057 return real_chunk_size;
4060 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4062 int element = getMappedElement(getFile16BitBE(file));
4063 int real_chunk_size = 2;
4064 struct ElementInfo *ei = &element_info[element];
4065 struct ElementGroupInfo *group = ei->group;
4070 xx_ei = *ei; // copy element data into temporary buffer
4071 xx_group = *group; // copy group data into temporary buffer
4073 while (!checkEndOfFile(file))
4075 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4078 if (real_chunk_size >= chunk_size)
4085 level->file_has_custom_elements = TRUE;
4087 return real_chunk_size;
4090 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4092 int element = getMappedElement(getFile16BitBE(file));
4093 int real_chunk_size = 2;
4094 struct ElementInfo *ei = &element_info[element];
4096 xx_ei = *ei; // copy element data into temporary buffer
4098 while (!checkEndOfFile(file))
4100 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4103 if (real_chunk_size >= chunk_size)
4109 level->file_has_custom_elements = TRUE;
4111 return real_chunk_size;
4114 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4115 struct LevelFileInfo *level_file_info,
4116 boolean level_info_only)
4118 char *filename = level_file_info->filename;
4119 char cookie[MAX_LINE_LEN];
4120 char chunk_name[CHUNK_ID_LEN + 1];
4124 if (!(file = openFile(filename, MODE_READ)))
4126 level->no_valid_file = TRUE;
4127 level->no_level_file = TRUE;
4129 if (level_info_only)
4132 Warn("cannot read level '%s' -- using empty level", filename);
4134 if (!setup.editor.use_template_for_new_levels)
4137 // if level file not found, try to initialize level data from template
4138 filename = getGlobalLevelTemplateFilename();
4140 if (!(file = openFile(filename, MODE_READ)))
4143 // default: for empty levels, use level template for custom elements
4144 level->use_custom_template = TRUE;
4146 level->no_valid_file = FALSE;
4149 getFileChunkBE(file, chunk_name, NULL);
4150 if (strEqual(chunk_name, "RND1"))
4152 getFile32BitBE(file); // not used
4154 getFileChunkBE(file, chunk_name, NULL);
4155 if (!strEqual(chunk_name, "CAVE"))
4157 level->no_valid_file = TRUE;
4159 Warn("unknown format of level file '%s'", filename);
4166 else // check for pre-2.0 file format with cookie string
4168 strcpy(cookie, chunk_name);
4169 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4171 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4172 cookie[strlen(cookie) - 1] = '\0';
4174 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4176 level->no_valid_file = TRUE;
4178 Warn("unknown format of level file '%s'", filename);
4185 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4187 level->no_valid_file = TRUE;
4189 Warn("unsupported version of level file '%s'", filename);
4196 // pre-2.0 level files have no game version, so use file version here
4197 level->game_version = level->file_version;
4200 if (level->file_version < FILE_VERSION_1_2)
4202 // level files from versions before 1.2.0 without chunk structure
4203 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4204 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4212 int (*loader)(File *, int, struct LevelInfo *);
4216 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4217 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4218 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4219 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4220 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4221 { "INFO", -1, LoadLevel_INFO },
4222 { "BODY", -1, LoadLevel_BODY },
4223 { "CONT", -1, LoadLevel_CONT },
4224 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4225 { "CNT3", -1, LoadLevel_CNT3 },
4226 { "CUS1", -1, LoadLevel_CUS1 },
4227 { "CUS2", -1, LoadLevel_CUS2 },
4228 { "CUS3", -1, LoadLevel_CUS3 },
4229 { "CUS4", -1, LoadLevel_CUS4 },
4230 { "GRP1", -1, LoadLevel_GRP1 },
4231 { "CONF", -1, LoadLevel_CONF },
4232 { "ELEM", -1, LoadLevel_ELEM },
4233 { "NOTE", -1, LoadLevel_NOTE },
4234 { "CUSX", -1, LoadLevel_CUSX },
4235 { "GRPX", -1, LoadLevel_GRPX },
4236 { "EMPX", -1, LoadLevel_EMPX },
4241 while (getFileChunkBE(file, chunk_name, &chunk_size))
4245 while (chunk_info[i].name != NULL &&
4246 !strEqual(chunk_name, chunk_info[i].name))
4249 if (chunk_info[i].name == NULL)
4251 Warn("unknown chunk '%s' in level file '%s'",
4252 chunk_name, filename);
4254 ReadUnusedBytesFromFile(file, chunk_size);
4256 else if (chunk_info[i].size != -1 &&
4257 chunk_info[i].size != chunk_size)
4259 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4260 chunk_size, chunk_name, filename);
4262 ReadUnusedBytesFromFile(file, chunk_size);
4266 // call function to load this level chunk
4267 int chunk_size_expected =
4268 (chunk_info[i].loader)(file, chunk_size, level);
4270 if (chunk_size_expected < 0)
4272 Warn("error reading chunk '%s' in level file '%s'",
4273 chunk_name, filename);
4278 // the size of some chunks cannot be checked before reading other
4279 // chunks first (like "HEAD" and "BODY") that contain some header
4280 // information, so check them here
4281 if (chunk_size_expected != chunk_size)
4283 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4284 chunk_size, chunk_name, filename);
4296 // ----------------------------------------------------------------------------
4297 // functions for loading BD level
4298 // ----------------------------------------------------------------------------
4300 #define LEVEL_TO_CAVE(e) (map_element_RND_to_BD_cave(e))
4301 #define CAVE_TO_LEVEL(e) (map_element_BD_to_RND_cave(e))
4303 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4305 struct LevelInfo_BD *level_bd = level->native_bd_level;
4306 GdCave *cave = NULL; // will be changed below
4307 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4308 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4311 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4313 // cave and map newly allocated when set to defaults above
4314 cave = level_bd->cave;
4317 cave->intermission = level->bd_intermission;
4320 cave->level_time[0] = level->time;
4321 cave->level_diamonds[0] = level->gems_needed;
4324 cave->scheduling = level->bd_scheduling_type;
4325 cave->pal_timing = level->bd_pal_timing;
4326 cave->level_speed[0] = level->bd_cycle_delay_ms;
4327 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4328 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4329 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4332 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4333 cave->diamond_value = level->score[SC_EMERALD];
4334 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4336 // compatibility settings
4337 cave->lineshift = level->bd_line_shifting_borders;
4338 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4339 cave->short_explosions = level->bd_short_explosions;
4341 // player properties
4342 cave->diagonal_movements = level->bd_diagonal_movements;
4343 cave->active_is_first_found = level->bd_topmost_player_active;
4344 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4345 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4346 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4347 cave->snap_element = LEVEL_TO_CAVE(level->bd_snap_element);
4349 // element properties
4350 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4351 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4352 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4353 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4354 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4355 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4356 cave->level_magic_wall_time[0] = level->bd_magic_wall_time;
4357 cave->magic_timer_zero_is_infinite = level->bd_magic_wall_zero_infinite;
4358 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4359 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4360 cave->magic_wall_breakscan = level->bd_magic_wall_break_scan;
4362 cave->magic_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4363 cave->magic_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4364 cave->magic_mega_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4365 cave->magic_nut_to = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4366 cave->magic_nitro_pack_to = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4367 cave->magic_flying_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4368 cave->magic_flying_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4370 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4371 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4372 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4373 cave->level_amoeba_threshold[0] = level->bd_amoeba_1_threshold_too_big;
4374 cave->level_amoeba_time[0] = level->bd_amoeba_1_slow_growth_time;
4375 cave->amoeba_growth_prob = level->bd_amoeba_1_slow_growth_rate * 10000;
4376 cave->amoeba_fast_growth_prob = level->bd_amoeba_1_fast_growth_rate * 10000;
4377 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4378 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4379 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4380 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4382 cave->amoeba_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_1_content_too_big);
4383 cave->amoeba_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_1_content_enclosed);
4384 cave->amoeba_2_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4385 cave->amoeba_2_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4386 cave->amoeba_2_explosion_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4387 cave->amoeba_2_looks_like = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4389 cave->slime_predictable = level->bd_slime_is_predictable;
4390 cave->slime_correct_random = level->bd_slime_correct_random;
4391 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4392 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4393 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4394 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4395 cave->slime_eats_1 = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4396 cave->slime_converts_1 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4397 cave->slime_eats_2 = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4398 cave->slime_converts_2 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4399 cave->slime_eats_3 = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4400 cave->slime_converts_3 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4402 cave->acid_eats_this = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4403 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4404 cave->acid_turns_to = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4406 cave->biter_delay_frame = level->bd_biter_move_delay;
4407 cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4409 cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4411 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4413 cave->replicators_active = level->bd_replicators_active;
4414 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4416 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4417 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4419 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4421 cave->nut_turns_to_when_crushed = LEVEL_TO_CAVE(level->bd_nut_content);
4423 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4424 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4425 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4427 cave->infinite_rockets = level->bd_infinite_rockets;
4429 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4430 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4432 cave->expanding_wall_looks_like = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4433 cave->dirt_looks_like = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4435 cave->creatures_backwards = level->bd_creatures_start_backwards;
4436 cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4437 cave->creatures_direction_auto_change_time = level->bd_creatures_auto_turn_delay;
4439 cave->gravity = level->bd_gravity_direction;
4440 cave->gravity_switch_active = level->bd_gravity_switch_active;
4441 cave->gravity_change_time = level->bd_gravity_switch_delay;
4442 cave->gravity_affects_all = level->bd_gravity_affects_all;
4444 cave->stone_falling_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4445 cave->stone_bouncing_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4446 cave->diamond_falling_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4447 cave->diamond_bouncing_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4449 cave->firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_1_explodes_to);
4450 cave->alt_firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4451 cave->butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_1_explodes_to);
4452 cave->alt_butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4453 cave->stonefly_explode_to = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4454 cave->dragonfly_explode_to = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4456 cave->diamond_birth_effect = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4457 cave->bomb_explosion_effect = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4458 cave->nitro_explosion_effect = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4459 cave->explosion_effect = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4461 cave->colorb = level->bd_color_b;
4462 cave->color0 = level->bd_color_0;
4463 cave->color1 = level->bd_color_1;
4464 cave->color2 = level->bd_color_2;
4465 cave->color3 = level->bd_color_3;
4466 cave->color4 = level->bd_color_4;
4467 cave->color5 = level->bd_color_5;
4470 strncpy(cave->name, level->name, sizeof(GdString));
4471 cave->name[sizeof(GdString) - 1] = '\0';
4473 // playfield elements
4474 for (x = 0; x < cave->w; x++)
4475 for (y = 0; y < cave->h; y++)
4476 cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4479 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4481 struct LevelInfo_BD *level_bd = level->native_bd_level;
4482 GdCave *cave = level_bd->cave;
4483 int bd_level_nr = level_bd->level_nr;
4486 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4487 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4490 level->bd_intermission = cave->intermission;
4493 level->time = cave->level_time[bd_level_nr];
4494 level->gems_needed = cave->level_diamonds[bd_level_nr];
4497 level->bd_scheduling_type = cave->scheduling;
4498 level->bd_pal_timing = cave->pal_timing;
4499 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4500 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4501 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4502 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4505 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4506 level->score[SC_EMERALD] = cave->diamond_value;
4507 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4509 // compatibility settings
4510 level->bd_line_shifting_borders = cave->lineshift;
4511 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4512 level->bd_short_explosions = cave->short_explosions;
4514 // player properties
4515 level->bd_diagonal_movements = cave->diagonal_movements;
4516 level->bd_topmost_player_active = cave->active_is_first_found;
4517 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4518 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4519 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4520 level->bd_snap_element = CAVE_TO_LEVEL(cave->snap_element);
4522 // element properties
4523 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4524 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4525 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4526 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4527 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4528 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4529 level->bd_magic_wall_time = cave->level_magic_wall_time[bd_level_nr];
4530 level->bd_magic_wall_zero_infinite = cave->magic_timer_zero_is_infinite;
4531 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4532 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4533 level->bd_magic_wall_break_scan = cave->magic_wall_breakscan;
4535 level->bd_magic_wall_diamond_to = CAVE_TO_LEVEL(cave->magic_diamond_to);
4536 level->bd_magic_wall_rock_to = CAVE_TO_LEVEL(cave->magic_stone_to);
4537 level->bd_magic_wall_mega_rock_to = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4538 level->bd_magic_wall_nut_to = CAVE_TO_LEVEL(cave->magic_nut_to);
4539 level->bd_magic_wall_nitro_pack_to = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4540 level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4541 level->bd_magic_wall_flying_rock_to = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4543 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4544 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4545 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4546 level->bd_amoeba_1_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4547 level->bd_amoeba_1_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4548 level->bd_amoeba_1_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4549 level->bd_amoeba_1_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4550 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4551 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4552 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4553 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4555 level->bd_amoeba_1_content_too_big = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4556 level->bd_amoeba_1_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4557 level->bd_amoeba_2_content_too_big = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4558 level->bd_amoeba_2_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4559 level->bd_amoeba_2_content_exploding = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4560 level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4562 level->bd_slime_is_predictable = cave->slime_predictable;
4563 level->bd_slime_correct_random = cave->slime_correct_random;
4564 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4565 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4566 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4567 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4568 level->bd_slime_eats_element_1 = CAVE_TO_LEVEL(cave->slime_eats_1);
4569 level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4570 level->bd_slime_eats_element_2 = CAVE_TO_LEVEL(cave->slime_eats_2);
4571 level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4572 level->bd_slime_eats_element_3 = CAVE_TO_LEVEL(cave->slime_eats_3);
4573 level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4575 level->bd_acid_eats_element = CAVE_TO_LEVEL(cave->acid_eats_this);
4576 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4577 level->bd_acid_turns_to_element = CAVE_TO_LEVEL(cave->acid_turns_to);
4579 level->bd_biter_move_delay = cave->biter_delay_frame;
4580 level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat);
4582 level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4584 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4586 level->bd_replicators_active = cave->replicators_active;
4587 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4589 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4590 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4592 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4594 level->bd_nut_content = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4596 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4597 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4598 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4600 level->bd_infinite_rockets = cave->infinite_rockets;
4602 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4603 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4605 level->bd_expanding_wall_looks_like = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4606 level->bd_sand_looks_like = CAVE_TO_LEVEL(cave->dirt_looks_like);
4608 level->bd_creatures_start_backwards = cave->creatures_backwards;
4609 level->bd_creatures_turn_on_hatching = cave->creatures_direction_auto_change_on_start;
4610 level->bd_creatures_auto_turn_delay = cave->creatures_direction_auto_change_time;
4612 level->bd_gravity_direction = cave->gravity;
4613 level->bd_gravity_switch_active = cave->gravity_switch_active;
4614 level->bd_gravity_switch_delay = cave->gravity_change_time;
4615 level->bd_gravity_affects_all = cave->gravity_affects_all;
4617 level->bd_rock_turns_to_on_falling = CAVE_TO_LEVEL(cave->stone_falling_effect);
4618 level->bd_rock_turns_to_on_impact = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4619 level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4620 level->bd_diamond_turns_to_on_impact = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4622 level->bd_firefly_1_explodes_to = CAVE_TO_LEVEL(cave->firefly_explode_to);
4623 level->bd_firefly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4624 level->bd_butterfly_1_explodes_to = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4625 level->bd_butterfly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4626 level->bd_stonefly_explodes_to = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4627 level->bd_dragonfly_explodes_to = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4629 level->bd_diamond_birth_turns_to = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4630 level->bd_bomb_explosion_turns_to = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4631 level->bd_nitro_explosion_turns_to = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4632 level->bd_explosion_turns_to = CAVE_TO_LEVEL(cave->explosion_effect);
4634 level->bd_color_b = cave->colorb;
4635 level->bd_color_0 = cave->color0;
4636 level->bd_color_1 = cave->color1;
4637 level->bd_color_2 = cave->color2;
4638 level->bd_color_3 = cave->color3;
4639 level->bd_color_4 = cave->color4;
4640 level->bd_color_5 = cave->color5;
4642 // set default color type and colors for BD style level colors
4643 SetDefaultLevelColorType_BD();
4644 SetDefaultLevelColors_BD();
4647 char *cave_name_latin1 = getLatin1FromUTF8(cave->name);
4648 char *cave_name_final = (gd_caveset_has_levels() ?
4649 getStringPrint("%s / %d", cave_name_latin1, bd_level_nr + 1) :
4650 getStringCopy(cave_name_latin1));
4652 strncpy(level->name, cave_name_final, MAX_LEVEL_NAME_LEN);
4653 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4655 // playfield elements
4656 for (x = 0; x < level->fieldx; x++)
4657 for (y = 0; y < level->fieldy; y++)
4658 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4660 checked_free(cave_name_latin1);
4661 checked_free(cave_name_final);
4664 static void setTapeInfoToDefaults(void);
4666 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4668 struct LevelInfo_BD *level_bd = level->native_bd_level;
4669 GdCave *cave = level_bd->cave;
4670 GdReplay *replay = level_bd->replay;
4676 // always start with reliable default values
4677 setTapeInfoToDefaults();
4679 tape.level_nr = level_nr; // (currently not used)
4680 tape.random_seed = replay->seed;
4682 TapeSetDateFromIsoDateString(replay->date);
4685 tape.pos[tape.counter].delay = 0;
4687 tape.bd_replay = TRUE;
4689 // all time calculations only used to display approximate tape time
4690 int cave_speed = cave->speed;
4691 int milliseconds_game = 0;
4692 int milliseconds_elapsed = 20;
4694 for (i = 0; i < replay->movements->len; i++)
4696 int replay_action = replay->movements->data[i];
4697 int tape_action = map_action_BD_to_RND(replay_action);
4698 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4699 boolean success = 0;
4703 success = TapeAddAction(action);
4705 milliseconds_game += milliseconds_elapsed;
4707 if (milliseconds_game >= cave_speed)
4709 milliseconds_game -= cave_speed;
4716 tape.pos[tape.counter].delay = 0;
4717 tape.pos[tape.counter].action[0] = 0;
4721 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4727 TapeHaltRecording();
4729 if (!replay->success)
4730 Warn("BD replay is marked as not successful");
4734 // ----------------------------------------------------------------------------
4735 // functions for loading EM level
4736 // ----------------------------------------------------------------------------
4738 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4740 static int ball_xy[8][2] =
4751 struct LevelInfo_EM *level_em = level->native_em_level;
4752 struct CAVE *cav = level_em->cav;
4755 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4756 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4758 cav->time_seconds = level->time;
4759 cav->gems_needed = level->gems_needed;
4761 cav->emerald_score = level->score[SC_EMERALD];
4762 cav->diamond_score = level->score[SC_DIAMOND];
4763 cav->alien_score = level->score[SC_ROBOT];
4764 cav->tank_score = level->score[SC_SPACESHIP];
4765 cav->bug_score = level->score[SC_BUG];
4766 cav->eater_score = level->score[SC_YAMYAM];
4767 cav->nut_score = level->score[SC_NUT];
4768 cav->dynamite_score = level->score[SC_DYNAMITE];
4769 cav->key_score = level->score[SC_KEY];
4770 cav->exit_score = level->score[SC_TIME_BONUS];
4772 cav->num_eater_arrays = level->num_yamyam_contents;
4774 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4775 for (y = 0; y < 3; y++)
4776 for (x = 0; x < 3; x++)
4777 cav->eater_array[i][y * 3 + x] =
4778 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4780 cav->amoeba_time = level->amoeba_speed;
4781 cav->wonderwall_time = level->time_magic_wall;
4782 cav->wheel_time = level->time_wheel;
4784 cav->android_move_time = level->android_move_time;
4785 cav->android_clone_time = level->android_clone_time;
4786 cav->ball_random = level->ball_random;
4787 cav->ball_active = level->ball_active_initial;
4788 cav->ball_time = level->ball_time;
4789 cav->num_ball_arrays = level->num_ball_contents;
4791 cav->lenses_score = level->lenses_score;
4792 cav->magnify_score = level->magnify_score;
4793 cav->slurp_score = level->slurp_score;
4795 cav->lenses_time = level->lenses_time;
4796 cav->magnify_time = level->magnify_time;
4798 cav->wind_time = 9999;
4799 cav->wind_direction =
4800 map_direction_RND_to_EM(level->wind_direction_initial);
4802 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4803 for (j = 0; j < 8; j++)
4804 cav->ball_array[i][j] =
4805 map_element_RND_to_EM_cave(level->ball_content[i].
4806 e[ball_xy[j][0]][ball_xy[j][1]]);
4808 map_android_clone_elements_RND_to_EM(level);
4810 // first fill the complete playfield with the empty space element
4811 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4812 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4813 cav->cave[x][y] = Cblank;
4815 // then copy the real level contents from level file into the playfield
4816 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4818 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4820 if (level->field[x][y] == EL_AMOEBA_DEAD)
4821 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4823 cav->cave[x][y] = new_element;
4826 for (i = 0; i < MAX_PLAYERS; i++)
4828 cav->player_x[i] = -1;
4829 cav->player_y[i] = -1;
4832 // initialize player positions and delete players from the playfield
4833 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4835 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4837 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4839 cav->player_x[player_nr] = x;
4840 cav->player_y[player_nr] = y;
4842 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4847 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4849 static int ball_xy[8][2] =
4860 struct LevelInfo_EM *level_em = level->native_em_level;
4861 struct CAVE *cav = level_em->cav;
4864 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4865 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4867 level->time = cav->time_seconds;
4868 level->gems_needed = cav->gems_needed;
4870 sprintf(level->name, "Level %d", level->file_info.nr);
4872 level->score[SC_EMERALD] = cav->emerald_score;
4873 level->score[SC_DIAMOND] = cav->diamond_score;
4874 level->score[SC_ROBOT] = cav->alien_score;
4875 level->score[SC_SPACESHIP] = cav->tank_score;
4876 level->score[SC_BUG] = cav->bug_score;
4877 level->score[SC_YAMYAM] = cav->eater_score;
4878 level->score[SC_NUT] = cav->nut_score;
4879 level->score[SC_DYNAMITE] = cav->dynamite_score;
4880 level->score[SC_KEY] = cav->key_score;
4881 level->score[SC_TIME_BONUS] = cav->exit_score;
4883 level->num_yamyam_contents = cav->num_eater_arrays;
4885 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4886 for (y = 0; y < 3; y++)
4887 for (x = 0; x < 3; x++)
4888 level->yamyam_content[i].e[x][y] =
4889 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4891 level->amoeba_speed = cav->amoeba_time;
4892 level->time_magic_wall = cav->wonderwall_time;
4893 level->time_wheel = cav->wheel_time;
4895 level->android_move_time = cav->android_move_time;
4896 level->android_clone_time = cav->android_clone_time;
4897 level->ball_random = cav->ball_random;
4898 level->ball_active_initial = cav->ball_active;
4899 level->ball_time = cav->ball_time;
4900 level->num_ball_contents = cav->num_ball_arrays;
4902 level->lenses_score = cav->lenses_score;
4903 level->magnify_score = cav->magnify_score;
4904 level->slurp_score = cav->slurp_score;
4906 level->lenses_time = cav->lenses_time;
4907 level->magnify_time = cav->magnify_time;
4909 level->wind_direction_initial =
4910 map_direction_EM_to_RND(cav->wind_direction);
4912 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4913 for (j = 0; j < 8; j++)
4914 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4915 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4917 map_android_clone_elements_EM_to_RND(level);
4919 // convert the playfield (some elements need special treatment)
4920 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4922 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4924 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4925 new_element = EL_AMOEBA_DEAD;
4927 level->field[x][y] = new_element;
4930 for (i = 0; i < MAX_PLAYERS; i++)
4932 // in case of all players set to the same field, use the first player
4933 int nr = MAX_PLAYERS - i - 1;
4934 int jx = cav->player_x[nr];
4935 int jy = cav->player_y[nr];
4937 if (jx != -1 && jy != -1)
4938 level->field[jx][jy] = EL_PLAYER_1 + nr;
4941 // time score is counted for each 10 seconds left in Emerald Mine levels
4942 level->time_score_base = 10;
4946 // ----------------------------------------------------------------------------
4947 // functions for loading SP level
4948 // ----------------------------------------------------------------------------
4950 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4952 struct LevelInfo_SP *level_sp = level->native_sp_level;
4953 LevelInfoType *header = &level_sp->header;
4956 level_sp->width = level->fieldx;
4957 level_sp->height = level->fieldy;
4959 for (x = 0; x < level->fieldx; x++)
4960 for (y = 0; y < level->fieldy; y++)
4961 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4963 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4965 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4966 header->LevelTitle[i] = level->name[i];
4967 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4969 header->InfotronsNeeded = level->gems_needed;
4971 header->SpecialPortCount = 0;
4973 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4975 boolean gravity_port_found = FALSE;
4976 boolean gravity_port_valid = FALSE;
4977 int gravity_port_flag;
4978 int gravity_port_base_element;
4979 int element = level->field[x][y];
4981 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4982 element <= EL_SP_GRAVITY_ON_PORT_UP)
4984 gravity_port_found = TRUE;
4985 gravity_port_valid = TRUE;
4986 gravity_port_flag = 1;
4987 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4989 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4990 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4992 gravity_port_found = TRUE;
4993 gravity_port_valid = TRUE;
4994 gravity_port_flag = 0;
4995 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4997 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4998 element <= EL_SP_GRAVITY_PORT_UP)
5000 // change R'n'D style gravity inverting special port to normal port
5001 // (there are no gravity inverting ports in native Supaplex engine)
5003 gravity_port_found = TRUE;
5004 gravity_port_valid = FALSE;
5005 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
5008 if (gravity_port_found)
5010 if (gravity_port_valid &&
5011 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
5013 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
5015 port->PortLocation = (y * level->fieldx + x) * 2;
5016 port->Gravity = gravity_port_flag;
5018 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
5020 header->SpecialPortCount++;
5024 // change special gravity port to normal port
5026 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
5029 level_sp->playfield[x][y] = element - EL_SP_START;
5034 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
5036 struct LevelInfo_SP *level_sp = level->native_sp_level;
5037 LevelInfoType *header = &level_sp->header;
5038 boolean num_invalid_elements = 0;
5041 level->fieldx = level_sp->width;
5042 level->fieldy = level_sp->height;
5044 for (x = 0; x < level->fieldx; x++)
5046 for (y = 0; y < level->fieldy; y++)
5048 int element_old = level_sp->playfield[x][y];
5049 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5051 if (element_new == EL_UNKNOWN)
5053 num_invalid_elements++;
5055 Debug("level:native:SP", "invalid element %d at position %d, %d",
5059 level->field[x][y] = element_new;
5063 if (num_invalid_elements > 0)
5064 Warn("found %d invalid elements%s", num_invalid_elements,
5065 (!options.debug ? " (use '--debug' for more details)" : ""));
5067 for (i = 0; i < MAX_PLAYERS; i++)
5068 level->initial_player_gravity[i] =
5069 (header->InitialGravity == 1 ? TRUE : FALSE);
5071 // skip leading spaces
5072 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5073 if (header->LevelTitle[i] != ' ')
5077 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5078 level->name[j] = header->LevelTitle[i];
5079 level->name[j] = '\0';
5081 // cut trailing spaces
5083 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5084 level->name[j - 1] = '\0';
5086 level->gems_needed = header->InfotronsNeeded;
5088 for (i = 0; i < header->SpecialPortCount; i++)
5090 SpecialPortType *port = &header->SpecialPort[i];
5091 int port_location = port->PortLocation;
5092 int gravity = port->Gravity;
5093 int port_x, port_y, port_element;
5095 port_x = (port_location / 2) % level->fieldx;
5096 port_y = (port_location / 2) / level->fieldx;
5098 if (port_x < 0 || port_x >= level->fieldx ||
5099 port_y < 0 || port_y >= level->fieldy)
5101 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5106 port_element = level->field[port_x][port_y];
5108 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5109 port_element > EL_SP_GRAVITY_PORT_UP)
5111 Warn("no special port at position (%d, %d)", port_x, port_y);
5116 // change previous (wrong) gravity inverting special port to either
5117 // gravity enabling special port or gravity disabling special port
5118 level->field[port_x][port_y] +=
5119 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5120 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5123 // change special gravity ports without database entries to normal ports
5124 for (x = 0; x < level->fieldx; x++)
5125 for (y = 0; y < level->fieldy; y++)
5126 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5127 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5128 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5130 level->time = 0; // no time limit
5131 level->amoeba_speed = 0;
5132 level->time_magic_wall = 0;
5133 level->time_wheel = 0;
5134 level->amoeba_content = EL_EMPTY;
5136 // original Supaplex does not use score values -- rate by playing time
5137 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5138 level->score[i] = 0;
5140 level->rate_time_over_score = TRUE;
5142 // there are no yamyams in supaplex levels
5143 for (i = 0; i < level->num_yamyam_contents; i++)
5144 for (x = 0; x < 3; x++)
5145 for (y = 0; y < 3; y++)
5146 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5149 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5151 struct LevelInfo_SP *level_sp = level->native_sp_level;
5152 struct DemoInfo_SP *demo = &level_sp->demo;
5155 // always start with reliable default values
5156 demo->is_available = FALSE;
5159 if (TAPE_IS_EMPTY(tape))
5162 demo->level_nr = tape.level_nr; // (currently not used)
5164 level_sp->header.DemoRandomSeed = tape.random_seed;
5168 for (i = 0; i < tape.length; i++)
5170 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5171 int demo_repeat = tape.pos[i].delay;
5172 int demo_entries = (demo_repeat + 15) / 16;
5174 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5176 Warn("tape truncated: size exceeds maximum SP demo size %d",
5182 for (j = 0; j < demo_repeat / 16; j++)
5183 demo->data[demo->length++] = 0xf0 | demo_action;
5185 if (demo_repeat % 16)
5186 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5189 demo->is_available = TRUE;
5192 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5194 struct LevelInfo_SP *level_sp = level->native_sp_level;
5195 struct DemoInfo_SP *demo = &level_sp->demo;
5196 char *filename = level->file_info.filename;
5199 // always start with reliable default values
5200 setTapeInfoToDefaults();
5202 if (!demo->is_available)
5205 tape.level_nr = demo->level_nr; // (currently not used)
5206 tape.random_seed = level_sp->header.DemoRandomSeed;
5208 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5211 tape.pos[tape.counter].delay = 0;
5213 for (i = 0; i < demo->length; i++)
5215 int demo_action = demo->data[i] & 0x0f;
5216 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5217 int tape_action = map_key_SP_to_RND(demo_action);
5218 int tape_repeat = demo_repeat + 1;
5219 byte action[MAX_TAPE_ACTIONS] = { tape_action };
5220 boolean success = 0;
5223 for (j = 0; j < tape_repeat; j++)
5224 success = TapeAddAction(action);
5228 Warn("SP demo truncated: size exceeds maximum tape size %d",
5235 TapeHaltRecording();
5239 // ----------------------------------------------------------------------------
5240 // functions for loading MM level
5241 // ----------------------------------------------------------------------------
5243 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5245 struct LevelInfo_MM *level_mm = level->native_mm_level;
5248 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5249 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5251 level_mm->time = level->time;
5252 level_mm->kettles_needed = level->gems_needed;
5253 level_mm->auto_count_kettles = level->auto_count_gems;
5255 level_mm->mm_laser_red = level->mm_laser_red;
5256 level_mm->mm_laser_green = level->mm_laser_green;
5257 level_mm->mm_laser_blue = level->mm_laser_blue;
5259 level_mm->df_laser_red = level->df_laser_red;
5260 level_mm->df_laser_green = level->df_laser_green;
5261 level_mm->df_laser_blue = level->df_laser_blue;
5263 strcpy(level_mm->name, level->name);
5264 strcpy(level_mm->author, level->author);
5266 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
5267 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
5268 level_mm->score[SC_KEY] = level->score[SC_KEY];
5269 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5270 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5272 level_mm->amoeba_speed = level->amoeba_speed;
5273 level_mm->time_fuse = level->mm_time_fuse;
5274 level_mm->time_bomb = level->mm_time_bomb;
5275 level_mm->time_ball = level->mm_time_ball;
5276 level_mm->time_block = level->mm_time_block;
5278 level_mm->num_ball_contents = level->num_mm_ball_contents;
5279 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5280 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5281 level_mm->explode_ball = level->explode_mm_ball;
5283 for (i = 0; i < level->num_mm_ball_contents; i++)
5284 level_mm->ball_content[i] =
5285 map_element_RND_to_MM(level->mm_ball_content[i]);
5287 for (x = 0; x < level->fieldx; x++)
5288 for (y = 0; y < level->fieldy; y++)
5290 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5293 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5295 struct LevelInfo_MM *level_mm = level->native_mm_level;
5298 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5299 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5301 level->time = level_mm->time;
5302 level->gems_needed = level_mm->kettles_needed;
5303 level->auto_count_gems = level_mm->auto_count_kettles;
5305 level->mm_laser_red = level_mm->mm_laser_red;
5306 level->mm_laser_green = level_mm->mm_laser_green;
5307 level->mm_laser_blue = level_mm->mm_laser_blue;
5309 level->df_laser_red = level_mm->df_laser_red;
5310 level->df_laser_green = level_mm->df_laser_green;
5311 level->df_laser_blue = level_mm->df_laser_blue;
5313 strcpy(level->name, level_mm->name);
5315 // only overwrite author from 'levelinfo.conf' if author defined in level
5316 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5317 strcpy(level->author, level_mm->author);
5319 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5320 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5321 level->score[SC_KEY] = level_mm->score[SC_KEY];
5322 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5323 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5325 level->amoeba_speed = level_mm->amoeba_speed;
5326 level->mm_time_fuse = level_mm->time_fuse;
5327 level->mm_time_bomb = level_mm->time_bomb;
5328 level->mm_time_ball = level_mm->time_ball;
5329 level->mm_time_block = level_mm->time_block;
5331 level->num_mm_ball_contents = level_mm->num_ball_contents;
5332 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5333 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5334 level->explode_mm_ball = level_mm->explode_ball;
5336 for (i = 0; i < level->num_mm_ball_contents; i++)
5337 level->mm_ball_content[i] =
5338 map_element_MM_to_RND(level_mm->ball_content[i]);
5340 for (x = 0; x < level->fieldx; x++)
5341 for (y = 0; y < level->fieldy; y++)
5342 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5346 // ----------------------------------------------------------------------------
5347 // functions for loading DC level
5348 // ----------------------------------------------------------------------------
5350 #define DC_LEVEL_HEADER_SIZE 344
5352 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5355 static int last_data_encoded;
5359 int diff_hi, diff_lo;
5360 int data_hi, data_lo;
5361 unsigned short data_decoded;
5365 last_data_encoded = 0;
5372 diff = data_encoded - last_data_encoded;
5373 diff_hi = diff & ~0xff;
5374 diff_lo = diff & 0xff;
5378 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5379 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5380 data_hi = data_hi & 0xff00;
5382 data_decoded = data_hi | data_lo;
5384 last_data_encoded = data_encoded;
5386 offset1 = (offset1 + 1) % 31;
5387 offset2 = offset2 & 0xff;
5389 return data_decoded;
5392 static int getMappedElement_DC(int element)
5400 // 0x0117 - 0x036e: (?)
5403 // 0x042d - 0x0684: (?)
5419 element = EL_CRYSTAL;
5422 case 0x0e77: // quicksand (boulder)
5423 element = EL_QUICKSAND_FAST_FULL;
5426 case 0x0e99: // slow quicksand (boulder)
5427 element = EL_QUICKSAND_FULL;
5431 element = EL_EM_EXIT_OPEN;
5435 element = EL_EM_EXIT_CLOSED;
5439 element = EL_EM_STEEL_EXIT_OPEN;
5443 element = EL_EM_STEEL_EXIT_CLOSED;
5446 case 0x0f4f: // dynamite (lit 1)
5447 element = EL_EM_DYNAMITE_ACTIVE;
5450 case 0x0f57: // dynamite (lit 2)
5451 element = EL_EM_DYNAMITE_ACTIVE;
5454 case 0x0f5f: // dynamite (lit 3)
5455 element = EL_EM_DYNAMITE_ACTIVE;
5458 case 0x0f67: // dynamite (lit 4)
5459 element = EL_EM_DYNAMITE_ACTIVE;
5466 element = EL_AMOEBA_WET;
5470 element = EL_AMOEBA_DROP;
5474 element = EL_DC_MAGIC_WALL;
5478 element = EL_SPACESHIP_UP;
5482 element = EL_SPACESHIP_DOWN;
5486 element = EL_SPACESHIP_LEFT;
5490 element = EL_SPACESHIP_RIGHT;
5494 element = EL_BUG_UP;
5498 element = EL_BUG_DOWN;
5502 element = EL_BUG_LEFT;
5506 element = EL_BUG_RIGHT;
5510 element = EL_MOLE_UP;
5514 element = EL_MOLE_DOWN;
5518 element = EL_MOLE_LEFT;
5522 element = EL_MOLE_RIGHT;
5530 element = EL_YAMYAM_UP;
5534 element = EL_SWITCHGATE_OPEN;
5538 element = EL_SWITCHGATE_CLOSED;
5542 element = EL_DC_SWITCHGATE_SWITCH_UP;
5546 element = EL_TIMEGATE_CLOSED;
5549 case 0x144c: // conveyor belt switch (green)
5550 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5553 case 0x144f: // conveyor belt switch (red)
5554 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5557 case 0x1452: // conveyor belt switch (blue)
5558 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5562 element = EL_CONVEYOR_BELT_3_MIDDLE;
5566 element = EL_CONVEYOR_BELT_3_LEFT;
5570 element = EL_CONVEYOR_BELT_3_RIGHT;
5574 element = EL_CONVEYOR_BELT_1_MIDDLE;
5578 element = EL_CONVEYOR_BELT_1_LEFT;
5582 element = EL_CONVEYOR_BELT_1_RIGHT;
5586 element = EL_CONVEYOR_BELT_4_MIDDLE;
5590 element = EL_CONVEYOR_BELT_4_LEFT;
5594 element = EL_CONVEYOR_BELT_4_RIGHT;
5598 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5602 element = EL_EXPANDABLE_WALL_VERTICAL;
5606 element = EL_EXPANDABLE_WALL_ANY;
5609 case 0x14ce: // growing steel wall (left/right)
5610 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5613 case 0x14df: // growing steel wall (up/down)
5614 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5617 case 0x14e8: // growing steel wall (up/down/left/right)
5618 element = EL_EXPANDABLE_STEELWALL_ANY;
5622 element = EL_SHIELD_DEADLY;
5626 element = EL_EXTRA_TIME;
5634 element = EL_EMPTY_SPACE;
5637 case 0x1578: // quicksand (empty)
5638 element = EL_QUICKSAND_FAST_EMPTY;
5641 case 0x1579: // slow quicksand (empty)
5642 element = EL_QUICKSAND_EMPTY;
5652 element = EL_EM_DYNAMITE;
5655 case 0x15a1: // key (red)
5656 element = EL_EM_KEY_1;
5659 case 0x15a2: // key (yellow)
5660 element = EL_EM_KEY_2;
5663 case 0x15a3: // key (blue)
5664 element = EL_EM_KEY_4;
5667 case 0x15a4: // key (green)
5668 element = EL_EM_KEY_3;
5671 case 0x15a5: // key (white)
5672 element = EL_DC_KEY_WHITE;
5676 element = EL_WALL_SLIPPERY;
5683 case 0x15a8: // wall (not round)
5687 case 0x15a9: // (blue)
5688 element = EL_CHAR_A;
5691 case 0x15aa: // (blue)
5692 element = EL_CHAR_B;
5695 case 0x15ab: // (blue)
5696 element = EL_CHAR_C;
5699 case 0x15ac: // (blue)
5700 element = EL_CHAR_D;
5703 case 0x15ad: // (blue)
5704 element = EL_CHAR_E;
5707 case 0x15ae: // (blue)
5708 element = EL_CHAR_F;
5711 case 0x15af: // (blue)
5712 element = EL_CHAR_G;
5715 case 0x15b0: // (blue)
5716 element = EL_CHAR_H;
5719 case 0x15b1: // (blue)
5720 element = EL_CHAR_I;
5723 case 0x15b2: // (blue)
5724 element = EL_CHAR_J;
5727 case 0x15b3: // (blue)
5728 element = EL_CHAR_K;
5731 case 0x15b4: // (blue)
5732 element = EL_CHAR_L;
5735 case 0x15b5: // (blue)
5736 element = EL_CHAR_M;
5739 case 0x15b6: // (blue)
5740 element = EL_CHAR_N;
5743 case 0x15b7: // (blue)
5744 element = EL_CHAR_O;
5747 case 0x15b8: // (blue)
5748 element = EL_CHAR_P;
5751 case 0x15b9: // (blue)
5752 element = EL_CHAR_Q;
5755 case 0x15ba: // (blue)
5756 element = EL_CHAR_R;
5759 case 0x15bb: // (blue)
5760 element = EL_CHAR_S;
5763 case 0x15bc: // (blue)
5764 element = EL_CHAR_T;
5767 case 0x15bd: // (blue)
5768 element = EL_CHAR_U;
5771 case 0x15be: // (blue)
5772 element = EL_CHAR_V;
5775 case 0x15bf: // (blue)
5776 element = EL_CHAR_W;
5779 case 0x15c0: // (blue)
5780 element = EL_CHAR_X;
5783 case 0x15c1: // (blue)
5784 element = EL_CHAR_Y;
5787 case 0x15c2: // (blue)
5788 element = EL_CHAR_Z;
5791 case 0x15c3: // (blue)
5792 element = EL_CHAR_AUMLAUT;
5795 case 0x15c4: // (blue)
5796 element = EL_CHAR_OUMLAUT;
5799 case 0x15c5: // (blue)
5800 element = EL_CHAR_UUMLAUT;
5803 case 0x15c6: // (blue)
5804 element = EL_CHAR_0;
5807 case 0x15c7: // (blue)
5808 element = EL_CHAR_1;
5811 case 0x15c8: // (blue)
5812 element = EL_CHAR_2;
5815 case 0x15c9: // (blue)
5816 element = EL_CHAR_3;
5819 case 0x15ca: // (blue)
5820 element = EL_CHAR_4;
5823 case 0x15cb: // (blue)
5824 element = EL_CHAR_5;
5827 case 0x15cc: // (blue)
5828 element = EL_CHAR_6;
5831 case 0x15cd: // (blue)
5832 element = EL_CHAR_7;
5835 case 0x15ce: // (blue)
5836 element = EL_CHAR_8;
5839 case 0x15cf: // (blue)
5840 element = EL_CHAR_9;
5843 case 0x15d0: // (blue)
5844 element = EL_CHAR_PERIOD;
5847 case 0x15d1: // (blue)
5848 element = EL_CHAR_EXCLAM;
5851 case 0x15d2: // (blue)
5852 element = EL_CHAR_COLON;
5855 case 0x15d3: // (blue)
5856 element = EL_CHAR_LESS;
5859 case 0x15d4: // (blue)
5860 element = EL_CHAR_GREATER;
5863 case 0x15d5: // (blue)
5864 element = EL_CHAR_QUESTION;
5867 case 0x15d6: // (blue)
5868 element = EL_CHAR_COPYRIGHT;
5871 case 0x15d7: // (blue)
5872 element = EL_CHAR_UP;
5875 case 0x15d8: // (blue)
5876 element = EL_CHAR_DOWN;
5879 case 0x15d9: // (blue)
5880 element = EL_CHAR_BUTTON;
5883 case 0x15da: // (blue)
5884 element = EL_CHAR_PLUS;
5887 case 0x15db: // (blue)
5888 element = EL_CHAR_MINUS;
5891 case 0x15dc: // (blue)
5892 element = EL_CHAR_APOSTROPHE;
5895 case 0x15dd: // (blue)
5896 element = EL_CHAR_PARENLEFT;
5899 case 0x15de: // (blue)
5900 element = EL_CHAR_PARENRIGHT;
5903 case 0x15df: // (green)
5904 element = EL_CHAR_A;
5907 case 0x15e0: // (green)
5908 element = EL_CHAR_B;
5911 case 0x15e1: // (green)
5912 element = EL_CHAR_C;
5915 case 0x15e2: // (green)
5916 element = EL_CHAR_D;
5919 case 0x15e3: // (green)
5920 element = EL_CHAR_E;
5923 case 0x15e4: // (green)
5924 element = EL_CHAR_F;
5927 case 0x15e5: // (green)
5928 element = EL_CHAR_G;
5931 case 0x15e6: // (green)
5932 element = EL_CHAR_H;
5935 case 0x15e7: // (green)
5936 element = EL_CHAR_I;
5939 case 0x15e8: // (green)
5940 element = EL_CHAR_J;
5943 case 0x15e9: // (green)
5944 element = EL_CHAR_K;
5947 case 0x15ea: // (green)
5948 element = EL_CHAR_L;
5951 case 0x15eb: // (green)
5952 element = EL_CHAR_M;
5955 case 0x15ec: // (green)
5956 element = EL_CHAR_N;
5959 case 0x15ed: // (green)
5960 element = EL_CHAR_O;
5963 case 0x15ee: // (green)
5964 element = EL_CHAR_P;
5967 case 0x15ef: // (green)
5968 element = EL_CHAR_Q;
5971 case 0x15f0: // (green)
5972 element = EL_CHAR_R;
5975 case 0x15f1: // (green)
5976 element = EL_CHAR_S;
5979 case 0x15f2: // (green)
5980 element = EL_CHAR_T;
5983 case 0x15f3: // (green)
5984 element = EL_CHAR_U;
5987 case 0x15f4: // (green)
5988 element = EL_CHAR_V;
5991 case 0x15f5: // (green)
5992 element = EL_CHAR_W;
5995 case 0x15f6: // (green)
5996 element = EL_CHAR_X;
5999 case 0x15f7: // (green)
6000 element = EL_CHAR_Y;
6003 case 0x15f8: // (green)
6004 element = EL_CHAR_Z;
6007 case 0x15f9: // (green)
6008 element = EL_CHAR_AUMLAUT;
6011 case 0x15fa: // (green)
6012 element = EL_CHAR_OUMLAUT;
6015 case 0x15fb: // (green)
6016 element = EL_CHAR_UUMLAUT;
6019 case 0x15fc: // (green)
6020 element = EL_CHAR_0;
6023 case 0x15fd: // (green)
6024 element = EL_CHAR_1;
6027 case 0x15fe: // (green)
6028 element = EL_CHAR_2;
6031 case 0x15ff: // (green)
6032 element = EL_CHAR_3;
6035 case 0x1600: // (green)
6036 element = EL_CHAR_4;
6039 case 0x1601: // (green)
6040 element = EL_CHAR_5;
6043 case 0x1602: // (green)
6044 element = EL_CHAR_6;
6047 case 0x1603: // (green)
6048 element = EL_CHAR_7;
6051 case 0x1604: // (green)
6052 element = EL_CHAR_8;
6055 case 0x1605: // (green)
6056 element = EL_CHAR_9;
6059 case 0x1606: // (green)
6060 element = EL_CHAR_PERIOD;
6063 case 0x1607: // (green)
6064 element = EL_CHAR_EXCLAM;
6067 case 0x1608: // (green)
6068 element = EL_CHAR_COLON;
6071 case 0x1609: // (green)
6072 element = EL_CHAR_LESS;
6075 case 0x160a: // (green)
6076 element = EL_CHAR_GREATER;
6079 case 0x160b: // (green)
6080 element = EL_CHAR_QUESTION;
6083 case 0x160c: // (green)
6084 element = EL_CHAR_COPYRIGHT;
6087 case 0x160d: // (green)
6088 element = EL_CHAR_UP;
6091 case 0x160e: // (green)
6092 element = EL_CHAR_DOWN;
6095 case 0x160f: // (green)
6096 element = EL_CHAR_BUTTON;
6099 case 0x1610: // (green)
6100 element = EL_CHAR_PLUS;
6103 case 0x1611: // (green)
6104 element = EL_CHAR_MINUS;
6107 case 0x1612: // (green)
6108 element = EL_CHAR_APOSTROPHE;
6111 case 0x1613: // (green)
6112 element = EL_CHAR_PARENLEFT;
6115 case 0x1614: // (green)
6116 element = EL_CHAR_PARENRIGHT;
6119 case 0x1615: // (blue steel)
6120 element = EL_STEEL_CHAR_A;
6123 case 0x1616: // (blue steel)
6124 element = EL_STEEL_CHAR_B;
6127 case 0x1617: // (blue steel)
6128 element = EL_STEEL_CHAR_C;
6131 case 0x1618: // (blue steel)
6132 element = EL_STEEL_CHAR_D;
6135 case 0x1619: // (blue steel)
6136 element = EL_STEEL_CHAR_E;
6139 case 0x161a: // (blue steel)
6140 element = EL_STEEL_CHAR_F;
6143 case 0x161b: // (blue steel)
6144 element = EL_STEEL_CHAR_G;
6147 case 0x161c: // (blue steel)
6148 element = EL_STEEL_CHAR_H;
6151 case 0x161d: // (blue steel)
6152 element = EL_STEEL_CHAR_I;
6155 case 0x161e: // (blue steel)
6156 element = EL_STEEL_CHAR_J;
6159 case 0x161f: // (blue steel)
6160 element = EL_STEEL_CHAR_K;
6163 case 0x1620: // (blue steel)
6164 element = EL_STEEL_CHAR_L;
6167 case 0x1621: // (blue steel)
6168 element = EL_STEEL_CHAR_M;
6171 case 0x1622: // (blue steel)
6172 element = EL_STEEL_CHAR_N;
6175 case 0x1623: // (blue steel)
6176 element = EL_STEEL_CHAR_O;
6179 case 0x1624: // (blue steel)
6180 element = EL_STEEL_CHAR_P;
6183 case 0x1625: // (blue steel)
6184 element = EL_STEEL_CHAR_Q;
6187 case 0x1626: // (blue steel)
6188 element = EL_STEEL_CHAR_R;
6191 case 0x1627: // (blue steel)
6192 element = EL_STEEL_CHAR_S;
6195 case 0x1628: // (blue steel)
6196 element = EL_STEEL_CHAR_T;
6199 case 0x1629: // (blue steel)
6200 element = EL_STEEL_CHAR_U;
6203 case 0x162a: // (blue steel)
6204 element = EL_STEEL_CHAR_V;
6207 case 0x162b: // (blue steel)
6208 element = EL_STEEL_CHAR_W;
6211 case 0x162c: // (blue steel)
6212 element = EL_STEEL_CHAR_X;
6215 case 0x162d: // (blue steel)
6216 element = EL_STEEL_CHAR_Y;
6219 case 0x162e: // (blue steel)
6220 element = EL_STEEL_CHAR_Z;
6223 case 0x162f: // (blue steel)
6224 element = EL_STEEL_CHAR_AUMLAUT;
6227 case 0x1630: // (blue steel)
6228 element = EL_STEEL_CHAR_OUMLAUT;
6231 case 0x1631: // (blue steel)
6232 element = EL_STEEL_CHAR_UUMLAUT;
6235 case 0x1632: // (blue steel)
6236 element = EL_STEEL_CHAR_0;
6239 case 0x1633: // (blue steel)
6240 element = EL_STEEL_CHAR_1;
6243 case 0x1634: // (blue steel)
6244 element = EL_STEEL_CHAR_2;
6247 case 0x1635: // (blue steel)
6248 element = EL_STEEL_CHAR_3;
6251 case 0x1636: // (blue steel)
6252 element = EL_STEEL_CHAR_4;
6255 case 0x1637: // (blue steel)
6256 element = EL_STEEL_CHAR_5;
6259 case 0x1638: // (blue steel)
6260 element = EL_STEEL_CHAR_6;
6263 case 0x1639: // (blue steel)
6264 element = EL_STEEL_CHAR_7;
6267 case 0x163a: // (blue steel)
6268 element = EL_STEEL_CHAR_8;
6271 case 0x163b: // (blue steel)
6272 element = EL_STEEL_CHAR_9;
6275 case 0x163c: // (blue steel)
6276 element = EL_STEEL_CHAR_PERIOD;
6279 case 0x163d: // (blue steel)
6280 element = EL_STEEL_CHAR_EXCLAM;
6283 case 0x163e: // (blue steel)
6284 element = EL_STEEL_CHAR_COLON;
6287 case 0x163f: // (blue steel)
6288 element = EL_STEEL_CHAR_LESS;
6291 case 0x1640: // (blue steel)
6292 element = EL_STEEL_CHAR_GREATER;
6295 case 0x1641: // (blue steel)
6296 element = EL_STEEL_CHAR_QUESTION;
6299 case 0x1642: // (blue steel)
6300 element = EL_STEEL_CHAR_COPYRIGHT;
6303 case 0x1643: // (blue steel)
6304 element = EL_STEEL_CHAR_UP;
6307 case 0x1644: // (blue steel)
6308 element = EL_STEEL_CHAR_DOWN;
6311 case 0x1645: // (blue steel)
6312 element = EL_STEEL_CHAR_BUTTON;
6315 case 0x1646: // (blue steel)
6316 element = EL_STEEL_CHAR_PLUS;
6319 case 0x1647: // (blue steel)
6320 element = EL_STEEL_CHAR_MINUS;
6323 case 0x1648: // (blue steel)
6324 element = EL_STEEL_CHAR_APOSTROPHE;
6327 case 0x1649: // (blue steel)
6328 element = EL_STEEL_CHAR_PARENLEFT;
6331 case 0x164a: // (blue steel)
6332 element = EL_STEEL_CHAR_PARENRIGHT;
6335 case 0x164b: // (green steel)
6336 element = EL_STEEL_CHAR_A;
6339 case 0x164c: // (green steel)
6340 element = EL_STEEL_CHAR_B;
6343 case 0x164d: // (green steel)
6344 element = EL_STEEL_CHAR_C;
6347 case 0x164e: // (green steel)
6348 element = EL_STEEL_CHAR_D;
6351 case 0x164f: // (green steel)
6352 element = EL_STEEL_CHAR_E;
6355 case 0x1650: // (green steel)
6356 element = EL_STEEL_CHAR_F;
6359 case 0x1651: // (green steel)
6360 element = EL_STEEL_CHAR_G;
6363 case 0x1652: // (green steel)
6364 element = EL_STEEL_CHAR_H;
6367 case 0x1653: // (green steel)
6368 element = EL_STEEL_CHAR_I;
6371 case 0x1654: // (green steel)
6372 element = EL_STEEL_CHAR_J;
6375 case 0x1655: // (green steel)
6376 element = EL_STEEL_CHAR_K;
6379 case 0x1656: // (green steel)
6380 element = EL_STEEL_CHAR_L;
6383 case 0x1657: // (green steel)
6384 element = EL_STEEL_CHAR_M;
6387 case 0x1658: // (green steel)
6388 element = EL_STEEL_CHAR_N;
6391 case 0x1659: // (green steel)
6392 element = EL_STEEL_CHAR_O;
6395 case 0x165a: // (green steel)
6396 element = EL_STEEL_CHAR_P;
6399 case 0x165b: // (green steel)
6400 element = EL_STEEL_CHAR_Q;
6403 case 0x165c: // (green steel)
6404 element = EL_STEEL_CHAR_R;
6407 case 0x165d: // (green steel)
6408 element = EL_STEEL_CHAR_S;
6411 case 0x165e: // (green steel)
6412 element = EL_STEEL_CHAR_T;
6415 case 0x165f: // (green steel)
6416 element = EL_STEEL_CHAR_U;
6419 case 0x1660: // (green steel)
6420 element = EL_STEEL_CHAR_V;
6423 case 0x1661: // (green steel)
6424 element = EL_STEEL_CHAR_W;
6427 case 0x1662: // (green steel)
6428 element = EL_STEEL_CHAR_X;
6431 case 0x1663: // (green steel)
6432 element = EL_STEEL_CHAR_Y;
6435 case 0x1664: // (green steel)
6436 element = EL_STEEL_CHAR_Z;
6439 case 0x1665: // (green steel)
6440 element = EL_STEEL_CHAR_AUMLAUT;
6443 case 0x1666: // (green steel)
6444 element = EL_STEEL_CHAR_OUMLAUT;
6447 case 0x1667: // (green steel)
6448 element = EL_STEEL_CHAR_UUMLAUT;
6451 case 0x1668: // (green steel)
6452 element = EL_STEEL_CHAR_0;
6455 case 0x1669: // (green steel)
6456 element = EL_STEEL_CHAR_1;
6459 case 0x166a: // (green steel)
6460 element = EL_STEEL_CHAR_2;
6463 case 0x166b: // (green steel)
6464 element = EL_STEEL_CHAR_3;
6467 case 0x166c: // (green steel)
6468 element = EL_STEEL_CHAR_4;
6471 case 0x166d: // (green steel)
6472 element = EL_STEEL_CHAR_5;
6475 case 0x166e: // (green steel)
6476 element = EL_STEEL_CHAR_6;
6479 case 0x166f: // (green steel)
6480 element = EL_STEEL_CHAR_7;
6483 case 0x1670: // (green steel)
6484 element = EL_STEEL_CHAR_8;
6487 case 0x1671: // (green steel)
6488 element = EL_STEEL_CHAR_9;
6491 case 0x1672: // (green steel)
6492 element = EL_STEEL_CHAR_PERIOD;
6495 case 0x1673: // (green steel)
6496 element = EL_STEEL_CHAR_EXCLAM;
6499 case 0x1674: // (green steel)
6500 element = EL_STEEL_CHAR_COLON;
6503 case 0x1675: // (green steel)
6504 element = EL_STEEL_CHAR_LESS;
6507 case 0x1676: // (green steel)
6508 element = EL_STEEL_CHAR_GREATER;
6511 case 0x1677: // (green steel)
6512 element = EL_STEEL_CHAR_QUESTION;
6515 case 0x1678: // (green steel)
6516 element = EL_STEEL_CHAR_COPYRIGHT;
6519 case 0x1679: // (green steel)
6520 element = EL_STEEL_CHAR_UP;
6523 case 0x167a: // (green steel)
6524 element = EL_STEEL_CHAR_DOWN;
6527 case 0x167b: // (green steel)
6528 element = EL_STEEL_CHAR_BUTTON;
6531 case 0x167c: // (green steel)
6532 element = EL_STEEL_CHAR_PLUS;
6535 case 0x167d: // (green steel)
6536 element = EL_STEEL_CHAR_MINUS;
6539 case 0x167e: // (green steel)
6540 element = EL_STEEL_CHAR_APOSTROPHE;
6543 case 0x167f: // (green steel)
6544 element = EL_STEEL_CHAR_PARENLEFT;
6547 case 0x1680: // (green steel)
6548 element = EL_STEEL_CHAR_PARENRIGHT;
6551 case 0x1681: // gate (red)
6552 element = EL_EM_GATE_1;
6555 case 0x1682: // secret gate (red)
6556 element = EL_EM_GATE_1_GRAY;
6559 case 0x1683: // gate (yellow)
6560 element = EL_EM_GATE_2;
6563 case 0x1684: // secret gate (yellow)
6564 element = EL_EM_GATE_2_GRAY;
6567 case 0x1685: // gate (blue)
6568 element = EL_EM_GATE_4;
6571 case 0x1686: // secret gate (blue)
6572 element = EL_EM_GATE_4_GRAY;
6575 case 0x1687: // gate (green)
6576 element = EL_EM_GATE_3;
6579 case 0x1688: // secret gate (green)
6580 element = EL_EM_GATE_3_GRAY;
6583 case 0x1689: // gate (white)
6584 element = EL_DC_GATE_WHITE;
6587 case 0x168a: // secret gate (white)
6588 element = EL_DC_GATE_WHITE_GRAY;
6591 case 0x168b: // secret gate (no key)
6592 element = EL_DC_GATE_FAKE_GRAY;
6596 element = EL_ROBOT_WHEEL;
6600 element = EL_DC_TIMEGATE_SWITCH;
6604 element = EL_ACID_POOL_BOTTOM;
6608 element = EL_ACID_POOL_TOPLEFT;
6612 element = EL_ACID_POOL_TOPRIGHT;
6616 element = EL_ACID_POOL_BOTTOMLEFT;
6620 element = EL_ACID_POOL_BOTTOMRIGHT;
6624 element = EL_STEELWALL;
6628 element = EL_STEELWALL_SLIPPERY;
6631 case 0x1695: // steel wall (not round)
6632 element = EL_STEELWALL;
6635 case 0x1696: // steel wall (left)
6636 element = EL_DC_STEELWALL_1_LEFT;
6639 case 0x1697: // steel wall (bottom)
6640 element = EL_DC_STEELWALL_1_BOTTOM;
6643 case 0x1698: // steel wall (right)
6644 element = EL_DC_STEELWALL_1_RIGHT;
6647 case 0x1699: // steel wall (top)
6648 element = EL_DC_STEELWALL_1_TOP;
6651 case 0x169a: // steel wall (left/bottom)
6652 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6655 case 0x169b: // steel wall (right/bottom)
6656 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6659 case 0x169c: // steel wall (right/top)
6660 element = EL_DC_STEELWALL_1_TOPRIGHT;
6663 case 0x169d: // steel wall (left/top)
6664 element = EL_DC_STEELWALL_1_TOPLEFT;
6667 case 0x169e: // steel wall (right/bottom small)
6668 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6671 case 0x169f: // steel wall (left/bottom small)
6672 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6675 case 0x16a0: // steel wall (right/top small)
6676 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6679 case 0x16a1: // steel wall (left/top small)
6680 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6683 case 0x16a2: // steel wall (left/right)
6684 element = EL_DC_STEELWALL_1_VERTICAL;
6687 case 0x16a3: // steel wall (top/bottom)
6688 element = EL_DC_STEELWALL_1_HORIZONTAL;
6691 case 0x16a4: // steel wall 2 (left end)
6692 element = EL_DC_STEELWALL_2_LEFT;
6695 case 0x16a5: // steel wall 2 (right end)
6696 element = EL_DC_STEELWALL_2_RIGHT;
6699 case 0x16a6: // steel wall 2 (top end)
6700 element = EL_DC_STEELWALL_2_TOP;
6703 case 0x16a7: // steel wall 2 (bottom end)
6704 element = EL_DC_STEELWALL_2_BOTTOM;
6707 case 0x16a8: // steel wall 2 (left/right)
6708 element = EL_DC_STEELWALL_2_HORIZONTAL;
6711 case 0x16a9: // steel wall 2 (up/down)
6712 element = EL_DC_STEELWALL_2_VERTICAL;
6715 case 0x16aa: // steel wall 2 (mid)
6716 element = EL_DC_STEELWALL_2_MIDDLE;
6720 element = EL_SIGN_EXCLAMATION;
6724 element = EL_SIGN_RADIOACTIVITY;
6728 element = EL_SIGN_STOP;
6732 element = EL_SIGN_WHEELCHAIR;
6736 element = EL_SIGN_PARKING;
6740 element = EL_SIGN_NO_ENTRY;
6744 element = EL_SIGN_HEART;
6748 element = EL_SIGN_GIVE_WAY;
6752 element = EL_SIGN_ENTRY_FORBIDDEN;
6756 element = EL_SIGN_EMERGENCY_EXIT;
6760 element = EL_SIGN_YIN_YANG;
6764 element = EL_WALL_EMERALD;
6768 element = EL_WALL_DIAMOND;
6772 element = EL_WALL_PEARL;
6776 element = EL_WALL_CRYSTAL;
6780 element = EL_INVISIBLE_WALL;
6784 element = EL_INVISIBLE_STEELWALL;
6788 // EL_INVISIBLE_SAND
6791 element = EL_LIGHT_SWITCH;
6795 element = EL_ENVELOPE_1;
6799 if (element >= 0x0117 && element <= 0x036e) // (?)
6800 element = EL_DIAMOND;
6801 else if (element >= 0x042d && element <= 0x0684) // (?)
6802 element = EL_EMERALD;
6803 else if (element >= 0x157c && element <= 0x158b)
6805 else if (element >= 0x1590 && element <= 0x159f)
6806 element = EL_DC_LANDMINE;
6807 else if (element >= 0x16bc && element <= 0x16cb)
6808 element = EL_INVISIBLE_SAND;
6811 Warn("unknown Diamond Caves element 0x%04x", element);
6813 element = EL_UNKNOWN;
6818 return getMappedElement(element);
6821 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6823 byte header[DC_LEVEL_HEADER_SIZE];
6825 int envelope_header_pos = 62;
6826 int envelope_content_pos = 94;
6827 int level_name_pos = 251;
6828 int level_author_pos = 292;
6829 int envelope_header_len;
6830 int envelope_content_len;
6832 int level_author_len;
6834 int num_yamyam_contents;
6837 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6839 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6841 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6843 header[i * 2 + 0] = header_word >> 8;
6844 header[i * 2 + 1] = header_word & 0xff;
6847 // read some values from level header to check level decoding integrity
6848 fieldx = header[6] | (header[7] << 8);
6849 fieldy = header[8] | (header[9] << 8);
6850 num_yamyam_contents = header[60] | (header[61] << 8);
6852 // do some simple sanity checks to ensure that level was correctly decoded
6853 if (fieldx < 1 || fieldx > 256 ||
6854 fieldy < 1 || fieldy > 256 ||
6855 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6857 level->no_valid_file = TRUE;
6859 Warn("cannot decode level from stream -- using empty level");
6864 // maximum envelope header size is 31 bytes
6865 envelope_header_len = header[envelope_header_pos];
6866 // maximum envelope content size is 110 (156?) bytes
6867 envelope_content_len = header[envelope_content_pos];
6869 // maximum level title size is 40 bytes
6870 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6871 // maximum level author size is 30 (51?) bytes
6872 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6876 for (i = 0; i < envelope_header_len; i++)
6877 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6878 level->envelope[0].text[envelope_size++] =
6879 header[envelope_header_pos + 1 + i];
6881 if (envelope_header_len > 0 && envelope_content_len > 0)
6883 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6884 level->envelope[0].text[envelope_size++] = '\n';
6885 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6886 level->envelope[0].text[envelope_size++] = '\n';
6889 for (i = 0; i < envelope_content_len; i++)
6890 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6891 level->envelope[0].text[envelope_size++] =
6892 header[envelope_content_pos + 1 + i];
6894 level->envelope[0].text[envelope_size] = '\0';
6896 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6897 level->envelope[0].ysize = 10;
6898 level->envelope[0].autowrap = TRUE;
6899 level->envelope[0].centered = TRUE;
6901 for (i = 0; i < level_name_len; i++)
6902 level->name[i] = header[level_name_pos + 1 + i];
6903 level->name[level_name_len] = '\0';
6905 for (i = 0; i < level_author_len; i++)
6906 level->author[i] = header[level_author_pos + 1 + i];
6907 level->author[level_author_len] = '\0';
6909 num_yamyam_contents = header[60] | (header[61] << 8);
6910 level->num_yamyam_contents =
6911 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6913 for (i = 0; i < num_yamyam_contents; i++)
6915 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6917 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6918 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6920 if (i < MAX_ELEMENT_CONTENTS)
6921 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6925 fieldx = header[6] | (header[7] << 8);
6926 fieldy = header[8] | (header[9] << 8);
6927 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6928 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6930 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6932 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6933 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6935 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6936 level->field[x][y] = getMappedElement_DC(element_dc);
6939 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6940 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6941 level->field[x][y] = EL_PLAYER_1;
6943 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6944 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6945 level->field[x][y] = EL_PLAYER_2;
6947 level->gems_needed = header[18] | (header[19] << 8);
6949 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6950 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6951 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6952 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6953 level->score[SC_NUT] = header[28] | (header[29] << 8);
6954 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6955 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6956 level->score[SC_BUG] = header[34] | (header[35] << 8);
6957 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6958 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6959 level->score[SC_KEY] = header[40] | (header[41] << 8);
6960 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6962 level->time = header[44] | (header[45] << 8);
6964 level->amoeba_speed = header[46] | (header[47] << 8);
6965 level->time_light = header[48] | (header[49] << 8);
6966 level->time_timegate = header[50] | (header[51] << 8);
6967 level->time_wheel = header[52] | (header[53] << 8);
6968 level->time_magic_wall = header[54] | (header[55] << 8);
6969 level->extra_time = header[56] | (header[57] << 8);
6970 level->shield_normal_time = header[58] | (header[59] << 8);
6972 // shield and extra time elements do not have a score
6973 level->score[SC_SHIELD] = 0;
6974 level->extra_time_score = 0;
6976 // set time for normal and deadly shields to the same value
6977 level->shield_deadly_time = level->shield_normal_time;
6979 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6980 // can slip down from flat walls, like normal walls and steel walls
6981 level->em_slippery_gems = TRUE;
6983 // time score is counted for each 10 seconds left in Diamond Caves levels
6984 level->time_score_base = 10;
6987 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6988 struct LevelFileInfo *level_file_info,
6989 boolean level_info_only)
6991 char *filename = level_file_info->filename;
6993 int num_magic_bytes = 8;
6994 char magic_bytes[num_magic_bytes + 1];
6995 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6997 if (!(file = openFile(filename, MODE_READ)))
6999 level->no_valid_file = TRUE;
7001 if (!level_info_only)
7002 Warn("cannot read level '%s' -- using empty level", filename);
7007 // fseek(file, 0x0000, SEEK_SET);
7009 if (level_file_info->packed)
7011 // read "magic bytes" from start of file
7012 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
7013 magic_bytes[0] = '\0';
7015 // check "magic bytes" for correct file format
7016 if (!strPrefix(magic_bytes, "DC2"))
7018 level->no_valid_file = TRUE;
7020 Warn("unknown DC level file '%s' -- using empty level", filename);
7025 if (strPrefix(magic_bytes, "DC2Win95") ||
7026 strPrefix(magic_bytes, "DC2Win98"))
7028 int position_first_level = 0x00fa;
7029 int extra_bytes = 4;
7032 // advance file stream to first level inside the level package
7033 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7035 // each block of level data is followed by block of non-level data
7036 num_levels_to_skip *= 2;
7038 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
7039 while (num_levels_to_skip >= 0)
7041 // advance file stream to next level inside the level package
7042 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7044 level->no_valid_file = TRUE;
7046 Warn("cannot fseek in file '%s' -- using empty level", filename);
7051 // skip apparently unused extra bytes following each level
7052 ReadUnusedBytesFromFile(file, extra_bytes);
7054 // read size of next level in level package
7055 skip_bytes = getFile32BitLE(file);
7057 num_levels_to_skip--;
7062 level->no_valid_file = TRUE;
7064 Warn("unknown DC2 level file '%s' -- using empty level", filename);
7070 LoadLevelFromFileStream_DC(file, level);
7076 // ----------------------------------------------------------------------------
7077 // functions for loading SB level
7078 // ----------------------------------------------------------------------------
7080 int getMappedElement_SB(int element_ascii, boolean use_ces)
7088 sb_element_mapping[] =
7090 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
7091 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
7092 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
7093 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
7094 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
7095 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
7096 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
7097 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
7104 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7105 if (element_ascii == sb_element_mapping[i].ascii)
7106 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7108 return EL_UNDEFINED;
7111 static void SetLevelSettings_SB(struct LevelInfo *level)
7115 level->use_step_counter = TRUE;
7118 level->score[SC_TIME_BONUS] = 0;
7119 level->time_score_base = 1;
7120 level->rate_time_over_score = TRUE;
7123 level->auto_exit_sokoban = TRUE;
7126 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7127 struct LevelFileInfo *level_file_info,
7128 boolean level_info_only)
7130 char *filename = level_file_info->filename;
7131 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7132 char last_comment[MAX_LINE_LEN];
7133 char level_name[MAX_LINE_LEN];
7136 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7137 boolean read_continued_line = FALSE;
7138 boolean reading_playfield = FALSE;
7139 boolean got_valid_playfield_line = FALSE;
7140 boolean invalid_playfield_char = FALSE;
7141 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7142 int file_level_nr = 0;
7143 int x = 0, y = 0; // initialized to make compilers happy
7145 last_comment[0] = '\0';
7146 level_name[0] = '\0';
7148 if (!(file = openFile(filename, MODE_READ)))
7150 level->no_valid_file = TRUE;
7152 if (!level_info_only)
7153 Warn("cannot read level '%s' -- using empty level", filename);
7158 while (!checkEndOfFile(file))
7160 // level successfully read, but next level may follow here
7161 if (!got_valid_playfield_line && reading_playfield)
7163 // read playfield from single level file -- skip remaining file
7164 if (!level_file_info->packed)
7167 if (file_level_nr >= num_levels_to_skip)
7172 last_comment[0] = '\0';
7173 level_name[0] = '\0';
7175 reading_playfield = FALSE;
7178 got_valid_playfield_line = FALSE;
7180 // read next line of input file
7181 if (!getStringFromFile(file, line, MAX_LINE_LEN))
7184 // cut trailing line break (this can be newline and/or carriage return)
7185 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7186 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7189 // copy raw input line for later use (mainly debugging output)
7190 strcpy(line_raw, line);
7192 if (read_continued_line)
7194 // append new line to existing line, if there is enough space
7195 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7196 strcat(previous_line, line_ptr);
7198 strcpy(line, previous_line); // copy storage buffer to line
7200 read_continued_line = FALSE;
7203 // if the last character is '\', continue at next line
7204 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7206 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
7207 strcpy(previous_line, line); // copy line to storage buffer
7209 read_continued_line = TRUE;
7215 if (line[0] == '\0')
7218 // extract comment text from comment line
7221 for (line_ptr = line; *line_ptr; line_ptr++)
7222 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7225 strcpy(last_comment, line_ptr);
7230 // extract level title text from line containing level title
7231 if (line[0] == '\'')
7233 strcpy(level_name, &line[1]);
7235 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7236 level_name[strlen(level_name) - 1] = '\0';
7241 // skip lines containing only spaces (or empty lines)
7242 for (line_ptr = line; *line_ptr; line_ptr++)
7243 if (*line_ptr != ' ')
7245 if (*line_ptr == '\0')
7248 // at this point, we have found a line containing part of a playfield
7250 got_valid_playfield_line = TRUE;
7252 if (!reading_playfield)
7254 reading_playfield = TRUE;
7255 invalid_playfield_char = FALSE;
7257 for (x = 0; x < MAX_LEV_FIELDX; x++)
7258 for (y = 0; y < MAX_LEV_FIELDY; y++)
7259 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7264 // start with topmost tile row
7268 // skip playfield line if larger row than allowed
7269 if (y >= MAX_LEV_FIELDY)
7272 // start with leftmost tile column
7275 // read playfield elements from line
7276 for (line_ptr = line; *line_ptr; line_ptr++)
7278 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7280 // stop parsing playfield line if larger column than allowed
7281 if (x >= MAX_LEV_FIELDX)
7284 if (mapped_sb_element == EL_UNDEFINED)
7286 invalid_playfield_char = TRUE;
7291 level->field[x][y] = mapped_sb_element;
7293 // continue with next tile column
7296 level->fieldx = MAX(x, level->fieldx);
7299 if (invalid_playfield_char)
7301 // if first playfield line, treat invalid lines as comment lines
7303 reading_playfield = FALSE;
7308 // continue with next tile row
7316 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7317 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7319 if (!reading_playfield)
7321 level->no_valid_file = TRUE;
7323 Warn("cannot read level '%s' -- using empty level", filename);
7328 if (*level_name != '\0')
7330 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7331 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7333 else if (*last_comment != '\0')
7335 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7336 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7340 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7343 // set all empty fields beyond the border walls to invisible steel wall
7344 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7346 if ((x == 0 || x == level->fieldx - 1 ||
7347 y == 0 || y == level->fieldy - 1) &&
7348 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7349 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7350 level->field, level->fieldx, level->fieldy);
7353 // set special level settings for Sokoban levels
7354 SetLevelSettings_SB(level);
7356 if (load_xsb_to_ces)
7358 // special global settings can now be set in level template
7359 level->use_custom_template = TRUE;
7364 // -------------------------------------------------------------------------
7365 // functions for handling native levels
7366 // -------------------------------------------------------------------------
7368 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7369 struct LevelFileInfo *level_file_info,
7370 boolean level_info_only)
7374 // determine position of requested level inside level package
7375 if (level_file_info->packed)
7376 pos = level_file_info->nr - leveldir_current->first_level;
7378 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7379 level->no_valid_file = TRUE;
7382 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7383 struct LevelFileInfo *level_file_info,
7384 boolean level_info_only)
7386 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7387 level->no_valid_file = TRUE;
7390 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7391 struct LevelFileInfo *level_file_info,
7392 boolean level_info_only)
7396 // determine position of requested level inside level package
7397 if (level_file_info->packed)
7398 pos = level_file_info->nr - leveldir_current->first_level;
7400 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7401 level->no_valid_file = TRUE;
7404 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7405 struct LevelFileInfo *level_file_info,
7406 boolean level_info_only)
7408 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7409 level->no_valid_file = TRUE;
7412 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7414 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7415 CopyNativeLevel_RND_to_BD(level);
7416 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7417 CopyNativeLevel_RND_to_EM(level);
7418 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7419 CopyNativeLevel_RND_to_SP(level);
7420 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7421 CopyNativeLevel_RND_to_MM(level);
7424 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7426 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7427 CopyNativeLevel_BD_to_RND(level);
7428 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7429 CopyNativeLevel_EM_to_RND(level);
7430 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7431 CopyNativeLevel_SP_to_RND(level);
7432 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7433 CopyNativeLevel_MM_to_RND(level);
7436 void SaveNativeLevel(struct LevelInfo *level)
7438 // saving native level files only supported for some game engines
7439 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7440 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7443 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7444 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7445 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7446 char *filename = getLevelFilenameFromBasename(basename);
7448 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7451 boolean success = FALSE;
7453 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7455 CopyNativeLevel_RND_to_BD(level);
7456 // CopyNativeTape_RND_to_BD(level);
7458 success = SaveNativeLevel_BD(filename);
7460 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7462 CopyNativeLevel_RND_to_SP(level);
7463 CopyNativeTape_RND_to_SP(level);
7465 success = SaveNativeLevel_SP(filename);
7469 Request("Native level file saved!", REQ_CONFIRM);
7471 Request("Failed to save native level file!", REQ_CONFIRM);
7475 // ----------------------------------------------------------------------------
7476 // functions for loading generic level
7477 // ----------------------------------------------------------------------------
7479 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7480 struct LevelFileInfo *level_file_info,
7481 boolean level_info_only)
7483 // always start with reliable default values
7484 setLevelInfoToDefaults(level, level_info_only, TRUE);
7486 switch (level_file_info->type)
7488 case LEVEL_FILE_TYPE_RND:
7489 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7492 case LEVEL_FILE_TYPE_BD:
7493 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7494 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7497 case LEVEL_FILE_TYPE_EM:
7498 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7499 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7502 case LEVEL_FILE_TYPE_SP:
7503 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7504 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7507 case LEVEL_FILE_TYPE_MM:
7508 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7509 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7512 case LEVEL_FILE_TYPE_DC:
7513 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7516 case LEVEL_FILE_TYPE_SB:
7517 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7521 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7525 // if level file is invalid, restore level structure to default values
7526 if (level->no_valid_file)
7527 setLevelInfoToDefaults(level, level_info_only, FALSE);
7529 if (check_special_flags("use_native_bd_game_engine"))
7530 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7532 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7533 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7535 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7536 CopyNativeLevel_Native_to_RND(level);
7539 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7541 static struct LevelFileInfo level_file_info;
7543 // always start with reliable default values
7544 setFileInfoToDefaults(&level_file_info);
7546 level_file_info.nr = 0; // unknown level number
7547 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7549 setString(&level_file_info.filename, filename);
7551 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7554 static void LoadLevel_FixEnvelopes(struct LevelInfo *level, boolean skip_single_lines)
7556 // This function removes newlines in envelopes after lines of text ending in the last column
7557 // of the envelope. In earlier versions, these newlines were removed when displaying envelopes,
7558 // but caused trouble in the level editor. In version 4.3.2.3, this problem was partially
7559 // fixed in the level editor (but only for single full-width text lines followed by a newline,
7560 // not for multiple lines ending in the last column, followed by a newline), but now produced
7561 // unwanted newlines in the game for envelopes stored by previous game versions, which was not
7562 // intended by the level author (and sometimes caused text lines not being displayed anymore at
7563 // the bottom of the envelope).
7565 // This function should solve these problems by removing such newline characters from envelopes
7566 // stored by older game versions.
7570 for (envelope_nr = 0; envelope_nr < NUM_ENVELOPES; envelope_nr++)
7572 char *envelope_ptr = level->envelope[envelope_nr].text;
7573 int envelope_xsize = level->envelope[envelope_nr].xsize;
7574 int envelope_size = strlen(envelope_ptr);
7578 for (i = 0; i < envelope_size; i++)
7580 // check for newlines in envelope
7581 if (envelope_ptr[i] == '\n')
7583 int line_length = i - start;
7585 // check for (non-empty) lines that are a multiple of the envelope width,
7586 // causing a line break inside the envelope (text area in editor and in game)
7587 if (line_length > 0 && line_length % envelope_xsize == 0)
7589 // special case: skip fixing single lines for newer versions
7590 boolean skip_fixing_line = (line_length == 1 && skip_single_lines);
7592 if (!skip_fixing_line)
7596 // remove newline character from string
7597 for (j = i; j < envelope_size; j++)
7598 envelope_ptr[j] = envelope_ptr[j + 1];
7601 // continue with next line (that was copied over the newline)
7606 // continue with next character after newline
7614 static void LoadLevel_InitVersion(struct LevelInfo *level)
7618 if (leveldir_current == NULL) // only when dumping level
7621 // all engine modifications also valid for levels which use latest engine
7622 if (level->game_version < VERSION_IDENT(3,2,0,5))
7624 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7625 level->time_score_base = 10;
7628 if (leveldir_current->latest_engine)
7630 // ---------- use latest game engine --------------------------------------
7632 /* For all levels which are forced to use the latest game engine version
7633 (normally all but user contributed, private and undefined levels), set
7634 the game engine version to the actual version; this allows for actual
7635 corrections in the game engine to take effect for existing, converted
7636 levels (from "classic" or other existing games) to make the emulation
7637 of the corresponding game more accurate, while (hopefully) not breaking
7638 existing levels created from other players. */
7640 level->game_version = GAME_VERSION_ACTUAL;
7642 /* Set special EM style gems behaviour: EM style gems slip down from
7643 normal, steel and growing wall. As this is a more fundamental change,
7644 it seems better to set the default behaviour to "off" (as it is more
7645 natural) and make it configurable in the level editor (as a property
7646 of gem style elements). Already existing converted levels (neither
7647 private nor contributed levels) are changed to the new behaviour. */
7649 if (level->file_version < FILE_VERSION_2_0)
7650 level->em_slippery_gems = TRUE;
7655 // ---------- use game engine the level was created with --------------------
7657 /* For all levels which are not forced to use the latest game engine
7658 version (normally user contributed, private and undefined levels),
7659 use the version of the game engine the levels were created for.
7661 Since 2.0.1, the game engine version is now directly stored
7662 in the level file (chunk "VERS"), so there is no need anymore
7663 to set the game version from the file version (except for old,
7664 pre-2.0 levels, where the game version is still taken from the
7665 file format version used to store the level -- see above). */
7667 // player was faster than enemies in 1.0.0 and before
7668 if (level->file_version == FILE_VERSION_1_0)
7669 for (i = 0; i < MAX_PLAYERS; i++)
7670 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7672 // default behaviour for EM style gems was "slippery" only in 2.0.1
7673 if (level->game_version == VERSION_IDENT(2,0,1,0))
7674 level->em_slippery_gems = TRUE;
7676 // springs could be pushed over pits before (pre-release version) 2.2.0
7677 if (level->game_version < VERSION_IDENT(2,2,0,0))
7678 level->use_spring_bug = TRUE;
7680 if (level->game_version < VERSION_IDENT(3,2,0,5))
7682 // time orb caused limited time in endless time levels before 3.2.0-5
7683 level->use_time_orb_bug = TRUE;
7685 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7686 level->block_snap_field = FALSE;
7688 // extra time score was same value as time left score before 3.2.0-5
7689 level->extra_time_score = level->score[SC_TIME_BONUS];
7692 if (level->game_version < VERSION_IDENT(3,2,0,7))
7694 // default behaviour for snapping was "not continuous" before 3.2.0-7
7695 level->continuous_snapping = FALSE;
7698 // only few elements were able to actively move into acid before 3.1.0
7699 // trigger settings did not exist before 3.1.0; set to default "any"
7700 if (level->game_version < VERSION_IDENT(3,1,0,0))
7702 // correct "can move into acid" settings (all zero in old levels)
7704 level->can_move_into_acid_bits = 0; // nothing can move into acid
7705 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7707 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7708 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7709 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7710 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7712 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7713 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7715 // correct trigger settings (stored as zero == "none" in old levels)
7717 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7719 int element = EL_CUSTOM_START + i;
7720 struct ElementInfo *ei = &element_info[element];
7722 for (j = 0; j < ei->num_change_pages; j++)
7724 struct ElementChangeInfo *change = &ei->change_page[j];
7726 change->trigger_player = CH_PLAYER_ANY;
7727 change->trigger_page = CH_PAGE_ANY;
7732 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7734 int element = EL_CUSTOM_256;
7735 struct ElementInfo *ei = &element_info[element];
7736 struct ElementChangeInfo *change = &ei->change_page[0];
7738 /* This is needed to fix a problem that was caused by a bugfix in function
7739 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7740 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7741 not replace walkable elements, but instead just placed the player on it,
7742 without placing the Sokoban field under the player). Unfortunately, this
7743 breaks "Snake Bite" style levels when the snake is halfway through a door
7744 that just closes (the snake head is still alive and can be moved in this
7745 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7746 player (without Sokoban element) which then gets killed as designed). */
7748 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7749 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7750 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7751 change->target_element = EL_PLAYER_1;
7754 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7755 if (level->game_version < VERSION_IDENT(3,2,5,0))
7757 /* This is needed to fix a problem that was caused by a bugfix in function
7758 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7759 corrects the behaviour when a custom element changes to another custom
7760 element with a higher element number that has change actions defined.
7761 Normally, only one change per frame is allowed for custom elements.
7762 Therefore, it is checked if a custom element already changed in the
7763 current frame; if it did, subsequent changes are suppressed.
7764 Unfortunately, this is only checked for element changes, but not for
7765 change actions, which are still executed. As the function above loops
7766 through all custom elements from lower to higher, an element change
7767 resulting in a lower CE number won't be checked again, while a target
7768 element with a higher number will also be checked, and potential change
7769 actions will get executed for this CE, too (which is wrong), while
7770 further changes are ignored (which is correct). As this bugfix breaks
7771 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7772 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7773 behaviour for existing levels and tapes that make use of this bug */
7775 level->use_action_after_change_bug = TRUE;
7778 // not centering level after relocating player was default only in 3.2.3
7779 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7780 level->shifted_relocation = TRUE;
7782 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7783 if (level->game_version < VERSION_IDENT(3,2,6,0))
7784 level->em_explodes_by_fire = TRUE;
7786 // levels were solved by the first player entering an exit up to 4.1.0.0
7787 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7788 level->solved_by_one_player = TRUE;
7790 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7791 if (level->game_version < VERSION_IDENT(4,1,1,1))
7792 level->use_life_bugs = TRUE;
7794 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7795 if (level->game_version < VERSION_IDENT(4,1,1,1))
7796 level->sb_objects_needed = FALSE;
7798 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7799 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7800 level->finish_dig_collect = FALSE;
7802 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7803 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7804 level->keep_walkable_ce = TRUE;
7806 // envelopes may contain broken or too many line breaks before 4.4.0.0
7807 if (level->game_version < VERSION_IDENT(4,4,0,0))
7808 LoadLevel_FixEnvelopes(level, (level->game_version >= VERSION_IDENT(4,3,2,3)));
7811 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7813 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7816 // check if this level is (not) a Sokoban level
7817 for (y = 0; y < level->fieldy; y++)
7818 for (x = 0; x < level->fieldx; x++)
7819 if (!IS_SB_ELEMENT(Tile[x][y]))
7820 is_sokoban_level = FALSE;
7822 if (is_sokoban_level)
7824 // set special level settings for Sokoban levels
7825 SetLevelSettings_SB(level);
7829 static void LoadLevel_InitSettings(struct LevelInfo *level)
7831 // adjust level settings for (non-native) Sokoban-style levels
7832 LoadLevel_InitSettings_SB(level);
7834 // rename levels with title "nameless level" or if renaming is forced
7835 if (leveldir_current->empty_level_name != NULL &&
7836 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7837 leveldir_current->force_level_name))
7838 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7839 leveldir_current->empty_level_name, level_nr);
7842 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7846 // map elements that have changed in newer versions
7847 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7848 level->game_version);
7849 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7850 for (x = 0; x < 3; x++)
7851 for (y = 0; y < 3; y++)
7852 level->yamyam_content[i].e[x][y] =
7853 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7854 level->game_version);
7858 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7862 // map custom element change events that have changed in newer versions
7863 // (these following values were accidentally changed in version 3.0.1)
7864 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7865 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7867 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7869 int element = EL_CUSTOM_START + i;
7871 // order of checking and copying events to be mapped is important
7872 // (do not change the start and end value -- they are constant)
7873 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7875 if (HAS_CHANGE_EVENT(element, j - 2))
7877 SET_CHANGE_EVENT(element, j - 2, FALSE);
7878 SET_CHANGE_EVENT(element, j, TRUE);
7882 // order of checking and copying events to be mapped is important
7883 // (do not change the start and end value -- they are constant)
7884 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7886 if (HAS_CHANGE_EVENT(element, j - 1))
7888 SET_CHANGE_EVENT(element, j - 1, FALSE);
7889 SET_CHANGE_EVENT(element, j, TRUE);
7895 // initialize "can_change" field for old levels with only one change page
7896 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7898 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7900 int element = EL_CUSTOM_START + i;
7902 if (CAN_CHANGE(element))
7903 element_info[element].change->can_change = TRUE;
7907 // correct custom element values (for old levels without these options)
7908 if (level->game_version < VERSION_IDENT(3,1,1,0))
7910 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7912 int element = EL_CUSTOM_START + i;
7913 struct ElementInfo *ei = &element_info[element];
7915 if (ei->access_direction == MV_NO_DIRECTION)
7916 ei->access_direction = MV_ALL_DIRECTIONS;
7920 // correct custom element values (fix invalid values for all versions)
7923 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7925 int element = EL_CUSTOM_START + i;
7926 struct ElementInfo *ei = &element_info[element];
7928 for (j = 0; j < ei->num_change_pages; j++)
7930 struct ElementChangeInfo *change = &ei->change_page[j];
7932 if (change->trigger_player == CH_PLAYER_NONE)
7933 change->trigger_player = CH_PLAYER_ANY;
7935 if (change->trigger_side == CH_SIDE_NONE)
7936 change->trigger_side = CH_SIDE_ANY;
7941 // initialize "can_explode" field for old levels which did not store this
7942 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7943 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7945 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7947 int element = EL_CUSTOM_START + i;
7949 if (EXPLODES_1X1_OLD(element))
7950 element_info[element].explosion_type = EXPLODES_1X1;
7952 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7953 EXPLODES_SMASHED(element) ||
7954 EXPLODES_IMPACT(element)));
7958 // correct previously hard-coded move delay values for maze runner style
7959 if (level->game_version < VERSION_IDENT(3,1,1,0))
7961 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7963 int element = EL_CUSTOM_START + i;
7965 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7967 // previously hard-coded and therefore ignored
7968 element_info[element].move_delay_fixed = 9;
7969 element_info[element].move_delay_random = 0;
7974 // set some other uninitialized values of custom elements in older levels
7975 if (level->game_version < VERSION_IDENT(3,1,0,0))
7977 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7979 int element = EL_CUSTOM_START + i;
7981 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7983 element_info[element].explosion_delay = 17;
7984 element_info[element].ignition_delay = 8;
7988 // set mouse click change events to work for left/middle/right mouse button
7989 if (level->game_version < VERSION_IDENT(4,2,3,0))
7991 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7993 int element = EL_CUSTOM_START + i;
7994 struct ElementInfo *ei = &element_info[element];
7996 for (j = 0; j < ei->num_change_pages; j++)
7998 struct ElementChangeInfo *change = &ei->change_page[j];
8000 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
8001 change->has_event[CE_PRESSED_BY_MOUSE] ||
8002 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
8003 change->has_event[CE_MOUSE_PRESSED_ON_X])
8004 change->trigger_side = CH_SIDE_ANY;
8010 static void LoadLevel_InitElements(struct LevelInfo *level)
8012 LoadLevel_InitStandardElements(level);
8014 if (level->file_has_custom_elements)
8015 LoadLevel_InitCustomElements(level);
8017 // initialize element properties for level editor etc.
8018 InitElementPropertiesEngine(level->game_version);
8019 InitElementPropertiesGfxElement();
8022 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
8026 // map elements that have changed in newer versions
8027 for (y = 0; y < level->fieldy; y++)
8028 for (x = 0; x < level->fieldx; x++)
8029 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
8030 level->game_version);
8032 // clear unused playfield data (nicer if level gets resized in editor)
8033 for (x = 0; x < MAX_LEV_FIELDX; x++)
8034 for (y = 0; y < MAX_LEV_FIELDY; y++)
8035 if (x >= level->fieldx || y >= level->fieldy)
8036 level->field[x][y] = EL_EMPTY;
8038 // copy elements to runtime playfield array
8039 for (x = 0; x < MAX_LEV_FIELDX; x++)
8040 for (y = 0; y < MAX_LEV_FIELDY; y++)
8041 Tile[x][y] = level->field[x][y];
8043 // initialize level size variables for faster access
8044 lev_fieldx = level->fieldx;
8045 lev_fieldy = level->fieldy;
8047 // determine border element for this level
8048 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
8049 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
8054 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
8056 struct LevelFileInfo *level_file_info = &level->file_info;
8058 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
8059 CopyNativeLevel_RND_to_Native(level);
8062 static void LoadLevelTemplate_LoadAndInit(void)
8064 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
8066 LoadLevel_InitVersion(&level_template);
8067 LoadLevel_InitElements(&level_template);
8068 LoadLevel_InitSettings(&level_template);
8070 ActivateLevelTemplate();
8073 void LoadLevelTemplate(int nr)
8075 if (!fileExists(getGlobalLevelTemplateFilename()))
8077 Warn("no level template found for this level");
8082 setLevelFileInfo(&level_template.file_info, nr);
8084 LoadLevelTemplate_LoadAndInit();
8087 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
8089 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
8091 LoadLevelTemplate_LoadAndInit();
8094 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
8096 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
8098 if (level.use_custom_template)
8100 if (network_level != NULL)
8101 LoadNetworkLevelTemplate(network_level);
8103 LoadLevelTemplate(-1);
8106 LoadLevel_InitVersion(&level);
8107 LoadLevel_InitElements(&level);
8108 LoadLevel_InitPlayfield(&level);
8109 LoadLevel_InitSettings(&level);
8111 LoadLevel_InitNativeEngines(&level);
8114 void LoadLevel(int nr)
8116 SetLevelSetInfo(leveldir_current->identifier, nr);
8118 setLevelFileInfo(&level.file_info, nr);
8120 LoadLevel_LoadAndInit(NULL);
8123 void LoadLevelInfoOnly(int nr)
8125 setLevelFileInfo(&level.file_info, nr);
8127 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8130 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8132 SetLevelSetInfo(network_level->leveldir_identifier,
8133 network_level->file_info.nr);
8135 copyLevelFileInfo(&network_level->file_info, &level.file_info);
8137 LoadLevel_LoadAndInit(network_level);
8140 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8144 chunk_size += putFileVersion(file, level->file_version);
8145 chunk_size += putFileVersion(file, level->game_version);
8150 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8154 chunk_size += putFile16BitBE(file, level->creation_date.year);
8155 chunk_size += putFile8Bit(file, level->creation_date.month);
8156 chunk_size += putFile8Bit(file, level->creation_date.day);
8161 #if ENABLE_HISTORIC_CHUNKS
8162 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8166 putFile8Bit(file, level->fieldx);
8167 putFile8Bit(file, level->fieldy);
8169 putFile16BitBE(file, level->time);
8170 putFile16BitBE(file, level->gems_needed);
8172 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8173 putFile8Bit(file, level->name[i]);
8175 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8176 putFile8Bit(file, level->score[i]);
8178 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8179 for (y = 0; y < 3; y++)
8180 for (x = 0; x < 3; x++)
8181 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8182 level->yamyam_content[i].e[x][y]));
8183 putFile8Bit(file, level->amoeba_speed);
8184 putFile8Bit(file, level->time_magic_wall);
8185 putFile8Bit(file, level->time_wheel);
8186 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8187 level->amoeba_content));
8188 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8189 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8190 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8191 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8193 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8195 putFile8Bit(file, (level->block_last_field ? 1 : 0));
8196 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8197 putFile32BitBE(file, level->can_move_into_acid_bits);
8198 putFile8Bit(file, level->dont_collide_with_bits);
8200 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8201 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8203 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8204 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8205 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8207 putFile8Bit(file, level->game_engine_type);
8209 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8213 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8218 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8219 chunk_size += putFile8Bit(file, level->name[i]);
8224 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8229 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8230 chunk_size += putFile8Bit(file, level->author[i]);
8235 #if ENABLE_HISTORIC_CHUNKS
8236 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8241 for (y = 0; y < level->fieldy; y++)
8242 for (x = 0; x < level->fieldx; x++)
8243 if (level->encoding_16bit_field)
8244 chunk_size += putFile16BitBE(file, level->field[x][y]);
8246 chunk_size += putFile8Bit(file, level->field[x][y]);
8252 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8257 for (y = 0; y < level->fieldy; y++)
8258 for (x = 0; x < level->fieldx; x++)
8259 chunk_size += putFile16BitBE(file, level->field[x][y]);
8264 #if ENABLE_HISTORIC_CHUNKS
8265 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8269 putFile8Bit(file, EL_YAMYAM);
8270 putFile8Bit(file, level->num_yamyam_contents);
8271 putFile8Bit(file, 0);
8272 putFile8Bit(file, 0);
8274 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8275 for (y = 0; y < 3; y++)
8276 for (x = 0; x < 3; x++)
8277 if (level->encoding_16bit_field)
8278 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8280 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8284 #if ENABLE_HISTORIC_CHUNKS
8285 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8288 int num_contents, content_xsize, content_ysize;
8289 int content_array[MAX_ELEMENT_CONTENTS][3][3];
8291 if (element == EL_YAMYAM)
8293 num_contents = level->num_yamyam_contents;
8297 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8298 for (y = 0; y < 3; y++)
8299 for (x = 0; x < 3; x++)
8300 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8302 else if (element == EL_BD_AMOEBA)
8308 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8309 for (y = 0; y < 3; y++)
8310 for (x = 0; x < 3; x++)
8311 content_array[i][x][y] = EL_EMPTY;
8312 content_array[0][0][0] = level->amoeba_content;
8316 // chunk header already written -- write empty chunk data
8317 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8319 Warn("cannot save content for element '%d'", element);
8324 putFile16BitBE(file, element);
8325 putFile8Bit(file, num_contents);
8326 putFile8Bit(file, content_xsize);
8327 putFile8Bit(file, content_ysize);
8329 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8331 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8332 for (y = 0; y < 3; y++)
8333 for (x = 0; x < 3; x++)
8334 putFile16BitBE(file, content_array[i][x][y]);
8338 #if ENABLE_HISTORIC_CHUNKS
8339 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8341 int envelope_nr = element - EL_ENVELOPE_1;
8342 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8346 chunk_size += putFile16BitBE(file, element);
8347 chunk_size += putFile16BitBE(file, envelope_len);
8348 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8349 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8351 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8352 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8354 for (i = 0; i < envelope_len; i++)
8355 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8361 #if ENABLE_HISTORIC_CHUNKS
8362 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8363 int num_changed_custom_elements)
8367 putFile16BitBE(file, num_changed_custom_elements);
8369 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8371 int element = EL_CUSTOM_START + i;
8373 struct ElementInfo *ei = &element_info[element];
8375 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8377 if (check < num_changed_custom_elements)
8379 putFile16BitBE(file, element);
8380 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8387 if (check != num_changed_custom_elements) // should not happen
8388 Warn("inconsistent number of custom element properties");
8392 #if ENABLE_HISTORIC_CHUNKS
8393 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8394 int num_changed_custom_elements)
8398 putFile16BitBE(file, num_changed_custom_elements);
8400 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8402 int element = EL_CUSTOM_START + i;
8404 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8406 if (check < num_changed_custom_elements)
8408 putFile16BitBE(file, element);
8409 putFile16BitBE(file, element_info[element].change->target_element);
8416 if (check != num_changed_custom_elements) // should not happen
8417 Warn("inconsistent number of custom target elements");
8421 #if ENABLE_HISTORIC_CHUNKS
8422 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8423 int num_changed_custom_elements)
8425 int i, j, x, y, check = 0;
8427 putFile16BitBE(file, num_changed_custom_elements);
8429 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8431 int element = EL_CUSTOM_START + i;
8432 struct ElementInfo *ei = &element_info[element];
8434 if (ei->modified_settings)
8436 if (check < num_changed_custom_elements)
8438 putFile16BitBE(file, element);
8440 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8441 putFile8Bit(file, ei->description[j]);
8443 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8445 // some free bytes for future properties and padding
8446 WriteUnusedBytesToFile(file, 7);
8448 putFile8Bit(file, ei->use_gfx_element);
8449 putFile16BitBE(file, ei->gfx_element_initial);
8451 putFile8Bit(file, ei->collect_score_initial);
8452 putFile8Bit(file, ei->collect_count_initial);
8454 putFile16BitBE(file, ei->push_delay_fixed);
8455 putFile16BitBE(file, ei->push_delay_random);
8456 putFile16BitBE(file, ei->move_delay_fixed);
8457 putFile16BitBE(file, ei->move_delay_random);
8459 putFile16BitBE(file, ei->move_pattern);
8460 putFile8Bit(file, ei->move_direction_initial);
8461 putFile8Bit(file, ei->move_stepsize);
8463 for (y = 0; y < 3; y++)
8464 for (x = 0; x < 3; x++)
8465 putFile16BitBE(file, ei->content.e[x][y]);
8467 putFile32BitBE(file, ei->change->events);
8469 putFile16BitBE(file, ei->change->target_element);
8471 putFile16BitBE(file, ei->change->delay_fixed);
8472 putFile16BitBE(file, ei->change->delay_random);
8473 putFile16BitBE(file, ei->change->delay_frames);
8475 putFile16BitBE(file, ei->change->initial_trigger_element);
8477 putFile8Bit(file, ei->change->explode);
8478 putFile8Bit(file, ei->change->use_target_content);
8479 putFile8Bit(file, ei->change->only_if_complete);
8480 putFile8Bit(file, ei->change->use_random_replace);
8482 putFile8Bit(file, ei->change->random_percentage);
8483 putFile8Bit(file, ei->change->replace_when);
8485 for (y = 0; y < 3; y++)
8486 for (x = 0; x < 3; x++)
8487 putFile16BitBE(file, ei->change->content.e[x][y]);
8489 putFile8Bit(file, ei->slippery_type);
8491 // some free bytes for future properties and padding
8492 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8499 if (check != num_changed_custom_elements) // should not happen
8500 Warn("inconsistent number of custom element properties");
8504 #if ENABLE_HISTORIC_CHUNKS
8505 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8507 struct ElementInfo *ei = &element_info[element];
8510 // ---------- custom element base property values (96 bytes) ----------------
8512 putFile16BitBE(file, element);
8514 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8515 putFile8Bit(file, ei->description[i]);
8517 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8519 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8521 putFile8Bit(file, ei->num_change_pages);
8523 putFile16BitBE(file, ei->ce_value_fixed_initial);
8524 putFile16BitBE(file, ei->ce_value_random_initial);
8525 putFile8Bit(file, ei->use_last_ce_value);
8527 putFile8Bit(file, ei->use_gfx_element);
8528 putFile16BitBE(file, ei->gfx_element_initial);
8530 putFile8Bit(file, ei->collect_score_initial);
8531 putFile8Bit(file, ei->collect_count_initial);
8533 putFile8Bit(file, ei->drop_delay_fixed);
8534 putFile8Bit(file, ei->push_delay_fixed);
8535 putFile8Bit(file, ei->drop_delay_random);
8536 putFile8Bit(file, ei->push_delay_random);
8537 putFile16BitBE(file, ei->move_delay_fixed);
8538 putFile16BitBE(file, ei->move_delay_random);
8540 // bits 0 - 15 of "move_pattern" ...
8541 putFile16BitBE(file, ei->move_pattern & 0xffff);
8542 putFile8Bit(file, ei->move_direction_initial);
8543 putFile8Bit(file, ei->move_stepsize);
8545 putFile8Bit(file, ei->slippery_type);
8547 for (y = 0; y < 3; y++)
8548 for (x = 0; x < 3; x++)
8549 putFile16BitBE(file, ei->content.e[x][y]);
8551 putFile16BitBE(file, ei->move_enter_element);
8552 putFile16BitBE(file, ei->move_leave_element);
8553 putFile8Bit(file, ei->move_leave_type);
8555 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8556 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8558 putFile8Bit(file, ei->access_direction);
8560 putFile8Bit(file, ei->explosion_delay);
8561 putFile8Bit(file, ei->ignition_delay);
8562 putFile8Bit(file, ei->explosion_type);
8564 // some free bytes for future custom property values and padding
8565 WriteUnusedBytesToFile(file, 1);
8567 // ---------- change page property values (48 bytes) ------------------------
8569 for (i = 0; i < ei->num_change_pages; i++)
8571 struct ElementChangeInfo *change = &ei->change_page[i];
8572 unsigned int event_bits;
8574 // bits 0 - 31 of "has_event[]" ...
8576 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8577 if (change->has_event[j])
8578 event_bits |= (1u << j);
8579 putFile32BitBE(file, event_bits);
8581 putFile16BitBE(file, change->target_element);
8583 putFile16BitBE(file, change->delay_fixed);
8584 putFile16BitBE(file, change->delay_random);
8585 putFile16BitBE(file, change->delay_frames);
8587 putFile16BitBE(file, change->initial_trigger_element);
8589 putFile8Bit(file, change->explode);
8590 putFile8Bit(file, change->use_target_content);
8591 putFile8Bit(file, change->only_if_complete);
8592 putFile8Bit(file, change->use_random_replace);
8594 putFile8Bit(file, change->random_percentage);
8595 putFile8Bit(file, change->replace_when);
8597 for (y = 0; y < 3; y++)
8598 for (x = 0; x < 3; x++)
8599 putFile16BitBE(file, change->target_content.e[x][y]);
8601 putFile8Bit(file, change->can_change);
8603 putFile8Bit(file, change->trigger_side);
8605 putFile8Bit(file, change->trigger_player);
8606 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8607 log_2(change->trigger_page)));
8609 putFile8Bit(file, change->has_action);
8610 putFile8Bit(file, change->action_type);
8611 putFile8Bit(file, change->action_mode);
8612 putFile16BitBE(file, change->action_arg);
8614 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8616 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8617 if (change->has_event[j])
8618 event_bits |= (1u << (j - 32));
8619 putFile8Bit(file, event_bits);
8624 #if ENABLE_HISTORIC_CHUNKS
8625 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8627 struct ElementInfo *ei = &element_info[element];
8628 struct ElementGroupInfo *group = ei->group;
8631 putFile16BitBE(file, element);
8633 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8634 putFile8Bit(file, ei->description[i]);
8636 putFile8Bit(file, group->num_elements);
8638 putFile8Bit(file, ei->use_gfx_element);
8639 putFile16BitBE(file, ei->gfx_element_initial);
8641 putFile8Bit(file, group->choice_mode);
8643 // some free bytes for future values and padding
8644 WriteUnusedBytesToFile(file, 3);
8646 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8647 putFile16BitBE(file, group->element[i]);
8651 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8652 boolean write_element)
8654 int save_type = entry->save_type;
8655 int data_type = entry->data_type;
8656 int conf_type = entry->conf_type;
8657 int byte_mask = conf_type & CONF_MASK_BYTES;
8658 int element = entry->element;
8659 int default_value = entry->default_value;
8661 boolean modified = FALSE;
8663 if (byte_mask != CONF_MASK_MULTI_BYTES)
8665 void *value_ptr = entry->value;
8666 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8669 // check if any settings have been modified before saving them
8670 if (value != default_value)
8673 // do not save if explicitly told or if unmodified default settings
8674 if ((save_type == SAVE_CONF_NEVER) ||
8675 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8679 num_bytes += putFile16BitBE(file, element);
8681 num_bytes += putFile8Bit(file, conf_type);
8682 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8683 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8684 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8687 else if (data_type == TYPE_STRING)
8689 char *default_string = entry->default_string;
8690 char *string = (char *)(entry->value);
8691 int string_length = strlen(string);
8694 // check if any settings have been modified before saving them
8695 if (!strEqual(string, default_string))
8698 // do not save if explicitly told or if unmodified default settings
8699 if ((save_type == SAVE_CONF_NEVER) ||
8700 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8704 num_bytes += putFile16BitBE(file, element);
8706 num_bytes += putFile8Bit(file, conf_type);
8707 num_bytes += putFile16BitBE(file, string_length);
8709 for (i = 0; i < string_length; i++)
8710 num_bytes += putFile8Bit(file, string[i]);
8712 else if (data_type == TYPE_ELEMENT_LIST)
8714 int *element_array = (int *)(entry->value);
8715 int num_elements = *(int *)(entry->num_entities);
8718 // check if any settings have been modified before saving them
8719 for (i = 0; i < num_elements; i++)
8720 if (element_array[i] != default_value)
8723 // do not save if explicitly told or if unmodified default settings
8724 if ((save_type == SAVE_CONF_NEVER) ||
8725 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8729 num_bytes += putFile16BitBE(file, element);
8731 num_bytes += putFile8Bit(file, conf_type);
8732 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8734 for (i = 0; i < num_elements; i++)
8735 num_bytes += putFile16BitBE(file, element_array[i]);
8737 else if (data_type == TYPE_CONTENT_LIST)
8739 struct Content *content = (struct Content *)(entry->value);
8740 int num_contents = *(int *)(entry->num_entities);
8743 // check if any settings have been modified before saving them
8744 for (i = 0; i < num_contents; i++)
8745 for (y = 0; y < 3; y++)
8746 for (x = 0; x < 3; x++)
8747 if (content[i].e[x][y] != default_value)
8750 // do not save if explicitly told or if unmodified default settings
8751 if ((save_type == SAVE_CONF_NEVER) ||
8752 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8756 num_bytes += putFile16BitBE(file, element);
8758 num_bytes += putFile8Bit(file, conf_type);
8759 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8761 for (i = 0; i < num_contents; i++)
8762 for (y = 0; y < 3; y++)
8763 for (x = 0; x < 3; x++)
8764 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8770 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8775 li = *level; // copy level data into temporary buffer
8777 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8778 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8783 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8788 li = *level; // copy level data into temporary buffer
8790 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8791 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8796 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8798 int envelope_nr = element - EL_ENVELOPE_1;
8802 chunk_size += putFile16BitBE(file, element);
8804 // copy envelope data into temporary buffer
8805 xx_envelope = level->envelope[envelope_nr];
8807 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8808 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8813 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8815 struct ElementInfo *ei = &element_info[element];
8819 chunk_size += putFile16BitBE(file, element);
8821 xx_ei = *ei; // copy element data into temporary buffer
8823 // set default description string for this specific element
8824 strcpy(xx_default_description, getDefaultElementDescription(ei));
8826 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8827 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8829 for (i = 0; i < ei->num_change_pages; i++)
8831 struct ElementChangeInfo *change = &ei->change_page[i];
8833 xx_current_change_page = i;
8835 xx_change = *change; // copy change data into temporary buffer
8838 setEventBitsFromEventFlags(change);
8840 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8841 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8848 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8850 struct ElementInfo *ei = &element_info[element];
8851 struct ElementGroupInfo *group = ei->group;
8855 chunk_size += putFile16BitBE(file, element);
8857 xx_ei = *ei; // copy element data into temporary buffer
8858 xx_group = *group; // copy group data into temporary buffer
8860 // set default description string for this specific element
8861 strcpy(xx_default_description, getDefaultElementDescription(ei));
8863 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8864 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8869 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8871 struct ElementInfo *ei = &element_info[element];
8875 chunk_size += putFile16BitBE(file, element);
8877 xx_ei = *ei; // copy element data into temporary buffer
8879 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8880 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8885 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8886 boolean save_as_template)
8892 if (!(file = fopen(filename, MODE_WRITE)))
8894 Warn("cannot save level file '%s'", filename);
8899 level->file_version = FILE_VERSION_ACTUAL;
8900 level->game_version = GAME_VERSION_ACTUAL;
8902 level->creation_date = getCurrentDate();
8904 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8905 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8907 chunk_size = SaveLevel_VERS(NULL, level);
8908 putFileChunkBE(file, "VERS", chunk_size);
8909 SaveLevel_VERS(file, level);
8911 chunk_size = SaveLevel_DATE(NULL, level);
8912 putFileChunkBE(file, "DATE", chunk_size);
8913 SaveLevel_DATE(file, level);
8915 chunk_size = SaveLevel_NAME(NULL, level);
8916 putFileChunkBE(file, "NAME", chunk_size);
8917 SaveLevel_NAME(file, level);
8919 chunk_size = SaveLevel_AUTH(NULL, level);
8920 putFileChunkBE(file, "AUTH", chunk_size);
8921 SaveLevel_AUTH(file, level);
8923 chunk_size = SaveLevel_INFO(NULL, level);
8924 putFileChunkBE(file, "INFO", chunk_size);
8925 SaveLevel_INFO(file, level);
8927 chunk_size = SaveLevel_BODY(NULL, level);
8928 putFileChunkBE(file, "BODY", chunk_size);
8929 SaveLevel_BODY(file, level);
8931 chunk_size = SaveLevel_ELEM(NULL, level);
8932 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8934 putFileChunkBE(file, "ELEM", chunk_size);
8935 SaveLevel_ELEM(file, level);
8938 for (i = 0; i < NUM_ENVELOPES; i++)
8940 int element = EL_ENVELOPE_1 + i;
8942 chunk_size = SaveLevel_NOTE(NULL, level, element);
8943 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8945 putFileChunkBE(file, "NOTE", chunk_size);
8946 SaveLevel_NOTE(file, level, element);
8950 // if not using template level, check for non-default custom/group elements
8951 if (!level->use_custom_template || save_as_template)
8953 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8955 int element = EL_CUSTOM_START + i;
8957 chunk_size = SaveLevel_CUSX(NULL, level, element);
8958 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8960 putFileChunkBE(file, "CUSX", chunk_size);
8961 SaveLevel_CUSX(file, level, element);
8965 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8967 int element = EL_GROUP_START + i;
8969 chunk_size = SaveLevel_GRPX(NULL, level, element);
8970 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8972 putFileChunkBE(file, "GRPX", chunk_size);
8973 SaveLevel_GRPX(file, level, element);
8977 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8979 int element = GET_EMPTY_ELEMENT(i);
8981 chunk_size = SaveLevel_EMPX(NULL, level, element);
8982 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8984 putFileChunkBE(file, "EMPX", chunk_size);
8985 SaveLevel_EMPX(file, level, element);
8992 SetFilePermissions(filename, PERMS_PRIVATE);
8995 void SaveLevel(int nr)
8997 char *filename = getDefaultLevelFilename(nr);
8999 SaveLevelFromFilename(&level, filename, FALSE);
9002 void SaveLevelTemplate(void)
9004 char *filename = getLocalLevelTemplateFilename();
9006 SaveLevelFromFilename(&level, filename, TRUE);
9009 boolean SaveLevelChecked(int nr)
9011 char *filename = getDefaultLevelFilename(nr);
9012 boolean new_level = !fileExists(filename);
9013 boolean level_saved = FALSE;
9015 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
9020 Request("Level saved!", REQ_CONFIRM);
9028 void DumpLevel(struct LevelInfo *level)
9030 if (level->no_level_file || level->no_valid_file)
9032 Warn("cannot dump -- no valid level file found");
9038 Print("Level xxx (file version %08d, game version %08d)\n",
9039 level->file_version, level->game_version);
9042 Print("Level author: '%s'\n", level->author);
9043 Print("Level title: '%s'\n", level->name);
9045 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
9047 Print("Level time: %d seconds\n", level->time);
9048 Print("Gems needed: %d\n", level->gems_needed);
9050 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
9051 Print("Time for wheel: %d seconds\n", level->time_wheel);
9052 Print("Time for light: %d seconds\n", level->time_light);
9053 Print("Time for timegate: %d seconds\n", level->time_timegate);
9055 Print("Amoeba speed: %d\n", level->amoeba_speed);
9058 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
9059 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
9060 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
9061 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
9062 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
9063 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
9069 for (i = 0; i < NUM_ENVELOPES; i++)
9071 char *text = level->envelope[i].text;
9072 int text_len = strlen(text);
9073 boolean has_text = FALSE;
9075 for (j = 0; j < text_len; j++)
9076 if (text[j] != ' ' && text[j] != '\n')
9082 Print("Envelope %d:\n'%s'\n", i + 1, text);
9090 void DumpLevels(void)
9092 static LevelDirTree *dumplevel_leveldir = NULL;
9094 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9095 global.dumplevel_leveldir);
9097 if (dumplevel_leveldir == NULL)
9098 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
9100 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
9101 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
9102 Fail("no such level number: %d", global.dumplevel_level_nr);
9104 leveldir_current = dumplevel_leveldir;
9106 LoadLevel(global.dumplevel_level_nr);
9112 void DumpLevelsetFromFilename_BD(char *filename)
9114 if (leveldir_current == NULL) // no levelsets loaded yet
9117 if (!LoadNativeLevel_BD(filename, 0, FALSE))
9118 CloseAllAndExit(0); // function has already printed warning
9121 Print("Levelset '%s'\n", filename);
9131 void DumpLevelset(void)
9133 static LevelDirTree *dumplevelset_leveldir = NULL;
9135 dumplevelset_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9136 global.dumplevelset_leveldir);
9137 if (dumplevelset_leveldir == NULL)
9138 Fail("no such level identifier: '%s'", global.dumplevelset_leveldir);
9141 Print("Levelset '%s'\n", dumplevelset_leveldir->identifier);
9144 Print("Number of levels: %d\n", dumplevelset_leveldir->levels);
9145 Print("First level number: %d\n", dumplevelset_leveldir->first_level);
9153 // ============================================================================
9154 // tape file functions
9155 // ============================================================================
9157 static void setTapeInfoToDefaults(void)
9161 // always start with reliable default values (empty tape)
9164 // default values (also for pre-1.2 tapes) with only the first player
9165 tape.player_participates[0] = TRUE;
9166 for (i = 1; i < MAX_PLAYERS; i++)
9167 tape.player_participates[i] = FALSE;
9169 // at least one (default: the first) player participates in every tape
9170 tape.num_participating_players = 1;
9172 tape.property_bits = TAPE_PROPERTY_NONE;
9174 tape.level_nr = level_nr;
9176 tape.changed = FALSE;
9177 tape.solved = FALSE;
9179 tape.recording = FALSE;
9180 tape.playing = FALSE;
9181 tape.pausing = FALSE;
9183 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9184 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9186 tape.no_info_chunk = TRUE;
9187 tape.no_valid_file = FALSE;
9190 static int getTapePosSize(struct TapeInfo *tape)
9192 int tape_pos_size = 0;
9194 if (tape->use_key_actions)
9195 tape_pos_size += tape->num_participating_players;
9197 if (tape->use_mouse_actions)
9198 tape_pos_size += 3; // x and y position and mouse button mask
9200 tape_pos_size += 1; // tape action delay value
9202 return tape_pos_size;
9205 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9207 tape->use_key_actions = FALSE;
9208 tape->use_mouse_actions = FALSE;
9210 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9211 tape->use_key_actions = TRUE;
9213 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9214 tape->use_mouse_actions = TRUE;
9217 static int getTapeActionValue(struct TapeInfo *tape)
9219 return (tape->use_key_actions &&
9220 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9221 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
9222 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9223 TAPE_ACTIONS_DEFAULT);
9226 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9228 tape->file_version = getFileVersion(file);
9229 tape->game_version = getFileVersion(file);
9234 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9238 tape->random_seed = getFile32BitBE(file);
9239 tape->date = getFile32BitBE(file);
9240 tape->length = getFile32BitBE(file);
9242 // read header fields that are new since version 1.2
9243 if (tape->file_version >= FILE_VERSION_1_2)
9245 byte store_participating_players = getFile8Bit(file);
9248 // since version 1.2, tapes store which players participate in the tape
9249 tape->num_participating_players = 0;
9250 for (i = 0; i < MAX_PLAYERS; i++)
9252 tape->player_participates[i] = FALSE;
9254 if (store_participating_players & (1 << i))
9256 tape->player_participates[i] = TRUE;
9257 tape->num_participating_players++;
9261 setTapeActionFlags(tape, getFile8Bit(file));
9263 tape->property_bits = getFile8Bit(file);
9264 tape->solved = getFile8Bit(file);
9266 engine_version = getFileVersion(file);
9267 if (engine_version > 0)
9268 tape->engine_version = engine_version;
9270 tape->engine_version = tape->game_version;
9276 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9278 tape->scr_fieldx = getFile8Bit(file);
9279 tape->scr_fieldy = getFile8Bit(file);
9284 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9286 char *level_identifier = NULL;
9287 int level_identifier_size;
9290 tape->no_info_chunk = FALSE;
9292 level_identifier_size = getFile16BitBE(file);
9294 level_identifier = checked_malloc(level_identifier_size);
9296 for (i = 0; i < level_identifier_size; i++)
9297 level_identifier[i] = getFile8Bit(file);
9299 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9300 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9302 checked_free(level_identifier);
9304 tape->level_nr = getFile16BitBE(file);
9306 chunk_size = 2 + level_identifier_size + 2;
9311 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9314 int tape_pos_size = getTapePosSize(tape);
9315 int chunk_size_expected = tape_pos_size * tape->length;
9317 if (chunk_size_expected != chunk_size)
9319 ReadUnusedBytesFromFile(file, chunk_size);
9320 return chunk_size_expected;
9323 for (i = 0; i < tape->length; i++)
9325 if (i >= MAX_TAPE_LEN)
9327 Warn("tape truncated -- size exceeds maximum tape size %d",
9330 // tape too large; read and ignore remaining tape data from this chunk
9331 for (;i < tape->length; i++)
9332 ReadUnusedBytesFromFile(file, tape_pos_size);
9337 if (tape->use_key_actions)
9339 for (j = 0; j < MAX_PLAYERS; j++)
9341 tape->pos[i].action[j] = MV_NONE;
9343 if (tape->player_participates[j])
9344 tape->pos[i].action[j] = getFile8Bit(file);
9348 if (tape->use_mouse_actions)
9350 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
9351 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
9352 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9355 tape->pos[i].delay = getFile8Bit(file);
9357 if (tape->file_version == FILE_VERSION_1_0)
9359 // eliminate possible diagonal moves in old tapes
9360 // this is only for backward compatibility
9362 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9363 byte action = tape->pos[i].action[0];
9364 int k, num_moves = 0;
9366 for (k = 0; k < 4; k++)
9368 if (action & joy_dir[k])
9370 tape->pos[i + num_moves].action[0] = joy_dir[k];
9372 tape->pos[i + num_moves].delay = 0;
9381 tape->length += num_moves;
9384 else if (tape->file_version < FILE_VERSION_2_0)
9386 // convert pre-2.0 tapes to new tape format
9388 if (tape->pos[i].delay > 1)
9391 tape->pos[i + 1] = tape->pos[i];
9392 tape->pos[i + 1].delay = 1;
9395 for (j = 0; j < MAX_PLAYERS; j++)
9396 tape->pos[i].action[j] = MV_NONE;
9397 tape->pos[i].delay--;
9404 if (checkEndOfFile(file))
9408 if (i != tape->length)
9409 chunk_size = tape_pos_size * i;
9414 static void LoadTape_SokobanSolution(char *filename)
9417 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9419 if (!(file = openFile(filename, MODE_READ)))
9421 tape.no_valid_file = TRUE;
9426 while (!checkEndOfFile(file))
9428 unsigned char c = getByteFromFile(file);
9430 if (checkEndOfFile(file))
9437 tape.pos[tape.length].action[0] = MV_UP;
9438 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9444 tape.pos[tape.length].action[0] = MV_DOWN;
9445 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9451 tape.pos[tape.length].action[0] = MV_LEFT;
9452 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9458 tape.pos[tape.length].action[0] = MV_RIGHT;
9459 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9467 // ignore white-space characters
9471 tape.no_valid_file = TRUE;
9473 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9481 if (tape.no_valid_file)
9484 tape.length_frames = GetTapeLengthFrames();
9485 tape.length_seconds = GetTapeLengthSeconds();
9488 void LoadTapeFromFilename(char *filename)
9490 char cookie[MAX_LINE_LEN];
9491 char chunk_name[CHUNK_ID_LEN + 1];
9495 // always start with reliable default values
9496 setTapeInfoToDefaults();
9498 if (strSuffix(filename, ".sln"))
9500 LoadTape_SokobanSolution(filename);
9505 if (!(file = openFile(filename, MODE_READ)))
9507 tape.no_valid_file = TRUE;
9512 getFileChunkBE(file, chunk_name, NULL);
9513 if (strEqual(chunk_name, "RND1"))
9515 getFile32BitBE(file); // not used
9517 getFileChunkBE(file, chunk_name, NULL);
9518 if (!strEqual(chunk_name, "TAPE"))
9520 tape.no_valid_file = TRUE;
9522 Warn("unknown format of tape file '%s'", filename);
9529 else // check for pre-2.0 file format with cookie string
9531 strcpy(cookie, chunk_name);
9532 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9534 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9535 cookie[strlen(cookie) - 1] = '\0';
9537 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9539 tape.no_valid_file = TRUE;
9541 Warn("unknown format of tape file '%s'", filename);
9548 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9550 tape.no_valid_file = TRUE;
9552 Warn("unsupported version of tape file '%s'", filename);
9559 // pre-2.0 tape files have no game version, so use file version here
9560 tape.game_version = tape.file_version;
9563 if (tape.file_version < FILE_VERSION_1_2)
9565 // tape files from versions before 1.2.0 without chunk structure
9566 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9567 LoadTape_BODY(file, 2 * tape.length, &tape);
9575 int (*loader)(File *, int, struct TapeInfo *);
9579 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9580 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9581 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9582 { "INFO", -1, LoadTape_INFO },
9583 { "BODY", -1, LoadTape_BODY },
9587 while (getFileChunkBE(file, chunk_name, &chunk_size))
9591 while (chunk_info[i].name != NULL &&
9592 !strEqual(chunk_name, chunk_info[i].name))
9595 if (chunk_info[i].name == NULL)
9597 Warn("unknown chunk '%s' in tape file '%s'",
9598 chunk_name, filename);
9600 ReadUnusedBytesFromFile(file, chunk_size);
9602 else if (chunk_info[i].size != -1 &&
9603 chunk_info[i].size != chunk_size)
9605 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9606 chunk_size, chunk_name, filename);
9608 ReadUnusedBytesFromFile(file, chunk_size);
9612 // call function to load this tape chunk
9613 int chunk_size_expected =
9614 (chunk_info[i].loader)(file, chunk_size, &tape);
9616 // the size of some chunks cannot be checked before reading other
9617 // chunks first (like "HEAD" and "BODY") that contain some header
9618 // information, so check them here
9619 if (chunk_size_expected != chunk_size)
9621 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9622 chunk_size, chunk_name, filename);
9630 tape.length_frames = GetTapeLengthFrames();
9631 tape.length_seconds = GetTapeLengthSeconds();
9634 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9636 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9638 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9639 tape.engine_version);
9643 void LoadTape(int nr)
9645 char *filename = getTapeFilename(nr);
9647 LoadTapeFromFilename(filename);
9650 void LoadSolutionTape(int nr)
9652 char *filename = getSolutionTapeFilename(nr);
9654 LoadTapeFromFilename(filename);
9656 if (TAPE_IS_EMPTY(tape))
9658 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9659 level.native_bd_level->replay != NULL)
9660 CopyNativeTape_BD_to_RND(&level);
9661 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9662 level.native_sp_level->demo.is_available)
9663 CopyNativeTape_SP_to_RND(&level);
9667 void LoadScoreTape(char *score_tape_basename, int nr)
9669 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9671 LoadTapeFromFilename(filename);
9674 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9676 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9678 LoadTapeFromFilename(filename);
9681 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9683 // chunk required for team mode tapes with non-default screen size
9684 return (tape->num_participating_players > 1 &&
9685 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9686 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9689 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9691 putFileVersion(file, tape->file_version);
9692 putFileVersion(file, tape->game_version);
9695 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9698 byte store_participating_players = 0;
9700 // set bits for participating players for compact storage
9701 for (i = 0; i < MAX_PLAYERS; i++)
9702 if (tape->player_participates[i])
9703 store_participating_players |= (1 << i);
9705 putFile32BitBE(file, tape->random_seed);
9706 putFile32BitBE(file, tape->date);
9707 putFile32BitBE(file, tape->length);
9709 putFile8Bit(file, store_participating_players);
9711 putFile8Bit(file, getTapeActionValue(tape));
9713 putFile8Bit(file, tape->property_bits);
9714 putFile8Bit(file, tape->solved);
9716 putFileVersion(file, tape->engine_version);
9719 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9721 putFile8Bit(file, tape->scr_fieldx);
9722 putFile8Bit(file, tape->scr_fieldy);
9725 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9727 int level_identifier_size = strlen(tape->level_identifier) + 1;
9730 putFile16BitBE(file, level_identifier_size);
9732 for (i = 0; i < level_identifier_size; i++)
9733 putFile8Bit(file, tape->level_identifier[i]);
9735 putFile16BitBE(file, tape->level_nr);
9738 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9742 for (i = 0; i < tape->length; i++)
9744 if (tape->use_key_actions)
9746 for (j = 0; j < MAX_PLAYERS; j++)
9747 if (tape->player_participates[j])
9748 putFile8Bit(file, tape->pos[i].action[j]);
9751 if (tape->use_mouse_actions)
9753 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9754 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9755 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9758 putFile8Bit(file, tape->pos[i].delay);
9762 void SaveTapeToFilename(char *filename)
9766 int info_chunk_size;
9767 int body_chunk_size;
9769 if (!(file = fopen(filename, MODE_WRITE)))
9771 Warn("cannot save level recording file '%s'", filename);
9776 tape_pos_size = getTapePosSize(&tape);
9778 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9779 body_chunk_size = tape_pos_size * tape.length;
9781 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9782 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9784 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9785 SaveTape_VERS(file, &tape);
9787 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9788 SaveTape_HEAD(file, &tape);
9790 if (checkSaveTape_SCRN(&tape))
9792 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9793 SaveTape_SCRN(file, &tape);
9796 putFileChunkBE(file, "INFO", info_chunk_size);
9797 SaveTape_INFO(file, &tape);
9799 putFileChunkBE(file, "BODY", body_chunk_size);
9800 SaveTape_BODY(file, &tape);
9804 SetFilePermissions(filename, PERMS_PRIVATE);
9807 static void SaveTapeExt(char *filename)
9811 tape.file_version = FILE_VERSION_ACTUAL;
9812 tape.game_version = GAME_VERSION_ACTUAL;
9814 tape.num_participating_players = 0;
9816 // count number of participating players
9817 for (i = 0; i < MAX_PLAYERS; i++)
9818 if (tape.player_participates[i])
9819 tape.num_participating_players++;
9821 SaveTapeToFilename(filename);
9823 tape.changed = FALSE;
9826 void SaveTape(int nr)
9828 char *filename = getTapeFilename(nr);
9830 InitTapeDirectory(leveldir_current->subdir);
9832 SaveTapeExt(filename);
9835 void SaveScoreTape(int nr)
9837 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9839 // used instead of "leveldir_current->subdir" (for network games)
9840 InitScoreTapeDirectory(levelset.identifier, nr);
9842 SaveTapeExt(filename);
9845 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9846 unsigned int req_state_added)
9848 char *filename = getTapeFilename(nr);
9849 boolean new_tape = !fileExists(filename);
9850 boolean tape_saved = FALSE;
9852 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9857 Request(msg_saved, REQ_CONFIRM | req_state_added);
9865 boolean SaveTapeChecked(int nr)
9867 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9870 boolean SaveTapeChecked_LevelSolved(int nr)
9872 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9873 "Level solved! Tape saved!", REQ_STAY_OPEN);
9876 void DumpTape(struct TapeInfo *tape)
9878 int tape_frame_counter;
9881 if (tape->no_valid_file)
9883 Warn("cannot dump -- no valid tape file found");
9890 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9891 tape->level_nr, tape->file_version, tape->game_version);
9892 Print(" (effective engine version %08d)\n",
9893 tape->engine_version);
9894 Print("Level series identifier: '%s'\n", tape->level_identifier);
9896 Print("Solution tape: %s\n",
9897 tape->solved ? "yes" :
9898 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9900 Print("Special tape properties: ");
9901 if (tape->property_bits == TAPE_PROPERTY_NONE)
9903 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9904 Print("[em_random_bug]");
9905 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9906 Print("[game_speed]");
9907 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9909 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9910 Print("[single_step]");
9911 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9912 Print("[snapshot]");
9913 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9914 Print("[replayed]");
9915 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9916 Print("[tas_keys]");
9917 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9918 Print("[small_graphics]");
9921 int year2 = tape->date / 10000;
9922 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9923 int month_index_raw = (tape->date / 100) % 100;
9924 int month_index = month_index_raw % 12; // prevent invalid index
9925 int month = month_index + 1;
9926 int day = tape->date % 100;
9928 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9932 tape_frame_counter = 0;
9934 for (i = 0; i < tape->length; i++)
9936 if (i >= MAX_TAPE_LEN)
9941 for (j = 0; j < MAX_PLAYERS; j++)
9943 if (tape->player_participates[j])
9945 int action = tape->pos[i].action[j];
9947 Print("%d:%02x ", j, action);
9948 Print("[%c%c%c%c|%c%c] - ",
9949 (action & JOY_LEFT ? '<' : ' '),
9950 (action & JOY_RIGHT ? '>' : ' '),
9951 (action & JOY_UP ? '^' : ' '),
9952 (action & JOY_DOWN ? 'v' : ' '),
9953 (action & JOY_BUTTON_1 ? '1' : ' '),
9954 (action & JOY_BUTTON_2 ? '2' : ' '));
9958 Print("(%03d) ", tape->pos[i].delay);
9959 Print("[%05d]\n", tape_frame_counter);
9961 tape_frame_counter += tape->pos[i].delay;
9967 void DumpTapes(void)
9969 static LevelDirTree *dumptape_leveldir = NULL;
9971 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9972 global.dumptape_leveldir);
9974 if (dumptape_leveldir == NULL)
9975 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9977 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9978 global.dumptape_level_nr > dumptape_leveldir->last_level)
9979 Fail("no such level number: %d", global.dumptape_level_nr);
9981 leveldir_current = dumptape_leveldir;
9983 if (options.mytapes)
9984 LoadTape(global.dumptape_level_nr);
9986 LoadSolutionTape(global.dumptape_level_nr);
9994 // ============================================================================
9995 // score file functions
9996 // ============================================================================
9998 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
10002 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10004 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
10005 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
10006 scores->entry[i].score = 0;
10007 scores->entry[i].time = 0;
10009 scores->entry[i].id = -1;
10010 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
10011 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
10012 strcpy(scores->entry[i].version, UNKNOWN_NAME);
10013 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
10014 strcpy(scores->entry[i].country_code, "??");
10017 scores->num_entries = 0;
10018 scores->last_added = -1;
10019 scores->last_added_local = -1;
10021 scores->updated = FALSE;
10022 scores->uploaded = FALSE;
10023 scores->tape_downloaded = FALSE;
10024 scores->force_last_added = FALSE;
10026 // The following values are intentionally not reset here:
10030 // - continue_playing
10031 // - continue_on_return
10034 static void setScoreInfoToDefaults(void)
10036 setScoreInfoToDefaultsExt(&scores);
10039 static void setServerScoreInfoToDefaults(void)
10041 setScoreInfoToDefaultsExt(&server_scores);
10044 static void LoadScore_OLD(int nr)
10047 char *filename = getScoreFilename(nr);
10048 char cookie[MAX_LINE_LEN];
10049 char line[MAX_LINE_LEN];
10053 if (!(file = fopen(filename, MODE_READ)))
10056 // check file identifier
10057 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
10059 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10060 cookie[strlen(cookie) - 1] = '\0';
10062 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10064 Warn("unknown format of score file '%s'", filename);
10071 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10073 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
10074 Warn("fscanf() failed; %s", strerror(errno));
10076 if (fgets(line, MAX_LINE_LEN, file) == NULL)
10079 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
10080 line[strlen(line) - 1] = '\0';
10082 for (line_ptr = line; *line_ptr; line_ptr++)
10084 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
10086 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
10087 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10096 static void ConvertScore_OLD(void)
10098 // only convert score to time for levels that rate playing time over score
10099 if (!level.rate_time_over_score)
10102 // convert old score to playing time for score-less levels (like Supaplex)
10103 int time_final_max = 999;
10106 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10108 int score = scores.entry[i].score;
10110 if (score > 0 && score < time_final_max)
10111 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
10115 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
10117 scores->file_version = getFileVersion(file);
10118 scores->game_version = getFileVersion(file);
10123 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
10125 char *level_identifier = NULL;
10126 int level_identifier_size;
10129 level_identifier_size = getFile16BitBE(file);
10131 level_identifier = checked_malloc(level_identifier_size);
10133 for (i = 0; i < level_identifier_size; i++)
10134 level_identifier[i] = getFile8Bit(file);
10136 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
10137 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
10139 checked_free(level_identifier);
10141 scores->level_nr = getFile16BitBE(file);
10142 scores->num_entries = getFile16BitBE(file);
10144 chunk_size = 2 + level_identifier_size + 2 + 2;
10149 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10153 for (i = 0; i < scores->num_entries; i++)
10155 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10156 scores->entry[i].name[j] = getFile8Bit(file);
10158 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10161 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10166 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10170 for (i = 0; i < scores->num_entries; i++)
10171 scores->entry[i].score = getFile16BitBE(file);
10173 chunk_size = scores->num_entries * 2;
10178 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10182 for (i = 0; i < scores->num_entries; i++)
10183 scores->entry[i].score = getFile32BitBE(file);
10185 chunk_size = scores->num_entries * 4;
10190 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10194 for (i = 0; i < scores->num_entries; i++)
10195 scores->entry[i].time = getFile32BitBE(file);
10197 chunk_size = scores->num_entries * 4;
10202 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10206 for (i = 0; i < scores->num_entries; i++)
10208 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10209 scores->entry[i].tape_basename[j] = getFile8Bit(file);
10211 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10214 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10219 void LoadScore(int nr)
10221 char *filename = getScoreFilename(nr);
10222 char cookie[MAX_LINE_LEN];
10223 char chunk_name[CHUNK_ID_LEN + 1];
10225 boolean old_score_file_format = FALSE;
10228 // always start with reliable default values
10229 setScoreInfoToDefaults();
10231 if (!(file = openFile(filename, MODE_READ)))
10234 getFileChunkBE(file, chunk_name, NULL);
10235 if (strEqual(chunk_name, "RND1"))
10237 getFile32BitBE(file); // not used
10239 getFileChunkBE(file, chunk_name, NULL);
10240 if (!strEqual(chunk_name, "SCOR"))
10242 Warn("unknown format of score file '%s'", filename);
10249 else // check for old file format with cookie string
10251 strcpy(cookie, chunk_name);
10252 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10254 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10255 cookie[strlen(cookie) - 1] = '\0';
10257 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10259 Warn("unknown format of score file '%s'", filename);
10266 old_score_file_format = TRUE;
10269 if (old_score_file_format)
10271 // score files from versions before 4.2.4.0 without chunk structure
10274 // convert score to time, if possible (mainly for Supaplex levels)
10275 ConvertScore_OLD();
10283 int (*loader)(File *, int, struct ScoreInfo *);
10287 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
10288 { "INFO", -1, LoadScore_INFO },
10289 { "NAME", -1, LoadScore_NAME },
10290 { "SCOR", -1, LoadScore_SCOR },
10291 { "SC4R", -1, LoadScore_SC4R },
10292 { "TIME", -1, LoadScore_TIME },
10293 { "TAPE", -1, LoadScore_TAPE },
10298 while (getFileChunkBE(file, chunk_name, &chunk_size))
10302 while (chunk_info[i].name != NULL &&
10303 !strEqual(chunk_name, chunk_info[i].name))
10306 if (chunk_info[i].name == NULL)
10308 Warn("unknown chunk '%s' in score file '%s'",
10309 chunk_name, filename);
10311 ReadUnusedBytesFromFile(file, chunk_size);
10313 else if (chunk_info[i].size != -1 &&
10314 chunk_info[i].size != chunk_size)
10316 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10317 chunk_size, chunk_name, filename);
10319 ReadUnusedBytesFromFile(file, chunk_size);
10323 // call function to load this score chunk
10324 int chunk_size_expected =
10325 (chunk_info[i].loader)(file, chunk_size, &scores);
10327 // the size of some chunks cannot be checked before reading other
10328 // chunks first (like "HEAD" and "BODY") that contain some header
10329 // information, so check them here
10330 if (chunk_size_expected != chunk_size)
10332 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10333 chunk_size, chunk_name, filename);
10342 #if ENABLE_HISTORIC_CHUNKS
10343 void SaveScore_OLD(int nr)
10346 char *filename = getScoreFilename(nr);
10349 // used instead of "leveldir_current->subdir" (for network games)
10350 InitScoreDirectory(levelset.identifier);
10352 if (!(file = fopen(filename, MODE_WRITE)))
10354 Warn("cannot save score for level %d", nr);
10359 fprintf(file, "%s\n\n", SCORE_COOKIE);
10361 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10362 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10366 SetFilePermissions(filename, PERMS_PRIVATE);
10370 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10372 putFileVersion(file, scores->file_version);
10373 putFileVersion(file, scores->game_version);
10376 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10378 int level_identifier_size = strlen(scores->level_identifier) + 1;
10381 putFile16BitBE(file, level_identifier_size);
10383 for (i = 0; i < level_identifier_size; i++)
10384 putFile8Bit(file, scores->level_identifier[i]);
10386 putFile16BitBE(file, scores->level_nr);
10387 putFile16BitBE(file, scores->num_entries);
10390 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10394 for (i = 0; i < scores->num_entries; i++)
10396 int name_size = strlen(scores->entry[i].name);
10398 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10399 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10403 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10407 for (i = 0; i < scores->num_entries; i++)
10408 putFile16BitBE(file, scores->entry[i].score);
10411 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10415 for (i = 0; i < scores->num_entries; i++)
10416 putFile32BitBE(file, scores->entry[i].score);
10419 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10423 for (i = 0; i < scores->num_entries; i++)
10424 putFile32BitBE(file, scores->entry[i].time);
10427 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10431 for (i = 0; i < scores->num_entries; i++)
10433 int size = strlen(scores->entry[i].tape_basename);
10435 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10436 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10440 static void SaveScoreToFilename(char *filename)
10443 int info_chunk_size;
10444 int name_chunk_size;
10445 int scor_chunk_size;
10446 int sc4r_chunk_size;
10447 int time_chunk_size;
10448 int tape_chunk_size;
10449 boolean has_large_score_values;
10452 if (!(file = fopen(filename, MODE_WRITE)))
10454 Warn("cannot save score file '%s'", filename);
10459 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10460 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10461 scor_chunk_size = scores.num_entries * 2;
10462 sc4r_chunk_size = scores.num_entries * 4;
10463 time_chunk_size = scores.num_entries * 4;
10464 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10466 has_large_score_values = FALSE;
10467 for (i = 0; i < scores.num_entries; i++)
10468 if (scores.entry[i].score > 0xffff)
10469 has_large_score_values = TRUE;
10471 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10472 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10474 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10475 SaveScore_VERS(file, &scores);
10477 putFileChunkBE(file, "INFO", info_chunk_size);
10478 SaveScore_INFO(file, &scores);
10480 putFileChunkBE(file, "NAME", name_chunk_size);
10481 SaveScore_NAME(file, &scores);
10483 if (has_large_score_values)
10485 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10486 SaveScore_SC4R(file, &scores);
10490 putFileChunkBE(file, "SCOR", scor_chunk_size);
10491 SaveScore_SCOR(file, &scores);
10494 putFileChunkBE(file, "TIME", time_chunk_size);
10495 SaveScore_TIME(file, &scores);
10497 putFileChunkBE(file, "TAPE", tape_chunk_size);
10498 SaveScore_TAPE(file, &scores);
10502 SetFilePermissions(filename, PERMS_PRIVATE);
10505 void SaveScore(int nr)
10507 char *filename = getScoreFilename(nr);
10510 // used instead of "leveldir_current->subdir" (for network games)
10511 InitScoreDirectory(levelset.identifier);
10513 scores.file_version = FILE_VERSION_ACTUAL;
10514 scores.game_version = GAME_VERSION_ACTUAL;
10516 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10517 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10518 scores.level_nr = level_nr;
10520 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10521 if (scores.entry[i].score == 0 &&
10522 scores.entry[i].time == 0 &&
10523 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10526 scores.num_entries = i;
10528 if (scores.num_entries == 0)
10531 SaveScoreToFilename(filename);
10534 static void LoadServerScoreFromCache(int nr)
10536 struct ScoreEntry score_entry;
10545 { &score_entry.score, FALSE, 0 },
10546 { &score_entry.time, FALSE, 0 },
10547 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10548 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10549 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10550 { &score_entry.id, FALSE, 0 },
10551 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10552 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10553 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10554 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10558 char *filename = getScoreCacheFilename(nr);
10559 SetupFileHash *score_hash = loadSetupFileHash(filename);
10562 server_scores.num_entries = 0;
10564 if (score_hash == NULL)
10567 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10569 score_entry = server_scores.entry[i];
10571 for (j = 0; score_mapping[j].value != NULL; j++)
10575 sprintf(token, "%02d.%d", i, j);
10577 char *value = getHashEntry(score_hash, token);
10582 if (score_mapping[j].is_string)
10584 char *score_value = (char *)score_mapping[j].value;
10585 int value_size = score_mapping[j].string_size;
10587 strncpy(score_value, value, value_size);
10588 score_value[value_size] = '\0';
10592 int *score_value = (int *)score_mapping[j].value;
10594 *score_value = atoi(value);
10597 server_scores.num_entries = i + 1;
10600 server_scores.entry[i] = score_entry;
10603 freeSetupFileHash(score_hash);
10606 void LoadServerScore(int nr, boolean download_score)
10608 if (!setup.use_api_server)
10611 // always start with reliable default values
10612 setServerScoreInfoToDefaults();
10614 // 1st step: load server scores from cache file (which may not exist)
10615 // (this should prevent reading it while the thread is writing to it)
10616 LoadServerScoreFromCache(nr);
10618 if (download_score && runtime.use_api_server)
10620 // 2nd step: download server scores from score server to cache file
10621 // (as thread, as it might time out if the server is not reachable)
10622 ApiGetScoreAsThread(nr);
10626 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10628 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10630 // if score tape not uploaded, ask for uploading missing tapes later
10631 if (!setup.has_remaining_tapes)
10632 setup.ask_for_remaining_tapes = TRUE;
10634 setup.provide_uploading_tapes = TRUE;
10635 setup.has_remaining_tapes = TRUE;
10637 SaveSetup_ServerSetup();
10640 void SaveServerScore(int nr, boolean tape_saved)
10642 if (!runtime.use_api_server)
10644 PrepareScoreTapesForUpload(leveldir_current->subdir);
10649 ApiAddScoreAsThread(nr, tape_saved, NULL);
10652 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10653 char *score_tape_filename)
10655 if (!runtime.use_api_server)
10658 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10661 void LoadLocalAndServerScore(int nr, boolean download_score)
10663 int last_added_local = scores.last_added_local;
10664 boolean force_last_added = scores.force_last_added;
10666 // needed if only showing server scores
10667 setScoreInfoToDefaults();
10669 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10672 // restore last added local score entry (before merging server scores)
10673 scores.last_added = scores.last_added_local = last_added_local;
10675 if (setup.use_api_server &&
10676 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10678 // load server scores from cache file and trigger update from server
10679 LoadServerScore(nr, download_score);
10681 // merge local scores with scores from server
10682 MergeServerScore();
10685 if (force_last_added)
10686 scores.force_last_added = force_last_added;
10690 // ============================================================================
10691 // setup file functions
10692 // ============================================================================
10694 #define TOKEN_STR_PLAYER_PREFIX "player_"
10697 static struct TokenInfo global_setup_tokens[] =
10701 &setup.player_name, "player_name"
10705 &setup.multiple_users, "multiple_users"
10709 &setup.sound, "sound"
10713 &setup.sound_loops, "repeating_sound_loops"
10717 &setup.sound_music, "background_music"
10721 &setup.sound_simple, "simple_sound_effects"
10725 &setup.toons, "toons"
10729 &setup.global_animations, "global_animations"
10733 &setup.scroll_delay, "scroll_delay"
10737 &setup.forced_scroll_delay, "forced_scroll_delay"
10741 &setup.scroll_delay_value, "scroll_delay_value"
10745 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10749 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10753 &setup.fade_screens, "fade_screens"
10757 &setup.autorecord, "automatic_tape_recording"
10761 &setup.autorecord_after_replay, "autorecord_after_replay"
10765 &setup.auto_pause_on_start, "auto_pause_on_start"
10769 &setup.show_titlescreen, "show_titlescreen"
10773 &setup.quick_doors, "quick_doors"
10777 &setup.team_mode, "team_mode"
10781 &setup.handicap, "handicap"
10785 &setup.skip_levels, "skip_levels"
10788 TYPE_SWITCH_3_STATES,
10789 &setup.allow_skipping_levels, "allow_skipping_levels"
10793 &setup.increment_levels, "increment_levels"
10797 &setup.auto_play_next_level, "auto_play_next_level"
10801 &setup.count_score_after_game, "count_score_after_game"
10805 &setup.show_scores_after_game, "show_scores_after_game"
10809 &setup.time_limit, "time_limit"
10813 &setup.fullscreen, "fullscreen"
10817 &setup.window_scaling_percent, "window_scaling_percent"
10821 &setup.window_scaling_quality, "window_scaling_quality"
10825 &setup.screen_rendering_mode, "screen_rendering_mode"
10829 &setup.vsync_mode, "vsync_mode"
10833 &setup.ask_on_escape, "ask_on_escape"
10837 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10841 &setup.ask_on_game_over, "ask_on_game_over"
10845 &setup.ask_on_quit_game, "ask_on_quit_game"
10849 &setup.ask_on_quit_program, "ask_on_quit_program"
10853 &setup.quick_switch, "quick_player_switch"
10857 &setup.input_on_focus, "input_on_focus"
10861 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10865 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10869 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10873 &setup.game_speed_extended, "game_speed_extended"
10877 &setup.game_frame_delay, "game_frame_delay"
10881 &setup.default_game_engine_type, "default_game_engine_type"
10885 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10889 &setup.bd_skip_hatching, "bd_skip_hatching"
10893 &setup.bd_scroll_delay, "bd_scroll_delay"
10897 &setup.bd_show_invisible_outbox, "bd_show_invisible_outbox"
10900 TYPE_SWITCH_3_STATES,
10901 &setup.bd_smooth_movements, "bd_smooth_movements"
10904 TYPE_SWITCH_3_STATES,
10905 &setup.bd_pushing_graphics, "bd_pushing_graphics"
10908 TYPE_SWITCH_3_STATES,
10909 &setup.bd_up_down_graphics, "bd_up_down_graphics"
10912 TYPE_SWITCH_3_STATES,
10913 &setup.bd_falling_sounds, "bd_falling_sounds"
10917 &setup.bd_palette_c64, "bd_palette_c64"
10921 &setup.bd_palette_c64dtv, "bd_palette_c64dtv"
10925 &setup.bd_palette_atari, "bd_palette_atari"
10929 &setup.bd_default_color_type, "bd_default_color_type"
10933 &setup.bd_random_colors, "bd_random_colors"
10937 &setup.sp_show_border_elements, "sp_show_border_elements"
10941 &setup.small_game_graphics, "small_game_graphics"
10945 &setup.show_load_save_buttons, "show_load_save_buttons"
10949 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10953 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10957 &setup.graphics_set, "graphics_set"
10961 &setup.sounds_set, "sounds_set"
10965 &setup.music_set, "music_set"
10968 TYPE_SWITCH_3_STATES,
10969 &setup.override_level_graphics, "override_level_graphics"
10972 TYPE_SWITCH_3_STATES,
10973 &setup.override_level_sounds, "override_level_sounds"
10976 TYPE_SWITCH_3_STATES,
10977 &setup.override_level_music, "override_level_music"
10981 &setup.volume_simple, "volume_simple"
10985 &setup.volume_loops, "volume_loops"
10989 &setup.volume_music, "volume_music"
10993 &setup.audio_sample_rate_44100, "audio_sample_rate_44100"
10997 &setup.network_mode, "network_mode"
11001 &setup.network_player_nr, "network_player"
11005 &setup.network_server_hostname, "network_server_hostname"
11009 &setup.touch.control_type, "touch.control_type"
11013 &setup.touch.move_distance, "touch.move_distance"
11017 &setup.touch.drop_distance, "touch.drop_distance"
11021 &setup.touch.transparency, "touch.transparency"
11025 &setup.touch.draw_outlined, "touch.draw_outlined"
11029 &setup.touch.draw_pressed, "touch.draw_pressed"
11033 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
11037 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
11041 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
11045 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
11049 &setup.touch.overlay_buttons, "touch.overlay_buttons"
11053 static struct TokenInfo auto_setup_tokens[] =
11057 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
11061 static struct TokenInfo server_setup_tokens[] =
11065 &setup.player_uuid, "player_uuid"
11069 &setup.player_version, "player_version"
11073 &setup.use_api_server, TEST_PREFIX "use_api_server"
11077 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
11081 &setup.api_server_password, TEST_PREFIX "api_server_password"
11085 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
11089 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
11093 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
11097 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
11101 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
11105 static struct TokenInfo editor_setup_tokens[] =
11109 &setup.editor.el_classic, "editor.el_classic"
11113 &setup.editor.el_custom, "editor.el_custom"
11117 &setup.editor.el_user_defined, "editor.el_user_defined"
11121 &setup.editor.el_dynamic, "editor.el_dynamic"
11125 &setup.editor.el_headlines, "editor.el_headlines"
11129 &setup.editor.show_element_token, "editor.show_element_token"
11133 &setup.editor.fast_game_start, "editor.fast_game_start"
11137 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
11141 static struct TokenInfo editor_cascade_setup_tokens[] =
11145 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
11149 &setup.editor_cascade.el_bdx, "editor.cascade.el_bdx"
11153 &setup.editor_cascade.el_bdx_effects, "editor.cascade.el_bdx_effects"
11157 &setup.editor_cascade.el_em, "editor.cascade.el_em"
11161 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
11165 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
11169 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
11173 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
11177 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
11181 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
11185 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
11189 &setup.editor_cascade.el_df, "editor.cascade.el_df"
11193 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
11197 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
11201 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
11205 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
11209 &setup.editor_cascade.el_es, "editor.cascade.el_es"
11213 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
11217 &setup.editor_cascade.el_user, "editor.cascade.el_user"
11221 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
11225 static struct TokenInfo shortcut_setup_tokens[] =
11229 &setup.shortcut.save_game, "shortcut.save_game"
11233 &setup.shortcut.load_game, "shortcut.load_game"
11237 &setup.shortcut.restart_game, "shortcut.restart_game"
11241 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
11245 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
11249 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
11253 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
11257 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
11261 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
11265 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
11269 &setup.shortcut.tape_eject, "shortcut.tape_eject"
11273 &setup.shortcut.tape_extra, "shortcut.tape_extra"
11277 &setup.shortcut.tape_stop, "shortcut.tape_stop"
11281 &setup.shortcut.tape_pause, "shortcut.tape_pause"
11285 &setup.shortcut.tape_record, "shortcut.tape_record"
11289 &setup.shortcut.tape_play, "shortcut.tape_play"
11293 &setup.shortcut.sound_simple, "shortcut.sound_simple"
11297 &setup.shortcut.sound_loops, "shortcut.sound_loops"
11301 &setup.shortcut.sound_music, "shortcut.sound_music"
11305 &setup.shortcut.snap_left, "shortcut.snap_left"
11309 &setup.shortcut.snap_right, "shortcut.snap_right"
11313 &setup.shortcut.snap_up, "shortcut.snap_up"
11317 &setup.shortcut.snap_down, "shortcut.snap_down"
11321 &setup.shortcut.speed_fast, "shortcut.speed_fast"
11325 &setup.shortcut.speed_slow, "shortcut.speed_slow"
11329 static struct SetupInputInfo setup_input;
11330 static struct TokenInfo player_setup_tokens[] =
11334 &setup_input.use_joystick, ".use_joystick"
11338 &setup_input.joy.device_name, ".joy.device_name"
11342 &setup_input.joy.xleft, ".joy.xleft"
11346 &setup_input.joy.xmiddle, ".joy.xmiddle"
11350 &setup_input.joy.xright, ".joy.xright"
11354 &setup_input.joy.yupper, ".joy.yupper"
11358 &setup_input.joy.ymiddle, ".joy.ymiddle"
11362 &setup_input.joy.ylower, ".joy.ylower"
11366 &setup_input.joy.snap, ".joy.snap_field"
11370 &setup_input.joy.drop, ".joy.place_bomb"
11374 &setup_input.key.left, ".key.move_left"
11378 &setup_input.key.right, ".key.move_right"
11382 &setup_input.key.up, ".key.move_up"
11386 &setup_input.key.down, ".key.move_down"
11390 &setup_input.key.snap, ".key.snap_field"
11394 &setup_input.key.drop, ".key.place_bomb"
11398 static struct TokenInfo system_setup_tokens[] =
11402 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
11406 &setup.system.sdl_videodriver, "system.sdl_videodriver"
11410 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
11414 &setup.system.audio_fragment_size, "system.audio_fragment_size"
11418 static struct TokenInfo internal_setup_tokens[] =
11422 &setup.internal.program_title, "program_title"
11426 &setup.internal.program_version, "program_version"
11430 &setup.internal.program_author, "program_author"
11434 &setup.internal.program_email, "program_email"
11438 &setup.internal.program_website, "program_website"
11442 &setup.internal.program_copyright, "program_copyright"
11446 &setup.internal.program_company, "program_company"
11450 &setup.internal.program_icon_file, "program_icon_file"
11454 &setup.internal.default_graphics_set, "default_graphics_set"
11458 &setup.internal.default_sounds_set, "default_sounds_set"
11462 &setup.internal.default_music_set, "default_music_set"
11466 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11470 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11474 &setup.internal.fallback_music_file, "fallback_music_file"
11478 &setup.internal.default_level_series, "default_level_series"
11482 &setup.internal.default_window_width, "default_window_width"
11486 &setup.internal.default_window_height, "default_window_height"
11490 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11494 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11498 &setup.internal.create_user_levelset, "create_user_levelset"
11502 &setup.internal.info_screens_from_main, "info_screens_from_main"
11506 &setup.internal.menu_game, "menu_game"
11510 &setup.internal.menu_engines, "menu_engines"
11514 &setup.internal.menu_editor, "menu_editor"
11518 &setup.internal.menu_graphics, "menu_graphics"
11522 &setup.internal.menu_sound, "menu_sound"
11526 &setup.internal.menu_artwork, "menu_artwork"
11530 &setup.internal.menu_input, "menu_input"
11534 &setup.internal.menu_touch, "menu_touch"
11538 &setup.internal.menu_shortcuts, "menu_shortcuts"
11542 &setup.internal.menu_exit, "menu_exit"
11546 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11550 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11554 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11558 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11562 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11566 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11570 &setup.internal.menu_shortcuts_speed, "menu_shortcuts_speed"
11574 &setup.internal.info_title, "info_title"
11578 &setup.internal.info_elements, "info_elements"
11582 &setup.internal.info_music, "info_music"
11586 &setup.internal.info_credits, "info_credits"
11590 &setup.internal.info_program, "info_program"
11594 &setup.internal.info_version, "info_version"
11598 &setup.internal.info_levelset, "info_levelset"
11602 &setup.internal.info_exit, "info_exit"
11606 static struct TokenInfo debug_setup_tokens[] =
11610 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11614 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11618 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11622 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11626 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11630 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11634 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11638 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11642 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11646 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11650 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11654 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11658 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11662 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11666 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11670 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11674 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11678 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11682 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11686 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11690 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11693 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11697 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11700 TYPE_SWITCH_3_STATES,
11701 &setup.debug.xsn_mode, "debug.xsn_mode"
11705 &setup.debug.xsn_percent, "debug.xsn_percent"
11709 static struct TokenInfo options_setup_tokens[] =
11713 &setup.options.verbose, "options.verbose"
11717 &setup.options.debug, "options.debug"
11721 &setup.options.debug_mode, "options.debug_mode"
11725 static void setSetupInfoToDefaults(struct SetupInfo *si)
11729 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11731 si->multiple_users = TRUE;
11734 si->sound_loops = TRUE;
11735 si->sound_music = TRUE;
11736 si->sound_simple = TRUE;
11738 si->global_animations = TRUE;
11739 si->scroll_delay = TRUE;
11740 si->forced_scroll_delay = FALSE;
11741 si->scroll_delay_value = STD_SCROLL_DELAY;
11742 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11743 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11744 si->fade_screens = TRUE;
11745 si->autorecord = TRUE;
11746 si->autorecord_after_replay = TRUE;
11747 si->auto_pause_on_start = FALSE;
11748 si->show_titlescreen = TRUE;
11749 si->quick_doors = FALSE;
11750 si->team_mode = FALSE;
11751 si->handicap = TRUE;
11752 si->skip_levels = TRUE;
11753 si->allow_skipping_levels = STATE_ASK;
11754 si->increment_levels = TRUE;
11755 si->auto_play_next_level = TRUE;
11756 si->count_score_after_game = TRUE;
11757 si->show_scores_after_game = TRUE;
11758 si->time_limit = TRUE;
11759 si->fullscreen = FALSE;
11760 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11761 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11762 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11763 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11764 si->ask_on_escape = TRUE;
11765 si->ask_on_escape_editor = TRUE;
11766 si->ask_on_game_over = TRUE;
11767 si->ask_on_quit_game = TRUE;
11768 si->ask_on_quit_program = TRUE;
11769 si->quick_switch = FALSE;
11770 si->input_on_focus = FALSE;
11771 si->prefer_aga_graphics = TRUE;
11772 si->prefer_lowpass_sounds = FALSE;
11773 si->prefer_extra_panel_items = TRUE;
11774 si->game_speed_extended = FALSE;
11775 si->game_frame_delay = GAME_FRAME_DELAY;
11776 si->default_game_engine_type = GAME_ENGINE_TYPE_RND;
11777 si->bd_skip_uncovering = FALSE;
11778 si->bd_skip_hatching = FALSE;
11779 si->bd_scroll_delay = TRUE;
11780 si->bd_show_invisible_outbox = FALSE;
11781 si->bd_smooth_movements = STATE_TRUE;
11782 si->bd_pushing_graphics = STATE_TRUE;
11783 si->bd_up_down_graphics = STATE_TRUE;
11784 si->bd_falling_sounds = STATE_AUTO;
11785 si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11786 si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11787 si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11788 si->bd_default_color_type = GD_DEFAULT_COLOR_TYPE;
11789 si->bd_random_colors = FALSE;
11790 si->sp_show_border_elements = FALSE;
11791 si->small_game_graphics = FALSE;
11792 si->show_load_save_buttons = FALSE;
11793 si->show_undo_redo_buttons = FALSE;
11794 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11796 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11797 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11798 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11800 si->override_level_graphics = STATE_FALSE;
11801 si->override_level_sounds = STATE_FALSE;
11802 si->override_level_music = STATE_FALSE;
11804 si->volume_simple = 100; // percent
11805 si->volume_loops = 100; // percent
11806 si->volume_music = 100; // percent
11807 si->audio_sample_rate_44100 = FALSE;
11809 si->network_mode = FALSE;
11810 si->network_player_nr = 0; // first player
11811 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11813 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11814 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11815 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11816 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11817 si->touch.draw_outlined = TRUE;
11818 si->touch.draw_pressed = TRUE;
11820 for (i = 0; i < 2; i++)
11822 char *default_grid_button[6][2] =
11828 { "111222", " vv " },
11829 { "111222", " vv " }
11831 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11832 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11833 int min_xsize = MIN(6, grid_xsize);
11834 int min_ysize = MIN(6, grid_ysize);
11835 int startx = grid_xsize - min_xsize;
11836 int starty = grid_ysize - min_ysize;
11839 // virtual buttons grid can only be set to defaults if video is initialized
11840 // (this will be repeated if virtual buttons are not loaded from setup file)
11841 if (video.initialized)
11843 si->touch.grid_xsize[i] = grid_xsize;
11844 si->touch.grid_ysize[i] = grid_ysize;
11848 si->touch.grid_xsize[i] = -1;
11849 si->touch.grid_ysize[i] = -1;
11852 for (x = 0; x < MAX_GRID_XSIZE; x++)
11853 for (y = 0; y < MAX_GRID_YSIZE; y++)
11854 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11856 for (x = 0; x < min_xsize; x++)
11857 for (y = 0; y < min_ysize; y++)
11858 si->touch.grid_button[i][x][starty + y] =
11859 default_grid_button[y][0][x];
11861 for (x = 0; x < min_xsize; x++)
11862 for (y = 0; y < min_ysize; y++)
11863 si->touch.grid_button[i][startx + x][starty + y] =
11864 default_grid_button[y][1][x];
11867 si->touch.grid_initialized = video.initialized;
11869 si->touch.overlay_buttons = FALSE;
11871 si->editor.el_boulderdash = TRUE;
11872 si->editor.el_boulderdash_native = TRUE;
11873 si->editor.el_boulderdash_effects = TRUE;
11874 si->editor.el_emerald_mine = TRUE;
11875 si->editor.el_emerald_mine_club = TRUE;
11876 si->editor.el_more = TRUE;
11877 si->editor.el_sokoban = TRUE;
11878 si->editor.el_supaplex = TRUE;
11879 si->editor.el_diamond_caves = TRUE;
11880 si->editor.el_dx_boulderdash = TRUE;
11882 si->editor.el_mirror_magic = TRUE;
11883 si->editor.el_deflektor = TRUE;
11885 si->editor.el_chars = TRUE;
11886 si->editor.el_steel_chars = TRUE;
11888 si->editor.el_classic = TRUE;
11889 si->editor.el_custom = TRUE;
11891 si->editor.el_user_defined = FALSE;
11892 si->editor.el_dynamic = TRUE;
11894 si->editor.el_headlines = TRUE;
11896 si->editor.show_element_token = FALSE;
11897 si->editor.fast_game_start = FALSE;
11899 si->editor.show_read_only_warning = TRUE;
11901 si->editor.use_template_for_new_levels = TRUE;
11903 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11904 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11905 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11906 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11907 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11909 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11910 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11911 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11912 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11913 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11915 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11916 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11917 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11918 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11919 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11920 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11922 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11923 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11924 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11926 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11927 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11928 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11929 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11931 si->shortcut.speed_fast = DEFAULT_KEY_SPEED_FAST;
11932 si->shortcut.speed_slow = DEFAULT_KEY_SPEED_SLOW;
11934 for (i = 0; i < MAX_PLAYERS; i++)
11936 si->input[i].use_joystick = FALSE;
11937 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11938 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11939 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11940 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11941 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11942 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11943 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11944 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11945 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11946 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11947 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11948 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11949 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11950 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11951 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11954 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11955 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11956 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11957 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11959 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11960 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11961 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11962 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11963 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11964 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11965 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11967 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11969 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11970 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11971 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11973 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11974 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11975 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11977 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11978 si->internal.choose_from_top_leveldir = FALSE;
11979 si->internal.show_scaling_in_title = TRUE;
11980 si->internal.create_user_levelset = TRUE;
11981 si->internal.info_screens_from_main = FALSE;
11983 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11984 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11986 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11987 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11988 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11989 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11990 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11991 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11992 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11993 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11994 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11995 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11997 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11998 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11999 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
12000 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
12001 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
12002 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
12003 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
12004 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
12005 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
12006 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
12008 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
12009 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
12011 si->debug.show_frames_per_second = FALSE;
12013 si->debug.xsn_mode = STATE_AUTO;
12014 si->debug.xsn_percent = 0;
12016 si->options.verbose = FALSE;
12017 si->options.debug = FALSE;
12018 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
12020 #if defined(PLATFORM_ANDROID)
12021 si->fullscreen = TRUE;
12022 si->touch.overlay_buttons = TRUE;
12025 setHideSetupEntry(&setup.debug.xsn_mode);
12028 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
12030 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
12033 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
12035 si->player_uuid = NULL; // (will be set later)
12036 si->player_version = 1; // (will be set later)
12038 si->use_api_server = TRUE;
12039 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
12040 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
12041 si->ask_for_uploading_tapes = TRUE;
12042 si->ask_for_remaining_tapes = FALSE;
12043 si->provide_uploading_tapes = TRUE;
12044 si->ask_for_using_api_server = TRUE;
12045 si->has_remaining_tapes = FALSE;
12048 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
12050 si->editor_cascade.el_bd = TRUE;
12051 si->editor_cascade.el_bdx = TRUE;
12052 si->editor_cascade.el_bdx_effects = FALSE;
12053 si->editor_cascade.el_em = TRUE;
12054 si->editor_cascade.el_emc = TRUE;
12055 si->editor_cascade.el_rnd = TRUE;
12056 si->editor_cascade.el_sb = TRUE;
12057 si->editor_cascade.el_sp = TRUE;
12058 si->editor_cascade.el_dc = TRUE;
12059 si->editor_cascade.el_dx = TRUE;
12061 si->editor_cascade.el_mm = TRUE;
12062 si->editor_cascade.el_df = TRUE;
12064 si->editor_cascade.el_chars = FALSE;
12065 si->editor_cascade.el_steel_chars = FALSE;
12066 si->editor_cascade.el_ce = FALSE;
12067 si->editor_cascade.el_ge = FALSE;
12068 si->editor_cascade.el_es = FALSE;
12069 si->editor_cascade.el_ref = FALSE;
12070 si->editor_cascade.el_user = FALSE;
12071 si->editor_cascade.el_dynamic = FALSE;
12074 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
12076 static char *getHideSetupToken(void *setup_value)
12078 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
12080 if (setup_value != NULL)
12081 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
12083 return hide_setup_token;
12086 void setHideSetupEntry(void *setup_value)
12088 char *hide_setup_token = getHideSetupToken(setup_value);
12090 if (hide_setup_hash == NULL)
12091 hide_setup_hash = newSetupFileHash();
12093 if (setup_value != NULL)
12094 setHashEntry(hide_setup_hash, hide_setup_token, "");
12097 void removeHideSetupEntry(void *setup_value)
12099 char *hide_setup_token = getHideSetupToken(setup_value);
12101 if (setup_value != NULL)
12102 removeHashEntry(hide_setup_hash, hide_setup_token);
12105 boolean hideSetupEntry(void *setup_value)
12107 char *hide_setup_token = getHideSetupToken(setup_value);
12109 return (setup_value != NULL &&
12110 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
12113 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
12114 struct TokenInfo *token_info,
12115 int token_nr, char *token_text)
12117 char *token_hide_text = getStringCat2(token_text, ".hide");
12118 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
12120 // set the value of this setup option in the setup option structure
12121 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
12123 // check if this setup option should be hidden in the setup menu
12124 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
12125 setHideSetupEntry(token_info[token_nr].value);
12127 free(token_hide_text);
12130 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
12131 struct TokenInfo *token_info,
12134 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
12135 token_info[token_nr].text);
12138 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
12142 if (!setup_file_hash)
12145 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12146 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
12148 setup.touch.grid_initialized = TRUE;
12149 for (i = 0; i < 2; i++)
12151 int grid_xsize = setup.touch.grid_xsize[i];
12152 int grid_ysize = setup.touch.grid_ysize[i];
12155 // if virtual buttons are not loaded from setup file, repeat initializing
12156 // virtual buttons grid with default values later when video is initialized
12157 if (grid_xsize == -1 ||
12160 setup.touch.grid_initialized = FALSE;
12165 for (y = 0; y < grid_ysize; y++)
12167 char token_string[MAX_LINE_LEN];
12169 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12171 char *value_string = getHashEntry(setup_file_hash, token_string);
12173 if (value_string == NULL)
12176 for (x = 0; x < grid_xsize; x++)
12178 char c = value_string[x];
12180 setup.touch.grid_button[i][x][y] =
12181 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
12186 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12187 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
12189 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12190 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
12192 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12196 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12198 setup_input = setup.input[pnr];
12199 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12201 char full_token[100];
12203 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
12204 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12207 setup.input[pnr] = setup_input;
12210 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12211 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12213 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12214 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12216 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12217 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12219 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12220 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12222 setHideRelatedSetupEntries();
12225 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12229 if (!setup_file_hash)
12232 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12233 setSetupInfo(auto_setup_tokens, i,
12234 getHashEntry(setup_file_hash,
12235 auto_setup_tokens[i].text));
12238 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12242 if (!setup_file_hash)
12245 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12246 setSetupInfo(server_setup_tokens, i,
12247 getHashEntry(setup_file_hash,
12248 server_setup_tokens[i].text));
12251 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12255 if (!setup_file_hash)
12258 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12259 setSetupInfo(editor_cascade_setup_tokens, i,
12260 getHashEntry(setup_file_hash,
12261 editor_cascade_setup_tokens[i].text));
12264 void LoadUserNames(void)
12266 int last_user_nr = user.nr;
12269 if (global.user_names != NULL)
12271 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12272 checked_free(global.user_names[i]);
12274 checked_free(global.user_names);
12277 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12279 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12283 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12285 if (setup_file_hash)
12287 char *player_name = getHashEntry(setup_file_hash, "player_name");
12289 global.user_names[i] = getFixedUserName(player_name);
12291 freeSetupFileHash(setup_file_hash);
12294 if (global.user_names[i] == NULL)
12295 global.user_names[i] = getStringCopy(getDefaultUserName(i));
12298 user.nr = last_user_nr;
12301 void LoadSetupFromFilename(char *filename)
12303 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12305 if (setup_file_hash)
12307 decodeSetupFileHash_Default(setup_file_hash);
12309 freeSetupFileHash(setup_file_hash);
12313 Debug("setup", "using default setup values");
12317 static void LoadSetup_SpecialPostProcessing(void)
12319 char *player_name_new;
12321 // needed to work around problems with fixed length strings
12322 player_name_new = getFixedUserName(setup.player_name);
12323 free(setup.player_name);
12324 setup.player_name = player_name_new;
12326 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12327 if (setup.scroll_delay == FALSE)
12329 setup.scroll_delay_value = MIN_SCROLL_DELAY;
12330 setup.scroll_delay = TRUE; // now always "on"
12333 // make sure that scroll delay value stays inside valid range
12334 setup.scroll_delay_value =
12335 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12338 void LoadSetup_Default(void)
12342 // always start with reliable default values
12343 setSetupInfoToDefaults(&setup);
12345 // try to load setup values from default setup file
12346 filename = getDefaultSetupFilename();
12348 if (fileExists(filename))
12349 LoadSetupFromFilename(filename);
12351 // try to load setup values from platform setup file
12352 filename = getPlatformSetupFilename();
12354 if (fileExists(filename))
12355 LoadSetupFromFilename(filename);
12357 // try to load setup values from user setup file
12358 filename = getSetupFilename();
12360 LoadSetupFromFilename(filename);
12362 LoadSetup_SpecialPostProcessing();
12365 void LoadSetup_AutoSetup(void)
12367 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12368 SetupFileHash *setup_file_hash = NULL;
12370 // always start with reliable default values
12371 setSetupInfoToDefaults_AutoSetup(&setup);
12373 setup_file_hash = loadSetupFileHash(filename);
12375 if (setup_file_hash)
12377 decodeSetupFileHash_AutoSetup(setup_file_hash);
12379 freeSetupFileHash(setup_file_hash);
12385 void LoadSetup_ServerSetup(void)
12387 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12388 SetupFileHash *setup_file_hash = NULL;
12390 // always start with reliable default values
12391 setSetupInfoToDefaults_ServerSetup(&setup);
12393 setup_file_hash = loadSetupFileHash(filename);
12395 if (setup_file_hash)
12397 decodeSetupFileHash_ServerSetup(setup_file_hash);
12399 freeSetupFileHash(setup_file_hash);
12404 if (setup.player_uuid == NULL)
12406 // player UUID does not yet exist in setup file
12407 setup.player_uuid = getStringCopy(getUUID());
12408 setup.player_version = 2;
12410 SaveSetup_ServerSetup();
12414 void LoadSetup_EditorCascade(void)
12416 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12417 SetupFileHash *setup_file_hash = NULL;
12419 // always start with reliable default values
12420 setSetupInfoToDefaults_EditorCascade(&setup);
12422 setup_file_hash = loadSetupFileHash(filename);
12424 if (setup_file_hash)
12426 decodeSetupFileHash_EditorCascade(setup_file_hash);
12428 freeSetupFileHash(setup_file_hash);
12434 void LoadSetup(void)
12436 LoadSetup_Default();
12437 LoadSetup_AutoSetup();
12438 LoadSetup_ServerSetup();
12439 LoadSetup_EditorCascade();
12442 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12443 char *mapping_line)
12445 char mapping_guid[MAX_LINE_LEN];
12446 char *mapping_start, *mapping_end;
12448 // get GUID from game controller mapping line: copy complete line
12449 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12450 mapping_guid[MAX_LINE_LEN - 1] = '\0';
12452 // get GUID from game controller mapping line: cut after GUID part
12453 mapping_start = strchr(mapping_guid, ',');
12454 if (mapping_start != NULL)
12455 *mapping_start = '\0';
12457 // cut newline from game controller mapping line
12458 mapping_end = strchr(mapping_line, '\n');
12459 if (mapping_end != NULL)
12460 *mapping_end = '\0';
12462 // add mapping entry to game controller mappings hash
12463 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12466 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12471 if (!(file = fopen(filename, MODE_READ)))
12473 Warn("cannot read game controller mappings file '%s'", filename);
12478 while (!feof(file))
12480 char line[MAX_LINE_LEN];
12482 if (!fgets(line, MAX_LINE_LEN, file))
12485 addGameControllerMappingToHash(mappings_hash, line);
12491 void SaveSetup_Default(void)
12493 char *filename = getSetupFilename();
12497 InitUserDataDirectory();
12499 if (!(file = fopen(filename, MODE_WRITE)))
12501 Warn("cannot write setup file '%s'", filename);
12506 fprintFileHeader(file, SETUP_FILENAME);
12508 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12510 // just to make things nicer :)
12511 if (global_setup_tokens[i].value == &setup.multiple_users ||
12512 global_setup_tokens[i].value == &setup.sound ||
12513 global_setup_tokens[i].value == &setup.graphics_set ||
12514 global_setup_tokens[i].value == &setup.volume_simple ||
12515 global_setup_tokens[i].value == &setup.network_mode ||
12516 global_setup_tokens[i].value == &setup.touch.control_type ||
12517 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12518 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12519 fprintf(file, "\n");
12521 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12524 for (i = 0; i < 2; i++)
12526 int grid_xsize = setup.touch.grid_xsize[i];
12527 int grid_ysize = setup.touch.grid_ysize[i];
12530 fprintf(file, "\n");
12532 for (y = 0; y < grid_ysize; y++)
12534 char token_string[MAX_LINE_LEN];
12535 char value_string[MAX_LINE_LEN];
12537 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12539 for (x = 0; x < grid_xsize; x++)
12541 char c = setup.touch.grid_button[i][x][y];
12543 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12546 value_string[grid_xsize] = '\0';
12548 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12552 fprintf(file, "\n");
12553 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12554 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12556 fprintf(file, "\n");
12557 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12558 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12560 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12564 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12565 fprintf(file, "\n");
12567 setup_input = setup.input[pnr];
12568 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12569 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12572 fprintf(file, "\n");
12573 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12574 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12576 // (internal setup values not saved to user setup file)
12578 fprintf(file, "\n");
12579 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12580 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12581 setup.debug.xsn_mode != STATE_AUTO)
12582 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12584 fprintf(file, "\n");
12585 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12586 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12590 SetFilePermissions(filename, PERMS_PRIVATE);
12593 void SaveSetup_AutoSetup(void)
12595 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12599 InitUserDataDirectory();
12601 if (!(file = fopen(filename, MODE_WRITE)))
12603 Warn("cannot write auto setup file '%s'", filename);
12610 fprintFileHeader(file, AUTOSETUP_FILENAME);
12612 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12613 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12617 SetFilePermissions(filename, PERMS_PRIVATE);
12622 void SaveSetup_ServerSetup(void)
12624 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12628 InitUserDataDirectory();
12630 if (!(file = fopen(filename, MODE_WRITE)))
12632 Warn("cannot write server setup file '%s'", filename);
12639 fprintFileHeader(file, SERVERSETUP_FILENAME);
12641 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12643 // just to make things nicer :)
12644 if (server_setup_tokens[i].value == &setup.use_api_server)
12645 fprintf(file, "\n");
12647 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12652 SetFilePermissions(filename, PERMS_PRIVATE);
12657 void SaveSetup_EditorCascade(void)
12659 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12663 InitUserDataDirectory();
12665 if (!(file = fopen(filename, MODE_WRITE)))
12667 Warn("cannot write editor cascade state file '%s'", filename);
12674 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12676 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12677 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12681 SetFilePermissions(filename, PERMS_PRIVATE);
12686 void SaveSetup(void)
12688 SaveSetup_Default();
12689 SaveSetup_AutoSetup();
12690 SaveSetup_ServerSetup();
12691 SaveSetup_EditorCascade();
12694 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12699 if (!(file = fopen(filename, MODE_WRITE)))
12701 Warn("cannot write game controller mappings file '%s'", filename);
12706 BEGIN_HASH_ITERATION(mappings_hash, itr)
12708 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12710 END_HASH_ITERATION(mappings_hash, itr)
12715 void SaveSetup_AddGameControllerMapping(char *mapping)
12717 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12718 SetupFileHash *mappings_hash = newSetupFileHash();
12720 InitUserDataDirectory();
12722 // load existing personal game controller mappings
12723 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12725 // add new mapping to personal game controller mappings
12726 addGameControllerMappingToHash(mappings_hash, mapping);
12728 // save updated personal game controller mappings
12729 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12731 freeSetupFileHash(mappings_hash);
12735 void LoadCustomElementDescriptions(void)
12737 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12738 SetupFileHash *setup_file_hash;
12741 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12743 if (element_info[i].custom_description != NULL)
12745 free(element_info[i].custom_description);
12746 element_info[i].custom_description = NULL;
12750 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12753 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12755 char *token = getStringCat2(element_info[i].token_name, ".name");
12756 char *value = getHashEntry(setup_file_hash, token);
12759 element_info[i].custom_description = getStringCopy(value);
12764 freeSetupFileHash(setup_file_hash);
12767 static int getElementFromToken(char *token)
12769 char *value = getHashEntry(element_token_hash, token);
12772 return atoi(value);
12774 Warn("unknown element token '%s'", token);
12776 return EL_UNDEFINED;
12779 void FreeGlobalAnimEventInfo(void)
12781 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12783 if (gaei->event_list == NULL)
12788 for (i = 0; i < gaei->num_event_lists; i++)
12790 checked_free(gaei->event_list[i]->event_value);
12791 checked_free(gaei->event_list[i]);
12794 checked_free(gaei->event_list);
12796 gaei->event_list = NULL;
12797 gaei->num_event_lists = 0;
12800 static int AddGlobalAnimEventList(void)
12802 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12803 int list_pos = gaei->num_event_lists++;
12805 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12806 sizeof(struct GlobalAnimEventListInfo *));
12808 gaei->event_list[list_pos] =
12809 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12811 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12813 gaeli->event_value = NULL;
12814 gaeli->num_event_values = 0;
12819 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12821 // do not add empty global animation events
12822 if (event_value == ANIM_EVENT_NONE)
12825 // if list position is undefined, create new list
12826 if (list_pos == ANIM_EVENT_UNDEFINED)
12827 list_pos = AddGlobalAnimEventList();
12829 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12830 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12831 int value_pos = gaeli->num_event_values++;
12833 gaeli->event_value = checked_realloc(gaeli->event_value,
12834 gaeli->num_event_values * sizeof(int *));
12836 gaeli->event_value[value_pos] = event_value;
12841 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12843 if (list_pos == ANIM_EVENT_UNDEFINED)
12844 return ANIM_EVENT_NONE;
12846 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12847 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12849 return gaeli->event_value[value_pos];
12852 int GetGlobalAnimEventValueCount(int list_pos)
12854 if (list_pos == ANIM_EVENT_UNDEFINED)
12857 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12858 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12860 return gaeli->num_event_values;
12863 // This function checks if a string <s> of the format "string1, string2, ..."
12864 // exactly contains a string <s_contained>.
12866 static boolean string_has_parameter(char *s, char *s_contained)
12870 if (s == NULL || s_contained == NULL)
12873 if (strlen(s_contained) > strlen(s))
12876 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12878 char next_char = s[strlen(s_contained)];
12880 // check if next character is delimiter or whitespace
12881 if (next_char == ',' || next_char == '\0' ||
12882 next_char == ' ' || next_char == '\t')
12886 // check if string contains another parameter string after a comma
12887 substring = strchr(s, ',');
12888 if (substring == NULL) // string does not contain a comma
12891 // advance string pointer to next character after the comma
12894 // skip potential whitespaces after the comma
12895 while (*substring == ' ' || *substring == '\t')
12898 return string_has_parameter(substring, s_contained);
12901 static int get_anim_parameter_value_ce(char *s)
12904 char *pattern_1 = "ce_change:custom_";
12905 char *pattern_2 = ".page_";
12906 int pattern_1_len = strlen(pattern_1);
12907 char *matching_char = strstr(s_ptr, pattern_1);
12908 int result = ANIM_EVENT_NONE;
12910 if (matching_char == NULL)
12911 return ANIM_EVENT_NONE;
12913 result = ANIM_EVENT_CE_CHANGE;
12915 s_ptr = matching_char + pattern_1_len;
12917 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12918 if (*s_ptr >= '0' && *s_ptr <= '9')
12920 int gic_ce_nr = (*s_ptr++ - '0');
12922 if (*s_ptr >= '0' && *s_ptr <= '9')
12924 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12926 if (*s_ptr >= '0' && *s_ptr <= '9')
12927 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12930 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12931 return ANIM_EVENT_NONE;
12933 // custom element stored as 0 to 255
12936 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12940 // invalid custom element number specified
12942 return ANIM_EVENT_NONE;
12945 // check for change page number ("page_X" or "page_XX") (optional)
12946 if (strPrefix(s_ptr, pattern_2))
12948 s_ptr += strlen(pattern_2);
12950 if (*s_ptr >= '0' && *s_ptr <= '9')
12952 int gic_page_nr = (*s_ptr++ - '0');
12954 if (*s_ptr >= '0' && *s_ptr <= '9')
12955 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12957 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12958 return ANIM_EVENT_NONE;
12960 // change page stored as 1 to 32 (0 means "all change pages")
12962 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12966 // invalid animation part number specified
12968 return ANIM_EVENT_NONE;
12972 // discard result if next character is neither delimiter nor whitespace
12973 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12974 *s_ptr == ' ' || *s_ptr == '\t'))
12975 return ANIM_EVENT_NONE;
12980 static int get_anim_parameter_value(char *s)
12982 int event_value[] =
12990 char *pattern_1[] =
12998 char *pattern_2 = ".part_";
12999 char *matching_char = NULL;
13001 int pattern_1_len = 0;
13002 int result = ANIM_EVENT_NONE;
13005 result = get_anim_parameter_value_ce(s);
13007 if (result != ANIM_EVENT_NONE)
13010 for (i = 0; i < ARRAY_SIZE(event_value); i++)
13012 matching_char = strstr(s_ptr, pattern_1[i]);
13013 pattern_1_len = strlen(pattern_1[i]);
13014 result = event_value[i];
13016 if (matching_char != NULL)
13020 if (matching_char == NULL)
13021 return ANIM_EVENT_NONE;
13023 s_ptr = matching_char + pattern_1_len;
13025 // check for main animation number ("anim_X" or "anim_XX")
13026 if (*s_ptr >= '0' && *s_ptr <= '9')
13028 int gic_anim_nr = (*s_ptr++ - '0');
13030 if (*s_ptr >= '0' && *s_ptr <= '9')
13031 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
13033 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
13034 return ANIM_EVENT_NONE;
13036 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
13040 // invalid main animation number specified
13042 return ANIM_EVENT_NONE;
13045 // check for animation part number ("part_X" or "part_XX") (optional)
13046 if (strPrefix(s_ptr, pattern_2))
13048 s_ptr += strlen(pattern_2);
13050 if (*s_ptr >= '0' && *s_ptr <= '9')
13052 int gic_part_nr = (*s_ptr++ - '0');
13054 if (*s_ptr >= '0' && *s_ptr <= '9')
13055 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
13057 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
13058 return ANIM_EVENT_NONE;
13060 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
13064 // invalid animation part number specified
13066 return ANIM_EVENT_NONE;
13070 // discard result if next character is neither delimiter nor whitespace
13071 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
13072 *s_ptr == ' ' || *s_ptr == '\t'))
13073 return ANIM_EVENT_NONE;
13078 static int get_anim_parameter_values(char *s)
13080 int list_pos = ANIM_EVENT_UNDEFINED;
13081 int event_value = ANIM_EVENT_DEFAULT;
13083 if (string_has_parameter(s, "any"))
13084 event_value |= ANIM_EVENT_ANY;
13086 if (string_has_parameter(s, "click:self") ||
13087 string_has_parameter(s, "click") ||
13088 string_has_parameter(s, "self"))
13089 event_value |= ANIM_EVENT_SELF;
13091 if (string_has_parameter(s, "unclick:any"))
13092 event_value |= ANIM_EVENT_UNCLICK_ANY;
13094 // if animation event found, add it to global animation event list
13095 if (event_value != ANIM_EVENT_NONE)
13096 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
13100 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
13101 event_value = get_anim_parameter_value(s);
13103 // if animation event found, add it to global animation event list
13104 if (event_value != ANIM_EVENT_NONE)
13105 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
13107 // continue with next part of the string, starting with next comma
13108 s = strchr(s + 1, ',');
13114 static int get_anim_action_parameter_value(char *token)
13116 // check most common default case first to massively speed things up
13117 if (strEqual(token, ARG_UNDEFINED))
13118 return ANIM_EVENT_ACTION_NONE;
13120 int result = getImageIDFromToken(token);
13124 char *gfx_token = getStringCat2("gfx.", token);
13126 result = getImageIDFromToken(gfx_token);
13128 checked_free(gfx_token);
13133 Key key = getKeyFromX11KeyName(token);
13135 if (key != KSYM_UNDEFINED)
13136 result = -(int)key;
13143 result = get_hash_from_string(token); // unsigned int => int
13144 result = ABS(result); // may be negative now
13145 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
13147 setHashEntry(anim_url_hash, int2str(result, 0), token);
13152 result = ANIM_EVENT_ACTION_NONE;
13157 int get_parameter_value(char *value_raw, char *suffix, int type)
13159 char *value = getStringToLower(value_raw);
13160 int result = 0; // probably a save default value
13162 if (strEqual(suffix, ".direction"))
13164 result = (strEqual(value, "left") ? MV_LEFT :
13165 strEqual(value, "right") ? MV_RIGHT :
13166 strEqual(value, "up") ? MV_UP :
13167 strEqual(value, "down") ? MV_DOWN : MV_NONE);
13169 else if (strEqual(suffix, ".position"))
13171 result = (strEqual(value, "left") ? POS_LEFT :
13172 strEqual(value, "right") ? POS_RIGHT :
13173 strEqual(value, "top") ? POS_TOP :
13174 strEqual(value, "upper") ? POS_UPPER :
13175 strEqual(value, "middle") ? POS_MIDDLE :
13176 strEqual(value, "lower") ? POS_LOWER :
13177 strEqual(value, "bottom") ? POS_BOTTOM :
13178 strEqual(value, "any") ? POS_ANY :
13179 strEqual(value, "ce") ? POS_CE :
13180 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
13181 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
13183 else if (strEqual(suffix, ".align"))
13185 result = (strEqual(value, "left") ? ALIGN_LEFT :
13186 strEqual(value, "right") ? ALIGN_RIGHT :
13187 strEqual(value, "center") ? ALIGN_CENTER :
13188 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
13190 else if (strEqual(suffix, ".valign"))
13192 result = (strEqual(value, "top") ? VALIGN_TOP :
13193 strEqual(value, "bottom") ? VALIGN_BOTTOM :
13194 strEqual(value, "middle") ? VALIGN_MIDDLE :
13195 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
13197 else if (strEqual(suffix, ".anim_mode"))
13199 result = (string_has_parameter(value, "none") ? ANIM_NONE :
13200 string_has_parameter(value, "loop") ? ANIM_LOOP :
13201 string_has_parameter(value, "linear") ? ANIM_LINEAR :
13202 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
13203 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
13204 string_has_parameter(value, "random") ? ANIM_RANDOM :
13205 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13206 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
13207 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
13208 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
13209 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13210 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
13211 string_has_parameter(value, "centered") ? ANIM_CENTERED :
13212 string_has_parameter(value, "all") ? ANIM_ALL :
13213 string_has_parameter(value, "tiled") ? ANIM_TILED :
13214 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
13217 if (string_has_parameter(value, "once"))
13218 result |= ANIM_ONCE;
13220 if (string_has_parameter(value, "reverse"))
13221 result |= ANIM_REVERSE;
13223 if (string_has_parameter(value, "opaque_player"))
13224 result |= ANIM_OPAQUE_PLAYER;
13226 if (string_has_parameter(value, "static_panel"))
13227 result |= ANIM_STATIC_PANEL;
13229 else if (strEqual(suffix, ".init_event") ||
13230 strEqual(suffix, ".anim_event"))
13232 result = get_anim_parameter_values(value);
13234 else if (strEqual(suffix, ".init_delay_action") ||
13235 strEqual(suffix, ".anim_delay_action") ||
13236 strEqual(suffix, ".post_delay_action") ||
13237 strEqual(suffix, ".init_event_action") ||
13238 strEqual(suffix, ".anim_event_action"))
13240 result = get_anim_action_parameter_value(value_raw);
13242 else if (strEqual(suffix, ".class"))
13244 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13245 get_hash_from_string(value));
13247 else if (strEqual(suffix, ".style"))
13249 result = STYLE_DEFAULT;
13251 if (string_has_parameter(value, "accurate_borders"))
13252 result |= STYLE_ACCURATE_BORDERS;
13254 if (string_has_parameter(value, "inner_corners"))
13255 result |= STYLE_INNER_CORNERS;
13257 if (string_has_parameter(value, "reverse"))
13258 result |= STYLE_REVERSE;
13260 if (string_has_parameter(value, "leftmost_position"))
13261 result |= STYLE_LEFTMOST_POSITION;
13263 if (string_has_parameter(value, "block_clicks"))
13264 result |= STYLE_BLOCK;
13266 if (string_has_parameter(value, "passthrough_clicks"))
13267 result |= STYLE_PASSTHROUGH;
13269 if (string_has_parameter(value, "multiple_actions"))
13270 result |= STYLE_MULTIPLE_ACTIONS;
13272 if (string_has_parameter(value, "consume_ce_event"))
13273 result |= STYLE_CONSUME_CE_EVENT;
13275 else if (strEqual(suffix, ".fade_mode"))
13277 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
13278 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
13279 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
13280 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
13281 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
13282 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
13283 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
13284 FADE_MODE_DEFAULT);
13286 else if (strEqual(suffix, ".auto_delay_unit"))
13288 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
13289 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13290 AUTO_DELAY_UNIT_DEFAULT);
13292 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
13294 result = gfx.get_font_from_token_function(value);
13296 else // generic parameter of type integer or boolean
13298 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13299 type == TYPE_INTEGER ? get_integer_from_string(value) :
13300 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13301 ARG_UNDEFINED_VALUE);
13309 static int get_token_parameter_value(char *token, char *value_raw)
13313 if (token == NULL || value_raw == NULL)
13314 return ARG_UNDEFINED_VALUE;
13316 suffix = strrchr(token, '.');
13317 if (suffix == NULL)
13320 if (strEqual(suffix, ".element"))
13321 return getElementFromToken(value_raw);
13323 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13324 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13327 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13328 boolean ignore_defaults)
13332 for (i = 0; image_config_vars[i].token != NULL; i++)
13334 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13336 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13337 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13341 *image_config_vars[i].value =
13342 get_token_parameter_value(image_config_vars[i].token, value);
13346 void InitMenuDesignSettings_Static(void)
13348 // always start with reliable default values from static default config
13349 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13352 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13356 // the following initializes hierarchical values from static configuration
13358 // special case: initialize "ARG_DEFAULT" values in static default config
13359 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13360 titlescreen_initial_first_default.fade_mode =
13361 title_initial_first_default.fade_mode;
13362 titlescreen_initial_first_default.fade_delay =
13363 title_initial_first_default.fade_delay;
13364 titlescreen_initial_first_default.post_delay =
13365 title_initial_first_default.post_delay;
13366 titlescreen_initial_first_default.auto_delay =
13367 title_initial_first_default.auto_delay;
13368 titlescreen_initial_first_default.auto_delay_unit =
13369 title_initial_first_default.auto_delay_unit;
13370 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
13371 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13372 titlescreen_first_default.post_delay = title_first_default.post_delay;
13373 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13374 titlescreen_first_default.auto_delay_unit =
13375 title_first_default.auto_delay_unit;
13376 titlemessage_initial_first_default.fade_mode =
13377 title_initial_first_default.fade_mode;
13378 titlemessage_initial_first_default.fade_delay =
13379 title_initial_first_default.fade_delay;
13380 titlemessage_initial_first_default.post_delay =
13381 title_initial_first_default.post_delay;
13382 titlemessage_initial_first_default.auto_delay =
13383 title_initial_first_default.auto_delay;
13384 titlemessage_initial_first_default.auto_delay_unit =
13385 title_initial_first_default.auto_delay_unit;
13386 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
13387 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13388 titlemessage_first_default.post_delay = title_first_default.post_delay;
13389 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13390 titlemessage_first_default.auto_delay_unit =
13391 title_first_default.auto_delay_unit;
13393 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
13394 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13395 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13396 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13397 titlescreen_initial_default.auto_delay_unit =
13398 title_initial_default.auto_delay_unit;
13399 titlescreen_default.fade_mode = title_default.fade_mode;
13400 titlescreen_default.fade_delay = title_default.fade_delay;
13401 titlescreen_default.post_delay = title_default.post_delay;
13402 titlescreen_default.auto_delay = title_default.auto_delay;
13403 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13404 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
13405 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13406 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13407 titlemessage_initial_default.auto_delay_unit =
13408 title_initial_default.auto_delay_unit;
13409 titlemessage_default.fade_mode = title_default.fade_mode;
13410 titlemessage_default.fade_delay = title_default.fade_delay;
13411 titlemessage_default.post_delay = title_default.post_delay;
13412 titlemessage_default.auto_delay = title_default.auto_delay;
13413 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13415 // special case: initialize "ARG_DEFAULT" values in static default config
13416 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13417 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13419 titlescreen_initial_first[i] = titlescreen_initial_first_default;
13420 titlescreen_first[i] = titlescreen_first_default;
13421 titlemessage_initial_first[i] = titlemessage_initial_first_default;
13422 titlemessage_first[i] = titlemessage_first_default;
13424 titlescreen_initial[i] = titlescreen_initial_default;
13425 titlescreen[i] = titlescreen_default;
13426 titlemessage_initial[i] = titlemessage_initial_default;
13427 titlemessage[i] = titlemessage_default;
13430 // special case: initialize "ARG_DEFAULT" values in static default config
13431 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13432 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13434 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
13437 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13438 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13439 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13442 // special case: initialize "ARG_DEFAULT" values in static default config
13443 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13444 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13446 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13447 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13448 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13450 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
13453 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13457 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13461 struct XY *dst, *src;
13463 game_buttons_xy[] =
13465 { &game.button.save, &game.button.stop },
13466 { &game.button.pause2, &game.button.pause },
13467 { &game.button.load, &game.button.play },
13468 { &game.button.undo, &game.button.stop },
13469 { &game.button.redo, &game.button.play },
13475 // special case: initialize later added SETUP list size from LEVELS value
13476 if (menu.list_size[GAME_MODE_SETUP] == -1)
13477 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13479 // set default position for snapshot buttons to stop/pause/play buttons
13480 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13481 if ((*game_buttons_xy[i].dst).x == -1 &&
13482 (*game_buttons_xy[i].dst).y == -1)
13483 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13485 // --------------------------------------------------------------------------
13486 // dynamic viewports (including playfield margins, borders and alignments)
13487 // --------------------------------------------------------------------------
13489 // dynamic viewports currently only supported for landscape mode
13490 int display_width = MAX(video.display_width, video.display_height);
13491 int display_height = MIN(video.display_width, video.display_height);
13493 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13495 struct RectWithBorder *vp_window = &viewport.window[i];
13496 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13497 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13498 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13499 boolean dynamic_window_width = (vp_window->min_width != -1);
13500 boolean dynamic_window_height = (vp_window->min_height != -1);
13501 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13502 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13504 // adjust window size if min/max width/height is specified
13506 if (vp_window->min_width != -1)
13508 int window_width = display_width;
13510 // when using static window height, use aspect ratio of display
13511 if (vp_window->min_height == -1)
13512 window_width = vp_window->height * display_width / display_height;
13514 vp_window->width = MAX(vp_window->min_width, window_width);
13517 if (vp_window->min_height != -1)
13519 int window_height = display_height;
13521 // when using static window width, use aspect ratio of display
13522 if (vp_window->min_width == -1)
13523 window_height = vp_window->width * display_height / display_width;
13525 vp_window->height = MAX(vp_window->min_height, window_height);
13528 if (vp_window->max_width != -1)
13529 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13531 if (vp_window->max_height != -1)
13532 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13534 int playfield_width = vp_window->width;
13535 int playfield_height = vp_window->height;
13537 // adjust playfield size and position according to specified margins
13539 playfield_width -= vp_playfield->margin_left;
13540 playfield_width -= vp_playfield->margin_right;
13542 playfield_height -= vp_playfield->margin_top;
13543 playfield_height -= vp_playfield->margin_bottom;
13545 // adjust playfield size if min/max width/height is specified
13547 if (vp_playfield->min_width != -1)
13548 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13550 if (vp_playfield->min_height != -1)
13551 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13553 if (vp_playfield->max_width != -1)
13554 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13556 if (vp_playfield->max_height != -1)
13557 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13559 // adjust playfield position according to specified alignment
13561 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13562 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13563 else if (vp_playfield->align == ALIGN_CENTER)
13564 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13565 else if (vp_playfield->align == ALIGN_RIGHT)
13566 vp_playfield->x += playfield_width - vp_playfield->width;
13568 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13569 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13570 else if (vp_playfield->valign == VALIGN_MIDDLE)
13571 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13572 else if (vp_playfield->valign == VALIGN_BOTTOM)
13573 vp_playfield->y += playfield_height - vp_playfield->height;
13575 vp_playfield->x += vp_playfield->margin_left;
13576 vp_playfield->y += vp_playfield->margin_top;
13578 // adjust individual playfield borders if only default border is specified
13580 if (vp_playfield->border_left == -1)
13581 vp_playfield->border_left = vp_playfield->border_size;
13582 if (vp_playfield->border_right == -1)
13583 vp_playfield->border_right = vp_playfield->border_size;
13584 if (vp_playfield->border_top == -1)
13585 vp_playfield->border_top = vp_playfield->border_size;
13586 if (vp_playfield->border_bottom == -1)
13587 vp_playfield->border_bottom = vp_playfield->border_size;
13589 // set dynamic playfield borders if borders are specified as undefined
13590 // (but only if window size was dynamic and playfield size was static)
13592 if (dynamic_window_width && !dynamic_playfield_width)
13594 if (vp_playfield->border_left == -1)
13596 vp_playfield->border_left = (vp_playfield->x -
13597 vp_playfield->margin_left);
13598 vp_playfield->x -= vp_playfield->border_left;
13599 vp_playfield->width += vp_playfield->border_left;
13602 if (vp_playfield->border_right == -1)
13604 vp_playfield->border_right = (vp_window->width -
13606 vp_playfield->width -
13607 vp_playfield->margin_right);
13608 vp_playfield->width += vp_playfield->border_right;
13612 if (dynamic_window_height && !dynamic_playfield_height)
13614 if (vp_playfield->border_top == -1)
13616 vp_playfield->border_top = (vp_playfield->y -
13617 vp_playfield->margin_top);
13618 vp_playfield->y -= vp_playfield->border_top;
13619 vp_playfield->height += vp_playfield->border_top;
13622 if (vp_playfield->border_bottom == -1)
13624 vp_playfield->border_bottom = (vp_window->height -
13626 vp_playfield->height -
13627 vp_playfield->margin_bottom);
13628 vp_playfield->height += vp_playfield->border_bottom;
13632 // adjust playfield size to be a multiple of a defined alignment tile size
13634 int align_size = vp_playfield->align_size;
13635 int playfield_xtiles = vp_playfield->width / align_size;
13636 int playfield_ytiles = vp_playfield->height / align_size;
13637 int playfield_width_corrected = playfield_xtiles * align_size;
13638 int playfield_height_corrected = playfield_ytiles * align_size;
13639 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13640 i == GFX_SPECIAL_ARG_EDITOR);
13642 if (is_playfield_mode &&
13643 dynamic_playfield_width &&
13644 vp_playfield->width != playfield_width_corrected)
13646 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13648 vp_playfield->width = playfield_width_corrected;
13650 if (vp_playfield->align == ALIGN_LEFT)
13652 vp_playfield->border_left += playfield_xdiff;
13654 else if (vp_playfield->align == ALIGN_RIGHT)
13656 vp_playfield->border_right += playfield_xdiff;
13658 else if (vp_playfield->align == ALIGN_CENTER)
13660 int border_left_diff = playfield_xdiff / 2;
13661 int border_right_diff = playfield_xdiff - border_left_diff;
13663 vp_playfield->border_left += border_left_diff;
13664 vp_playfield->border_right += border_right_diff;
13668 if (is_playfield_mode &&
13669 dynamic_playfield_height &&
13670 vp_playfield->height != playfield_height_corrected)
13672 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13674 vp_playfield->height = playfield_height_corrected;
13676 if (vp_playfield->valign == VALIGN_TOP)
13678 vp_playfield->border_top += playfield_ydiff;
13680 else if (vp_playfield->align == VALIGN_BOTTOM)
13682 vp_playfield->border_right += playfield_ydiff;
13684 else if (vp_playfield->align == VALIGN_MIDDLE)
13686 int border_top_diff = playfield_ydiff / 2;
13687 int border_bottom_diff = playfield_ydiff - border_top_diff;
13689 vp_playfield->border_top += border_top_diff;
13690 vp_playfield->border_bottom += border_bottom_diff;
13694 // adjust door positions according to specified alignment
13696 for (j = 0; j < 2; j++)
13698 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13700 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13701 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13702 else if (vp_door->align == ALIGN_CENTER)
13703 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13704 else if (vp_door->align == ALIGN_RIGHT)
13705 vp_door->x += vp_window->width - vp_door->width;
13707 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13708 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13709 else if (vp_door->valign == VALIGN_MIDDLE)
13710 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13711 else if (vp_door->valign == VALIGN_BOTTOM)
13712 vp_door->y += vp_window->height - vp_door->height;
13717 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13721 struct XYTileSize *dst, *src;
13724 editor_buttons_xy[] =
13727 &editor.button.element_left, &editor.palette.element_left,
13728 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13731 &editor.button.element_middle, &editor.palette.element_middle,
13732 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13735 &editor.button.element_right, &editor.palette.element_right,
13736 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13743 // set default position for element buttons to element graphics
13744 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13746 if ((*editor_buttons_xy[i].dst).x == -1 &&
13747 (*editor_buttons_xy[i].dst).y == -1)
13749 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13751 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13753 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13757 // adjust editor palette rows and columns if specified to be dynamic
13759 if (editor.palette.cols == -1)
13761 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13762 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13763 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13765 editor.palette.cols = (vp_width - sc_width) / bt_width;
13767 if (editor.palette.x == -1)
13769 int palette_width = editor.palette.cols * bt_width + sc_width;
13771 editor.palette.x = (vp_width - palette_width) / 2;
13775 if (editor.palette.rows == -1)
13777 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13778 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13779 int tx_height = getFontHeight(FONT_TEXT_2);
13781 editor.palette.rows = (vp_height - tx_height) / bt_height;
13783 if (editor.palette.y == -1)
13785 int palette_height = editor.palette.rows * bt_height + tx_height;
13787 editor.palette.y = (vp_height - palette_height) / 2;
13792 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13793 boolean initialize)
13795 // special case: check if network and preview player positions are redefined,
13796 // to compare this later against the main menu level preview being redefined
13797 struct TokenIntPtrInfo menu_config_players[] =
13799 { "main.network_players.x", &menu.main.network_players.redefined },
13800 { "main.network_players.y", &menu.main.network_players.redefined },
13801 { "main.preview_players.x", &menu.main.preview_players.redefined },
13802 { "main.preview_players.y", &menu.main.preview_players.redefined },
13803 { "preview.x", &preview.redefined },
13804 { "preview.y", &preview.redefined }
13810 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13811 *menu_config_players[i].value = FALSE;
13815 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13816 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13817 *menu_config_players[i].value = TRUE;
13821 static void InitMenuDesignSettings_PreviewPlayers(void)
13823 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13826 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13828 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13831 static void LoadMenuDesignSettingsFromFilename(char *filename)
13833 static struct TitleFadingInfo tfi;
13834 static struct TitleMessageInfo tmi;
13835 static struct TokenInfo title_tokens[] =
13837 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13838 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13839 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13840 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13841 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13845 static struct TokenInfo titlemessage_tokens[] =
13847 { TYPE_INTEGER, &tmi.x, ".x" },
13848 { TYPE_INTEGER, &tmi.y, ".y" },
13849 { TYPE_INTEGER, &tmi.width, ".width" },
13850 { TYPE_INTEGER, &tmi.height, ".height" },
13851 { TYPE_INTEGER, &tmi.chars, ".chars" },
13852 { TYPE_INTEGER, &tmi.lines, ".lines" },
13853 { TYPE_INTEGER, &tmi.align, ".align" },
13854 { TYPE_INTEGER, &tmi.valign, ".valign" },
13855 { TYPE_INTEGER, &tmi.font, ".font" },
13856 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13857 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13858 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13859 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13860 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13861 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13862 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13863 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13864 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13870 struct TitleFadingInfo *info;
13875 // initialize first titles from "enter screen" definitions, if defined
13876 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13877 { &title_first_default, "menu.enter_screen.TITLE" },
13879 // initialize title screens from "next screen" definitions, if defined
13880 { &title_initial_default, "menu.next_screen.TITLE" },
13881 { &title_default, "menu.next_screen.TITLE" },
13887 struct TitleMessageInfo *array;
13890 titlemessage_arrays[] =
13892 // initialize first titles from "enter screen" definitions, if defined
13893 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13894 { titlescreen_first, "menu.enter_screen.TITLE" },
13895 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13896 { titlemessage_first, "menu.enter_screen.TITLE" },
13898 // initialize titles from "next screen" definitions, if defined
13899 { titlescreen_initial, "menu.next_screen.TITLE" },
13900 { titlescreen, "menu.next_screen.TITLE" },
13901 { titlemessage_initial, "menu.next_screen.TITLE" },
13902 { titlemessage, "menu.next_screen.TITLE" },
13904 // overwrite titles with title definitions, if defined
13905 { titlescreen_initial_first, "[title_initial]" },
13906 { titlescreen_first, "[title]" },
13907 { titlemessage_initial_first, "[title_initial]" },
13908 { titlemessage_first, "[title]" },
13910 { titlescreen_initial, "[title_initial]" },
13911 { titlescreen, "[title]" },
13912 { titlemessage_initial, "[title_initial]" },
13913 { titlemessage, "[title]" },
13915 // overwrite titles with title screen/message definitions, if defined
13916 { titlescreen_initial_first, "[titlescreen_initial]" },
13917 { titlescreen_first, "[titlescreen]" },
13918 { titlemessage_initial_first, "[titlemessage_initial]" },
13919 { titlemessage_first, "[titlemessage]" },
13921 { titlescreen_initial, "[titlescreen_initial]" },
13922 { titlescreen, "[titlescreen]" },
13923 { titlemessage_initial, "[titlemessage_initial]" },
13924 { titlemessage, "[titlemessage]" },
13928 SetupFileHash *setup_file_hash;
13931 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13934 // the following initializes hierarchical values from dynamic configuration
13936 // special case: initialize with default values that may be overwritten
13937 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13938 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13940 struct TokenIntPtrInfo menu_config[] =
13942 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13943 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13944 { "menu.list_size", &menu.list_size[i] }
13947 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13949 char *token = menu_config[j].token;
13950 char *value = getHashEntry(setup_file_hash, token);
13953 *menu_config[j].value = get_integer_from_string(value);
13957 // special case: initialize with default values that may be overwritten
13958 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13959 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13961 struct TokenIntPtrInfo menu_config[] =
13963 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13964 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13965 { "menu.list_size.INFO", &menu.list_size_info[i] },
13966 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13967 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13970 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13972 char *token = menu_config[j].token;
13973 char *value = getHashEntry(setup_file_hash, token);
13976 *menu_config[j].value = get_integer_from_string(value);
13980 // special case: initialize with default values that may be overwritten
13981 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13982 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13984 struct TokenIntPtrInfo menu_config[] =
13986 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13987 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13990 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13992 char *token = menu_config[j].token;
13993 char *value = getHashEntry(setup_file_hash, token);
13996 *menu_config[j].value = get_integer_from_string(value);
14000 // special case: initialize with default values that may be overwritten
14001 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
14002 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
14004 struct TokenIntPtrInfo menu_config[] =
14006 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
14007 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
14008 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
14009 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
14010 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
14011 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
14012 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
14013 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
14014 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
14015 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
14018 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
14020 char *token = menu_config[j].token;
14021 char *value = getHashEntry(setup_file_hash, token);
14024 *menu_config[j].value = get_integer_from_string(value);
14028 // special case: initialize with default values that may be overwritten
14029 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
14030 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
14032 struct TokenIntPtrInfo menu_config[] =
14034 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
14035 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
14036 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
14037 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
14038 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
14039 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
14040 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
14041 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
14042 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
14045 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
14047 char *token = menu_config[j].token;
14048 char *value = getHashEntry(setup_file_hash, token);
14051 *menu_config[j].value = get_token_parameter_value(token, value);
14055 // special case: initialize with default values that may be overwritten
14056 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
14057 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
14061 char *token_prefix;
14062 struct RectWithBorder *struct_ptr;
14066 { "viewport.window", &viewport.window[i] },
14067 { "viewport.playfield", &viewport.playfield[i] },
14068 { "viewport.door_1", &viewport.door_1[i] },
14069 { "viewport.door_2", &viewport.door_2[i] }
14072 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
14074 struct TokenIntPtrInfo vp_config[] =
14076 { ".x", &vp_struct[j].struct_ptr->x },
14077 { ".y", &vp_struct[j].struct_ptr->y },
14078 { ".width", &vp_struct[j].struct_ptr->width },
14079 { ".height", &vp_struct[j].struct_ptr->height },
14080 { ".min_width", &vp_struct[j].struct_ptr->min_width },
14081 { ".min_height", &vp_struct[j].struct_ptr->min_height },
14082 { ".max_width", &vp_struct[j].struct_ptr->max_width },
14083 { ".max_height", &vp_struct[j].struct_ptr->max_height },
14084 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
14085 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
14086 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
14087 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
14088 { ".border_left", &vp_struct[j].struct_ptr->border_left },
14089 { ".border_right", &vp_struct[j].struct_ptr->border_right },
14090 { ".border_top", &vp_struct[j].struct_ptr->border_top },
14091 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
14092 { ".border_size", &vp_struct[j].struct_ptr->border_size },
14093 { ".align_size", &vp_struct[j].struct_ptr->align_size },
14094 { ".align", &vp_struct[j].struct_ptr->align },
14095 { ".valign", &vp_struct[j].struct_ptr->valign }
14098 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
14100 char *token = getStringCat2(vp_struct[j].token_prefix,
14101 vp_config[k].token);
14102 char *value = getHashEntry(setup_file_hash, token);
14105 *vp_config[k].value = get_token_parameter_value(token, value);
14112 // special case: initialize with default values that may be overwritten
14113 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
14114 for (i = 0; title_info[i].info != NULL; i++)
14116 struct TitleFadingInfo *info = title_info[i].info;
14117 char *base_token = title_info[i].text;
14119 for (j = 0; title_tokens[j].type != -1; j++)
14121 char *token = getStringCat2(base_token, title_tokens[j].text);
14122 char *value = getHashEntry(setup_file_hash, token);
14126 int parameter_value = get_token_parameter_value(token, value);
14130 *(int *)title_tokens[j].value = (int)parameter_value;
14139 // special case: initialize with default values that may be overwritten
14140 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
14141 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
14143 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
14144 char *base_token = titlemessage_arrays[i].text;
14146 for (j = 0; titlemessage_tokens[j].type != -1; j++)
14148 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
14149 char *value = getHashEntry(setup_file_hash, token);
14153 int parameter_value = get_token_parameter_value(token, value);
14155 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
14159 if (titlemessage_tokens[j].type == TYPE_INTEGER)
14160 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
14162 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
14172 // read (and overwrite with) values that may be specified in config file
14173 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
14175 // special case: check if network and preview player positions are redefined
14176 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
14178 freeSetupFileHash(setup_file_hash);
14181 void LoadMenuDesignSettings(void)
14183 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14185 InitMenuDesignSettings_Static();
14186 InitMenuDesignSettings_SpecialPreProcessing();
14187 InitMenuDesignSettings_PreviewPlayers();
14189 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
14191 // first look for special settings configured in level series config
14192 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
14194 if (fileExists(filename_base))
14195 LoadMenuDesignSettingsFromFilename(filename_base);
14198 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
14200 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14201 LoadMenuDesignSettingsFromFilename(filename_local);
14203 InitMenuDesignSettings_SpecialPostProcessing();
14206 void LoadMenuDesignSettings_AfterGraphics(void)
14208 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14211 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14212 boolean ignore_defaults)
14216 for (i = 0; sound_config_vars[i].token != NULL; i++)
14218 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14220 // (ignore definitions set to "[DEFAULT]" which are already initialized)
14221 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14225 *sound_config_vars[i].value =
14226 get_token_parameter_value(sound_config_vars[i].token, value);
14230 void InitSoundSettings_Static(void)
14232 // always start with reliable default values from static default config
14233 InitSoundSettings_FromHash(sound_config_hash, FALSE);
14236 static void LoadSoundSettingsFromFilename(char *filename)
14238 SetupFileHash *setup_file_hash;
14240 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14243 // read (and overwrite with) values that may be specified in config file
14244 InitSoundSettings_FromHash(setup_file_hash, TRUE);
14246 freeSetupFileHash(setup_file_hash);
14249 void LoadSoundSettings(void)
14251 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14253 InitSoundSettings_Static();
14255 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14257 // first look for special settings configured in level series config
14258 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14260 if (fileExists(filename_base))
14261 LoadSoundSettingsFromFilename(filename_base);
14264 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14266 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14267 LoadSoundSettingsFromFilename(filename_local);
14270 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14272 char *filename = getEditorSetupFilename();
14273 SetupFileList *setup_file_list, *list;
14274 SetupFileHash *element_hash;
14275 int num_unknown_tokens = 0;
14278 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14281 element_hash = newSetupFileHash();
14283 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14284 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14286 // determined size may be larger than needed (due to unknown elements)
14288 for (list = setup_file_list; list != NULL; list = list->next)
14291 // add space for up to 3 more elements for padding that may be needed
14292 *num_elements += 3;
14294 // free memory for old list of elements, if needed
14295 checked_free(*elements);
14297 // allocate memory for new list of elements
14298 *elements = checked_malloc(*num_elements * sizeof(int));
14301 for (list = setup_file_list; list != NULL; list = list->next)
14303 char *value = getHashEntry(element_hash, list->token);
14305 if (value == NULL) // try to find obsolete token mapping
14307 char *mapped_token = get_mapped_token(list->token);
14309 if (mapped_token != NULL)
14311 value = getHashEntry(element_hash, mapped_token);
14313 free(mapped_token);
14319 (*elements)[(*num_elements)++] = atoi(value);
14323 if (num_unknown_tokens == 0)
14326 Warn("unknown token(s) found in config file:");
14327 Warn("- config file: '%s'", filename);
14329 num_unknown_tokens++;
14332 Warn("- token: '%s'", list->token);
14336 if (num_unknown_tokens > 0)
14339 while (*num_elements % 4) // pad with empty elements, if needed
14340 (*elements)[(*num_elements)++] = EL_EMPTY;
14342 freeSetupFileList(setup_file_list);
14343 freeSetupFileHash(element_hash);
14346 for (i = 0; i < *num_elements; i++)
14347 Debug("editor", "element '%s' [%d]\n",
14348 element_info[(*elements)[i]].token_name, (*elements)[i]);
14352 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14355 SetupFileHash *setup_file_hash = NULL;
14356 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14357 char *filename_music, *filename_prefix, *filename_info;
14363 token_to_value_ptr[] =
14365 { "title_header", &tmp_music_file_info.title_header },
14366 { "artist_header", &tmp_music_file_info.artist_header },
14367 { "album_header", &tmp_music_file_info.album_header },
14368 { "year_header", &tmp_music_file_info.year_header },
14369 { "played_header", &tmp_music_file_info.played_header },
14371 { "title", &tmp_music_file_info.title },
14372 { "artist", &tmp_music_file_info.artist },
14373 { "album", &tmp_music_file_info.album },
14374 { "year", &tmp_music_file_info.year },
14375 { "played", &tmp_music_file_info.played },
14381 filename_music = (is_sound ? getCustomSoundFilename(basename) :
14382 getCustomMusicFilename(basename));
14384 if (filename_music == NULL)
14387 // ---------- try to replace file extension ----------
14389 filename_prefix = getStringCopy(filename_music);
14390 if (strrchr(filename_prefix, '.') != NULL)
14391 *strrchr(filename_prefix, '.') = '\0';
14392 filename_info = getStringCat2(filename_prefix, ".txt");
14394 if (fileExists(filename_info))
14395 setup_file_hash = loadSetupFileHash(filename_info);
14397 free(filename_prefix);
14398 free(filename_info);
14400 if (setup_file_hash == NULL)
14402 // ---------- try to add file extension ----------
14404 filename_prefix = getStringCopy(filename_music);
14405 filename_info = getStringCat2(filename_prefix, ".txt");
14407 if (fileExists(filename_info))
14408 setup_file_hash = loadSetupFileHash(filename_info);
14410 free(filename_prefix);
14411 free(filename_info);
14414 if (setup_file_hash == NULL)
14417 // ---------- music file info found ----------
14419 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14421 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14423 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14425 *token_to_value_ptr[i].value_ptr =
14426 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14429 tmp_music_file_info.basename = getStringCopy(basename);
14430 tmp_music_file_info.music = music;
14431 tmp_music_file_info.is_sound = is_sound;
14433 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14434 *new_music_file_info = tmp_music_file_info;
14436 return new_music_file_info;
14439 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14441 return get_music_file_info_ext(basename, music, FALSE);
14444 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14446 return get_music_file_info_ext(basename, sound, TRUE);
14449 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14450 char *basename, boolean is_sound)
14452 for (; list != NULL; list = list->next)
14453 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14459 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14461 return music_info_listed_ext(list, basename, FALSE);
14464 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14466 return music_info_listed_ext(list, basename, TRUE);
14469 void LoadMusicInfo(void)
14471 int num_music_noconf = getMusicListSize_NoConf();
14472 int num_music = getMusicListSize();
14473 int num_sounds = getSoundListSize();
14474 struct FileInfo *music, *sound;
14475 struct MusicFileInfo *next, **new;
14479 while (music_file_info != NULL)
14481 next = music_file_info->next;
14483 checked_free(music_file_info->basename);
14485 checked_free(music_file_info->title_header);
14486 checked_free(music_file_info->artist_header);
14487 checked_free(music_file_info->album_header);
14488 checked_free(music_file_info->year_header);
14489 checked_free(music_file_info->played_header);
14491 checked_free(music_file_info->title);
14492 checked_free(music_file_info->artist);
14493 checked_free(music_file_info->album);
14494 checked_free(music_file_info->year);
14495 checked_free(music_file_info->played);
14497 free(music_file_info);
14499 music_file_info = next;
14502 new = &music_file_info;
14504 // get (configured or unconfigured) music file info for all levels
14505 for (i = leveldir_current->first_level;
14506 i <= leveldir_current->last_level; i++)
14510 if (levelset.music[i] != MUS_UNDEFINED)
14512 // get music file info for configured level music
14513 music_nr = levelset.music[i];
14515 else if (num_music_noconf > 0)
14517 // get music file info for unconfigured level music
14518 int level_pos = i - leveldir_current->first_level;
14520 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14527 char *basename = getMusicInfoEntryFilename(music_nr);
14529 if (basename == NULL)
14532 if (!music_info_listed(music_file_info, basename))
14534 *new = get_music_file_info(basename, music_nr);
14537 new = &(*new)->next;
14541 // get music file info for all remaining configured music files
14542 for (i = 0; i < num_music; i++)
14544 music = getMusicListEntry(i);
14546 if (music->filename == NULL)
14549 if (strEqual(music->filename, UNDEFINED_FILENAME))
14552 // a configured file may be not recognized as music
14553 if (!FileIsMusic(music->filename))
14556 if (!music_info_listed(music_file_info, music->filename))
14558 *new = get_music_file_info(music->filename, i);
14561 new = &(*new)->next;
14565 // get sound file info for all configured sound files
14566 for (i = 0; i < num_sounds; i++)
14568 sound = getSoundListEntry(i);
14570 if (sound->filename == NULL)
14573 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14576 // a configured file may be not recognized as sound
14577 if (!FileIsSound(sound->filename))
14580 if (!sound_info_listed(music_file_info, sound->filename))
14582 *new = get_sound_file_info(sound->filename, i);
14584 new = &(*new)->next;
14588 // add pointers to previous list nodes
14590 struct MusicFileInfo *node = music_file_info;
14592 while (node != NULL)
14595 node->next->prev = node;
14601 static void add_helpanim_entry(int element, int action, int direction,
14602 int delay, int *num_list_entries)
14604 struct HelpAnimInfo *new_list_entry;
14605 (*num_list_entries)++;
14608 checked_realloc(helpanim_info,
14609 *num_list_entries * sizeof(struct HelpAnimInfo));
14610 new_list_entry = &helpanim_info[*num_list_entries - 1];
14612 new_list_entry->element = element;
14613 new_list_entry->action = action;
14614 new_list_entry->direction = direction;
14615 new_list_entry->delay = delay;
14618 static void print_unknown_token(char *filename, char *token, int token_nr)
14623 Warn("unknown token(s) found in config file:");
14624 Warn("- config file: '%s'", filename);
14627 Warn("- token: '%s'", token);
14630 static void print_unknown_token_end(int token_nr)
14636 void LoadHelpAnimInfo(void)
14638 char *filename = getHelpAnimFilename();
14639 SetupFileList *setup_file_list = NULL, *list;
14640 SetupFileHash *element_hash, *action_hash, *direction_hash;
14641 int num_list_entries = 0;
14642 int num_unknown_tokens = 0;
14645 if (fileExists(filename))
14646 setup_file_list = loadSetupFileList(filename);
14648 if (setup_file_list == NULL)
14650 // use reliable default values from static configuration
14651 SetupFileList *insert_ptr;
14653 insert_ptr = setup_file_list =
14654 newSetupFileList(helpanim_config[0].token,
14655 helpanim_config[0].value);
14657 for (i = 1; helpanim_config[i].token; i++)
14658 insert_ptr = addListEntry(insert_ptr,
14659 helpanim_config[i].token,
14660 helpanim_config[i].value);
14663 element_hash = newSetupFileHash();
14664 action_hash = newSetupFileHash();
14665 direction_hash = newSetupFileHash();
14667 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14668 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14670 for (i = 0; i < NUM_ACTIONS; i++)
14671 setHashEntry(action_hash, element_action_info[i].suffix,
14672 i_to_a(element_action_info[i].value));
14674 // do not store direction index (bit) here, but direction value!
14675 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14676 setHashEntry(direction_hash, element_direction_info[i].suffix,
14677 i_to_a(1 << element_direction_info[i].value));
14679 for (list = setup_file_list; list != NULL; list = list->next)
14681 char *element_token, *action_token, *direction_token;
14682 char *element_value, *action_value, *direction_value;
14683 int delay = atoi(list->value);
14685 if (strEqual(list->token, "end"))
14687 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14692 /* first try to break element into element/action/direction parts;
14693 if this does not work, also accept combined "element[.act][.dir]"
14694 elements (like "dynamite.active"), which are unique elements */
14696 if (strchr(list->token, '.') == NULL) // token contains no '.'
14698 element_value = getHashEntry(element_hash, list->token);
14699 if (element_value != NULL) // element found
14700 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14701 &num_list_entries);
14704 // no further suffixes found -- this is not an element
14705 print_unknown_token(filename, list->token, num_unknown_tokens++);
14711 // token has format "<prefix>.<something>"
14713 action_token = strchr(list->token, '.'); // suffix may be action ...
14714 direction_token = action_token; // ... or direction
14716 element_token = getStringCopy(list->token);
14717 *strchr(element_token, '.') = '\0';
14719 element_value = getHashEntry(element_hash, element_token);
14721 if (element_value == NULL) // this is no element
14723 element_value = getHashEntry(element_hash, list->token);
14724 if (element_value != NULL) // combined element found
14725 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14726 &num_list_entries);
14728 print_unknown_token(filename, list->token, num_unknown_tokens++);
14730 free(element_token);
14735 action_value = getHashEntry(action_hash, action_token);
14737 if (action_value != NULL) // action found
14739 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14740 &num_list_entries);
14742 free(element_token);
14747 direction_value = getHashEntry(direction_hash, direction_token);
14749 if (direction_value != NULL) // direction found
14751 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14752 &num_list_entries);
14754 free(element_token);
14759 if (strchr(action_token + 1, '.') == NULL)
14761 // no further suffixes found -- this is not an action nor direction
14763 element_value = getHashEntry(element_hash, list->token);
14764 if (element_value != NULL) // combined element found
14765 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14766 &num_list_entries);
14768 print_unknown_token(filename, list->token, num_unknown_tokens++);
14770 free(element_token);
14775 // token has format "<prefix>.<suffix>.<something>"
14777 direction_token = strchr(action_token + 1, '.');
14779 action_token = getStringCopy(action_token);
14780 *strchr(action_token + 1, '.') = '\0';
14782 action_value = getHashEntry(action_hash, action_token);
14784 if (action_value == NULL) // this is no action
14786 element_value = getHashEntry(element_hash, list->token);
14787 if (element_value != NULL) // combined element found
14788 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14789 &num_list_entries);
14791 print_unknown_token(filename, list->token, num_unknown_tokens++);
14793 free(element_token);
14794 free(action_token);
14799 direction_value = getHashEntry(direction_hash, direction_token);
14801 if (direction_value != NULL) // direction found
14803 add_helpanim_entry(atoi(element_value), atoi(action_value),
14804 atoi(direction_value), delay, &num_list_entries);
14806 free(element_token);
14807 free(action_token);
14812 // this is no direction
14814 element_value = getHashEntry(element_hash, list->token);
14815 if (element_value != NULL) // combined element found
14816 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14817 &num_list_entries);
14819 print_unknown_token(filename, list->token, num_unknown_tokens++);
14821 free(element_token);
14822 free(action_token);
14825 print_unknown_token_end(num_unknown_tokens);
14827 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14828 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14830 freeSetupFileList(setup_file_list);
14831 freeSetupFileHash(element_hash);
14832 freeSetupFileHash(action_hash);
14833 freeSetupFileHash(direction_hash);
14836 for (i = 0; i < num_list_entries; i++)
14837 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14838 EL_NAME(helpanim_info[i].element),
14839 helpanim_info[i].element,
14840 helpanim_info[i].action,
14841 helpanim_info[i].direction,
14842 helpanim_info[i].delay);
14846 void LoadHelpTextInfo(void)
14848 char *filename = getHelpTextFilename();
14851 if (helptext_info != NULL)
14853 freeSetupFileHash(helptext_info);
14854 helptext_info = NULL;
14857 if (fileExists(filename))
14858 helptext_info = loadSetupFileHash(filename);
14860 if (helptext_info == NULL)
14862 // use reliable default values from static configuration
14863 helptext_info = newSetupFileHash();
14865 for (i = 0; helptext_config[i].token; i++)
14866 setHashEntry(helptext_info,
14867 helptext_config[i].token,
14868 helptext_config[i].value);
14872 BEGIN_HASH_ITERATION(helptext_info, itr)
14874 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14875 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14877 END_HASH_ITERATION(hash, itr)
14882 // ----------------------------------------------------------------------------
14884 // ----------------------------------------------------------------------------
14886 #define MAX_NUM_CONVERT_LEVELS 1000
14888 void ConvertLevels(void)
14890 static LevelDirTree *convert_leveldir = NULL;
14891 static int convert_level_nr = -1;
14892 static int num_levels_handled = 0;
14893 static int num_levels_converted = 0;
14894 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14897 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14898 global.convert_leveldir);
14900 if (convert_leveldir == NULL)
14901 Fail("no such level identifier: '%s'", global.convert_leveldir);
14903 leveldir_current = convert_leveldir;
14905 if (global.convert_level_nr != -1)
14907 convert_leveldir->first_level = global.convert_level_nr;
14908 convert_leveldir->last_level = global.convert_level_nr;
14911 convert_level_nr = convert_leveldir->first_level;
14913 PrintLine("=", 79);
14914 Print("Converting levels\n");
14915 PrintLine("-", 79);
14916 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14917 Print("Level series name: '%s'\n", convert_leveldir->name);
14918 Print("Level series author: '%s'\n", convert_leveldir->author);
14919 Print("Number of levels: %d\n", convert_leveldir->levels);
14920 PrintLine("=", 79);
14923 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14924 levels_failed[i] = FALSE;
14926 while (convert_level_nr <= convert_leveldir->last_level)
14928 char *level_filename;
14931 level_nr = convert_level_nr++;
14933 Print("Level %03d: ", level_nr);
14935 LoadLevel(level_nr);
14936 if (level.no_level_file || level.no_valid_file)
14938 Print("(no level)\n");
14942 Print("converting level ... ");
14945 // special case: conversion of some EMC levels as requested by ACME
14946 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14949 level_filename = getDefaultLevelFilename(level_nr);
14950 new_level = !fileExists(level_filename);
14954 SaveLevel(level_nr);
14956 num_levels_converted++;
14958 Print("converted.\n");
14962 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14963 levels_failed[level_nr] = TRUE;
14965 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14968 num_levels_handled++;
14972 PrintLine("=", 79);
14973 Print("Number of levels handled: %d\n", num_levels_handled);
14974 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14975 (num_levels_handled ?
14976 num_levels_converted * 100 / num_levels_handled : 0));
14977 PrintLine("-", 79);
14978 Print("Summary (for automatic parsing by scripts):\n");
14979 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14980 convert_leveldir->identifier, num_levels_converted,
14981 num_levels_handled,
14982 (num_levels_handled ?
14983 num_levels_converted * 100 / num_levels_handled : 0));
14985 if (num_levels_handled != num_levels_converted)
14987 Print(", FAILED:");
14988 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14989 if (levels_failed[i])
14994 PrintLine("=", 79);
14996 CloseAllAndExit(0);
15000 // ----------------------------------------------------------------------------
15001 // create and save images for use in level sketches (raw BMP format)
15002 // ----------------------------------------------------------------------------
15004 void CreateLevelSketchImages(void)
15010 InitElementPropertiesGfxElement();
15012 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
15013 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
15015 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
15017 int element = getMappedElement(i);
15018 char basename1[16];
15019 char basename2[16];
15023 sprintf(basename1, "%04d.bmp", i);
15024 sprintf(basename2, "%04ds.bmp", i);
15026 filename1 = getPath2(global.create_sketch_images_dir, basename1);
15027 filename2 = getPath2(global.create_sketch_images_dir, basename2);
15029 DrawSizedElement(0, 0, element, TILESIZE);
15030 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
15032 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
15033 Fail("cannot save level sketch image file '%s'", filename1);
15035 DrawSizedElement(0, 0, element, MINI_TILESIZE);
15036 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
15038 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
15039 Fail("cannot save level sketch image file '%s'", filename2);
15044 // create corresponding SQL statements (for normal and small images)
15047 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
15048 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
15051 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
15052 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
15054 // optional: create content for forum level sketch demonstration post
15056 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
15059 FreeBitmap(bitmap1);
15060 FreeBitmap(bitmap2);
15063 fprintf(stderr, "\n");
15065 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
15067 CloseAllAndExit(0);
15071 // ----------------------------------------------------------------------------
15072 // create and save images for element collecting animations (raw BMP format)
15073 // ----------------------------------------------------------------------------
15075 static boolean createCollectImage(int element)
15077 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
15080 void CreateCollectElementImages(void)
15084 int anim_frames = num_steps - 1;
15085 int tile_size = TILESIZE;
15086 int anim_width = tile_size * anim_frames;
15087 int anim_height = tile_size;
15088 int num_collect_images = 0;
15089 int pos_collect_images = 0;
15091 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15092 if (createCollectImage(i))
15093 num_collect_images++;
15095 Info("Creating %d element collecting animation images ...",
15096 num_collect_images);
15098 int dst_width = anim_width * 2;
15099 int dst_height = anim_height * num_collect_images / 2;
15100 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
15101 char *basename_bmp = "RocksCollect.bmp";
15102 char *basename_png = "RocksCollect.png";
15103 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
15104 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
15105 int len_filename_bmp = strlen(filename_bmp);
15106 int len_filename_png = strlen(filename_png);
15107 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
15108 char cmd_convert[max_command_len];
15110 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
15114 // force using RGBA surface for destination bitmap
15115 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
15116 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
15118 dst_bitmap->surface =
15119 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15121 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15123 if (!createCollectImage(i))
15126 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
15127 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
15128 int graphic = el2img(i);
15129 char *token_name = element_info[i].token_name;
15130 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
15131 Bitmap *src_bitmap;
15134 Info("- creating collecting image for '%s' ...", token_name);
15136 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
15138 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
15139 tile_size, tile_size, 0, 0);
15141 // force using RGBA surface for temporary bitmap (using transparent black)
15142 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
15143 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
15145 tmp_bitmap->surface =
15146 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15148 tmp_bitmap->surface_masked = tmp_bitmap->surface;
15150 for (j = 0; j < anim_frames; j++)
15152 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
15153 int frame_size = frame_size_final * num_steps;
15154 int offset = (tile_size - frame_size_final) / 2;
15155 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
15157 while (frame_size > frame_size_final)
15161 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
15163 FreeBitmap(frame_bitmap);
15165 frame_bitmap = half_bitmap;
15168 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
15169 frame_size_final, frame_size_final,
15170 dst_x + j * tile_size + offset, dst_y + offset);
15172 FreeBitmap(frame_bitmap);
15175 tmp_bitmap->surface_masked = NULL;
15177 FreeBitmap(tmp_bitmap);
15179 pos_collect_images++;
15182 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
15183 Fail("cannot save element collecting image file '%s'", filename_bmp);
15185 FreeBitmap(dst_bitmap);
15187 Info("Converting image file from BMP to PNG ...");
15189 if (system(cmd_convert) != 0)
15190 Fail("converting image file failed");
15192 unlink(filename_bmp);
15196 CloseAllAndExit(0);
15200 // ----------------------------------------------------------------------------
15201 // create and save images for custom and group elements (raw BMP format)
15202 // ----------------------------------------------------------------------------
15204 void CreateCustomElementImages(char *directory)
15206 char *src_basename = "RocksCE-template.ilbm";
15207 char *dst_basename = "RocksCE.bmp";
15208 char *src_filename = getPath2(directory, src_basename);
15209 char *dst_filename = getPath2(directory, dst_basename);
15210 Bitmap *src_bitmap;
15212 int yoffset_ce = 0;
15213 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15216 InitVideoDefaults();
15218 ReCreateBitmap(&backbuffer, video.width, video.height);
15220 src_bitmap = LoadImage(src_filename);
15222 bitmap = CreateBitmap(TILEX * 16 * 2,
15223 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15226 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15233 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15234 TILEX * x, TILEY * y + yoffset_ce);
15236 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15238 TILEX * x + TILEX * 16,
15239 TILEY * y + yoffset_ce);
15241 for (j = 2; j >= 0; j--)
15245 BlitBitmap(src_bitmap, bitmap,
15246 TILEX + c * 7, 0, 6, 10,
15247 TILEX * x + 6 + j * 7,
15248 TILEY * y + 11 + yoffset_ce);
15250 BlitBitmap(src_bitmap, bitmap,
15251 TILEX + c * 8, TILEY, 6, 10,
15252 TILEX * 16 + TILEX * x + 6 + j * 8,
15253 TILEY * y + 10 + yoffset_ce);
15259 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15266 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15267 TILEX * x, TILEY * y + yoffset_ge);
15269 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15271 TILEX * x + TILEX * 16,
15272 TILEY * y + yoffset_ge);
15274 for (j = 1; j >= 0; j--)
15278 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15279 TILEX * x + 6 + j * 10,
15280 TILEY * y + 11 + yoffset_ge);
15282 BlitBitmap(src_bitmap, bitmap,
15283 TILEX + c * 8, TILEY + 12, 6, 10,
15284 TILEX * 16 + TILEX * x + 10 + j * 8,
15285 TILEY * y + 10 + yoffset_ge);
15291 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15292 Fail("cannot save CE graphics file '%s'", dst_filename);
15294 FreeBitmap(bitmap);
15296 CloseAllAndExit(0);